From e598567583686a49ad48e0b407dac3b9021859b5 Mon Sep 17 00:00:00 2001 From: Yang Zhang Date: Mon, 20 Apr 2020 17:26:18 +0800 Subject: [PATCH] [Cherry-pick Release 2.0] Add `paddle.nn.loss.MSELoss` (#23981) * Add `paddle.nn.loss.MSELoss` test=develop * Move to `nn/layer/loss.py` test=develop * Fix dygraph test=develop * Add test test=develop * Increase numel in test test=develop * Add test for input with different dimensions test=develop --- .../fluid/tests/unittests/test_mse_loss.py | 115 +++++++++++++++++- python/paddle/nn/layer/loss.py | 93 +++++++++++++- 2 files changed, 206 insertions(+), 2 deletions(-) diff --git a/python/paddle/fluid/tests/unittests/test_mse_loss.py b/python/paddle/fluid/tests/unittests/test_mse_loss.py index 7ad8086bd87..89052396cf9 100644 --- a/python/paddle/fluid/tests/unittests/test_mse_loss.py +++ b/python/paddle/fluid/tests/unittests/test_mse_loss.py @@ -16,7 +16,7 @@ from __future__ import print_function import unittest import numpy as np -import sys +import paddle import paddle.fluid.core as core import paddle.fluid as fluid import paddle.fluid.layers as layers @@ -64,5 +64,118 @@ class TestMseInvalidInput(unittest.TestCase): self.assertRaises(TypeError, test_invalid_label) +class TestNNMseLoss(unittest.TestCase): + def test_NNMseLoss_mean(self): + for dim in [[10, 10], [2, 10, 10], [3, 3, 10, 10]]: + input_np = np.random.uniform(0.1, 0.5, dim).astype("float32") + label_np = np.random.uniform(0.1, 0.5, dim).astype("float32") + prog = fluid.Program() + startup_prog = fluid.Program() + place = fluid.CUDAPlace(0) if fluid.core.is_compiled_with_cuda( + ) else fluid.CPUPlace() + with fluid.program_guard(prog, startup_prog): + input = fluid.layers.data( + name='input', shape=dim, dtype='float32') + label = fluid.layers.data( + name='label', shape=dim, dtype='float32') + mse_loss = paddle.nn.loss.MSELoss() + ret = mse_loss(input, label) + + exe = fluid.Executor(place) + static_result = exe.run( + prog, + feed={"input": input_np, + "label": label_np}, + fetch_list=[ret]) + + with fluid.dygraph.guard(): + mse_loss = paddle.nn.loss.MSELoss() + dy_ret = mse_loss( + fluid.dygraph.to_variable(input_np), + fluid.dygraph.to_variable(label_np)) + dy_result = dy_ret.numpy() + + sub = input_np - label_np + expected = np.mean(sub * sub) + self.assertTrue(np.allclose(static_result, expected)) + self.assertTrue(np.allclose(static_result, dy_result)) + self.assertTrue(np.allclose(dy_result, expected)) + self.assertTrue(dy_result.shape, [1]) + + def test_NNMseLoss_sum(self): + for dim in [[10, 10], [2, 10, 10], [3, 3, 10, 10]]: + input_np = np.random.uniform(0.1, 0.5, dim).astype("float32") + label_np = np.random.uniform(0.1, 0.5, dim).astype("float32") + prog = fluid.Program() + startup_prog = fluid.Program() + place = fluid.CUDAPlace(0) if fluid.core.is_compiled_with_cuda( + ) else fluid.CPUPlace() + with fluid.program_guard(prog, startup_prog): + input = fluid.layers.data( + name='input', shape=dim, dtype='float32') + label = fluid.layers.data( + name='label', shape=dim, dtype='float32') + mse_loss = paddle.nn.loss.MSELoss(reduction='sum') + ret = mse_loss(input, label) + + exe = fluid.Executor(place) + static_result = exe.run( + prog, + feed={"input": input_np, + "label": label_np}, + fetch_list=[ret]) + + with fluid.dygraph.guard(): + mse_loss = paddle.nn.loss.MSELoss(reduction='sum') + dy_ret = mse_loss( + fluid.dygraph.to_variable(input_np), + fluid.dygraph.to_variable(label_np)) + dy_result = dy_ret.numpy() + + sub = input_np - label_np + expected = np.sum(sub * sub) + self.assertTrue(np.allclose(static_result, expected)) + self.assertTrue(np.allclose(static_result, dy_result)) + self.assertTrue(np.allclose(dy_result, expected)) + self.assertTrue(dy_result.shape, [1]) + + def test_NNMseLoss_none(self): + for dim in [[10, 10], [2, 10, 10], [3, 3, 10, 10]]: + input_np = np.random.uniform(0.1, 0.5, dim).astype("float32") + label_np = np.random.uniform(0.1, 0.5, dim).astype("float32") + prog = fluid.Program() + startup_prog = fluid.Program() + place = fluid.CUDAPlace(0) if fluid.core.is_compiled_with_cuda( + ) else fluid.CPUPlace() + with fluid.program_guard(prog, startup_prog): + input = fluid.layers.data( + name='input', shape=dim, dtype='float32') + label = fluid.layers.data( + name='label', shape=dim, dtype='float32') + mse_loss = paddle.nn.loss.MSELoss(reduction='none') + ret = mse_loss(input, label) + + exe = fluid.Executor(place) + static_result = exe.run( + prog, + feed={"input": input_np, + "label": label_np}, + fetch_list=[ret]) + + with fluid.dygraph.guard(): + mse_loss = paddle.nn.loss.MSELoss(reduction='none') + dy_ret = mse_loss( + fluid.dygraph.to_variable(input_np), + fluid.dygraph.to_variable(label_np)) + dy_result = dy_ret.numpy() + + sub = input_np - label_np + expected = (sub * sub) + self.assertTrue(np.allclose(static_result, expected)) + self.assertTrue(np.allclose(static_result, dy_result)) + self.assertTrue(np.allclose(dy_result, expected)) + self.assertTrue(dy_result.shape, [1]) + + if __name__ == "__main__": unittest.main() diff --git a/python/paddle/nn/layer/loss.py b/python/paddle/nn/layer/loss.py index 65bb9215e29..38d53ff9195 100644 --- a/python/paddle/nn/layer/loss.py +++ b/python/paddle/nn/layer/loss.py @@ -17,7 +17,7 @@ import paddle.fluid as fluid __all__ = [ #'NCELoss', 'CrossEntropyLoss', - # 'MSELoss', + 'MSELoss', 'L1Loss', # 'NLLLoss', 'BCELoss' @@ -135,6 +135,97 @@ class CrossEntropyLoss(fluid.dygraph.Layer): return out +class MSELoss(fluid.dygraph.layers.Layer): + """ + **Mean Square Error Loss** + Computes the mean square error (squared L2 norm) of given input and label. + + If :attr:`reduction` is set to ``'none'``, loss is calculated as: + + .. math:: + Out = (input - label)^2 + + If :attr:`reduction` is set to ``'mean'``, loss is calculated as: + + .. math:: + Out = \operatorname{mean}((input - label)^2) + + If :attr:`reduction` is set to ``'sum'``, loss is calculated as: + + .. math:: + Out = \operatorname{sum}((input - label)^2) + + where `input` and `label` are `float32` tensors of arbitrary shapes. + + Parameters: + reduction (string, optional): The reduction method for the output, + could be 'none' | 'mean' | 'sum'. + 'none': no reduction will be applied + 'mean': the output will be averaged + 'sum': the output will be summed + + Examples: + .. code-block:: python + import numpy as np + import paddle + from paddle import fluid + import paddle.fluid.dygraph as dg + + mse_loss = paddle.nn.loss.MSELoss() + input = fluid.data(name="input", shape=[1]) + label = fluid.data(name="label", shape=[1]) + place = fluid.CPUPlace() + input_data = np.array([1.5]).astype("float32") + label_data = np.array([1.7]).astype("float32") + + # declarative mode + output = mse_loss(input,label) + exe = fluid.Executor(place) + exe.run(fluid.default_startup_program()) + output_data = exe.run( + fluid.default_main_program(), + feed={"input":input_data, "label":label_data}, + fetch_list=[output], + return_numpy=True) + print(output_data) + # [array([0.04000002], dtype=float32)] + + # imperative mode + with dg.guard(place) as g: + input = dg.to_variable(input_data) + label = dg.to_variable(label_data) + output = mse_loss(input, label) + print(output.numpy()) + # [0.04000002] + """ + + def __init__(self, reduction='mean'): + super(MSELoss, self).__init__() + if reduction not in ['sum', 'mean', 'none']: + raise ValueError( + "'reduction' in 'MSELoss' should be 'sum', 'mean' or 'none', " + "but received {}.".format(reduction)) + self.reduction = reduction + + def forward(self, input, label): + if not fluid.framework.in_dygraph_mode(): + fluid.data_feeder.check_variable_and_dtype(input, 'input', + ['float32'], 'MSELoss') + fluid.data_feeder.check_variable_and_dtype(label, 'label', + ['float32'], 'MSELoss') + + square_out = fluid.layers.square( + fluid.layers.elementwise_sub(input, label)) + if self.reduction == 'none': + return square_out + + reduce_op = 'reduce_mean' + if self.reduction == 'sum': + reduce_op = 'reduce_sum' + + return getattr(fluid.layers, reduce_op)(square_out) + + class L1Loss(fluid.dygraph.Layer): """ This interface is used to construct a callable object of the ``L1Loss`` class. -- GitLab