Skip to content

Commit 4e0d5d8

Browse files
committed
fix_loss_args_legacy
1 parent 658be31 commit 4e0d5d8

File tree

4 files changed

+233
-0
lines changed

4 files changed

+233
-0
lines changed

python/paddle/nn/layer/loss.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import paddle
2020
from paddle import base, in_dynamic_mode
2121
from paddle.base.framework import in_dynamic_or_pir_mode
22+
from paddle.nn.layer.utils import check_deprecated_params
2223
from paddle.utils.decorator_utils import ParamAliasDecorator
2324

2425
from .. import functional as F
@@ -36,6 +37,7 @@
3637
__all__ = []
3738

3839

40+
@check_deprecated_params
3941
class BCEWithLogitsLoss(Layer):
4042
r"""
4143
@@ -151,6 +153,7 @@ def forward(self, logit: Tensor, label: Tensor) -> Tensor:
151153
return out
152154

153155

156+
@check_deprecated_params
154157
class CrossEntropyLoss(Layer):
155158
r"""
156159
@@ -603,6 +606,7 @@ def forward(
603606
return out
604607

605608

609+
@check_deprecated_params
606610
class MSELoss(Layer):
607611
r"""
608612
**Mean Square Error Loss**
@@ -688,6 +692,7 @@ def forward(self, input: Tensor, label: Tensor) -> Tensor:
688692
return paddle.mean(square_out)
689693

690694

695+
@check_deprecated_params
691696
class L1Loss(Layer):
692697
r"""
693698
@@ -776,6 +781,7 @@ def forward(self, input: Tensor, label: Tensor) -> Tensor:
776781
)
777782

778783

784+
@check_deprecated_params
779785
class BCELoss(Layer):
780786
"""
781787
@@ -872,6 +878,7 @@ def forward(self, input: Tensor, label: Tensor) -> Tensor:
872878
return out
873879

874880

881+
@check_deprecated_params
875882
class NLLLoss(Layer):
876883
r"""
877884
@@ -989,6 +996,7 @@ def forward(self, input: Tensor, label: Tensor) -> Tensor:
989996
)
990997

991998

999+
@check_deprecated_params
9921000
class PoissonNLLLoss(Layer):
9931001
r"""Generate a callable object of 'PoissonNLLLoss' to calculate the
9941002
Poisson negative log likelihood loss between Input(input) and
@@ -1084,6 +1092,7 @@ def forward(self, input: Tensor, label: Tensor) -> Tensor:
10841092
)
10851093

10861094

1095+
@check_deprecated_params
10871096
class KLDivLoss(Layer):
10881097
r"""
10891098
@@ -1191,6 +1200,7 @@ def forward(self, input: Tensor, label: Tensor) -> Tensor:
11911200
return out
11921201

11931202

1203+
@check_deprecated_params
11941204
class MarginRankingLoss(Layer):
11951205
r"""
11961206
@@ -1460,6 +1470,7 @@ def forward(
14601470
)
14611471

14621472

1473+
@check_deprecated_params
14631474
class SmoothL1Loss(Layer):
14641475
r"""
14651476
This operator calculates smooth_l1_loss. Creates a criterion that uses a squared
@@ -1548,6 +1559,7 @@ def forward(self, input: Tensor, label: Tensor) -> Tensor:
15481559
)
15491560

15501561

1562+
@check_deprecated_params
15511563
class MultiLabelSoftMarginLoss(Layer):
15521564
r"""Creates a criterion that optimizes a multi-class multi-classification
15531565
hinge loss (margin-based loss) between input :math:`x` (a 2D mini-batch `Tensor`)
@@ -1638,6 +1650,7 @@ def forward(self, input: Tensor, label: Tensor) -> Tensor:
16381650
)
16391651

16401652

1653+
@check_deprecated_params
16411654
class HingeEmbeddingLoss(Layer):
16421655
r"""
16431656
Create a callable object of `HingeEmbeddingLoss` to calculates hinge_embedding_loss. Measures the loss given an input tensor :math:`x` and a labels tensor :math:`y`(containing 1 or -1).
@@ -1745,6 +1758,7 @@ def forward(self, input: Tensor, label: Tensor) -> Tensor:
17451758
)
17461759

17471760

1761+
@check_deprecated_params
17481762
class CosineEmbeddingLoss(Layer):
17491763
r"""
17501764
This interface is used to construct a callable object of the ``CosineEmbeddingLoss`` class.
@@ -1975,6 +1989,7 @@ def forward(
19751989
)
19761990

19771991

1992+
@check_deprecated_params
19781993
class TripletMarginLoss(Layer):
19791994
r"""
19801995
Creates a criterion that measures the triplet loss given an input
@@ -2097,6 +2112,7 @@ def forward(
20972112
)
20982113

