Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[zero-dim] Support 0-d for kthvalue and mode #49340

Merged
57 changes: 32 additions & 25 deletions paddle/phi/infermeta/unary.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1751,13 +1751,15 @@ void KthvalueInferMeta(const MetaTensor& x,
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 @@ -1766,9 +1768,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 @@ -1781,7 +1783,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 @@ -2030,33 +2032,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_LT(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
52 changes: 52 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 @@ -712,6 +712,36 @@ 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()
self.assertEqual(out[0].shape, [])
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

动态图还需要测下反向grad的shape,把具体值也测下吧,因为比较固定

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done, thanks

self.assertEqual(out[1].shape, [])

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()
self.assertEqual(out[0].shape, [])
self.assertEqual(out[1].shape, [])


class TestSundryAPIStatic(unittest.TestCase):
def setUp(self):
Expand Down Expand Up @@ -914,6 +944,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)


# Use to test API whose zero-dim input tensors don't have grad and not need to test backward in OpTest.
class TestNoBackwardAPI(unittest.TestCase):
Expand Down