未验证 提交 e52e7944 编写于 作者: G Guanghua Yu 提交者: GitHub

add paddle.nn.SmoothL1Loss,test=develop (#26398)

上级 e3612de8
# Copyright (c) 2020 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 paddle
import paddle.fluid as fluid
import numpy as np
import unittest
def smooth_l1_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)
def smooth_l1_loss_np(input, label, reduction='mean', delta=1.0):
diff = input - label
out = np.vectorize(smooth_l1_loss_forward)(diff, delta)
if reduction == 'sum':
return np.sum(out)
elif reduction == 'mean':
return np.mean(out)
elif reduction == 'none':
return out
class SmoothL1Loss(unittest.TestCase):
def setUp(self):
np.random.seed(123)
def test_smooth_l1_loss_mean(self):
input_np = np.random.random([100, 200]).astype(np.float32)
label_np = np.random.random([100, 200]).astype(np.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.data(name='input', shape=[100, 200], dtype='float32')
label = fluid.data(name='label', shape=[100, 200], dtype='float32')
smooth_l1_loss = paddle.nn.loss.SmoothL1Loss()
ret = smooth_l1_loss(input, label)
exe = fluid.Executor(place)
static_ret = exe.run(prog,
feed={
'input': input_np,
'label': label_np,
},
fetch_list=[ret])
self.assertIsNotNone(static_ret)
with fluid.dygraph.guard():
smooth_l1_loss = paddle.nn.loss.SmoothL1Loss()
dy_ret = smooth_l1_loss(
fluid.dygraph.to_variable(input_np),
fluid.dygraph.to_variable(label_np))
dy_ret_value = dy_ret.numpy()
self.assertIsNotNone(dy_ret_value)
expected = smooth_l1_loss_np(input_np, label_np, reduction='mean')
self.assertTrue(np.allclose(static_ret, dy_ret_value))
self.assertTrue(np.allclose(static_ret, expected))
self.assertTrue(np.allclose(dy_ret_value, expected))
def test_smooth_l1_loss_sum(self):
input_np = np.random.random([100, 200]).astype(np.float32)
label_np = np.random.random([100, 200]).astype(np.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.data(name='input', shape=[100, 200], dtype='float32')
label = fluid.data(name='label', shape=[100, 200], dtype='float32')
smooth_l1_loss = paddle.nn.loss.SmoothL1Loss(reduction='sum')
ret = smooth_l1_loss(input, label)
exe = fluid.Executor(place)
static_ret = exe.run(prog,
feed={
'input': input_np,
'label': label_np,
},
fetch_list=[ret])
self.assertIsNotNone(static_ret)
with fluid.dygraph.guard():
smooth_l1_loss = paddle.nn.loss.SmoothL1Loss(reduction='sum')
dy_ret = smooth_l1_loss(
fluid.dygraph.to_variable(input_np),
fluid.dygraph.to_variable(label_np))
dy_ret_value = dy_ret.numpy()
self.assertIsNotNone(dy_ret_value)
expected = smooth_l1_loss_np(input_np, label_np, reduction='sum')
self.assertTrue(np.allclose(static_ret, dy_ret_value))
self.assertTrue(np.allclose(static_ret, expected))
self.assertTrue(np.allclose(dy_ret_value, expected))
def test_smooth_l1_loss_none(self):
input_np = np.random.random([100, 200]).astype(np.float32)
label_np = np.random.random([100, 200]).astype(np.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.data(name='input', shape=[100, 200], dtype='float32')
label = fluid.data(name='label', shape=[100, 200], dtype='float32')
smooth_l1_loss = paddle.nn.loss.SmoothL1Loss(reduction='none')
ret = smooth_l1_loss(input, label)
exe = fluid.Executor(place)
static_ret = exe.run(prog,
feed={
'input': input_np,
'label': label_np,
},
fetch_list=[ret])
self.assertIsNotNone(static_ret)
with fluid.dygraph.guard():
smooth_l1_loss = paddle.nn.loss.SmoothL1Loss(reduction='none')
dy_ret = smooth_l1_loss(
fluid.dygraph.to_variable(input_np),
fluid.dygraph.to_variable(label_np))
dy_ret_value = dy_ret.numpy()
self.assertIsNotNone(dy_ret_value)
expected = smooth_l1_loss_np(input_np, label_np, reduction='none')
self.assertTrue(np.allclose(static_ret, dy_ret_value))
self.assertTrue(np.allclose(static_ret, expected))
self.assertTrue(np.allclose(dy_ret_value, expected))
def test_smooth_l1_loss_delta(self):
input_np = np.random.random([100, 200]).astype(np.float32)
label_np = np.random.random([100, 200]).astype(np.float32)
delta = np.random.rand()
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.data(name='input', shape=[100, 200], dtype='float32')
label = fluid.data(name='label', shape=[100, 200], dtype='float32')
smooth_l1_loss = paddle.nn.loss.SmoothL1Loss(delta=delta)
ret = smooth_l1_loss(input, label)
exe = fluid.Executor(place)
static_ret = exe.run(prog,
feed={
'input': input_np,
'label': label_np,
},
fetch_list=[ret])
self.assertIsNotNone(static_ret)
with fluid.dygraph.guard():
smooth_l1_loss = paddle.nn.loss.SmoothL1Loss(delta=delta)
dy_ret = smooth_l1_loss(
fluid.dygraph.to_variable(input_np),
fluid.dygraph.to_variable(label_np))
dy_ret_value = dy_ret.numpy()
self.assertIsNotNone(dy_ret_value)
expected = smooth_l1_loss_np(input_np, label_np, delta=delta)
self.assertTrue(np.allclose(static_ret, dy_ret_value))
self.assertTrue(np.allclose(static_ret, expected))
self.assertTrue(np.allclose(dy_ret_value, expected))
if __name__ == "__main__":
unittest.main()
......@@ -101,6 +101,7 @@ from .layer.loss import NLLLoss #DEFINE_ALIAS
from .layer.loss import BCELoss #DEFINE_ALIAS
from .layer.loss import KLDivLoss #DEFINE_ALIAS
from .layer.loss import MarginRankingLoss #DEFINE_ALIAS
from .layer.loss import SmoothL1Loss #DEFINE_ALIAS
from .layer.norm import BatchNorm #DEFINE_ALIAS
from .layer.norm import SyncBatchNorm #DEFINE_ALIAS
from .layer.norm import GroupNorm #DEFINE_ALIAS
......
......@@ -140,6 +140,7 @@ from .loss import sampled_softmax_with_cross_entropy #DEFINE_ALIAS
from .loss import sigmoid_cross_entropy_with_logits #DEFINE_ALIAS
from .loss import sigmoid_focal_loss #DEFINE_ALIAS
from .loss import smooth_l1 #DEFINE_ALIAS
from .loss import smooth_l1_loss #DEFINE_ALIAS
from .loss import softmax_with_cross_entropy #DEFINE_ALIAS
from .loss import square_error_cost #DEFINE_ALIAS
from .loss import ssd_loss #DEFINE_ALIAS
......
......@@ -65,6 +65,7 @@ __all__ = [
'sigmoid_cross_entropy_with_logits',
'sigmoid_focal_loss',
'smooth_l1',
'smooth_l1_loss',
'softmax_with_cross_entropy',
'square_error_cost',
'ssd_loss',
......@@ -72,6 +73,84 @@ __all__ = [
]
def smooth_l1_loss(input, label, reduction='mean', delta=1.0, name=None):
"""
This operator calculates smooth_l1_loss. Creates a criterion that uses a squared
term if the absolute element-wise error falls below 1 and an L1 term otherwise.
In some cases it can prevent exploding gradients and it is more robust and less
sensitivity to outliers. Also known as the Huber loss:
.. math::
loss(x,y)=\\frac{1}{n}\\sum_{i}z_i
where z_i is given by:
.. math::
\\mathop{z_i}=\\left\\{\\begin{array}{rcl}
0.5(x_i - y_i)^2 & & {if |x_i - y_i| < delta} \\\\
delta * |x_i - y_i| - 0.5 * delta^2 & & {otherwise}
\\end{array} \\right.
Parameters:
input (Tensor): Input tensor, the data type is float32 or float64. Shape is
(N, C), where C is number of classes, and if shape is more than 2D, this
is (N, C, D1, D2,..., Dk), k >= 1.
label (Tensor): Label tensor, the data type is float32 or float64. The shape of label
is the same as the shape of input.
reduction (str, optional): Indicate how to average the loss by batch_size,
the candicates are ``'none'`` | ``'mean'`` | ``'sum'``.
If :attr:`reduction` is ``'mean'``, the reduced mean loss is returned;
If :attr:`reduction` is ``'sum'``, the reduced sum loss is returned.
If :attr:`reduction` is ``'none'``, the unreduced loss is returned.
Default is ``'mean'``.
delta (float, optional): Specifies the hyperparameter delta to be used.
The value determines how large the errors need to be to use L1. Errors
smaller than delta are minimized with L2. Parameter is ignored for
negative/zero values. Default = 1.0
name (str, optional): Name for the operation (optional, default is
None). For more information, please refer to :ref:`api_guide_Name`.
Returns:
The tensor variable storing the smooth_l1_loss of input and label.
Return type: Tensor.
Examples:
.. code-block:: python
import paddle
import numpy as np
paddle.disable_static()
input_data = np.random.rand(3,3).astype("float32")
label_data = np.random.rand(3,3).astype("float32")
input = paddle.to_tensor(input_data)
label = paddle.to_tensor(label_data)
output = paddle.nn.functioanl.smooth_l1_loss(input, label)
print(output.numpy())
"""
fluid.data_feeder.check_variable_and_dtype(
input, 'input', ['float32', 'float64'], 'smooth_l1_loss')
fluid.data_feeder.check_variable_and_dtype(
label, 'label', ['float32', 'float64'], 'smooth_l1_loss')
out = huber_loss(input=input, label=label, delta=delta)
if reduction not in ['sum', 'mean', 'none']:
raise ValueError(
"The value of 'reduction' in smooth_l1_loss should be 'sum', 'mean' or"
" 'none', but received %s, which is not allowed." % reduction)
if reduction == 'none':
return out
elif reduction == 'mean':
return fluid.layers.reduce_mean(out)
elif reduction == 'sum':
return fluid.layers.reduce_sum(out)
def margin_ranking_loss(input,
other,
label,
......
......@@ -74,6 +74,7 @@ from .loss import NLLLoss #DEFINE_ALIAS
from .loss import BCELoss #DEFINE_ALIAS
from .loss import KLDivLoss #DEFINE_ALIAS
from .loss import MarginRankingLoss #DEFINE_ALIAS
from .loss import SmoothL1Loss #DEFINE_ALIAS
from .norm import BatchNorm #DEFINE_ALIAS
from .norm import SyncBatchNorm #DEFINE_ALIAS
from .norm import GroupNorm #DEFINE_ALIAS
......
......@@ -27,7 +27,8 @@ __all__ = [
'NLLLoss',
'BCELoss',
'KLDivLoss',
'MarginRankingLoss'
'MarginRankingLoss',
'SmoothL1Loss',
]
......@@ -700,7 +701,7 @@ class MarginRankingLoss(fluid.dygraph.Layer):
def __init__(self, margin=0.0, reduction='mean', name=None):
if reduction not in ['sum', 'mean', 'none']:
raise ValueError(
"The value of 'reduction' in L1Loss should be 'sum', 'mean' or 'none', but "
"The value of 'reduction' in MarginRankingLoss should be 'sum', 'mean' or 'none', but "
"received %s, which is not allowed." % reduction)
super(MarginRankingLoss, self).__init__()
self.margin = margin
......@@ -711,3 +712,79 @@ class MarginRankingLoss(fluid.dygraph.Layer):
out = paddle.nn.functional.margin_ranking_loss(
input, other, label, self.margin, self.reduction, self.name)
return out
class SmoothL1Loss(fluid.dygraph.Layer):
"""
This operator calculates smooth_l1_loss. Creates a criterion that uses a squared
term if the absolute element-wise error falls below 1 and an L1 term otherwise.
In some cases it can prevent exploding gradients and it is more robust and less
sensitivity to outliers. Also known as the Huber loss:
.. math::
loss(x,y)=\\frac{1}{n}\\sum_{i}z_i
where z_i is given by:
.. math::
\\mathop{z_i}=\\left\\{\\begin{array}{rcl}
0.5(x_i - y_i)^2 & & {if |x_i - y_i| < delta} \\\\
delta * |x_i - y_i| - 0.5 * delta^2 & & {otherwise}
\\end{array} \\right.
Parameters:
reduction (str, optional): Indicate how to average the loss by batch_size,
the candicates are ``'none'`` | ``'mean'`` | ``'sum'``.
If :attr:`reduction` is ``'mean'``, the reduced mean loss is returned;
If :attr:`reduction` is ``'sum'``, the reduced sum loss is returned.
If :attr:`reduction` is ``'none'``, the unreduced loss is returned.
Default is ``'mean'``.
delta (float, optional): Specifies the hyperparameter delta to be used.
The value determines how large the errors need to be to use L1. Errors
smaller than delta are minimized with L2. Parameter is ignored for
negative/zero values. Default = 1.0
name (str, optional): Name for the operation (optional, default is
None). For more information, please refer to :ref:`api_guide_Name`.
Call Parameters:
input (Tensor): Input tensor, the data type is float32 or float64. Shape is
(N, C), where C is number of classes, and if shape is more than 2D, this
is (N, C, D1, D2,..., Dk), k >= 1.
label (Tensor): Label tensor, the data type is float32 or float64. The shape of label
is the same as the shape of input.
Returns:
The tensor variable storing the smooth_l1_loss of input and label.
Return type: Tensor.
Examples:
.. code-block:: python
import paddle
import numpy as np
paddle.disable_static()
input_data = np.random.rand(3,3).astype("float32")
label_data = np.random.rand(3,3).astype("float32")
input = paddle.to_tensor(input_data)
label = paddle.to_tensor(label_data)
loss = paddle.nn.SmoothL1Loss()
output = loss(input, label)
print(output.numpy())
"""
def __init__(self, reduction='mean', delta=1.0, name=None):
super(SmoothL1Loss, self).__init__()
self.reduction = reduction
self.delta = delta
self.name = name
def forward(self, input, label):
return F.smooth_l1_loss(
input,
label,
reduction=self.reduction,
delta=self.delta,
name=self.name)
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册