Skip to content

Commit 886db26

Browse files
authored
[API Compatibility] align smooth_l1_loss by adding it to compat module -part (#79277)
* align smooth_l1_loss by adding it to compat module * remove additional comments * add coverage
1 parent c87be77 commit 886db26

3 files changed

Lines changed: 411 additions & 0 deletions

File tree

python/paddle/compat/nn/__init__.py

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
from __future__ import annotations
1616

1717
import collections
18+
import warnings
1819
from itertools import repeat
1920
from math import sqrt
2021
from typing import TYPE_CHECKING
@@ -48,6 +49,7 @@
4849
'AvgPool2d',
4950
'AvgPool3d',
5051
'MultiheadAttention',
52+
'SmoothL1Loss',
5153
]
5254

5355

@@ -745,6 +747,96 @@ def extra_repr(self) -> str:
745747
return f"dim={self.dim}"
746748

747749

750+
class SmoothL1Loss(nn.Layer):
751+
r"""
752+
753+
PyTorch compatible version of :ref:`api_paddle_nn_SmoothL1Loss`, aligned with
754+
``torch.nn.SmoothL1Loss``. The per-element loss is
755+
756+
.. math::
757+
758+
z_i = \left\{\begin{array}{rcl}
759+
0.5 (x_i - y_i)^2 / beta & & {if |x_i - y_i| < beta} \\
760+
|x_i - y_i| - 0.5 * beta & & {otherwise}
761+
\end{array} \right.
762+
763+
which equals Paddle's Huber loss divided by ``beta``. This differs from
764+
:ref:`api_paddle_nn_SmoothL1Loss` whose default ``is_huber=True`` returns the
765+
raw Huber loss.
766+
767+
Parameters:
768+
size_average (bool|None, optional): Deprecated (see ``reduction``). When
769+
``size_average`` or ``reduce`` is not ``None``, it is translated into
770+
``reduction`` with a ``DeprecationWarning``. Default is ``None``.
771+
reduce (bool|None, optional): Deprecated (see ``reduction``). Default is ``None``.
772+
reduction (str, optional): Indicate how to calculate the loss, the candidates
773+
are ``'none'`` | ``'mean'`` | ``'sum'``. Default is ``'mean'``.
774+
beta (float, optional): Non-negative threshold at which to change between L1
775+
and L2 loss. When ``beta == 0`` the loss degrades to the L1 loss, matching
776+
PyTorch. Default is ``1.0``.
777+
778+
Call Parameters:
779+
input (Tensor): Input tensor, the data type is float32 or float64.
780+
target (Tensor): Label tensor with the same shape as ``input``.
781+
782+
Returns:
783+
Tensor, The tensor storing the smooth L1 loss of ``input`` and ``target``.
784+
785+
Examples:
786+
.. code-block:: pycon
787+
788+
>>> import paddle
789+
790+
>>> input = paddle.to_tensor([[0.5, 1.5], [2.0, 0.0]], dtype='float32')
791+
>>> target = paddle.to_tensor([[1.0, 1.0], [1.0, 0.5]], dtype='float32')
792+
>>> loss = paddle.compat.nn.SmoothL1Loss(beta=1.0)
793+
>>> output = loss(input, target)
794+
>>> print(output)
795+
Tensor(shape=[], dtype=float32, place=Place(cpu), stop_gradient=True,
796+
0.21875000)
797+
"""
798+
799+
__constants__ = ["reduction", "beta"]
800+
reduction: str
801+
beta: float
802+
803+
@ForbidKeywordsDecorator(
804+
illegal_keys={"delta", "is_huber", "name", "label"},
805+
func_name="paddle.compat.nn.SmoothL1Loss",
806+
correct_name="paddle.nn.SmoothL1Loss",
807+
)
808+
def __init__(
809+
self,
810+
size_average: bool | None = None,
811+
reduce: bool | None = None,
812+
reduction: str = 'mean',
813+
beta: float = 1.0,
814+
) -> None:
815+
super().__init__()
816+
if size_average is not None or reduce is not None:
817+
reduction = (
818+
'none'
819+
if reduce is False
820+
else ('sum' if size_average is False else 'mean')
821+
)
822+
warnings.warn(
823+
"'size_average' and 'reduce' args of 'SmoothL1Loss' will be "
824+
f"deprecated, please use reduction='{reduction}' instead.",
825+
DeprecationWarning,
826+
stacklevel=2,
827+
)
828+
self.reduction = reduction
829+
self.beta = beta
830+
831+
def forward(self, input: Tensor, target: Tensor) -> Tensor:
832+
return functional.smooth_l1_loss.__wrapped__(
833+
input, target, reduction=self.reduction, beta=self.beta
834+
)
835+
836+
def extra_repr(self) -> str:
837+
return f"reduction={self.reduction}, beta={self.beta}"
838+
839+
748840
AvgPool1d = AvgPool1D
749841
AvgPool2d = AvgPool2D
750842
AvgPool3d = AvgPool3D