20992114

2115+
@check_deprecated_params
21002116
class MultiMarginLoss(Layer):
21012117
r"""Creates a criterion that optimizes a multi-class classification hinge loss (margin-based loss) between
21022118
input :math:`input` and label :math:`label`:
@@ -2207,6 +2223,7 @@ def forward(self, input: Tensor, label: Tensor) -> Tensor:
22072223
)
22082224

22092225

2226+
@check_deprecated_params
22102227
class MultiLabelMarginLoss(Layer):
22112228
r"""Creates a criterion that optimizes a multi-class multi-classification hinge loss (margin-based loss)
22122229
between input :math:`input` and label :math:`label`:
@@ -2293,6 +2310,7 @@ def forward(self, input: Tensor, label: Tensor) -> Tensor:
22932310
)
22942311

22952312

2313+
@check_deprecated_params
22962314
class SoftMarginLoss(Layer):
22972315
r"""
22982316

python/paddle/nn/layer/utils.py

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
# Copyright (c) 2025 PaddlePaddle Authors. All Rights Reserved.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
import functools
16+
17+
_SENTINEL = object()
18+
19+
20+
def compute_legacy_reduction(reduce_val, size_average_val):
21+
if reduce_val is False:
22+
return 'none'
23+
if reduce_val is True:
24+
return 'sum' if size_average_val is False else 'mean'
25+
return 'sum' if size_average_val is False else 'mean'
26+
27+
28+
def check_deprecated_params(cls):
29+
"""
30+
Class decorator: intercept deprecated 'reduce' and 'size_average' kwargs.
31+
- If provided by user, raise ValueError immediately with the suggested
32+
'reduction' value.
33+
- Pop these keys so that future policies (e.g., warn-and-continue) won't
34+
pass unknown kwargs into the original __init__.
35+
"""
36+
original_init = cls.__init__
37+
38+
@functools.wraps(original_init)
39+
def new_init(self, *args, **kwargs):
40+
reduce_raw = kwargs.pop('reduce', _SENTINEL)
41+
size_avg_raw = kwargs.pop('size_average', _SENTINEL)
42+
43+
has_reduce = reduce_raw is not _SENTINEL
44+
has_size_avg = size_avg_raw is not _SENTINEL
45+
46+
if has_reduce or has_size_avg:
47+
reduce_val = None if reduce_raw is _SENTINEL else reduce_raw
48+
size_avg_val = None if size_avg_raw is _SENTINEL else size_avg_raw
49+
suggested = compute_legacy_reduction(reduce_val, size_avg_val)
50+
raise ValueError(
51+
f"{cls.__name__} no longer supports 'reduce' and 'size_average'."
52+
f"\nDetected: reduce={reduce_val}, size_average={size_avg_val}"
53+
f"\nPlease use: reduction='{suggested}' instead."
54+
)
55+
56+
return original_init(self, *args, **kwargs)
57+
58+
cls.__init__ = new_init
59+
return cls

test/legacy_test/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -873,6 +873,7 @@ endif()
873873

874874
set_tests_properties(test_profiler PROPERTIES TIMEOUT 120)
875875
set_tests_properties(test_cross_entropy_loss PROPERTIES TIMEOUT 180)
876+
set_tests_properties(test_legacy_loss_args PROPERTIES TIMEOUT 30)
876877
set_tests_properties(test_activation_nn_grad PROPERTIES TIMEOUT 250)
877878
set_tests_properties(test_empty_op PROPERTIES TIMEOUT 120)
878879
set_tests_properties(test_elementwise_div_op PROPERTIES TIMEOUT 120)
Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
# Copyright (c) 2025 PaddlePaddle Authors. All Rights Reserved.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
import unittest
16+
17+
import paddle
18+
from paddle.nn import (
19+
BCELoss,
20+
BCEWithLogitsLoss,
21+
CosineEmbeddingLoss,
22+
CrossEntropyLoss,
23+
HingeEmbeddingLoss,
24+
KLDivLoss,
25+
L1Loss,
26+
MarginRankingLoss,
27+
MSELoss,
28+
MultiLabelMarginLoss,
29+
MultiLabelSoftMarginLoss,
30+
MultiMarginLoss,
31+
NLLLoss,
32+
PoissonNLLLoss,
33+
SmoothL1Loss,
34+
SoftMarginLoss,
35+
TripletMarginLoss,
36+
)
37+
38+
39+
class TestLegacyLossArgs(unittest.TestCase):
40+
def assertSuggests(self, loss_ctor, expected_reduction, **legacy_kwargs):
41+
with self.assertRaises(ValueError) as cm:
42+
loss_ctor(**legacy_kwargs)
43+
msg = str(cm.exception)
44+
self.assertIn(f"reduction='{expected_reduction}'", msg)
45+
46+
def test_no_legacy_all_constructible(self):
47+
# Ensure all 17 losses still construct with reduction only
48+
ctors = [
49+
L1Loss,
50+
NLLLoss,
51+
PoissonNLLLoss,
52+
KLDivLoss,
53+
MSELoss,
54+
BCELoss,
55+
BCEWithLogitsLoss,
56+
HingeEmbeddingLoss,
57+
MultiLabelMarginLoss,
58+
SmoothL1Loss,
59+
SoftMarginLoss,
60+
CrossEntropyLoss,
61+
MultiLabelSoftMarginLoss,
62+
CosineEmbeddingLoss,
63+
MarginRankingLoss,
64+
MultiMarginLoss,
65+
TripletMarginLoss,
66+
]
67+
for ctor in ctors:
68+
_ = ctor(reduction='mean')
69+
70+
def test_no_args_all_constructible_with_defaults(self):
71+
# Ensure all 17 losses construct with default args (no legacy, no explicit reduction)
72+
ctors = [
73+
L1Loss,
74+
NLLLoss,
75+
PoissonNLLLoss,
76+
KLDivLoss,
77+
MSELoss,
78+
BCELoss,
79+
BCEWithLogitsLoss,
80+
HingeEmbeddingLoss,
81+
MultiLabelMarginLoss,
82+
SmoothL1Loss,
83+
SoftMarginLoss,
84+
CrossEntropyLoss,
85+
MultiLabelSoftMarginLoss,
86+
CosineEmbeddingLoss,
87+
MarginRankingLoss,
88+
MultiMarginLoss,
89+
TripletMarginLoss,
90+
]
91+
for ctor in ctors:
92+
loss = ctor()
93+
self.assertIsInstance(loss, paddle.nn.Layer)
94+
95+
# Cover legacy combos across the family (not each loss needs all combos)
96+
97+
def test_cross_entropy_reduce_false(self):
98+
self.assertSuggests(CrossEntropyLoss, 'none', reduce=False)
99+
100+
def test_mse_reduce_true_size_average_false(self):
101+
self.assertSuggests(MSELoss, 'sum', reduce=True, size_average=False)
102+
103+
def test_bcewithlogits_reduce_true_size_average_true(self):
104+
self.assertSuggests(
105+
BCEWithLogitsLoss, 'mean', reduce=True, size_average=True
106+
)
107+
108+
def test_l1_size_average_false_only(self):
109+
self.assertSuggests(L1Loss, 'sum', size_average=False)
110+
111+
def test_kldiv_reduce_true_size_average_none(self):
112+
self.assertSuggests(KLDivLoss, 'mean', reduce=True, size_average=None)
113+
114+
def test_multimargin_reduce_false(self):
115+
self.assertSuggests(MultiMarginLoss, 'none', reduce=False)
116+
117+
def test_multilabel_margin_size_average_false(self):
118+
self.assertSuggests(MultiLabelMarginLoss, 'sum', size_average=False)
119+
120+
def test_cosine_embedding_reduce_true_size_average_true(self):
121+
self.assertSuggests(
122+
CosineEmbeddingLoss, 'mean', reduce=True, size_average=True
123+
)
124+
125+
def test_margin_ranking_reduce_true_size_average_false(self):
126+
self.assertSuggests(
127+
MarginRankingLoss, 'sum', reduce=True, size_average=False
128+
)
129+
130+
def test_soft_margin_reduce_false(self):
131+
self.assertSuggests(SoftMarginLoss, 'none', reduce=False)
132+
133+
def test_smooth_l1_size_average_false(self):
134+
self.assertSuggests(SmoothL1Loss, 'sum', size_average=False)
135+
136+
def test_bce_reduce_true_size_average_true(self):
137+
self.assertSuggests(BCELoss, 'mean', reduce=True, size_average=True)
138+
139+
def test_nll_reduce_true_size_average_none(self):
140+
self.assertSuggests(NLLLoss, 'mean', reduce=True, size_average=None)
141+
142+
def test_poisson_nll_reduce_false(self):
143+
self.assertSuggests(PoissonNLLLoss, 'none', reduce=False)
144+
145+
def test_multilabel_soft_margin_size_average_false(self):
146+
self.assertSuggests(MultiLabelSoftMarginLoss, 'sum', size_average=False)
147+
148+
def test_triplet_margin_reduce_true_size_average_false(self):
149+
self.assertSuggests(
150+
TripletMarginLoss, 'sum', reduce=True, size_average=False
151+
)
152+
153+
154+
if __name__ == '__main__':
155+
unittest.main()

0 commit comments

Comments
 (0)