「機器學習領域」,訓練深度學習神經網路,在 PyTorch迴圈中,梯度剪裁(torch.nn.utils.clip_grad_norm_)處理 必須安插在 loss.backward() 之後、optimizer.step() 之前。
💡 費曼比喻:PyTorch 訓練迴圈「水桶接水」🪣5 大步驟
PyTorch 採用動態計算圖 (Autograd),其核心物理機制是:梯度預設為「累加 (Accumulate, param.grad += ...)」。
想像每個神經元權重身旁都放了一個「梯度水桶 (param.grad)」:
[步驟 1] : 倒乾淨舊水optimizer.zero_grad();映射動作(🫗水桶歸零 empty)
▼
[步驟] 2: 前向算出損失output = model(x);映射動作loss = criterion()
▼
[步驟 3] : 反向向水桶注水loss.backward() ;映射動作(寫入 param.grad)
▼
[步驟 4] : 檢查並截斷溢水 clip_grad_norm_() ;映射動作 (防止溢位 NaN)
▼
[步驟 5] : 拿水去蓋房子optimizer.step() ;映射動作(更新 w = w - lr * grad)。
1️⃣ optimizer.zero_grad() —— 為何要放在最前面?(倒乾淨舊水)
• 物理原因:PyTorch 在執行 loss.backward() 時,不會主動覆蓋原本的梯度,而是將新算出的梯度直接加上去 (param.grad += new_grad)。
• 如果不清空會怎樣? 上一個 Batch 留下來的舊梯度會和這次的梯度混在一起,導致梯度越加越大、方向亂掉,最終引發梯度爆炸 (NaN)!
• 安插位置:必須在 loss.backward() 之前執行(通常放在每個 Batch 迭代的最開頭)。
2️⃣ output = model(inputs) 與 loss = criterion(output, targets) —— 前向傳播
• 計算模型當前預估值,並算出與真實標籤的差距(Loss 值)。
3️⃣ loss.backward() —— 反向傳播(往水桶注水)
• Autograd 引擎根據鏈式法則 (Chain Rule) 從後往前算出一階偏微分,並填入每個參數的 .grad 屬性(水桶裡裝滿了這次 Batch 的梯度)。
4️⃣ torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm=1.0) —— 梯度裁剪(防溢水護欄)
• 修復 NaN 的關鍵:此時水桶剛裝滿梯度,但可能因為輸入極端值或學習率過大導致水桶裡的水爆表 (梯度範數 ||g|| > max_norm)。
• 裁剪機制:趁水還沒拿去更新權重前,將過大的梯度向量按比例縮放剪短,強行壓回 max_norm 安全區內。
5️⃣ optimizer.step() —— 為何一定要放在最後面?(拿水更新權重)
• 物理原因:optimizer.step() 的工作是根據公式 w_new = w - lr * grad 拿水桶裡的梯度去修改權重 w。
• 順序邏輯:沒有算完梯度 (backward) 3️⃣並裁剪好 (clip)4️⃣,就絕對不能執行更新 (step)5️⃣! 所以 optimizer.step() 一定放在整個 Batch 更新流程的最後一步。