梯度累積 是什麼?
Gradient Accumulation:梯度累積 的完整解釋
多個訓練步驟內累加梯度,最後進行一次參數更新,有效增加批量大小而不增加記憶體占用,用於訓練記憶體受限的大型模型。
核心概念
Gradient Accumulation 的核心思想是:參數更新的依據是梯度,而梯度本身可以分步累加。標準訓練中,一個批次計算梯度後立即更新。在梯度累積中,多個小批次的梯度先累加,最後再用累加的梯度進行一次大更新。
這個設計利用了梯度的可加性:G_total = G_batch1 + G_batch2 + ... + G_batchN。用累加梯度更新參數 θ = θ - η * G_total,等價於用大批量大小訓練的一個梯度步。
梯度累積特別適合記憶體受限的場景。例如,要訓練批量 1024 的大模型,但 GPU 記憶體只容納批量 256。此時可以分 4 次前向-反向傳播,每次用批量 256,累積 4 個梯度,最後用累加梯度更新一次,等效於批量 1024 訓練。
運作原理
Gradient Accumulation 的實現步驟:
- 初始化梯度累積器 grad_accum = 0
- 設定累積步數 N(例如 4)
- 在循環中執行 N 次迭代: a. 前向傳播:loss = model(batch) b. 反向傳播:loss.backward() c. 梯度累積:grad_accum += model.parameters().grad(或自動完成)
- 執行優化器步驟:optimizer.step() 更新所有參數
- 清空梯度:optimizer.zero_grad()
- 重複步驟 3-5
在 PyTorch 中的實現簡單例子:
accumulation_steps = 4
for i, (inputs, targets) in enumerate(dataloader):
outputs = model(inputs)
loss = criterion(outputs, targets) / accumulation_steps
loss.backward()
if (i + 1) % accumulation_steps == 0:
optimizer.step()
optimizer.zero_grad()
核心注意事項:
- 損失除以累積步數,確保梯度量級正確
- 優化器步驟只在累積完成後執行
- 梯度清零也只在累積完成後執行
實際應用
Gradient Accumulation 在大規模模型訓練中廣泛應用。Transformer 訓練(BERT、GPT)常用梯度累積來模擬大批量。例如,預訓練 BERT 時,有效批量可能是 32768,但單個 GPU 可能只容納 256。通過梯度累積 128 步,可以達到大批量的效果。
在自然語言處理中,序列長度會直接影響記憶體占用。長序列模型(如全文檢索)常用梯度累積來維持大批量同時保持長序列。
在計算機視覺中,訓練高分辨率模型(如檢測器)時,單個高分辨率圖片占用大量記憶體。梯度累積讓研究者能在消費級 GPU 上訓練這些模型。
在分布式訓練中,梯度累積可與數據並行結合,進一步提升有效批量。Horovod 和 PyTorch Distributed 都支持梯度累積,使其成為大規模訓練的標準工具。
微調場景中,梯度累積也很有用。預訓練模型常用大批量訓練,微調時保持相同的有效批量可以穩定調整過程。
常見誤區
誤區一:認為梯度累積可以完全替代增加批量大小。梯度累積確實模擬了大批量,但訓練時間會增加(需要多次前向-反向傳播)。實際有效批量相同,但計算時間成比例增加。
誤區二:在梯度累積時忘記調整損失或梯度大小。若不除以累積步數,梯度會被放大,導致訓練不穩定。同樣,如果使用梯度剪裁,應在累積後進行。
誤區三:認為梯度累積會改變最終模型性能。實際上,若超參數調整得當,梯度累積應該不改變最終性能,只改變記憶體使用和訓練速度。
與相關技術的比較
Gradient Accumulation vs 批量大小增加:增加批量直接提供大批量梯度,但需要更多記憶體。梯度累積通過時間換空間,達到類似效果。
Gradient Accumulation vs 梯度檢查點:梯度檢查點重新計算激活值而非儲存,也能節省記憶體。兩者可結合:梯度檢查點降低記憶體,梯度累積進一步增加有效批量。
Gradient Accumulation vs 混合精度:混合精度使用 FP16 加速計算,梯度累積通過延遲更新增加有效批量。兩者互補,可同時應用。
Gradient Accumulation vs 優化器狀態分片(ZeRO):ZeRO 將優化器狀態分片存儲在多 GPU 上,梯度累積在單個設備上增加有效批量。在分布式訓練中常結合使用。