python/paddle/compat/nn/functional/__init__.py

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414

1515
from __future__ import annotations
1616

17+
import warnings
1718
from typing import TYPE_CHECKING, Literal
1819

1920
import paddle
@@ -39,6 +40,7 @@
3940
_PaddingTensorMode: TypeAlias = Literal[
4041
"zeros", "constant", "reflect", "replicate", "circular"
4142
]
43+
_ReduceMode: TypeAlias = Literal["mean", "sum", "none"]
4244

4345

4446
__all__ = [
@@ -48,6 +50,7 @@
4850
'linear',
4951
'scaled_dot_product_attention',
5052
'unfold',
53+
'smooth_l1_loss',
5154
]
5255

5356

@@ -380,3 +383,90 @@ def to_list_if_necessary(x):
380383
paddings=to_list_if_necessary(padding),
381384
dilations=to_list_if_necessary(dilation),
382385
)
386+
387+
388+
@ForbidKeywordsDecorator(
389+
illegal_keys={"label", "delta", "is_huber", "name"},
390+
func_name="paddle.compat.nn.functional.smooth_l1_loss",
391+
correct_name="paddle.nn.functional.smooth_l1_loss",
392+
)
393+
def smooth_l1_loss(
394+
input: Tensor,
395+
target: Tensor,
396+
size_average: bool | None = None,
397+
reduce: bool | None = None,
398+
reduction: _ReduceMode = 'mean',
399+
beta: float = 1.0,
400+
) -> Tensor:
401+
r"""
402+
403+
PyTorch compatible version of :ref:`api_paddle_nn_functional_smooth_l1_loss`.
404+
405+
Computes the Smooth L1 loss, aligned with ``torch.nn.functional.smooth_l1_loss``.
406+
The per-element loss is:
407+
408+
.. math::
409+
410+
z_i = \left\{\begin{array}{rcl}
411+
0.5 (x_i - y_i)^2 / beta & & {if |x_i - y_i| < beta} \\
412+
|x_i - y_i| - 0.5 * beta & & {otherwise}
413+
\end{array} \right.
414+
415+
This equals Paddle's Huber loss divided by ``beta`` (i.e. ``is_huber=False`` with
416+
``delta=beta``), which is the key difference from
417+
:ref:`api_paddle_nn_functional_smooth_l1_loss` whose default ``is_huber=True``
418+
returns the raw Huber loss.
419+
420+
Args:
421+
input (Tensor): Input tensor, the data type is float32 or float64.
422+
target (Tensor): Label tensor with the same shape as ``input``.
423+
size_average (bool|None, optional): Deprecated (see ``reduction``). When
424+
``size_average`` or ``reduce`` is not ``None``, it is translated into
425+
``reduction`` with a ``DeprecationWarning``. Default is ``None``.
426+
reduce (bool|None, optional): Deprecated (see ``reduction``). Default is ``None``.
427+
reduction (str, optional): Indicate how to calculate the loss, the candidates
428+
are ``'none'`` | ``'mean'`` | ``'sum'``. Default is ``'mean'``.
429+
beta (float, optional): Specifies the threshold at which to change between L1
430+
and L2 loss. The value must be non-negative. When ``beta == 0`` the loss
431+
degrades to the L1 loss, matching PyTorch. Default is ``1.0``.
432+
433+
Returns:
434+
Tensor, The tensor storing the smooth L1 loss of ``input`` and ``target``.
435+
436+
Examples:
437+
.. code-block:: pycon
438+
439+
>>> import paddle
440+
441+
>>> input = paddle.to_tensor([[0.5, 1.5], [2.0, 0.0]], dtype='float32')
442+
>>> target = paddle.to_tensor([[1.0, 1.0], [1.0, 0.5]], dtype='float32')
443+
>>> output = paddle.compat.nn.functional.smooth_l1_loss(input, target, beta=1.0)
444+
>>> print(output)
445+
Tensor(shape=[], dtype=float32, place=Place(cpu), stop_gradient=True,
446+
0.21875000)
447+
"""
448+
# Translate PyTorch's deprecated size_average / reduce into reduction.
449+
if size_average is not None or reduce is not None:
450+
reduction = (
451+
'none'
452+
if reduce is False
453+
else ('sum' if size_average is False else 'mean')
454+
)
455+
warnings.warn(
456+
"'size_average' and 'reduce' args of 'smooth_l1_loss' will be "
457+
f"deprecated, please use reduction='{reduction}' instead.",
458+
DeprecationWarning,
459+
stacklevel=2,
460+
)
461+
462+
if beta < 0:
463+
raise ValueError(
464+
f"smooth_l1_loss does not accept negative beta, but got beta={beta}."
465+
)
466+
467+
if beta == 0:
468+
return paddle.nn.functional.l1_loss(input, target, reduction=reduction)
469+
470+
return paddle.nn.functional.smooth_l1_loss(
471+
input, target, reduction=reduction, delta=beta, is_huber=False
472+
)

0 commit comments

Comments
 (0)