Files
paddlepaddle--paddle/test/amp/test_grad_scaler_out.py
T
2026-07-13 12:40:42 +08:00

315 lines
11 KiB
Python

# Copyright (c) 2025 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.
import unittest
import paddle
from paddle.amp import AmpScaler, GradScaler
class TestGradScalerParamAlias(unittest.TestCase):
"""Test ParamAliasDecorator: PyTorch aliases, Paddle names, mixed,
positional args, single alias, conflict detection, and non-aliased params."""
def _assert_scaler(self, scaler, **expected):
"""Helper to check multiple scaler attributes at once."""
attr_map = {
'enable': '_enable',
'init_loss_scaling': '_init_loss_scaling',
'incr_ratio': '_incr_ratio',
'decr_ratio': '_decr_ratio',
'incr_every_n_steps': '_incr_every_n_steps',
'decr_every_n_nan_or_inf': '_decr_every_n_nan_or_inf',
'use_dynamic_loss_scaling': '_use_dynamic_loss_scaling',
}
for key, val in expected.items():
self.assertEqual(getattr(scaler, attr_map[key]), val, msg=key)
def test_default_values(self):
self._assert_scaler(
GradScaler(),
enable=True,
init_loss_scaling=2.0**16,
incr_ratio=2.0,
decr_ratio=0.5,
incr_every_n_steps=2000,
decr_every_n_nan_or_inf=1,
use_dynamic_loss_scaling=True,
)
def test_pytorch_style_kwargs(self):
self._assert_scaler(
GradScaler(
enabled=True,
init_scale=1024.0,
growth_factor=3.0,
backoff_factor=0.25,
growth_interval=500,
),
enable=True,
init_loss_scaling=1024.0,
incr_ratio=3.0,
decr_ratio=0.25,
incr_every_n_steps=500,
)
def test_mixed_kwargs(self):
self._assert_scaler(
GradScaler(
enable=True,
init_loss_scaling=1024.0,
growth_factor=3.0,
decr_ratio=0.25,
growth_interval=500,
),
init_loss_scaling=1024.0,
incr_ratio=3.0,
decr_ratio=0.25,
incr_every_n_steps=500,
)
def test_single_alias_each(self):
self.assertFalse(GradScaler(enabled=False)._enable)
self.assertEqual(GradScaler(init_scale=512.0)._init_loss_scaling, 512.0)
self.assertEqual(GradScaler(growth_factor=5.0)._incr_ratio, 5.0)
self.assertEqual(GradScaler(backoff_factor=0.1)._decr_ratio, 0.1)
self.assertEqual(
GradScaler(growth_interval=100)._incr_every_n_steps, 100
)
def test_positional_args(self):
self._assert_scaler(
GradScaler(True, 1024.0, 3.0, 0.25, 500, 2, True),
enable=True,
init_loss_scaling=1024.0,
incr_ratio=3.0,
decr_ratio=0.25,
incr_every_n_steps=500,
decr_every_n_nan_or_inf=2,
)
def test_positional_with_alias_kwarg(self):
self._assert_scaler(
GradScaler(True, 1024.0, growth_factor=5.0),
enable=True,
init_loss_scaling=1024.0,
incr_ratio=5.0,
)
def test_non_aliased_with_aliases(self):
self._assert_scaler(
GradScaler(
enabled=True,
init_scale=2048.0,
decr_every_n_nan_or_inf=3,
use_dynamic_loss_scaling=False,
),
enable=True,
init_loss_scaling=2048.0,
decr_every_n_nan_or_inf=3,
use_dynamic_loss_scaling=False,
)
def test_pytorch_vs_paddle_equivalence(self):
pt = GradScaler(
enabled=True,
init_scale=1024.0,
growth_factor=3.0,
backoff_factor=0.25,
growth_interval=500,
)
pd = GradScaler(
enable=True,
init_loss_scaling=1024.0,
incr_ratio=3.0,
decr_ratio=0.25,
incr_every_n_steps=500,
)
for attr in (
'_enable',
'_init_loss_scaling',
'_incr_ratio',
'_decr_ratio',
'_incr_every_n_steps',
):
self.assertEqual(getattr(pt, attr), getattr(pd, attr), msg=attr)
def test_conflict_raises_error(self):
conflicts = [
{"enable": True, "enabled": False},
{"init_loss_scaling": 1024.0, "init_scale": 2048.0},
{"incr_ratio": 2.0, "growth_factor": 3.0},
{"decr_ratio": 0.5, "backoff_factor": 0.25},
{"incr_every_n_steps": 1000, "growth_interval": 2000},
]
for kwargs in conflicts:
with self.assertRaises(ValueError, msg=str(kwargs)):
GradScaler(**kwargs)
def test_torch_positional_no_device(self):
# PyTorch older API: init_scale first (float -> detected as torch positional)
self._assert_scaler(
GradScaler(1024.0, 3.0, 0.25, 500, True),
enable=True,
init_loss_scaling=1024.0,
incr_ratio=3.0,
decr_ratio=0.25,
incr_every_n_steps=500,
)
def test_torch_positional_no_device_disabled(self):
# PyTorch: GradScaler(init_scale, ..., enabled=False)
scaler = GradScaler(1024.0, 2.0, 0.5, 2000, False)
self.assertFalse(scaler._enable)
self.assertEqual(scaler._init_loss_scaling, 1.0)
def test_torch_positional_with_device(self):
# PyTorch newer API: device string first
self._assert_scaler(
GradScaler('cuda', 1024.0, 3.0, 0.25, 500, True),
enable=True,
init_loss_scaling=1024.0,
incr_ratio=3.0,
decr_ratio=0.25,
incr_every_n_steps=500,
)
def test_torch_device_kwarg_dropped(self):
# device kwarg is silently ignored (no Paddle equivalent)
self._assert_scaler(
GradScaler(device='cuda', init_scale=2048.0, growth_factor=3.0),
init_loss_scaling=2048.0,
incr_ratio=3.0,
)
def test_torch_device_string_with_kwargs(self):
# device as positional string combined with torch keyword aliases
self._assert_scaler(
GradScaler('cuda', init_scale=512.0, enabled=True),
enable=True,
init_loss_scaling=512.0,
)
def test_torch_positional_partial(self):
# Only init_scale positionally, rest default
self._assert_scaler(
GradScaler(4096.0),
init_loss_scaling=4096.0,
incr_ratio=2.0,
decr_ratio=0.5,
incr_every_n_steps=2000,
)
def test_torch_device_string_only(self):
# GradScaler('cuda') — device only, all remaining params default
self._assert_scaler(
GradScaler('cuda'),
enable=True,
init_loss_scaling=2.0**16,
incr_ratio=2.0,
decr_ratio=0.5,
incr_every_n_steps=2000,
)
def test_torch_device_string_partial_positional(self):
# GradScaler('cuda', init_scale) — device + only first positional param
self._assert_scaler(
GradScaler('cuda', 1024.0),
enable=True,
init_loss_scaling=1024.0,
incr_ratio=2.0,
decr_ratio=0.5,
incr_every_n_steps=2000,
)
def test_torch_positional_with_kwarg_disabled(self):
# GradScaler(init_scale, enabled=False) — positional float + torch kwarg
scaler = GradScaler(1024.0, enabled=False)
self.assertFalse(scaler._enable)
self.assertEqual(scaler._init_loss_scaling, 1.0)
def test_disabled_scaler(self):
scaler = GradScaler(enabled=False)
self.assertFalse(scaler._enable)
self.assertEqual(scaler._init_loss_scaling, 1.0)
class TestGradScalerPytorchCompatMethods(unittest.TestCase):
"""Test PyTorch-compatible getter/setter methods."""
def test_is_enabled(self):
self.assertTrue(GradScaler(enabled=True).is_enabled())
self.assertFalse(GradScaler(enabled=False).is_enabled())
def test_get_scale(self):
self.assertEqual(GradScaler(init_scale=2048.0).get_scale(), 2048.0)
self.assertEqual(GradScaler(enabled=False).get_scale(), 0.0)
def test_growth_factor_get_set(self):
s = GradScaler(growth_factor=3.0)
self.assertEqual(s.get_growth_factor(), 3.0)
s.set_growth_factor(5.0)
self.assertEqual(s.get_growth_factor(), 5.0)
self.assertEqual(s.get_incr_ratio(), 5.0)
def test_backoff_factor_get_set(self):
s = GradScaler(backoff_factor=0.25)
self.assertEqual(s.get_backoff_factor(), 0.25)
s.set_backoff_factor(0.1)
self.assertEqual(s.get_backoff_factor(), 0.1)
self.assertEqual(s.get_decr_ratio(), 0.1)
def test_growth_interval_get_set(self):
s = GradScaler(growth_interval=500)
self.assertEqual(s.get_growth_interval(), 500)
s.set_growth_interval(100)
self.assertEqual(s.get_growth_interval(), 100)
self.assertEqual(s.get_incr_every_n_steps(), 100)
class TestGradScalerCallPathsAndInheritance(unittest.TestCase):
"""Test all public call paths and AmpScaler vs GradScaler defaults."""
def test_all_paths_same_class(self):
self.assertIs(paddle.device.amp.GradScaler, GradScaler)
self.assertIs(paddle.cuda.amp.GradScaler, GradScaler)
def test_gradscaler_is_subclass_of_ampscaler(self):
self.assertTrue(issubclass(GradScaler, AmpScaler))
def test_alias_via_device_and_cuda(self):
s1 = paddle.device.amp.GradScaler(init_scale=512.0, growth_factor=4.0)
self.assertEqual(s1._init_loss_scaling, 512.0)
self.assertEqual(s1._incr_ratio, 4.0)
s2 = paddle.cuda.amp.GradScaler(backoff_factor=0.3, growth_interval=800)
self.assertEqual(s2._decr_ratio, 0.3)
self.assertEqual(s2._incr_every_n_steps, 800)
def test_defaults_differ_from_ampscaler(self):
a, g = AmpScaler(), GradScaler()
# Intentionally different: GradScaler aligns with PyTorch
self.assertEqual(a._init_loss_scaling, 2.0**15)
self.assertEqual(g._init_loss_scaling, 2.0**16)
self.assertEqual(a._incr_every_n_steps, 1000)
self.assertEqual(g._incr_every_n_steps, 2000)
# Shared defaults stay the same
self.assertEqual(a._incr_ratio, g._incr_ratio)
self.assertEqual(a._decr_ratio, g._decr_ratio)
self.assertEqual(a._decr_every_n_nan_or_inf, g._decr_every_n_nan_or_inf)
if __name__ == "__main__":
unittest.main()