From 4e67cd17304a935b456352d3778b276c79b6d47b Mon Sep 17 00:00:00 2001 From: zhulei <563755780@qq.com> Date: Fri, 3 Sep 2021 17:46:53 +0800 Subject: [PATCH] [NPU] Add huber_loss op (#34826) * [NPU] Add huber_loss op * [NPU] Add huber_loss op * [NPU] Add huber_loss p[ * [NPU] Add huber_loss --- paddle/fluid/operators/huber_loss_op_npu.cc | 128 +++++++++++++++ .../unittests/npu/test_huber_loss_op_npu.py | 151 ++++++++++++++++++ 2 files changed, 279 insertions(+) create mode 100644 paddle/fluid/operators/huber_loss_op_npu.cc create mode 100644 python/paddle/fluid/tests/unittests/npu/test_huber_loss_op_npu.py diff --git a/paddle/fluid/operators/huber_loss_op_npu.cc b/paddle/fluid/operators/huber_loss_op_npu.cc new file mode 100644 index 0000000000..a942615594 --- /dev/null +++ b/paddle/fluid/operators/huber_loss_op_npu.cc @@ -0,0 +1,128 @@ +/* Copyright (c) 2021 PaddlePaddle Authors. All Rights Reserved. +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the Licnse. */ + +#include "paddle/fluid/operators/huber_loss_op.h" +#include "paddle/fluid/operators/npu_op_runner.h" + +namespace paddle { +namespace operators { + +using Tensor = framework::Tensor; + +template +void HuberLossSub(const platform::Place& place, const aclrtStream& stream, + const Tensor* x, const Tensor* y, Tensor* z) { + // Calculate z = x - y + z->mutable_data(x->dims(), place); + const auto& runner = NpuOpRunner("Sub", {*x, *y}, {*z}, {}); + runner.Run(stream); +} + +template +void HuberLossMuls(const platform::Place& place, const aclrtStream& stream, + const Tensor* x, float scalar, Tensor* y) { + // Calculate y = x + scale + y->mutable_data(x->dims(), place); + const auto& runner = NpuOpRunner("Muls", {*x}, {*y}, {{"value", scalar}}); + runner.Run(stream); +} + +template +void HuberLossZerosLike(const platform::Place& place, const aclrtStream& stream, + const Tensor* x, Tensor* y) { + y->mutable_data(x->dims(), place); + const auto& runner = NpuOpRunner("ZerosLike", {*x}, {*y}, {}); + runner.Run(stream); +} + +template +void HuberLossSmoothL1Loss(const platform::Place& place, + const aclrtStream& stream, const Tensor* x, + const Tensor* y, float delta, Tensor* z) { + z->mutable_data(x->dims(), place); + const auto& runner = + NpuOpRunner("SmoothL1Loss", {*x, *y}, {*z}, {{"sigma", delta}}); + runner.Run(stream); +} + +template +void HuberLossSmoothL1LossGrad(const platform::Place& place, + const aclrtStream& stream, const Tensor* pred, + const Tensor* lab, const Tensor* dout, + float sigma, Tensor* grad) { + grad->mutable_data(pred->dims(), place); + const auto& runner = NpuOpRunner("SmoothL1LossGrad", {*pred, *lab, *dout}, + {*grad}, {{"sigma", sigma}}); + runner.Run(stream); +} + +template +class HuberLossNPUKernel : public framework::OpKernel { + public: + void Compute(const framework::ExecutionContext& ctx) const override { + auto* in0 = ctx.Input("X"); + auto* in1 = ctx.Input("Y"); + auto* residual = ctx.Output("Residual"); + auto* out = ctx.Output("Out"); + auto delta = ctx.Attr("delta"); + + auto stream = + ctx.template device_context() + .stream(); + auto place = ctx.GetPlace(); + HuberLossSub(place, stream, in1, in0, residual); + + HuberLossSmoothL1Loss(place, stream, in0, in1, delta, out); + HuberLossMuls(place, stream, out, delta, out); + } +}; + +template +class HuberLossGradNPUKernel : public framework::OpKernel { + public: + void Compute(const framework::ExecutionContext& ctx) const override { + auto* residual = ctx.Input("Residual"); + auto* dout = ctx.Input(framework::GradVarName("Out")); + auto* dx = ctx.Output(framework::GradVarName("X")); + auto* dy = ctx.Output(framework::GradVarName("Y")); + auto delta = ctx.Attr("delta"); + + auto stream = + ctx.template device_context() + .stream(); + auto place = ctx.GetPlace(); + + Tensor t_grad_rd; + if (dx || dy) { + Tensor t_zero; + HuberLossZerosLike(place, stream, residual, &t_zero); + HuberLossSmoothL1LossGrad(place, stream, residual, &t_zero, dout, + delta, &t_grad_rd); + } + if (dx) { + HuberLossMuls(place, stream, &t_grad_rd, -delta, dx); + } + if (dy) { + HuberLossMuls(place, stream, &t_grad_rd, delta, dy); + } + } +}; + +} // namespace operators +} // namespace paddle + +namespace ops = paddle::operators; +namespace plat = paddle::platform; + +REGISTER_OP_NPU_KERNEL(huber_loss, ops::HuberLossNPUKernel, + ops::HuberLossNPUKernel); +REGISTER_OP_NPU_KERNEL(huber_loss_grad, ops::HuberLossGradNPUKernel, + ops::HuberLossGradNPUKernel); diff --git a/python/paddle/fluid/tests/unittests/npu/test_huber_loss_op_npu.py b/python/paddle/fluid/tests/unittests/npu/test_huber_loss_op_npu.py new file mode 100644 index 0000000000..1c9f499d22 --- /dev/null +++ b/python/paddle/fluid/tests/unittests/npu/test_huber_loss_op_npu.py @@ -0,0 +1,151 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from __future__ import print_function + +import unittest +import numpy as np +import sys +sys.path.append("..") +from op_test import OpTest +import paddle +import paddle.fluid as fluid +from paddle.fluid import compiler, Program, program_guard + +paddle.enable_static() + + +def huber_loss_forward(val, delta): + abs_val = abs(val) + if abs_val <= delta: + return 0.5 * val * val + else: + return delta * (abs_val - 0.5 * delta) + + +@unittest.skipIf(not paddle.is_compiled_with_npu(), + "core is not compiled with NPU") +class TestHuberLossOp(OpTest): + def setUp(self): + self.set_npu() + self.op_type = 'huber_loss' + self.place = paddle.NPUPlace(0) + + self.init_dtype() + + self.set_inputs() + self.set_attrs() + self.set_outputs() + + def set_inputs(self): + shape = self.set_shape() + x = np.random.uniform(0, 1., shape).astype(self.dtype) + y = np.random.uniform(0, 1., shape).astype(self.dtype) + self.inputs = { + 'X': OpTest.np_dtype_to_fluid_dtype(x), + 'Y': OpTest.np_dtype_to_fluid_dtype(y) + } + + def set_attrs(self): + self.attrs = {'delta': 0.5} + + def set_outputs(self): + delta = self.attrs['delta'] + shape = self.set_shape() + residual = self.inputs['Y'] - self.inputs['X'] + loss = np.vectorize(huber_loss_forward)(residual, + delta).astype(self.dtype) + self.outputs = {'Residual': residual, 'Out': loss.reshape(shape)} + + def set_shape(self): + return (100, 1) + + def set_npu(self): + self.__class__.use_npu = True + + def init_dtype(self): + self.dtype = np.float32 + + def test_check_output(self): + self.check_output_with_place(self.place) + + def test_check_grad_normal(self): + if self.dtype == np.float16: + return + self.check_grad_with_place(self.place, ['X', 'Y'], 'Out') + + def test_check_grad_ingore_x(self): + if self.dtype == np.float16: + return + self.check_grad_with_place( + self.place, ['Y'], + 'Out', + max_relative_error=0.008, + no_grad_set=set("residual")) + + def test_check_grad_ingore_y(self): + if self.dtype == np.float16: + return + self.check_grad_with_place( + self.place, ['X'], + 'Out', + max_relative_error=0.008, + no_grad_set=set('residual')) + + +def TestHuberLossOp1(TestHuberLossOp): + def set_shape(self): + return (64) + + +def TestHuberLossOp2(TestHuberLossOp): + def set_shape(self): + return (6, 6) + + +def TestHuberLossOp3(TestHuberLossOp): + def set_shape(self): + return (6, 6, 1) + + +def TestHuberLossOpFP16(TestHuberLossOp): + def init_dtype(self): + self.dtype = np.float16 + + +@unittest.skipIf(not paddle.is_compiled_with_npu(), + "core is not compiled with NPU") +class TestHuberLossOpError(unittest.TestCase): + def test_errors(self): + with program_guard(Program(), Program()): + # the input and label must be Variable + xw = np.random.random((6, 6)).astype("float32") + xr = fluid.data(name='xr', shape=[None, 6], dtype="float32") + lw = np.random.random((6, 6)).astype("float32") + lr = fluid.data(name='lr', shape=[None, 6], dtype="float32") + delta = 1.0 + self.assertRaises(TypeError, fluid.layers.huber_loss, xr, lw, delta) + self.assertRaises(TypeError, fluid.layers.huber_loss, xw, lr, delta) + + # the dtype of input and label must be float32 or float64 + xw2 = fluid.data(name='xw2', shape=[None, 6], dtype="int32") + lw2 = fluid.data(name='lw2', shape=[None, 6], dtype="int32") + self.assertRaises(TypeError, fluid.layers.huber_loss, xw2, lr, + delta) + self.assertRaises(TypeError, fluid.layers.huber_loss, xr, lw2, + delta) + + +if __name__ == '__main__': + unittest.main() -- GitLab