diff --git a/tester/accuracy.py b/tester/accuracy.py index a0f994fe..6a18cde7 100644 --- a/tester/accuracy.py +++ b/tester/accuracy.py @@ -27,6 +27,30 @@ def __init__(self, api_config, **kwargs): torch.set_printoptions(profile="short") self.converter = get_converter() + def _reset_random_state(self, seed: int = 42): + """Reset numpy / paddle / torch (CPU+CUDA) RNGs so random APIs + (uniform, normal, randn, bernoulli, dropout, ...) produce + reproducible outputs across the torch run and the paddle run.""" + numpy.random.seed(seed) + try: + paddle.seed(seed) + if paddle.device.is_compiled_with_cuda(): + # paddle.seed already seeds the GPU default generator, + # but reset all device generators explicitly for safety. + try: + for i in range(paddle.device.cuda.device_count()): + paddle.framework.core.default_cuda_generator(i).manual_seed(seed) + except Exception: + pass + except Exception: + pass + try: + torch.manual_seed(seed) + if torch.cuda.is_available(): + torch.cuda.manual_seed_all(seed) + except Exception: + pass + # @func_set_timeout(600) def test(self): if self.need_skip(): @@ -78,6 +102,11 @@ def test(self): print("gen_torch_input failed", flush=True) return + # Reseed before executing torch, so that random APIs + # (e.g. torch.rand / uniform / normal / dropout) produce + # deterministic outputs across runs when --random_seed is set. + self._reset_random_state() + # torch_args 与 torch_kwargs 是尚未映射的 torch 参数(即按 paddle 的参数顺序与关键字排列的 torch tensors) # (弃用)以下代码等价于: # torch_output = Paddle2TorchConverter.execute(convert_result, self.torch_args, self.torch_kwargs) @@ -212,6 +241,11 @@ def process_torch_outputs(obj): if not self.gen_paddle_input(): print("gen_paddle_input failed") return + + # Reseed before executing paddle so that random APIs + # (paddle.uniform / normal / randn / bernoulli / dropout ...) + # match the torch run with the same seed. + self._reset_random_state() if "paddle.Tensor." in self.api_config.api_name: api = getattr( self.paddle_args[0], diff --git a/tester/accuracy_stable.py b/tester/accuracy_stable.py index 4d1313d6..b1713709 100644 --- a/tester/accuracy_stable.py +++ b/tester/accuracy_stable.py @@ -20,6 +20,28 @@ def __init__(self, api_config, **kwargs): torch.set_printoptions(profile="short", edgeitems=2, threshold=100, linewidth=120) torch.set_default_device("cuda") + def _reset_random_state(self, seed: int = 42): + """Reset numpy / paddle / torch (CPU+CUDA) RNGs so random APIs + (uniform, normal, randn, bernoulli, dropout, ...) produce + reproducible outputs across the torch run and the paddle run.""" + numpy.random.seed(seed) + try: + paddle.seed(seed) + if paddle.device.is_compiled_with_cuda(): + try: + for i in range(paddle.device.cuda.device_count()): + paddle.framework.core.default_cuda_generator(i).manual_seed(seed) + except Exception: + pass + except Exception: + pass + try: + torch.manual_seed(seed) + if torch.cuda.is_available(): + torch.cuda.manual_seed_all(seed) + except Exception: + pass + def test(self): if self.need_skip(): print("[Skip]", flush=True) @@ -71,6 +93,7 @@ def test(self): # iter twice for _i in range(2): # ======== torch ======== + self._reset_random_state() torch_output, torch_out_grads, torch_grad_success = self.get_torch_output( convert_result ) @@ -79,6 +102,7 @@ def test(self): torch.cuda.empty_cache() # ======== paddle ======== + self._reset_random_state() paddle_output, paddle_out_grads = self.get_paddle_output(torch_grad_success) if paddle_output is None: return diff --git a/tester/api_config/config_analyzer.py b/tester/api_config/config_analyzer.py index 7ae85515..53fbeb60 100644 --- a/tester/api_config/config_analyzer.py +++ b/tester/api_config/config_analyzer.py @@ -19,9 +19,10 @@ # Format: {api_name: {arg_index: init_method}} # init_method: "zeros" = fill with 0, "small_positive" = fill with small positive value optimizer_apis = { - "paddle._C_ops.adamw_": {3: "zeros", 4: "zeros"}, # moment1, moment2 - "paddle._C_ops.adam_": {3: "zeros", 4: "zeros"}, - "paddle._C_ops.merged_adam_": {3: "zeros", 4: "zeros"}, + # moment1, moment2, moment2_max (must be non-negative for amsgrad) + "paddle._C_ops.adamw_": {3: "zeros", 4: "zeros", 5: "zeros"}, + "paddle._C_ops.adam_": {3: "zeros", 4: "zeros", 5: "zeros"}, + "paddle._C_ops.merged_adam_": {3: "zeros", 4: "zeros", 5: "zeros"}, } not_zero_apis = frozenset( @@ -283,6 +284,21 @@ def get_numpy_tensor(self, api_config, index=None, key=None, **kwargs): self.numpy_tensor = (numpy.random.random(self.shape) + 0.5).astype( self.dtype ) + elif api_config.api_name == "paddle._C_ops.adamw_": + if self.check_arg(api_config, 6, "beta1_pow") or self.check_arg( + api_config, 7, "beta2_pow" + ): + if not hasattr(api_config, "adamw_step"): + api_config.adamw_step = numpy.random.randint(1, 101) + beta = self.get_arg(api_config, 10, "beta1") + if self.check_arg(api_config, 7, "beta2_pow"): + beta = self.get_arg(api_config, 11, "beta2") + self.numpy_tensor = numpy.full( + self.shape, + beta**api_config.adamw_step, + dtype=self.dtype, + ) + elif api_config.api_name == "paddle.arange": start_val = self.get_arg(api_config, 0, "start", 0) end_val = self.get_arg(api_config, 1, "end", None) diff --git a/tester/paddle_to_torch/rules.py b/tester/paddle_to_torch/rules.py index e7650c57..ae0cd414 100644 --- a/tester/paddle_to_torch/rules.py +++ b/tester/paddle_to_torch/rules.py @@ -7036,6 +7036,9 @@ def _adamw_bool(value): step = math.log(b2_pow) / math.log(b2) if math.isfinite(step) and step > 0.0: + # step is the training iteration count, which is always an + # integer. Round to undo float32 round-trip noise from log/log. + step = round(step) b1_pow_from_step = b1**step bias_correction1 = 1.0 - b1_pow fused_bias_correction1 = 1.0 - b1_pow_from_step