diff --git a/paddle/fluid/API.spec b/paddle/fluid/API.spec index 38b5746f9edc6ea87bcaf8e89ec9bf95b45c5769..671233b43b84ebd43611bc850a9780e86c8df7f5 100644 --- a/paddle/fluid/API.spec +++ b/paddle/fluid/API.spec @@ -188,11 +188,11 @@ paddle.fluid.layers.label_smooth (ArgSpec(args=['label', 'prior_dist', 'epsilon' paddle.fluid.layers.roi_pool (ArgSpec(args=['input', 'rois', 'pooled_height', 'pooled_width', 'spatial_scale'], varargs=None, keywords=None, defaults=(1, 1, 1.0)), ('document', '49368d724023a66b41b0071be41c0ba5')) paddle.fluid.layers.roi_align (ArgSpec(args=['input', 'rois', 'pooled_height', 'pooled_width', 'spatial_scale', 'sampling_ratio', 'name'], varargs=None, keywords=None, defaults=(1, 1, 1.0, -1, None)), ('document', '9a7a3b88a4fae41d58d3ca9b10ba0591')) paddle.fluid.layers.dice_loss (ArgSpec(args=['input', 'label', 'epsilon'], varargs=None, keywords=None, defaults=(1e-05,)), ('document', '7e8e4bf1f0f8612961ed113e8af8f0c5')) -paddle.fluid.layers.image_resize (ArgSpec(args=['input', 'out_shape', 'scale', 'name', 'resample', 'actual_shape', 'align_corners', 'align_mode'], varargs=None, keywords=None, defaults=(None, None, None, 'BILINEAR', None, True, 1)), ('document', '8cfc4f69dbbedb687b6c20732aa8f09e')) +paddle.fluid.layers.image_resize (ArgSpec(args=['input', 'out_shape', 'scale', 'name', 'resample', 'actual_shape', 'align_corners', 'align_mode'], varargs=None, keywords=None, defaults=(None, None, None, 'BILINEAR', None, True, 1)), ('document', '0e8567334d72a214c2e3ce0ce19e4d37')) paddle.fluid.layers.image_resize_short (ArgSpec(args=['input', 'out_short_len', 'resample'], varargs=None, keywords=None, defaults=('BILINEAR',)), ('document', 'bd97ebfe4bdf5110a5fcb8ecb626a447')) -paddle.fluid.layers.resize_bilinear (ArgSpec(args=['input', 'out_shape', 'scale', 'name', 'actual_shape', 'align_corners', 'align_mode'], varargs=None, keywords=None, defaults=(None, None, None, None, True, 1)), ('document', '832b2412652d84a6631b1012c6e2d18b')) -paddle.fluid.layers.resize_trilinear (ArgSpec(args=['input', 'out_shape', 'scale', 'name', 'actual_shape', 'align_corners', 'align_mode'], varargs=None, keywords=None, defaults=(None, None, None, None, True, 1)), ('document', '4836e98a634f6fbea26d0cdaa303f867')) -paddle.fluid.layers.resize_nearest (ArgSpec(args=['input', 'out_shape', 'scale', 'name', 'actual_shape', 'align_corners'], varargs=None, keywords=None, defaults=(None, None, None, None, True)), ('document', '32ffc0e8818d7319ed1bf63a791e985d')) +paddle.fluid.layers.resize_bilinear (ArgSpec(args=['input', 'out_shape', 'scale', 'name', 'actual_shape', 'align_corners', 'align_mode'], varargs=None, keywords=None, defaults=(None, None, None, None, True, 1)), ('document', '0a7b98e57eb74bab6e3c2a95e41298a7')) +paddle.fluid.layers.resize_trilinear (ArgSpec(args=['input', 'out_shape', 'scale', 'name', 'actual_shape', 'align_corners', 'align_mode'], varargs=None, keywords=None, defaults=(None, None, None, None, True, 1)), ('document', '6baf2ddf375d3059e5aa74d7fde76517')) +paddle.fluid.layers.resize_nearest (ArgSpec(args=['input', 'out_shape', 'scale', 'name', 'actual_shape', 'align_corners'], varargs=None, keywords=None, defaults=(None, None, None, None, True)), ('document', '699bf1de6af91235367e9c7a9a6e252c')) paddle.fluid.layers.gather (ArgSpec(args=['input', 'index', 'overwrite'], varargs=None, keywords=None, defaults=(True,)), ('document', 'f985c9b66e3aec96fa753a8eb44c991c')) paddle.fluid.layers.gather_nd (ArgSpec(args=['input', 'index', 'name'], varargs=None, keywords=None, defaults=(None,)), ('document', '3cc24f9cf135770aa6263dba25b457f9')) paddle.fluid.layers.scatter (ArgSpec(args=['input', 'index', 'updates', 'name', 'overwrite'], varargs=None, keywords=None, defaults=(None, True)), ('document', '69b22affd4a6326502af166f04c095ab')) diff --git a/paddle/fluid/operators/interpolate_op.cc b/paddle/fluid/operators/interpolate_op.cc index cd3fdc79acf2c364bdc39e9bdb3192683c8fd4e9..efe49a3e045340bb6d2485fd716a9b66163b4436 100644 --- a/paddle/fluid/operators/interpolate_op.cc +++ b/paddle/fluid/operators/interpolate_op.cc @@ -29,20 +29,41 @@ static void Interpolate2DInferShapeCheck(framework::InferShapeContext* ctx) { "Interpolation method can only be \"bilinear\" or \"nearest\" when " "Input(X) dimension is 4"); + if (ctx->HasInputs("SizeTensor")) { + // top prority size + auto inputs_name = ctx->Inputs("SizeTensor"); + PADDLE_ENFORCE_EQ( + inputs_name.size(), 2, + "Input(SizeTensor)'size of Op(interpolate) must be 2. " + "Attr(out_shape)'s length must be 2 for 4-D input tensor."); + int out_h = ctx->Attrs().Get("out_h"); + int out_w = ctx->Attrs().Get("out_w"); + std::vector dim_out({dim_x[0], dim_x[1], out_h, out_w}); + ctx->SetOutputDim("Out", framework::make_ddim(dim_out)); + + return; + } + int out_h, out_w; - float scale = ctx->Attrs().Get("scale"); - if (scale > 0) { - // round down - out_h = static_cast(dim_x[2] * scale); - out_w = static_cast(dim_x[3] * scale); - // protect when input shape is -1 - out_h = out_h > 0 ? out_h : -1; - out_w = out_w > 0 ? out_w : -1; + if (ctx->HasInput("Scale")) { + auto scale_tensor = ctx->GetInputDim("Scale"); + PADDLE_ENFORCE_EQ(scale_tensor.size(), 1, + "Scale's dimension size must be 1."); + out_h = -1; + out_w = -1; } else { - out_h = ctx->Attrs().Get("out_h"); - out_w = ctx->Attrs().Get("out_w"); - PADDLE_ENFORCE_GT(out_h, 0, "out_h should be greater than 0."); - PADDLE_ENFORCE_GT(out_w, 0, "out_w should be greater than 0."); + float scale = ctx->Attrs().Get("scale"); + if (scale > 0) { + // round down + out_h = static_cast(dim_x[2] * scale); + out_w = static_cast(dim_x[3] * scale); + // protect when input shape is -1 + out_h = out_h > 0 ? out_h : -1; + out_w = out_w > 0 ? out_w : -1; + } else { + out_h = ctx->Attrs().Get("out_h"); + out_w = ctx->Attrs().Get("out_w"); + } } if (ctx->HasInput("OutSize") && ctx->IsRuntime()) { @@ -66,24 +87,46 @@ static void Interpolate3DInferShapeCheck(framework::InferShapeContext* ctx) { "Interpolation method can only be \"trilinear\" when Input(X) " "dimension is 5"); + if (ctx->HasInputs("SizeTensor")) { + // top prority size + auto inputs_name = ctx->Inputs("SizeTensor"); + PADDLE_ENFORCE_EQ( + inputs_name.size(), 3, + "Input(SizeTensor)'s size of Op(interpolate) must be 3. " + "Attr(out_shape)'s length must be 3 for 5-D input tensor."); + int out_d = ctx->Attrs().Get("out_d"); + int out_h = ctx->Attrs().Get("out_h"); + int out_w = ctx->Attrs().Get("out_w"); + std::vector dim_out({dim_x[0], dim_x[1], out_d, out_h, out_w}); + ctx->SetOutputDim("Out", framework::make_ddim(dim_out)); + + return; + } + int out_d, out_h, out_w; - float scale = ctx->Attrs().Get("scale"); - if (scale > 0) { - // round down - out_d = static_cast(dim_x[2] * scale); - out_h = static_cast(dim_x[3] * scale); - out_w = static_cast(dim_x[4] * scale); - // protect when input shape is -1 - out_d = out_d > 0 ? out_d : -1; - out_h = out_h > 0 ? out_h : -1; - out_w = out_w > 0 ? out_w : -1; + if (ctx->HasInput("Scale")) { + auto scale_tensor = ctx->GetInputDim("Scale"); + PADDLE_ENFORCE_EQ(scale_tensor.size(), 1, + "Scale's dimension size must be 1"); + out_d = -1; + out_h = -1; + out_w = -1; } else { - out_d = ctx->Attrs().Get("out_d"); - out_h = ctx->Attrs().Get("out_h"); - out_w = ctx->Attrs().Get("out_w"); - PADDLE_ENFORCE_GT(out_d, 0, "out_d should be greater than 0."); - PADDLE_ENFORCE_GT(out_h, 0, "out_h should be greater than 0."); - PADDLE_ENFORCE_GT(out_w, 0, "out_w should be greater than 0."); + float scale = ctx->Attrs().Get("scale"); + if (scale > 0) { + // round down + out_d = static_cast(dim_x[2] * scale); + out_h = static_cast(dim_x[3] * scale); + out_w = static_cast(dim_x[4] * scale); + // protect when input shape is -1 + out_d = out_d > 0 ? out_d : -1; + out_h = out_h > 0 ? out_h : -1; + out_w = out_w > 0 ? out_w : -1; + } else { + out_d = ctx->Attrs().Get("out_d"); + out_h = ctx->Attrs().Get("out_h"); + out_w = ctx->Attrs().Get("out_w"); + } } if (ctx->HasInput("OutSize") && ctx->IsRuntime()) { @@ -129,6 +172,16 @@ class InterpolateOp : public framework::OperatorWithKernel { return framework::OpKernelType(ctx.Input("X")->type(), ctx.GetPlace()); } + + framework::OpKernelType GetKernelTypeForVar( + const std::string& var_name, const Tensor& tensor, + const framework::OpKernelType& expected_kernel_type) const override { + if (var_name == "SizeTensor" || var_name == "Scale") { + return expected_kernel_type; + } + return framework::OpKernelType(expected_kernel_type.data_type_, + tensor.place(), tensor.layout()); + } }; class InterpolateOpMaker : public framework::OpProtoAndCheckerMaker { @@ -142,7 +195,19 @@ class InterpolateOpMaker : public framework::OpProtoAndCheckerMaker { "This is a 1-D tensor with two numbers to specify output size. " "It should be [output_height, output_width] when input is a 4-D " "tensor and should be [output_depth, output_height, output_width] " - "when input is a 5-D tensor.") + "when input is a 5-D tensor. It has a higher priority than " + "the attr(out_d), attr(out_h), attr(out_w) and attr(scale).") + .AsDispensable(); + AddInput("SizeTensor", + "(vector>, optional). If provided, interpolate will " + "use this. The shape of the tensor in vector MUST BE [1]. " + "It has the highest priority compare with Input(OutSize) and " + "attr(out_d), attr(out_h), attr(out_w) and attr(scale).") + .AsDuplicable() + .AsDispensable(); + AddInput("Scale", + "This is a 1-D tensor with one number to specify output scale. " + "It has the higher priority compare with attr(scale).") .AsDispensable(); AddOutput("Out", "The output tensor of interpolate operator, " @@ -304,6 +369,16 @@ class InterpolateOpGrad : public framework::OperatorWithKernel { ctx.Input(framework::GradVarName("Out"))->type(), ctx.GetPlace()); } + + framework::OpKernelType GetKernelTypeForVar( + const std::string& var_name, const Tensor& tensor, + const framework::OpKernelType& expected_kernel_type) const override { + if (var_name == "SizeTensor" || var_name == "Scale") { + return expected_kernel_type; + } + return framework::OpKernelType(expected_kernel_type.data_type_, + tensor.place(), tensor.layout()); + } }; class InterpolateGradDescMaker : public framework::SingleGradOpDescMaker { @@ -315,9 +390,15 @@ class InterpolateGradDescMaker : public framework::SingleGradOpDescMaker { std::unique_ptr op(new framework::OpDesc()); op->SetType(ForwardOp().Type() + "_grad"); op->SetInput("X", Input("X")); + if (ForwardOp().Inputs().count("SizeTensor") > 0) { + op->SetInput("SizeTensor", Input("SizeTensor")); + } if (ForwardOp().Inputs().count("OutSize") > 0) { op->SetInput("OutSize", Input("OutSize")); } + if (ForwardOp().Inputs().count("Scale") > 0) { + op->SetInput("Scale", Input("Scale")); + } op->SetInput(framework::GradVarName("Out"), OutputGrad("Out")); op->SetOutput(framework::GradVarName("X"), InputGrad("X")); op->SetAttrMap(Attrs()); diff --git a/paddle/fluid/operators/interpolate_op.cu b/paddle/fluid/operators/interpolate_op.cu index cfe441f6c192b5a2cb33bf685cb0cb95b8abe3a7..45e606f3885c0846a538ab0695a1692bf908b7b9 100644 --- a/paddle/fluid/operators/interpolate_op.cu +++ b/paddle/fluid/operators/interpolate_op.cu @@ -365,20 +365,41 @@ static void Interpolate2DCUDAFwd(const framework::ExecutionContext& ctx, int out_h = ctx.Attr("out_h"); int out_w = ctx.Attr("out_w"); - float scale = ctx.Attr("scale"); - if (scale > 0) { - out_h = static_cast(in_h * scale); - out_w = static_cast(in_w * scale); - } - auto out_size = ctx.Input("OutSize"); - if (out_size != nullptr) { - Tensor sizes; - framework::TensorCopy(*out_size, platform::CPUPlace(), &sizes); - auto size_data = sizes.data(); - out_h = size_data[0]; - out_w = size_data[1]; + auto list_new_shape_tensor = ctx.MultiInput("SizeTensor"); + if (list_new_shape_tensor.size() > 0) { + // have size tensor + auto new_size = get_new_shape(list_new_shape_tensor); + out_h = new_size[0]; + out_w = new_size[1]; + } else { + float scale; + auto scale_tensor = ctx.Input("Scale"); + if (scale_tensor != nullptr) { + auto scale_data = get_new_data_from_tensor(scale_tensor); + scale = scale_data[0]; + } else { + scale = ctx.Attr("scale"); + } + if (scale > 0) { + out_h = static_cast(in_h * scale); + out_w = static_cast(in_w * scale); + } + auto out_size = ctx.Input("OutSize"); + if (out_size != nullptr) { + Tensor sizes; + framework::TensorCopySync(*out_size, platform::CPUPlace(), &sizes); + auto size_data = sizes.data(); + out_h = size_data[0]; + out_w = size_data[1]; + } } + PADDLE_ENFORCE_GT( + out_h, 0, + "out_h in Attr(out_shape) of Op(interpolate) should be greater than 0."); + PADDLE_ENFORCE_GT( + out_w, 0, + "out_w in Attr(out_shape) of Op(interpolate) should be greater than 0."); auto output_data = output->mutable_data({n, c, out_h, out_w}, ctx.GetPlace()); @@ -439,22 +460,47 @@ static void Interpolate3DCUDAFwd(const framework::ExecutionContext& ctx, int out_d = ctx.Attr("out_d"); int out_h = ctx.Attr("out_h"); int out_w = ctx.Attr("out_w"); - float scale = ctx.Attr("scale"); - if (scale > 0) { - out_d = static_cast(in_d * scale); - out_h = static_cast(in_h * scale); - out_w = static_cast(in_w * scale); - } - auto out_size = ctx.Input("OutSize"); - if (out_size != nullptr) { - Tensor sizes; - framework::TensorCopy(*out_size, platform::CPUPlace(), &sizes); - auto size_data = sizes.data(); - out_d = size_data[0]; - out_h = size_data[1]; - out_w = size_data[2]; + auto list_new_shape_tensor = ctx.MultiInput("SizeTensor"); + if (list_new_shape_tensor.size() > 0) { + // have size tensor + auto new_size = get_new_shape(list_new_shape_tensor); + out_d = new_size[0]; + out_h = new_size[1]; + out_w = new_size[2]; + } else { + float scale; + auto scale_tensor = ctx.Input("Scale"); + if (scale_tensor != nullptr) { + auto scale_data = get_new_data_from_tensor(scale_tensor); + scale = scale_data[0]; + } else { + scale = ctx.Attr("scale"); + } + if (scale > 0) { + out_d = static_cast(in_d * scale); + out_h = static_cast(in_h * scale); + out_w = static_cast(in_w * scale); + } + auto out_size = ctx.Input("OutSize"); + if (out_size != nullptr) { + Tensor sizes; + framework::TensorCopySync(*out_size, platform::CPUPlace(), &sizes); + auto size_data = sizes.data(); + out_d = size_data[0]; + out_h = size_data[1]; + out_w = size_data[2]; + } } + PADDLE_ENFORCE_GT( + out_d, 0, + "out_d in Attr(out_shape) of Op(interpolate) should be greater than 0."); + PADDLE_ENFORCE_GT( + out_h, 0, + "out_h in Attr(out_shape) of Op(interpolate) should be greater than 0."); + PADDLE_ENFORCE_GT( + out_w, 0, + "out_w in Attr(out_shape) of Op(interpolate) should be greater than 0."); auto output_data = output->mutable_data({n, c, out_d, out_h, out_w}, ctx.GetPlace()); @@ -513,7 +559,14 @@ static void Interpolate2DCUDABwd(const framework::ExecutionContext& ctx, int out_h = ctx.Attr("out_h"); int out_w = ctx.Attr("out_w"); - float scale = ctx.Attr("scale"); + float scale; + auto scale_tensor = ctx.Input("Scale"); + if (scale_tensor != nullptr) { + auto scale_data = get_new_data_from_tensor(scale_tensor); + scale = scale_data[0]; + } else { + scale = ctx.Attr("scale"); + } if (scale > 0) { out_h = static_cast(in_h * scale); out_w = static_cast(in_w * scale); @@ -522,11 +575,18 @@ static void Interpolate2DCUDABwd(const framework::ExecutionContext& ctx, auto out_size = ctx.Input("OutSize"); if (out_size != nullptr) { Tensor sizes; - framework::TensorCopy(*out_size, platform::CPUPlace(), &sizes); + framework::TensorCopySync(*out_size, platform::CPUPlace(), &sizes); auto size_data = sizes.data(); out_h = size_data[0]; out_w = size_data[1]; } + auto list_new_size_tensor = ctx.MultiInput("SizeTensor"); + if (list_new_size_tensor.size() > 0) { + // have size tensor + auto new_size = get_new_shape(list_new_size_tensor); + out_h = new_size[0]; + out_w = new_size[1]; + } auto* output_grad_data = output_grad.data(); auto* input_grad_data = @@ -591,7 +651,14 @@ static void Interpolate3DCUDABwd(const framework::ExecutionContext& ctx, int out_d = ctx.Attr("out_d"); int out_h = ctx.Attr("out_h"); int out_w = ctx.Attr("out_w"); - float scale = ctx.Attr("scale"); + float scale; + auto scale_tensor = ctx.Input("Scale"); + if (scale_tensor != nullptr) { + auto scale_data = get_new_data_from_tensor(scale_tensor); + scale = scale_data[0]; + } else { + scale = ctx.Attr("scale"); + } if (scale > 0) { out_d = static_cast(in_d * scale); out_h = static_cast(in_h * scale); @@ -601,12 +668,20 @@ static void Interpolate3DCUDABwd(const framework::ExecutionContext& ctx, auto out_size = ctx.Input("OutSize"); if (out_size != nullptr) { Tensor sizes; - framework::TensorCopy(*out_size, platform::CPUPlace(), &sizes); + framework::TensorCopySync(*out_size, platform::CPUPlace(), &sizes); auto size_data = sizes.data(); out_d = size_data[0]; out_h = size_data[1]; out_w = size_data[2]; } + auto list_new_size_tensor = ctx.MultiInput("SizeTensor"); + if (list_new_size_tensor.size() > 0) { + // have size tensor + auto new_size = get_new_shape(list_new_size_tensor); + out_d = new_size[0]; + out_h = new_size[1]; + out_w = new_size[2]; + } auto* output_grad_data = output_grad.data(); auto* input_grad_data = diff --git a/paddle/fluid/operators/interpolate_op.h b/paddle/fluid/operators/interpolate_op.h index 8fffe1ca48ef0f4fed20c7b1108bec755c1dc64f..b87c41256db928cfea35a631ebbd466f3fc39563 100644 --- a/paddle/fluid/operators/interpolate_op.h +++ b/paddle/fluid/operators/interpolate_op.h @@ -23,6 +23,40 @@ template ; using Tensor = framework::Tensor; +inline std::vector get_new_shape( + const std::vector& list_new_shape_tensor) { + // get tensor from + std::vector vec_new_shape; + for (size_t i = 0; i < list_new_shape_tensor.size(); ++i) { + auto tensor = list_new_shape_tensor[i]; + PADDLE_ENFORCE_EQ(tensor->dims(), framework::make_ddim({1}), + "shape of dim tensor should be [1]"); + if (platform::is_gpu_place(tensor->place())) { + framework::Tensor temp; + TensorCopySync(*tensor, platform::CPUPlace(), &temp); + + vec_new_shape.push_back(static_cast(*temp.data())); + } else { + vec_new_shape.push_back(static_cast(*tensor->data())); + } + } + + return vec_new_shape; +} + +template +inline std::vector get_new_data_from_tensor(const Tensor* new_data_tensor) { + std::vector vec_new_data; + auto* new_data = new_data_tensor->data(); + framework::Tensor cpu_starts_tensor; + if (platform::is_gpu_place(new_data_tensor->place())) { + TensorCopySync(*new_data_tensor, platform::CPUPlace(), &cpu_starts_tensor); + new_data = cpu_starts_tensor.data(); + } + vec_new_data = std::vector(new_data, new_data + new_data_tensor->numel()); + return vec_new_data; +} + template static void NearestNeighborInterpolate(const Tensor& input, Tensor* output, const float ratio_h, const float ratio_w, @@ -403,19 +437,39 @@ static void Interpolate2DCPUFwd(const framework::ExecutionContext& ctx, int out_h = ctx.Attr("out_h"); int out_w = ctx.Attr("out_w"); - float scale = ctx.Attr("scale"); - if (scale > 0) { - out_h = static_cast(in_h * scale); - out_w = static_cast(in_w * scale); - } - auto out_size = ctx.Input("OutSize"); - if (out_size != nullptr) { - auto out_size_data = out_size->data(); - out_h = out_size_data[0]; - out_w = out_size_data[1]; + auto list_new_size_tensor = ctx.MultiInput("SizeTensor"); + if (list_new_size_tensor.size() > 0) { + // have size tensor + auto new_size = get_new_shape(list_new_size_tensor); + out_h = new_size[0]; + out_w = new_size[1]; + } else { + float scale; + auto scale_tensor = ctx.Input("Scale"); + if (scale_tensor != nullptr) { + auto scale_data = get_new_data_from_tensor(scale_tensor); + scale = scale_data[0]; + } else { + scale = ctx.Attr("scale"); + } + if (scale > 0) { + out_h = static_cast(in_h * scale); + out_w = static_cast(in_w * scale); + } + auto out_size = ctx.Input("OutSize"); + if (out_size != nullptr) { + auto out_size_data = get_new_data_from_tensor(out_size); + out_h = out_size_data[0]; + out_w = out_size_data[1]; + } } - + PADDLE_ENFORCE_GT( + out_h, 0, + "out_h in Attr(out_shape) of Op(interpolate) should be greater than 0."); + PADDLE_ENFORCE_GT( + out_w, 0, + "out_w in Attr(out_shape) of Op(interpolate) should be greater than 0."); output->mutable_data({n, c, out_h, out_w}, ctx.GetPlace()); if (in_h == out_h && in_w == out_w) { @@ -459,21 +513,45 @@ static void Interpolate3DCPUFwd(const framework::ExecutionContext& ctx, int out_d = ctx.Attr("out_d"); int out_h = ctx.Attr("out_h"); int out_w = ctx.Attr("out_w"); - float scale = ctx.Attr("scale"); - if (scale > 0) { - out_d = static_cast(in_d * scale); - out_h = static_cast(in_h * scale); - out_w = static_cast(in_w * scale); - } - auto out_size = ctx.Input("OutSize"); - if (out_size != nullptr) { - auto out_size_data = out_size->data(); - out_d = out_size_data[0]; - out_h = out_size_data[1]; - out_w = out_size_data[2]; + auto list_new_size_tensor = ctx.MultiInput("SizeTensor"); + if (list_new_size_tensor.size() > 0) { + // have size tensor + auto new_size = get_new_shape(list_new_size_tensor); + out_d = new_size[0]; + out_h = new_size[1]; + out_w = new_size[2]; + } else { + float scale; + auto scale_tensor = ctx.Input("Scale"); + if (scale_tensor != nullptr) { + auto scale_data = get_new_data_from_tensor(scale_tensor); + scale = scale_data[0]; + } else { + scale = ctx.Attr("scale"); + } + if (scale > 0) { + out_d = static_cast(in_d * scale); + out_h = static_cast(in_h * scale); + out_w = static_cast(in_w * scale); + } + auto out_size = ctx.Input("OutSize"); + if (out_size != nullptr) { + auto out_size_data = get_new_data_from_tensor(out_size); + out_d = out_size_data[0]; + out_h = out_size_data[1]; + out_w = out_size_data[2]; + } } - + PADDLE_ENFORCE_GT( + out_d, 0, + "out_d in Attr(out_shape) of Op(interpolate) should be greater than 0."); + PADDLE_ENFORCE_GT( + out_h, 0, + "out_h in Attr(out_shape) of Op(interpolate) should be greater than 0."); + PADDLE_ENFORCE_GT( + out_w, 0, + "out_w in Attr(out_shape) of Op(interpolate) should be greater than 0."); output->mutable_data({n, c, out_d, out_h, out_w}, ctx.GetPlace()); if (in_d == out_d && in_h == out_h && in_w == out_w) { @@ -519,18 +597,31 @@ static void Interpolate2DCPUBwd(const framework::ExecutionContext& ctx, int out_h = ctx.Attr("out_h"); int out_w = ctx.Attr("out_w"); - float scale = ctx.Attr("scale"); + float scale; + auto scale_tensor = ctx.Input("Scale"); + if (scale_tensor != nullptr) { + auto scale_data = get_new_data_from_tensor(scale_tensor); + scale = scale_data[0]; + } else { + scale = ctx.Attr("scale"); + } if (scale > 0) { out_h = static_cast(in_h * scale); out_w = static_cast(in_w * scale); } - auto out_size = ctx.Input("OutSize"); if (out_size != nullptr) { - auto out_size_data = out_size->data(); + auto out_size_data = get_new_data_from_tensor(out_size); out_h = out_size_data[0]; out_w = out_size_data[1]; } + auto list_new_size_tensor = ctx.MultiInput("SizeTensor"); + if (list_new_size_tensor.size() > 0) { + // have size tensor + auto new_size = get_new_shape(list_new_size_tensor); + out_h = new_size[0]; + out_w = new_size[1]; + } input_grad->mutable_data({n, c, in_h, in_w}, ctx.GetPlace()); auto& device_ctx = ctx.template device_context(); @@ -580,20 +671,34 @@ static void Interpolate3DCPUBwd(const framework::ExecutionContext& ctx, int out_d = ctx.Attr("out_d"); int out_h = ctx.Attr("out_h"); int out_w = ctx.Attr("out_w"); - float scale = ctx.Attr("scale"); + float scale; + auto scale_tensor = ctx.Input("Scale"); + if (scale_tensor != nullptr) { + auto scale_data = get_new_data_from_tensor(scale_tensor); + scale = scale_data[0]; + } else { + scale = ctx.Attr("scale"); + } if (scale > 0) { out_d = static_cast(in_d * scale); out_h = static_cast(in_h * scale); out_w = static_cast(in_w * scale); } - auto out_size = ctx.Input("OutSize"); if (out_size != nullptr) { - auto out_size_data = out_size->data(); + auto out_size_data = get_new_data_from_tensor(out_size); out_d = out_size_data[0]; out_h = out_size_data[1]; out_w = out_size_data[2]; } + auto list_new_size_tensor = ctx.MultiInput("SizeTensor"); + if (list_new_size_tensor.size() > 0) { + // have size tensor + auto new_size = get_new_shape(list_new_size_tensor); + out_d = new_size[0]; + out_h = new_size[1]; + out_w = new_size[2]; + } input_grad->mutable_data({n, c, in_d, in_h, in_w}, ctx.GetPlace()); auto& device_ctx = ctx.template device_context(); diff --git a/python/paddle/fluid/layers/nn.py b/python/paddle/fluid/layers/nn.py index 84343679332d08523b36485b9995f9879ce029b3..aa3dea655bfe17ce4940ea8ceb142ed851b581b1 100755 --- a/python/paddle/fluid/layers/nn.py +++ b/python/paddle/fluid/layers/nn.py @@ -7902,6 +7902,9 @@ def image_resize(input, or (num_batches, channels, in_d, in_h, in_w), and the resizing only applies on the last two/three dimensions(depth, hight and width). + **Warning:** the parameter :attr:`actual_shape` will be deprecated in the + future and only use :attr:`out_shape` instead. + Supporting resample methods: 'BILINEAR' : Bilinear interpolation @@ -8022,13 +8025,13 @@ def image_resize(input, 5-D tensor of the shape (num_batches, channls, in_d, in_h, in_w). out_shape(list|tuple|Variable|None): Output shape of image resize - layer, the shape is (out_h, out_w) when - input is a 4-D tensor and is - (out_d, out_h, out_w) when input is a - 5-D tensor. Default: None - scale(float|None): The multiplier for the input height or width. At - least one of :attr:`out_shape` or :attr:`scale` must be set. - And :attr:`out_shape` has a higher priority than :attr:`scale`. + layer, the shape is (out_h, out_w) when input is a 4-D tensor and is + (out_d, out_h, out_w) when input is a 5-D tensor. Default: None. If + a list, each element can be an integer or a tensor Variable of shape: [1]. + If a tesnosr Variable, its dimensions size should be a 1. + scale(float|Variable|None): The multiplier for the input height or width. At + least one of :attr:`out_shape` or :attr:`scale` must be set. + And :attr:`out_shape` has a higher priority than :attr:`scale`. Default: None. name(str|None): A name for this layer(optional). If set None, the layer will be named automatically. @@ -8040,12 +8043,12 @@ def image_resize(input, :attr:`out_shape` and :attr:`scale` specifying shape. That is to say actual_shape has the highest priority. It is recommended to use - actual_shape instead of :attr:`out_shape` if you - want to specify output shape dynamically. When - using actual_shape to specify output shape, one of - :attr:`out_shape` and :attr:`scale` should also be - set, otherwise errors would be occured in graph - constructing stage. + :attr:`out_shape` if you want to specify output + shape dynamically, because :attr:`actual_shape` + will be deprecated. When using actual_shape to + specify output shape, one of :attr:`out_shape` + and :attr:`scale` should also be set, otherwise + errors would be occured in graph constructing stage. Default: None align_corners(bool) : An optional bool, If True, the centers of the 4 corner pixels of the input and output tensors are aligned, preserving the values at the @@ -8078,8 +8081,32 @@ def image_resize(input, .. code-block:: python import paddle.fluid as fluid - input = fluid.layers.data(name="input", shape=[3,6,9], dtype="float32") - out = fluid.layers.image_resize(input, out_shape=[12, 12], resample="NEAREST") + input = fluid.layers.data(name="input", shape=[3, 6, 9], dtype="float32") + # input.shape = [-1, 3, 6, 9], where -1 indicates batch size, and it will get the exact value in runtime. + + out0 = fluid.layers.image_resize(input, out_shape=[12, 12], resample="NEAREST") + # out0.shape = [-1, 3, 12, 12], it means out0.shape[0] = input.shape[0] in runtime. + + # out_shape is a list in which each element is a integer or a tensor Variable + dim1 = fluid.layers.data(name="dim1", shape=[1], dtype="int32", append_batch_size=False) + out1 = fluid.layers.image_resize(input, out_shape=[12, dim1], resample="NEAREST") + # out1.shape = [-1, 3, 12, -1] + + # out_shape is a 1-D tensor Variable + shape_tensor = fluid.layers.data(name="shape_tensor", shape=[2], dtype="int32", append_batch_size=False) + out2 = fluid.layers.image_resize(input, out_shape=shape_tensor, resample="NEAREST") + # out2.shape = [-1, 3, -1, -1] + + # when use actual_shape + actual_shape_tensor = fluid.layers.data(name="actual_shape_tensor", shape=[2], dtype="int32", append_batch_size=False) + out3 = fluid.layers.image_resize(input, out_shape=[4, 4], resample="NEAREST", actual_shape=actual_shape_tensor) + # out3.shape = [-1, 3, 4, 4] + + # scale is a Variable + scale_tensor = fluid.layers.data(name="scale", shape=[1], dtype="float32", append_batch_size=False) + out4 = fluid.layers.image_resize(input, scale=scale_tensor) + # out4.shape = [-1, 3, -1, -1] + """ resample_methods = { 'BILINEAR': 'bilinear', @@ -8112,9 +8139,9 @@ def image_resize(input, inputs = {"X": input} attrs = { - "out_d": 0, - "out_h": 0, - "out_w": 0, + "out_d": -1, + "out_h": -1, + "out_w": -1, "interp_method": resample_type, "align_corners": align_corners, "align_mode": align_mode @@ -8122,36 +8149,80 @@ def image_resize(input, if out_shape is not None: if isinstance(out_shape, Variable): - warnings.warn("out_shape as Variable type is deprecated, \ - it is recommended to use actual_shape instead of \ - out_shape to specify output shape dynamically.") + out_shape.stop_gradient = True inputs['OutSize'] = out_shape else: if not (_is_list_or_turple_(out_shape)): raise TypeError( "out_shape should be a list or tuple or Variable.") + # Validate the shape + contain_var = False + for dim_idx, dim_size in enumerate(out_shape): + if isinstance(dim_size, Variable): + contain_var = True + continue + assert dim_size > 0, ( + "Each dimension size given in out_shape must be greater than 0." + ) + + if contain_var: + new_size_tensor = [] + size_list = [] + for dim in out_shape: + if isinstance(dim, Variable): + dim.stop_gradient = True + new_size_tensor.append(dim) + size_list.append(-1) + else: + assert (isinstance(dim, int)) + temp_out = helper.create_variable_for_type_inference( + 'int32') + fill_constant( + [1], 'int32', dim, force_cpu=True, out=temp_out) + new_size_tensor.append(temp_out) + size_list.append(dim) + inputs['SizeTensor'] = new_size_tensor + if len(input.shape) == 4: if len(out_shape) != 2: raise ValueError("out_shape length should be 2 for " "input 4-D tensor.") - out_shape = list(map(int, out_shape)) - attrs['out_h'] = out_shape[0] - attrs['out_w'] = out_shape[1] + if contain_var: + attrs['out_h'] = size_list[0] + attrs['out_w'] = size_list[1] + else: + out_shape = list(map(int, out_shape)) + attrs['out_h'] = out_shape[0] + attrs['out_w'] = out_shape[1] if len(input.shape) == 5: if len(out_shape) != 3: raise ValueError("out_shape length should be 3 for " "input 5-D tensor.") - out_shape = list(map(int, out_shape)) - attrs['out_d'] = out_shape[0] - attrs['out_h'] = out_shape[1] - attrs['out_w'] = out_shape[2] + if contain_var: + attrs['out_d'] = size_list[0] + attrs['out_h'] = size_list[1] + attrs['out_w'] = size_list[2] + else: + out_shape = list(map(int, out_shape)) + attrs['out_d'] = out_shape[0] + attrs['out_h'] = out_shape[1] + attrs['out_w'] = out_shape[2] else: - if scale <= 0: - raise ValueError("scale should be greater than zero.") - attrs['scale'] = float(scale) + if isinstance(scale, Variable): + scale.stop_gradient = True + inputs["Scale"] = scale + if isinstance(scale, float): + if scale <= 0: + raise ValueError("scale should be greater than zero.") + attrs['scale'] = float(scale) if isinstance(actual_shape, Variable): + warnings.warn( + "actual_shape will be deprecated, it is recommended to use " + "out_shape instead of actual_shape to specify output shape dynamically." + ) + actual_shape.stop_gradient = True inputs["OutSize"] = actual_shape elif actual_shape is not None: raise TypeError("actual_shape should either be Variable or None.") @@ -8178,6 +8249,9 @@ def resize_bilinear(input, output shape which specified by actual_shape, out_shape and scale in priority order. + **Warning:** the parameter :attr:`actual_shape` will be deprecated in + the future and only use :attr:`out_shape` instead. + Bilinear interpolation is an extension of linear interpolation for interpolating functions of two variables (e.g. H-direction and W-direction in this op) on a rectilinear 2D grid. The key idea is @@ -8227,13 +8301,15 @@ def resize_bilinear(input, Args: - input(${x_type}): input should be a 4-D tensor. + input(${x_type}): input should be a 4-D tensor of shape + (num_batches, channels, in_h, in_w). out_shape(list|tuple|Variable|None): Output shape of resize bilinear - layer, the shape is (out_h, out_w). - Default: None + layer, the shape is (out_h, out_w).Default: None. If a list, each + element can be an integer or a tensor Variable with shape: [1]. If a + tensor Variable, its dimension size should be 1. - scale(float|None): The multiplier for the input height or width. At + scale(float|Variable|None): The multiplier for the input height or width. At least one of :attr:`out_shape` or :attr:`scale` must be set. And :attr:`out_shape` has a higher priority than :attr:`scale`. Default: None. @@ -8245,12 +8321,12 @@ def resize_bilinear(input, :attr:`out_shape` and :attr:`scale` specifying shape. That is to say actual_shape has the highest priority. It is recommended to use - actual_shape instead of :attr:`out_shape` if you - want to specify output shape dynamically. When - using actual_shape to specify output shape, one of - :attr:`out_shape` and :attr:`scale` should also be - set, otherwise errors would be occured in graph - constructing stage. + :attr:`out_shape` if you want to specify output + shape dynamically, because :attr:`actual_shape` + will be deprecated. When using actual_shape to + specify output shape, one of :attr:`out_shape` + and :attr:`scale` should also be set, otherwise + errors would be occured in graph constructing stage. Default: None align_corners(bool): ${align_corners_comment} align_mode(bool): ${align_mode_comment} @@ -8262,8 +8338,31 @@ def resize_bilinear(input, .. code-block:: python import paddle.fluid as fluid - input = fluid.layers.data(name="input", shape=[3,6,9], dtype="float32") - out = fluid.layers.resize_bilinear(input, out_shape=[12, 12]) + input = fluid.layers.data(name="input", shape=[3, 6, 9], dtype="float32") + # input.shape = [-1, 3, 6, 9], where -1 indicates batch size, and it will get the exact value in runtime. + + out0 = fluid.layers.resize_bilinear(input, out_shape=[12, 12]) + # out0.shape = [-1, 3, 12, 12], it means out0.shape[0] = input.shape[0] in runtime. + + # out_shape is a list in which each element is a integer or a tensor Variable + dim1 = fluid.layers.data(name="dim1", shape=[1], dtype="int32", append_batch_size=False) + out1 = fluid.layers.resize_bilinear(input, out_shape=[12, dim1]) + # out1.shape = [-1, 3, 12, -1] + + # out_shape is a 1-D tensor Variable + shape_tensor = fluid.layers.data(name="shape_tensor", shape=[2], dtype="int32", append_batch_size=False) + out2 = fluid.layers.resize_bilinear(input, out_shape=shape_tensor) + # out2.shape = [-1, 3, -1, -1] + + # when use actual_shape + actual_shape_tensor = fluid.layers.data(name="actual_shape_tensor", shape=[2], dtype="int32", append_batch_size=False) + out3 = fluid.layers.resize_bilinear(input, out_shape=[4, 4], actual_shape=actual_shape_tensor) + # out3.shape = [-1, 3, 4, 4] + + # scale is a Variable + scale_tensor = fluid.layers.data(name="scale", shape=[1], dtype="float32", append_batch_size=False) + out4 = fluid.layers.resize_bilinear(input, scale=scale_tensor) + # out4.shape = [-1, 3, -1, -1] """ return image_resize(input, out_shape, scale, name, 'BILINEAR', actual_shape, @@ -8283,6 +8382,9 @@ def resize_trilinear(input, output shape which specified by actual_shape, out_shape and scale in priority order. + **Warning:** the parameter :attr:`actual_shape` will be deprecated + in the future and only use :attr:`out_shape` instead. + Trilinear interpolation is an extension of linear interpolation for interpolating functions of three variables (e.g. D-direction, H-direction and W-direction in this op) on a rectilinear 3D grid. @@ -8333,13 +8435,15 @@ def resize_trilinear(input, Args: - input(${x_type}): input should be a 4-D tensor. + input(${x_type}): input should be a 5-D tensor of shape + (num_batches, channls, in_d, in_h, in_w). out_shape(list|tuple|Variable|None): Output shape of resize bilinear - layer, the shape is (out_d, out_h, out_w). - Default: None + layer, the shape is (out_d, out_h, out_w). Default: None. If a list, + each element can be an integer or a tensor Variable with shape: [1]. If + a tensor Variable, its dimension size should be 1. - scale(float|None): The multiplier for the input depth, height or width. + scale(float|Variable|None): The multiplier for the input depth, height or width. At least one of :attr:`out_shape` or :attr:`scale` must be set. And :attr:`out_shape` has a higher priority than :attr:`scale`. Default: None. @@ -8351,12 +8455,12 @@ def resize_trilinear(input, :attr:`out_shape` and :attr:`scale` specifying shape. That is to say actual_shape has the highest priority. It is recommended to use - actual_shape instead of :attr:`out_shape` if you - want to specify output shape dynamically. When - using actual_shape to specify output shape, one of - :attr:`out_shape` and :attr:`scale` should also be - set, otherwise errors would be occured in graph - constructing stage. + :attr:`out_shape` if you want to specify output + shape dynamically, because :attr:`actual_shape` + will be deprecated. When using actual_shape to + specify output shape, one of :attr:`out_shape` + and :attr:`scale` should also be set, otherwise + errors would be occured in graph constructing stage. Default: None align_corners(bool): ${align_corners_comment} align_mode(bool): ${align_mode_comment} @@ -8368,8 +8472,32 @@ def resize_trilinear(input, .. code-block:: python import paddle.fluid as fluid - input = fluid.layers.data(name="input", shape=[3,6,9,11], dtype="float32") - out = fluid.layers.resize_trilinear(input, out_shape=[12, 12, 12]) + input = fluid.layers.data(name="input", shape=[3, 6, 9, 11], dtype="float32") + # input.shape = [-1, 3, 6, 9, 11], where -1 indicates batch size, and it will get the exact value in runtime. + + out0 = fluid.layers.resize_trilinear(input, out_shape=[12, 12, 12]) + # out0.shape = [-1, 3, 12, 12, 12], it means out0.shape[0] = input.shape[0] in runtime. + + # out_shape is a list in which each element is a integer or a tensor Variable + dim1 = fluid.layers.data(name="dim1", shape=[1], dtype="int32", append_batch_size=False) + out1 = fluid.layers.resize_trilinear(input, out_shape=[12, dim1, 4]) + # out1.shape = [-1, 3, 12, -1, 4] + + # out_shape is a 1-D tensor Variable + shape_tensor = fluid.layers.data(name="shape_tensor", shape=[3], dtype="int32", append_batch_size=False) + out2 = fluid.layers.resize_trilinear(input, out_shape=shape_tensor) + # out2.shape = [-1, 3, -1, -1, -1] + + # when use actual_shape + actual_shape_tensor = fluid.layers.data(name="actual_shape_tensor", shape=[3], dtype="int32", append_batch_size=False) + out3 = fluid.layers.resize_trilinear(input, out_shape=[4, 4, 8], actual_shape=actual_shape_tensor) + # out3.shape = [-1, 3, 4, 4, 8] + + # scale is a Variable + scale_tensor = fluid.layers.data(name="scale", shape=[1], dtype="float32", append_batch_size=False) + out4 = fluid.layers.resize_trilinear(input, scale=scale_tensor) + # out4.shape = [-1, 3, -1, -1, -1] + """ return image_resize(input, out_shape, scale, name, 'TRILINEAR', @@ -8389,6 +8517,9 @@ def resize_nearest(input, direction) based on given output shape which is specified by actual_shape, out_shape and scale in priority order. + **Warning:** the parameter :attr:`actual_shape` will be deprecated in the + future and only use :attr:`out_shape` instead. + Example: .. code-block:: text @@ -8429,13 +8560,15 @@ def resize_nearest(input, https://en.wikipedia.org/wiki/Nearest-neighbor_interpolation Args: - input(${x_type}): input should be a 4-D tensor. + input(${x_type}): input should be a 4-D tensor of shape + (num_batches, channls, in_h, in_w). out_shape(list|tuple|Variable|None): Output shape of resize nearest - layer, the shape is (out_h, out_w). - Default: None + layer, the shape is (out_h, out_w). Default: None. If a list, each + element can be integer or a tensor Variable with shape: [1]. If a + tensor Variable, its dimension size should be 1. - scale(float|None): The multiplier for the input height or width. At + scale(float|Variable|None): The multiplier for the input height or width. At least one of :attr:`out_shape` or :attr:`scale` must be set. And :attr:`out_shape` has a higher priority than :attr:`scale`. Default: None. @@ -8447,12 +8580,12 @@ def resize_nearest(input, :attr:`out_shape` and :attr:`scale` specifying shape. That is to say actual_shape has the highest priority. It is recommended to use - actual_shape instead of :attr:`out_shape` if you - want to specify output shape dynamically. When - using actual_shape to specify output shape, one of - :attr:`out_shape` and :attr:`scale` should also be - set, otherwise errors would be occured in graph - constructing stage. + :attr:`out_shape` if you want to specify output + shape dynamically, because :attr:`actual_shape` + will be deprecated. When using actual_shape to + specify output shape, one of :attr:`out_shape` + and :attr:`scale` should also be set, otherwise + errors would be occured in graph constructing stage. Default: None align_corners(bool): ${align_corners_comment} @@ -8463,8 +8596,32 @@ def resize_nearest(input, .. code-block:: python import paddle.fluid as fluid - input = fluid.layers.data(name="input", shape=[3,6,9], dtype="float32") - out = fluid.layers.resize_nearest(input, out_shape=[12, 12]) + input = fluid.layers.data(name="input", shape=[3, 6, 9], dtype="float32") + # input.shape = [-1, 3, 6, 9], where -1 indicates batch size, and it will get the exact value in runtime. + + out0 = fluid.layers.resize_nearest(input, out_shape=[12, 12]) + # out0.shape = [-1, 3, 12, 12], it means out0.shape[0] = input.shape[0] in runtime. + + # out_shape is a list in which each element is a integer or a tensor Variable + dim1 = fluid.layers.data(name="dim1", shape=[1], dtype="int32", append_batch_size=False) + out1 = fluid.layers.resize_nearest(input, out_shape=[12, dim1]) + # out1.shape = [-1, 3, 12, -1] + + # out_shape is a 1-D tensor Variable + shape_tensor = fluid.layers.data(name="resize_shape", shape=[2], dtype="int32", append_batch_size=False) + out2 = fluid.layers.resize_nearest(input, out_shape=shape_tensor) + # out2.shape = [-1, 3, -1, -1] + + # when use actual_shape + actual_shape_tensor = fluid.layers.data(name="actual_shape_tensor", shape=[2], dtype="int32", append_batch_size=False) + out3 = fluid.layers.resize_nearest(input, out_shape=[4, 4], actual_shape=actual_shape_tensor) + # out3.shape = [-1, 3, 4, 4] + + # scale is a Variable + scale_tensor = fluid.layers.data(name="scale", shape=[1], dtype="float32", append_batch_size=False) + out4 = fluid.layers.resize_nearest(input, scale=scale_tensor) + # out4.shape = [-1, 3, -1, -1] + """ return image_resize(input, out_shape, scale, name, 'NEAREST', actual_shape, diff --git a/python/paddle/fluid/tests/unittests/test_bilinear_interp_op.py b/python/paddle/fluid/tests/unittests/test_bilinear_interp_op.py index 199a446a11a64fe1627ec5a80e340bd6073a0a30..068a83f4a6cf37b8a9d94eca0b6eb9897acbc38d 100644 --- a/python/paddle/fluid/tests/unittests/test_bilinear_interp_op.py +++ b/python/paddle/fluid/tests/unittests/test_bilinear_interp_op.py @@ -18,6 +18,7 @@ import unittest import numpy as np from op_test import OpTest import paddle.fluid.core as core +import paddle.fluid as fluid def bilinear_interp_np(input, @@ -359,5 +360,154 @@ class TestBilinearInterpZero(TestBilinearInterpOp): self.align_mode = 0 +class TestBilinearInterpOp_attr_tensor(OpTest): + def setUp(self): + self.out_size = None + self.actual_shape = None + self.init_test_case() + self.op_type = "bilinear_interp" + self.shape_by_1Dtensor = False + self.scale_by_1Dtensor = False + self.attrs = { + 'interp_method': self.interp_method, + 'align_corners': self.align_corners, + } + + input_np = np.random.random(self.input_shape).astype("float32") + self.inputs = {'X': input_np} + + if self.scale_by_1Dtensor: + self.inputs['Scale'] = np.array([self.scale]).astype("float32") + elif self.scale > 0: + out_h = int(self.input_shape[2] * self.scale) + out_w = int(self.input_shape[3] * self.scale) + self.attrs['scale'] = self.scale + else: + out_h = self.out_h + out_w = self.out_w + + if self.shape_by_1Dtensor: + self.inputs['OutSize'] = self.out_size + elif self.out_size is not None: + size_tensor = [] + for index, ele in enumerate(self.out_size): + size_tensor.append(("x" + str(index), np.ones( + (1)).astype('int32') * ele)) + self.inputs['SizeTensor'] = size_tensor + + self.attrs['out_h'] = self.out_h + self.attrs['out_w'] = self.out_w + output_np = bilinear_interp_np(input_np, out_h, out_w, self.out_size, + self.actual_shape, self.align_corners) + self.outputs = {'Out': output_np} + + def test_check_output(self): + self.check_output() + + def test_check_grad(self): + self.check_grad(['X'], 'Out', in_place=True) + + def init_test_case(self): + self.interp_method = 'bilinear' + self.input_shape = [2, 3, 4, 4] + self.out_h = 3 + self.out_w = 3 + self.scale = 0. + self.out_size = [3, 3] + self.align_corners = True + + +# out_size is a 1-D tensor +class TestBilinearInterp_attr_tensor_Case1(TestBilinearInterpOp_attr_tensor): + def init_test_case(self): + self.interp_method = 'bilinear' + self.input_shape = [3, 3, 9, 6] + self.out_h = 12 + self.out_w = 12 + self.scale = 0. + self.out_size = [8, 12] + self.align_corners = True + + +# scale is a 1-D tensor +class TestBilinearInterp_attr_tensor_Case2(TestBilinearInterpOp_attr_tensor): + def init_test_case(self): + self.interp_method = 'bilinear' + self.input_shape = [3, 2, 32, 16] + self.out_h = 64 + self.out_w = 32 + self.scale = 0. + self.out_size = np.array([66, 40]).astype("int32") + self.align_corners = True + self.shape_by_1Dtensor = True + + +# scale is a 1-D tensor +class TestBilinearInterp_attr_tensor_Case3(TestBilinearInterpOp_attr_tensor): + def init_test_case(self): + self.interp_method = 'bilinear' + self.input_shape = [3, 2, 32, 16] + self.out_h = 64 + self.out_w = 32 + self.scale = 2.0 + self.out_size = None + self.align_corners = True + self.scale_by_1Dtensor = True + + +class TestBilinearInterpOpAPI(OpTest): + def test_case(self): + x = fluid.layers.data(name="x", shape=[3, 6, 6], dtype="float32") + + dim = fluid.layers.data( + name="dim", shape=[1], dtype="int32", append_batch_size=False) + shape_tensor = fluid.layers.data( + name="shape_tensor", + shape=[2], + dtype="int32", + append_batch_size=False) + actual_size = fluid.layers.data( + name="actual_size", + shape=[2], + dtype="int32", + append_batch_size=False) + scale_tensor = fluid.layers.data( + name="scale_tensor", + shape=[1], + dtype="float32", + append_batch_size=False) + + out1 = fluid.layers.resize_bilinear(x, out_shape=[12, 12]) + out2 = fluid.layers.resize_bilinear(x, out_shape=[12, dim]) + out3 = fluid.layers.resize_bilinear(x, out_shape=shape_tensor) + out4 = fluid.layers.resize_bilinear( + x, out_shape=[4, 4], actual_shape=actual_size) + out5 = fluid.layers.resize_bilinear(x, scale=scale_tensor) + + x_data = np.random.random((1, 3, 6, 6)).astype("float32") + dim_data = np.array([12]).astype("int32") + shape_data = np.array([12, 12]).astype("int32") + actual_size_data = np.array([12, 12]).astype("int32") + scale_data = np.array([2.0]).astype("float32") + + place = core.CPUPlace() + exe = fluid.Executor(place) + results = exe.run(fluid.default_main_program(), + feed={ + "x": x_data, + "dim": dim_data, + "shape_tensor": shape_data, + "actual_size": actual_size_data, + "scale_tensor": scale_data + }, + fetch_list=[out1, out2, out3, out4, out5], + return_numpy=True) + + expect_res = bilinear_interp_np( + x_data, out_h=12, out_w=12, align_corners=True) + for res in results: + self.assertTrue(np.allclose(res, expect_res)) + + if __name__ == "__main__": unittest.main() diff --git a/python/paddle/fluid/tests/unittests/test_nearest_interp_op.py b/python/paddle/fluid/tests/unittests/test_nearest_interp_op.py index 163293621f9f64e3290ff964e068b63603b91c42..e3240f4c8c591f9879d76adab293025ed2513edd 100644 --- a/python/paddle/fluid/tests/unittests/test_nearest_interp_op.py +++ b/python/paddle/fluid/tests/unittests/test_nearest_interp_op.py @@ -18,6 +18,7 @@ import unittest import numpy as np from op_test import OpTest import paddle.fluid.core as core +import paddle.fluid as fluid def nearest_neighbor_interp_np(X, @@ -299,5 +300,155 @@ class TestNearestNeighborInterpScale3(TestNearestInterpOp): self.align_corners = True +class TestNearestInterpOp_attr_tensor(OpTest): + def setUp(self): + self.out_size = None + self.actual_shape = None + self.init_test_case() + self.op_type = "nearest_interp" + self.shape_by_1Dtensor = False + self.scale_by_1Dtensor = False + self.attrs = { + 'interp_method': self.interp_method, + 'align_corners': self.align_corners, + } + + input_np = np.random.random(self.input_shape).astype("float32") + self.inputs = {'X': input_np} + + if self.scale_by_1Dtensor: + self.inputs['Scale'] = np.array([self.scale]).astype("float32") + elif self.scale > 0: + out_h = int(self.input_shape[2] * self.scale) + out_w = int(self.input_shape[3] * self.scale) + self.attrs['scale'] = self.scale + else: + out_h = self.out_h + out_w = self.out_w + + if self.shape_by_1Dtensor: + self.inputs['OutSize'] = self.out_size + elif self.out_size is not None: + size_tensor = [] + for index, ele in enumerate(self.out_size): + size_tensor.append(("x" + str(index), np.ones( + (1)).astype('int32') * ele)) + self.inputs['SizeTensor'] = size_tensor + + self.attrs['out_h'] = self.out_h + self.attrs['out_w'] = self.out_w + output_np = nearest_neighbor_interp_np(input_np, out_h, out_w, + self.out_size, self.actual_shape, + self.align_corners) + self.outputs = {'Out': output_np} + + def test_check_output(self): + self.check_output() + + def test_check_grad(self): + self.check_grad(['X'], 'Out', in_place=True) + + def init_test_case(self): + self.interp_method = 'nearest' + self.input_shape = [2, 3, 4, 4] + self.out_h = 3 + self.out_w = 3 + self.scale = 0. + self.out_size = [3, 3] + self.align_corners = True + + +# out_size is a tensor list +class TestNearestInterp_attr_tensor_Case1(TestNearestInterpOp_attr_tensor): + def init_test_case(self): + self.interp_method = 'nearest' + self.input_shape = [3, 3, 9, 6] + self.out_h = 12 + self.out_w = 12 + self.scale = 0. + self.out_size = [8, 12] + self.align_corners = True + + +# out_size is a 1-D tensor +class TestNearestInterp_attr_tensor_Case2(TestNearestInterpOp_attr_tensor): + def init_test_case(self): + self.interp_method = 'nearest' + self.input_shape = [3, 2, 32, 16] + self.out_h = 64 + self.out_w = 32 + self.scale = 0. + self.out_size = np.array([66, 40]).astype("int32") + self.align_corners = True + self.shape_by_1Dtensor = True + + +# scale is a 1-D tensor +class TestNearestInterp_attr_tensor_Case3(TestNearestInterpOp_attr_tensor): + def init_test_case(self): + self.interp_method = 'nearest' + self.input_shape = [3, 2, 32, 16] + self.out_h = 64 + self.out_w = 32 + self.scale = 2.0 + self.out_size = None + self.align_corners = True + self.scale_by_1Dtensor = True + + +class TestNearestAPI(OpTest): + def test_case(self): + x = fluid.layers.data(name="x", shape=[3, 6, 6], dtype="float32") + + dim = fluid.layers.data( + name="dim", shape=[1], dtype="int32", append_batch_size=False) + shape_tensor = fluid.layers.data( + name="shape_tensor", + shape=[2], + dtype="int32", + append_batch_size=False) + actual_size = fluid.layers.data( + name="actual_size", + shape=[2], + dtype="int32", + append_batch_size=False) + scale_tensor = fluid.layers.data( + name="scale_tensor", + shape=[1], + dtype="float32", + append_batch_size=False) + + out1 = fluid.layers.resize_nearest(x, out_shape=[12, 12]) + out2 = fluid.layers.resize_nearest(x, out_shape=[12, dim]) + out3 = fluid.layers.resize_nearest(x, out_shape=shape_tensor) + out4 = fluid.layers.resize_nearest( + x, out_shape=[4, 4], actual_shape=actual_size) + out5 = fluid.layers.resize_nearest(x, scale=scale_tensor) + + x_data = np.random.random((1, 3, 6, 6)).astype("float32") + dim_data = np.array([12]).astype("int32") + shape_data = np.array([12, 12]).astype("int32") + actual_size_data = np.array([12, 12]).astype("int32") + scale_data = np.array([2.0]).astype("float32") + + place = core.CPUPlace() + exe = fluid.Executor(place) + results = exe.run(fluid.default_main_program(), + feed={ + "x": x_data, + "dim": dim_data, + "shape_tensor": shape_data, + "actual_size": actual_size_data, + "scale_tensor": scale_data + }, + fetch_list=[out1, out2, out3, out4, out5], + return_numpy=True) + + expect_res = nearest_neighbor_interp_np( + x_data, out_h=12, out_w=12, align_corners=True) + for res in results: + self.assertTrue(np.allclose(res, expect_res)) + + if __name__ == "__main__": unittest.main() diff --git a/python/paddle/fluid/tests/unittests/test_trilinear_interp_op.py b/python/paddle/fluid/tests/unittests/test_trilinear_interp_op.py index 1d712e8485aa9a048ca75f94fe48cd5652adc102..1f418834562c5b9074243fe8925bb6854c822fac 100644 --- a/python/paddle/fluid/tests/unittests/test_trilinear_interp_op.py +++ b/python/paddle/fluid/tests/unittests/test_trilinear_interp_op.py @@ -18,6 +18,7 @@ import unittest import numpy as np from op_test import OpTest import paddle.fluid.core as core +import paddle.fluid as fluid def trilinear_interp_np(input, @@ -424,5 +425,166 @@ class TestTrilinearInterpZero(TestTrilinearInterpOp): self.align_mode = 0 +class TestTrilinearInterpOp_attr_tensor(OpTest): + def setUp(self): + self.out_size = None + self.actual_shape = None + self.init_test_case() + self.op_type = "trilinear_interp" + self.shape_by_1Dtensor = False + self.scale_by_1Dtensor = False + self.attrs = { + 'interp_method': self.interp_method, + 'align_corners': self.align_corners, + 'align_mode': self.align_mode + } + + input_np = np.random.random(self.input_shape).astype("float32") + self.inputs = {'X': input_np} + + if self.scale_by_1Dtensor: + self.inputs['Scale'] = np.array([self.scale]).astype("float32") + elif self.scale > 0: + out_d = int(self.input_shape[2] * self.scale) + out_h = int(self.input_shape[3] * self.scale) + out_w = int(self.input_shape[4] * self.scale) + self.attrs['scale'] = self.scale + else: + out_d = self.out_d + out_h = self.out_h + out_w = self.out_w + + if self.shape_by_1Dtensor: + self.inputs['OutSize'] = self.out_size + elif self.out_size is not None: + size_tensor = [] + for index, ele in enumerate(self.out_size): + size_tensor.append(("x" + str(index), np.ones( + (1)).astype('int32') * ele)) + self.inputs['SizeTensor'] = size_tensor + + self.attrs['out_d'] = self.out_d + self.attrs['out_h'] = self.out_h + self.attrs['out_w'] = self.out_w + output_np = trilinear_interp_np(input_np, out_d, out_h, out_w, + self.out_size, self.actual_shape, + self.align_corners, self.align_mode) + self.outputs = {'Out': output_np} + + def test_check_output(self): + self.check_output() + + def test_check_grad(self): + self.check_grad(['X'], 'Out', in_place=True) + + def init_test_case(self): + self.interp_method = 'trilinear' + self.input_shape = [2, 3, 4, 4, 4] + self.out_d = 2 + self.out_h = 3 + self.out_w = 3 + self.scale = 0. + self.out_size = [2, 3, 3] + self.align_corners = True + self.align_mode = 1 + + +# out_size is a 1-D tensor +class TestTrilinearInterp_attr_tensor_Case1(TestTrilinearInterpOp_attr_tensor): + def init_test_case(self): + self.interp_method = 'trilinear' + self.input_shape = [3, 2, 9, 6, 8] + self.out_d = 32 + self.out_h = 16 + self.out_w = 8 + self.scale = 0.3 + self.out_size = [12, 4, 4] + self.align_corners = True + self.align_mode = 1 + + +# scale is a 1-D tensor +class TestTrilinearInterp_attr_tensor_Case2(TestTrilinearInterpOp_attr_tensor): + def init_test_case(self): + self.interp_method = 'trilinear' + self.input_shape = [2, 3, 8, 8, 4] + self.out_d = 16 + self.out_h = 12 + self.out_w = 4 + self.scale = 0. + self.out_size = [16, 4, 10] + self.align_corners = True + self.align_mode = 1 + self.shape_by_1Dtensor = True + + +# scale is a 1-D tensor +class TestTrilinearInterp_attr_tensor_Case3(TestTrilinearInterpOp_attr_tensor): + def init_test_case(self): + self.interp_method = 'trilinear' + self.input_shape = [2, 3, 8, 8, 4] + self.out_d = 16 + self.out_h = 16 + self.out_w = 8 + self.scale = 2.0 + self.out_size = None + self.align_corners = True + self.align_mode = 1 + self.scale_by_1Dtensor = True + + +class TestTrilinearInterpAPI(OpTest): + def test_case(self): + x = fluid.layers.data(name="x", shape=[3, 6, 9, 4], dtype="float32") + + dim = fluid.layers.data(name="dim", shape=[1], dtype="int32") + shape_tensor = fluid.layers.data( + name="shape_tensor", + shape=[3], + dtype="int32", + append_batch_size=False) + actual_size = fluid.layers.data( + name="actual_size", + shape=[3], + dtype="int32", + append_batch_size=False) + scale_tensor = fluid.layers.data( + name="scale_tensor", + shape=[1], + dtype="float32", + append_batch_size=False) + + out1 = fluid.layers.resize_trilinear(x, out_shape=[12, 18, 8]) + out2 = fluid.layers.resize_trilinear(x, out_shape=[12, dim, 8]) + out3 = fluid.layers.resize_trilinear(x, out_shape=shape_tensor) + out4 = fluid.layers.resize_trilinear( + x, out_shape=[4, 4, 8], actual_shape=actual_size) + out5 = fluid.layers.resize_trilinear(x, scale=scale_tensor) + + x_data = np.random.random((1, 3, 6, 9, 4)).astype("float32") + dim_data = np.array([18]).astype("int32") + shape_data = np.array([12, 18, 8]).astype("int32") + actual_size_data = np.array([12, 18, 8]).astype("int32") + scale_data = np.array([2.0]).astype("float32") + + place = core.CPUPlace() + exe = fluid.Executor(place) + results = exe.run(fluid.default_main_program(), + feed={ + "x": x_data, + "dim": dim_data, + "shape_tensor": shape_data, + "actual_size": actual_size_data, + "scale_tensor": scale_data + }, + fetch_list=[out1, out2, out3, out4, out5], + return_numpy=True) + + expect_res = trilinear_interp_np( + x_data, out_d=12, out_h=18, out_w=8, align_mode=1) + for res in results: + self.assertTrue(np.allclose(res, expect_res)) + + if __name__ == "__main__": unittest.main()