Skip to content

Commit a746b27

Browse files
committed
Honor adam_w_mode in the CPU multi_tensor_adam binding
The CPU fused_adam extension created its Adam_Optimizer once with default arguments and ignored the mode parameter, so FusedAdam on the CPU backend always applied decoupled (AdamW) weight decay even when constructed with adam_w_mode=False. Keep one optimizer instance per mode instead; everything else is already passed per call. This surfaced as cpu-torch-latest failures in the fp32-adam case of test_fused_adam_matches_torch. The test's bf16 cases are dropped: torch.optim in bf16 does its math in bf16 while the fused kernels compute in fp32, so it was never a valid bf16 reference. Low-precision dtypes get an explicit fp32-math reference in the FusedAdam rework. Signed-off-by: PKUWZP <zhipeng.rainbowserie@gmail.com>
1 parent a1040cb commit a746b27

2 files changed

Lines changed: 13 additions & 8 deletions

File tree

csrc/cpu/adam/fused_adam.cpp

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,16 @@ void multi_tensor_adam(int chunk_size,
1919
const int bias_correction,
2020
const float weight_decay)
2121
{
22-
static bool initialized = false;
23-
if (!initialized) {
24-
create_adam_optimizer(0);
25-
initialized = true;
22+
// ds_adam_step reads lr/betas/eps/weight_decay per call; only AdamW-vs-L2 is fixed at
23+
// construction, so keep one optimizer instance per mode (mode 1 == AdamW, as in CUDA).
24+
static bool initialized[2] = {false, false};
25+
const int optimizer_id = mode;
26+
if (!initialized[mode]) {
27+
create_adam_optimizer(optimizer_id, 1e-3f, 0.9f, 0.999f, 1e-8f, 0.0f, mode == 1);
28+
initialized[mode] = true;
2629
}
2730
for (int i = 0; i < tensor_lists[0].size(); i++) {
28-
ds_adam_step(0,
31+
ds_adam_step(optimizer_id,
2932
step,
3033
lr,
3134
beta1,

tests/unit/ops/adam/test_adamw.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,10 @@ def test(self,
8181

8282

8383
@pytest.mark.parametrize('adam_w_mode', [True, False], ids=["adamw", "adam"])
84-
@pytest.mark.parametrize('dtype', [torch.float, torch.bfloat16], ids=["fp32", "bf16"])
84+
# fp32 only: torch.optim in bf16 does its math in bf16 while the fused kernels compute in fp32,
85+
# so it is not a valid bf16 reference. Low-precision dtypes are covered against an explicit
86+
# fp32-math reference in the FusedAdam rework (#8300).
87+
@pytest.mark.parametrize('dtype', [torch.float], ids=["fp32"])
8588
def test_fused_adam_matches_torch(adam_w_mode, dtype):
8689
if dtype not in get_accelerator().supported_dtypes():
8790
pytest.skip(f"{dtype} not supported on {get_accelerator().device_name()}")
@@ -105,7 +108,6 @@ def test_fused_adam_matches_torch(adam_w_mode, dtype):
105108
ref_optimizer.step()
106109
ds_optimizer.step()
107110

108-
# bf16 storage rounds differently depending on where intermediates are kept, so allow one ulp.
109-
atol = 1e-5 if dtype == torch.float else 2e-2
111+
atol = 1e-5
110112
for ref_param, ds_param in zip(ref_params, ds_params):
111113
torch.testing.assert_close(ds_param.float(), ref_param.float(), atol=atol, rtol=0)

0 commit comments

Comments
 (0)