Skip to content

Commit

Permalink
[zero-dim] Support 0-d for kthvalue and mode (#49340)
Browse files Browse the repository at this point in the history
* add 0-d support for paddle.kthvalue

* add 0-d support for paddle.mode

* fix coverage test for device

* fix check-bug in windows

* change axis check from LT to LE

* add shape & value check for grad when input is 0d tensor
  • Loading branch information
zoooo0820 authored Jan 6, 2023
1 parent 6ec8dfd commit 292738f
Show file tree
Hide file tree
Showing 11 changed files with 180 additions and 29 deletions.
59 changes: 33 additions & 26 deletions paddle/phi/infermeta/unary.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1785,20 +1785,22 @@ void KthvalueInferMeta(const MetaTensor& x,
MetaConfig config) {
auto input_dims = x.dims();
const int& dim_size = input_dims.size();
PADDLE_ENFORCE_LT(axis,
PADDLE_ENFORCE_LE(axis,
dim_size,
phi::errors::InvalidArgument(
"the axis must be [-%d, %d), but received %d .",
dim_size,
dim_size,
axis));
PADDLE_ENFORCE_GE(axis,
-dim_size,
phi::errors::InvalidArgument(
"the axis must be [-%d, %d), but received %d .",
dim_size,
dim_size,
axis));
if (dim_size > 0) {
PADDLE_ENFORCE_GE(axis,
-dim_size,
phi::errors::InvalidArgument(
"the axis must be [-%d, %d), but received %d .",
dim_size,
dim_size,
axis));
}
if (axis < 0) axis += dim_size;
PADDLE_ENFORCE_GE(
k,
Expand All @@ -1807,9 +1809,9 @@ void KthvalueInferMeta(const MetaTensor& x,
"the k in the kthvalue must >= 1, but received %d .", k));
PADDLE_ENFORCE_GE(
input_dims.size(),
1,
phi::errors::InvalidArgument("input of kthvalue must have >= 1d shape"));
if (config.is_runtime) {
0,
phi::errors::InvalidArgument("input of kthvalue must have >= 0d shape"));
if (dim_size > 0 && config.is_runtime) {
PADDLE_ENFORCE_GE(
input_dims[axis],
k,
Expand All @@ -1822,7 +1824,7 @@ void KthvalueInferMeta(const MetaTensor& x,
for (int64_t i = 0; i < axis; i++) {
dimvec.emplace_back(input_dims[i]);
}
if (keepdim) {
if (keepdim && dim_size > 0) {
dimvec.emplace_back(static_cast<int64_t>(1));
}
for (int64_t i = axis + 1; i < dim_size; i++) {
Expand Down Expand Up @@ -2071,33 +2073,38 @@ void ModeInferMeta(const MetaTensor& x,
MetaTensor* indices) {
auto input_dims = x.dims();
const int& dim_size = input_dims.size();
PADDLE_ENFORCE_EQ(
(axis < dim_size) && (axis >= (-1 * dim_size)),
true,
errors::InvalidArgument(
"the axis of ModeOp must be [-%d, %d), but you set axis is %d",
dim_size,
dim_size,
axis));
PADDLE_ENFORCE_LE(axis,
dim_size,
phi::errors::InvalidArgument(
"the axis must be [-%d, %d), but received %d .",
dim_size,
dim_size,
axis));
if (dim_size > 0) {
PADDLE_ENFORCE_GE(axis,
-dim_size,
phi::errors::InvalidArgument(
"the axis must be [-%d, %d), but received %d .",
dim_size,
dim_size,
axis));
}
PADDLE_ENFORCE_GE(
input_dims.size(),
1,
errors::InvalidArgument("input of ModeOp must have >= 1d shape"));
0,
errors::InvalidArgument("input of ModeOp must have >= 0d shape"));
if (axis < 0) axis += dim_size;
std::vector<int64_t> dimvec;
for (int64_t i = 0; i < axis; i++) {
dimvec.emplace_back(input_dims[i]);
}
if (keepdim) {
if (keepdim && dim_size > 0) {
dimvec.emplace_back(static_cast<int64_t>(1));
}
for (int64_t i = axis + 1; i < dim_size; i++) {
dimvec.emplace_back(input_dims[i]);
}
DDim dims = phi::make_ddim(dimvec);
PADDLE_ENFORCE_GE(input_dims.size(),
1,
errors::InvalidArgument("input shape should >= 1d"));
out->set_dims(dims);
out->share_lod(x);
out->set_dtype(x.dtype());
Expand Down
10 changes: 9 additions & 1 deletion paddle/phi/kernels/cpu/kthvalue_grad_kernel.cc
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,14 @@ void KthvalueGradKernel(const Context& dev_ctx,
DenseTensor* d_x) {
auto in_dims = x.dims();
auto out_dims = indices.dims();
T* x_grad_data = dev_ctx.template Alloc<T>(d_x);

// For 0D Tensor
if (in_dims.size() == 0) {
phi::funcs::set_constant(dev_ctx, d_x, 1.0);
return;
}

axis = (axis < 0) ? (in_dims.size() + axis) : axis;
if (!keepdim) {
std::vector<int> tmp_out_shape;
Expand All @@ -67,7 +75,7 @@ void KthvalueGradKernel(const Context& dev_ctx,
}
out_dims = phi::make_ddim(tmp_out_shape);
}
T* x_grad_data = dev_ctx.template Alloc<T>(d_x);

if (axis == in_dims.size() - 1) {
const int64_t input_height =
phi::product(phi::slice_ddim(in_dims, 0, in_dims.size() - 1));
Expand Down
14 changes: 14 additions & 0 deletions paddle/phi/kernels/cpu/kthvalue_kernel.cc
Original file line number Diff line number Diff line change
Expand Up @@ -82,8 +82,22 @@ void KthvalueKernel(const Context& dev_ctx,
DenseTensor* indices) {
const auto& in_dims = x.dims();
if (axis < 0) axis += in_dims.size();

T* output_data = dev_ctx.template Alloc<T>(output);
int64_t* indices_data = dev_ctx.template Alloc<int64_t>(indices);
// For 0D Tensor
if (in_dims.size() == 0) {
PADDLE_ENFORCE_EQ(k,
1,
phi::errors::InvalidArgument(
"the k in the kthvalue must less equal than the "
"elemenents number of the input X, but received %d .",
k));

phi::Copy<Context>(dev_ctx, x, dev_ctx.GetPlace(), false, output);
phi::funcs::set_constant(dev_ctx, indices, 0);
return;
}
auto out_dims = output->dims();
if (axis == in_dims.size() - 1) {
const int64_t& input_height =
Expand Down
10 changes: 9 additions & 1 deletion paddle/phi/kernels/cpu/mode_grad_kernel.cc
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
#include "paddle/phi/backends/cpu/cpu_context.h"
#include "paddle/phi/core/kernel_registry.h"
#include "paddle/phi/core/tensor_utils.h"
#include "paddle/phi/kernels/funcs/math_function.h"
#include "paddle/phi/kernels/funcs/mode.h"

namespace phi {
Expand All @@ -32,9 +33,17 @@ void ModeGradKernel(const Context& dev_ctx,
auto in_dims = x.dims();
auto out_dims = indices.dims();

T* x_grad_data = dev_ctx.template Alloc<T>(x_grad);

// axis < 0, get the real axis
axis = (axis < 0) ? (in_dims.size() + axis) : axis;

// For 0D Tensor
if (in_dims.size() == 0) {
phi::funcs::set_constant(dev_ctx, x_grad, 1.0);
return;
}

if (!keepdim) {
std::vector<int> tmp_out_shape;
for (int i = 0; i < axis; i++) {
Expand All @@ -46,7 +55,6 @@ void ModeGradKernel(const Context& dev_ctx,
}
out_dims = phi::make_ddim(tmp_out_shape);
}
T* x_grad_data = dev_ctx.template Alloc<T>(x_grad);

if (axis == in_dims.size() - 1) {
// allocate the memory for the input_grad
Expand Down
8 changes: 8 additions & 0 deletions paddle/phi/kernels/cpu/mode_kernel.cc
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

#include "paddle/phi/backends/cpu/cpu_context.h"
#include "paddle/phi/core/kernel_registry.h"
#include "paddle/phi/kernels/funcs/math_function.h"
#include "paddle/phi/kernels/funcs/mode.h"

namespace phi {
Expand All @@ -34,6 +35,13 @@ void ModeKernel(const Context& dev_ctx,

T* output_data = dev_ctx.template Alloc<T>(out);
int64_t* indices_data = dev_ctx.template Alloc<int64_t>(indices);

if (in_dims.size() == 0) {
phi::Copy<Context>(dev_ctx, x, dev_ctx.GetPlace(), false, out);
phi::funcs::set_constant(dev_ctx, indices, 0);
return;
}

// if axis is not the last dim, transpose it to the last dim, do the
// calculation, then tranpose it back to original axis.
if (axis == in_dims.size() - 1) {
Expand Down
10 changes: 9 additions & 1 deletion paddle/phi/kernels/gpu/kthvalue_grad_kernel.cu
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

#include "paddle/phi/backends/gpu/gpu_context.h"
#include "paddle/phi/core/kernel_registry.h"
#include "paddle/phi/kernels/funcs/math_function.h"
#include "paddle/phi/kernels/funcs/top_k_function_cuda.h"

namespace phi {
Expand Down Expand Up @@ -43,8 +44,15 @@ void KthvalueGradKernel(const Context& dev_ctx,
DenseTensor* d_x) {
const auto& in_dims = x.dims();
auto out_dims = indices.dims();
if (axis < 0) axis += in_dims.size();
T* x_grad_data = dev_ctx.template Alloc<T>(d_x);
// For 0D Tensor
if (in_dims.size() == 0) {
phi::funcs::set_constant(dev_ctx, d_x, 1.0);
return;
}

if (axis < 0) axis += in_dims.size();

const T* out_grad_data = d_out.data<T>();
const int64_t* indices_data = indices.data<int64_t>();
int pre, n, post;
Expand Down
13 changes: 13 additions & 0 deletions paddle/phi/kernels/gpu/kthvalue_kernel.cu
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,19 @@ void KthvalueKernel(const Context& dev_ctx,
T* output_data = dev_ctx.template Alloc<T>(output);
int64_t* indices_data = dev_ctx.template Alloc<int64_t>(indices);

// For 0D Tensor
if (in_dims.size() == 0) {
PADDLE_ENFORCE_EQ(k,
1,
phi::errors::InvalidArgument(
"the k in the kthvalue must less equal than the "
"elemenents number of the input X, but received %d .",
k));

phi::Copy<Context>(dev_ctx, x, dev_ctx.GetPlace(), false, output);
phi::funcs::set_constant(dev_ctx, indices, 0);
return;
}
if (axis == in_dims.size() - 1) {
const int64_t& input_height =
phi::product(phi::slice_ddim(in_dims, 0, in_dims.size() - 1));
Expand Down
7 changes: 7 additions & 0 deletions paddle/phi/kernels/gpu/mode_grad_kernel.cu
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

#include "paddle/phi/backends/gpu/gpu_context.h"
#include "paddle/phi/core/kernel_registry.h"
#include "paddle/phi/kernels/funcs/math_function.h"
#include "paddle/phi/kernels/funcs/mode.h"

namespace phi {
Expand Down Expand Up @@ -61,6 +62,12 @@ void ModeGradKernel(const Context& dev_ctx,
const T* out_grad_data = out_grad.data<T>();
const int64_t* indices_data = indices.data<int64_t>();

// For 0D Tensor
if (in_dims.size() == 0) {
phi::funcs::set_constant(dev_ctx, x_grad, 1.0);
return;
}

int pre, n, post;
funcs::GetDims(in_dims, axis, &pre, &n, &post);

Expand Down
8 changes: 8 additions & 0 deletions paddle/phi/kernels/gpu/mode_kernel.cu
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

#include "paddle/phi/backends/gpu/gpu_context.h"
#include "paddle/phi/core/kernel_registry.h"
#include "paddle/phi/kernels/funcs/math_function.h"
#include "paddle/phi/kernels/funcs/mode.h"

namespace phi {
Expand All @@ -38,6 +39,13 @@ void ModeKernel(const Context& dev_ctx,
T* output_data = dev_ctx.template Alloc<T>(out);
int64_t* indices_data = dev_ctx.template Alloc<int64_t>(indices);

// For 0D Tensor
if (in_dims.size() == 0) {
phi::Copy<Context>(dev_ctx, x, dev_ctx.GetPlace(), false, out);
phi::funcs::set_constant(dev_ctx, indices, 0);
return;
}

if (axis == in_dims.size() - 1) {
const int64_t& input_height =
phi::product(phi::slice_ddim(in_dims, 0, in_dims.size() - 1));
Expand Down
6 changes: 6 additions & 0 deletions python/paddle/fluid/tests/unittests/test_kthvalue_op.py
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,12 @@ def test_dim_range_error():

self.assertRaises(ValueError, test_dim_range_error)

def test_k_error_0_dim_input():
x_0d = paddle.full([], 1)
x_0d.kthvalue(k=8)

self.assertRaises(ValueError, test_k_error_0_dim_input)


class TestModeOpInStatic(unittest.TestCase):
def setUp(self):
Expand Down
64 changes: 64 additions & 0 deletions python/paddle/fluid/tests/unittests/test_zero_dim_tensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -721,6 +721,48 @@ def test_scatter_nd(self):
self.assertEqual(out.numpy()[3], 2)
self.assertEqual(out.grad.shape, [5])

def test_kthvalue(self):
places = ['cpu']
if paddle.is_compiled_with_cuda():
places.append('gpu')
for place in places:
paddle.set_device(place)

x = paddle.randn(())
x.stop_gradient = False

out = paddle.kthvalue(x, 1)
out[0].backward()

# check shape of output value and indice
self.assertEqual(out[0].shape, [])
self.assertEqual(out[1].shape, [])

# check grad shape and value
self.assertEqual(x.grad.shape, [])
self.assertTrue(x.grad.numpy() == 1)

def test_mode(self):
places = ['cpu']
if paddle.is_compiled_with_cuda():
places.append('gpu')
for place in places:
paddle.set_device(place)

x = paddle.randn(())
x.stop_gradient = False

out = paddle.mode(x)
out[0].backward()

# check shape of output value and indice
self.assertEqual(out[0].shape, [])
self.assertEqual(out[1].shape, [])

# check grad shape and value
self.assertEqual(x.grad.shape, [])
self.assertTrue(x.grad.numpy() == 1)

def test_flatten(self):
x = paddle.rand([])
x.stop_gradient = False
Expand Down Expand Up @@ -1126,6 +1168,28 @@ def test_scatter_nd(self):
self.assertEqual(res[0].shape, (5,))
self.assertEqual(res[0][3], 2)

@prog_scope()
def test_kthvalue(self):
x = paddle.full([], 1, 'float32')
out = paddle.kthvalue(x, 1)
paddle.static.append_backward(out[0])

prog = paddle.static.default_main_program()
res = self.exe.run(prog, fetch_list=[out])
self.assertEqual(len(res[0].shape), 0)
self.assertEqual(len(res[0].shape), 0)

@prog_scope()
def test_mode(self):
x = paddle.full([], 1, 'float32')
out = paddle.mode(x)
paddle.static.append_backward(out[0])

prog = paddle.static.default_main_program()
res = self.exe.run(prog, fetch_list=[out])
self.assertEqual(len(res[0].shape), 0)
self.assertEqual(len(res[0].shape), 0)

@prog_scope()
def test_flatten(self):
x = paddle.full([], 1, 'float32')
Expand Down

0 comments on commit 292738f

Please sign in to comment.