Skip to content

Commit 6787b32

Browse files
Sid Mohanclaude
andcommitted
Fix FP16 gradient scaler crash: ensure FP32 master weights
from_pretrained() may load weights in FP16 (safetensors dtype), but FP16 mixed precision requires FP32 parameters for gradient unscaling. Added model.float() in create_optimizer() before constructing param groups. Also: force pip reinstall in setup cell, verify eps=1.0 and float() fixes are present via source inspection, add optimizer logging. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1 parent 2147d4c commit 6787b32

2 files changed

Lines changed: 24 additions & 7 deletions

File tree

pii-ner-v1/notebooks/full_training.ipynb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@
3131
"execution_count": null,
3232
"metadata": {},
3333
"outputs": [],
34-
"source": "import os, sys\n\n# Clone or force-update to latest\nif not os.path.exists(\"/content/datafog-labs\"):\n !git clone https://github.com/DataFog/datafog-labs.git /content/datafog-labs\nelse:\n !cd /content/datafog-labs && git fetch origin && git reset --hard origin/main\n\n!pip install -e \"/content/datafog-labs/pii-ner-v1[dev]\" -q\n\nsys.path.insert(0, \"/content/datafog-labs/pii-ner-v1/src\")\n\nimport datafog_pii_ner\nprint(f\"datafog_pii_ner loaded from: {datafog_pii_ner.__file__}\")\n!cd /content/datafog-labs && git log -1 --oneline"
34+
"source": "import os, sys\n\n# Clone or force-update to latest\nif not os.path.exists(\"/content/datafog-labs\"):\n !git clone https://github.com/DataFog/datafog-labs.git /content/datafog-labs\nelse:\n !cd /content/datafog-labs && git fetch origin && git reset --hard origin/main\n\n# Force reinstall to ensure latest code (pip may cache stale editable installs)\n!pip install -e \"/content/datafog-labs/pii-ner-v1[dev]\" --force-reinstall --no-deps -q\n!pip install -e \"/content/datafog-labs/pii-ner-v1[dev]\" -q # install deps if missing\n\nsys.path.insert(0, \"/content/datafog-labs/pii-ner-v1/src\")\n\n# Reload to pick up fresh install\nimport importlib\nimport datafog_pii_ner\nimportlib.reload(datafog_pii_ner)\nprint(f\"datafog_pii_ner loaded from: {datafog_pii_ner.__file__}\")\n!cd /content/datafog-labs && git log -1 --oneline\n\n# Verify critical fixes are present\nimport inspect\nfrom datafog_pii_ner.training.train import PiiTrainer\nsource = inspect.getsource(PiiTrainer.create_optimizer)\nassert \"eps\" in source, \"MISSING: eps=1.0 fix for AdamW NaN\"\nassert \"model.float()\" in source or \"self.model.float()\" in source, \"MISSING: FP32 master weights fix\"\nprint(\"Verified: eps=1.0 and FP32 master weights fixes are present\")"
3535
},
3636
{
3737
"cell_type": "code",

pii-ner-v1/src/datafog_pii_ner/training/train.py

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -35,18 +35,35 @@ def __init__(self, *args, lr_backbone: float | None = None, lr_head: float | Non
3535
def create_optimizer(self):
3636
"""Create AdamW with differential learning rates for backbone vs head.
3737
38-
The backbone group uses eps=1.0 to dampen AdamW's adaptive per-parameter
39-
scaling. Without this, AdamW's bias-corrected update at step 1 applies
40-
~lr per weight regardless of gradient magnitude, which produces NaN in
41-
DeBERTa on PyTorch 2.9+. Setting eps=1.0 makes backbone updates behave
42-
like SGD (update ≈ lr * grad), which is numerically safe and standard
43-
for fine-tuning pretrained transformers.
38+
Key details:
39+
- model.float() ensures FP32 master weights. from_pretrained() may load
40+
in the saved dtype (FP16 safetensors), but FP16 mixed precision requires
41+
FP32 parameters so the gradient scaler can unscale FP32 gradients.
42+
- Backbone eps=1.0 dampens AdamW's adaptive per-parameter scaling.
43+
Without this, bias-corrected updates at step 1 apply ~lr per weight
44+
regardless of gradient magnitude, producing NaN in DeBERTa on PyTorch 2.9+.
4445
"""
4546
if self.lr_backbone is not None and self.lr_head is not None:
47+
import torch
4648
from torch.optim import AdamW
4749

50+
# Ensure FP32 master weights — required for FP16 gradient scaler.
51+
# from_pretrained() may load in FP16 if safetensors are stored that way.
52+
self.model.float()
53+
4854
backbone_params = [p for n, p in self.model.named_parameters() if "deberta" in n and p.requires_grad]
4955
head_params = [p for n, p in self.model.named_parameters() if "deberta" not in n and p.requires_grad]
56+
57+
logger.info(
58+
f"Optimizer: backbone={len(backbone_params)} tensors (lr={self.lr_backbone}, eps=1.0), "
59+
f"head={len(head_params)} tensors (lr={self.lr_head})"
60+
)
61+
62+
# Verify all params are FP32
63+
non_fp32 = [(n, p.dtype) for n, p in self.model.named_parameters() if p.dtype != torch.float32]
64+
if non_fp32:
65+
logger.warning(f"Non-FP32 params after .float(): {non_fp32[:5]}")
66+
5067
self.optimizer = AdamW(
5168
[
5269
{"params": backbone_params, "lr": self.lr_backbone, "eps": 1.0},

0 commit comments

Comments
 (0)