PyTorch 的優化器模塊 torch.optim,是神經網絡訓練中負責更新模型參數的核心模塊。它根據自動求導得到的梯度,按照一定的優化算法調整權重和偏置,使模型的損失函數逐步下降。
簡單地說,torch.optim 模塊回答的是:模型參數應該朝哪個方向更新、每次更新多少,以及如何讓訓練過程更穩定、更高效。
如果說 torch.nn 模塊負責構建模型,torch.autograd 模塊負責計算梯度,那么 torch.optim 模塊就負責把梯度真正轉化為參數更新。沒有優化器,模型即使能夠計算損失和梯度,也無法通過訓練不斷改進。
一、認識 torch.optim 模塊
torch.optim 是 PyTorch 中專門用于優化模型參數的模塊。它提供了多種常見優化算法,如 SGD、Adam、AdamW、RMSprop 等。
![]()
圖 1:優化器模塊在 PyTorch 訓練流程中的位置
在一個典型訓練過程中,優化器通常位于反向傳播之后、下一輪前向傳播之前:
→ 進入下一輪訓練一個最簡單的優化器使用方式如下:
這里的核心是:
? model.parameters() 提供需要更新的模型參數
? optim.Adam(...) 指定使用 Adam 優化算法
? lr=0.001 指定學習率
優化器本身并不定義模型結構,也不負責計算梯度。它只根據參數已有的 .grad 信息執行更新。
二、優化器在訓練閉環中的作用
神經網絡訓練的目標,是讓損失函數盡可能小。優化器的作用,就是根據梯度信息調整參數,使模型向更小的損失方向移動。
![]()
圖 2:從梯度到參數更新的基本過程
一個簡化的參數更新過程可以理解為:
其中:
? θ 表示模型參數
? L 表示損失函數
? ?θL 表示損失函數對參數 θ 的梯度
? η 表示學習率
? ← 表示用新值替換舊值
這條公式體現了梯度下降的基本思想:如果梯度指出損失上升最快的方向,那么參數更新就朝相反方向移動,從而盡量降低損失。
在 PyTorch 中,這個過程通常由三行代碼完成:
optimizer.step()它們分別表示:
? 清空舊梯度
? 反向傳播計算新梯度
? 根據梯度更新參數
這三步是理解 PyTorch 優化器的關鍵。
三、創建優化器:把參數交給優化算法
創建優化器時,通常需要把模型參數傳入優化器:
這里的 model.parameters() 會返回模型中所有需要訓練的參數,包括各層的權重和偏置。
可以查看這些參數:
print(name, param.shape, param.requires_grad)輸出結果類似:
2.bias torch.Size([3]) True優化器只會更新傳入給它的參數。如果某個參數沒有被傳入優化器,即使它有梯度,也不會被這個優化器更新。
這也是為什么自定義模型時,網絡層通常要定義在 __init__() 中,并正確注冊為 nn.Module 的子模塊。只有被模型管理的參數,才能通過 model.parameters() 被優化器找到。
四、zero_grad():清空上一輪梯度
在 PyTorch 中,梯度默認會累積,而不是每次自動覆蓋。因此,每次反向傳播前通常需要清空舊梯度。
例如:
optimizer.zero_grad()如果不清空梯度,當前批次計算出的梯度會與上一輪梯度相加,導致參數更新不符合預期。
一個簡單例子如下:
第二次打印的 x.grad 并不是只包含 y2 對 x 的梯度,而是前后兩次梯度的累加。
在普通訓練循環中,通常應寫成:
optimizer.step()這個順序很重要:
? 先清空舊梯度
? 再計算當前梯度
? 最后更新參數
也可以寫成:
optimizer.zero_grad(set_to_none=True)這種寫法會把梯度設為 None,在某些場景下可以減少內存占用。但初學階段可以先使用默認寫法,理解梯度清零的作用更重要。
五、backward() 與 step() 的關系
loss.backward() 和 optimizer.step() 經常連在一起出現,但它們的作用完全不同。
loss.backward()表示:根據當前計算圖,計算損失函數對模型參數的梯度。
optimizer.step()表示:根據參數中的 .grad,按照優化算法更新參數。
例如:
這里可以理解為:
? pred = model(x) 構建前向計算圖
? loss = criterion(pred, target) 得到損失
? loss.backward() 計算梯度
? optimizer.step() 使用梯度更新參數
需要注意的是,optimizer.step() 不會自動執行反向傳播。如果沒有先調用 loss.backward(),參數通常沒有可用梯度,優化器也就無法完成有效更新。
六、學習率:控制每次參數更新的步長
學習率(Learning Rate)是優化器中最重要的超參數之一。它決定每次參數沿梯度方向更新的幅度。
學習率過大,可能導致訓練震蕩,甚至損失無法下降;學習率過小,訓練會非常緩慢,甚至長時間停留在效果較差的位置。
例如:
optimizer = optim.SGD(model.parameters(), lr=0.01)其中 lr=0.01 就是學習率。
可以把學習率理解為參數更新的“步長”:
其中 η 就是學習率。
常見經驗是:
? SGD 常使用相對較大的學習率,例如 0.1、0.01、0.001
? Adam 常使用較小的學習率,例如 0.001、0.0001
? 學習率沒有固定最優值,需要結合任務、模型和數據調整
在實際訓練中,如果損失劇烈震蕩,可以嘗試降低學習率;如果損失下降很慢,可以嘗試適當增大學習率。
七、SGD:最基礎的梯度下降優化器
SGD 是隨機梯度下降(Stochastic Gradient Descent)的縮寫,是最基礎、也最重要的優化算法之一。
基本寫法如下:
SGD 的基本思想是:根據當前批次數據計算出的梯度,更新模型參數。
加入動量(momentum)后,SGD 可以在一定程度上減少震蕩,并加快沿穩定方向的更新:
動量可以理解為給參數更新加入“慣性”。如果多個批次的梯度方向比較一致,參數會沿這個方向更穩定地前進;如果梯度方向頻繁變化,動量可以緩解短期波動。
SGD 的優點是:
? 原理清晰
? 行為可控
? 在許多任務中泛化表現較好
SGD 的不足是:
? 對學習率較敏感
? 訓練速度可能較慢
? 通常需要更仔細地調參
因此,在教學和理解優化原理時,SGD 非常重要;在實際工程中,Adam 和 AdamW 也非常常用。
八、Adam:自適應學習率優化器
Adam 是深度學習中非常常用的優化器。它會根據梯度的一階矩和二階矩估計,為不同參數自適應地調整更新幅度。
常見寫法如下:
optimizer = optim.Adam(model.parameters(), lr=0.001)Adam 的優勢是:
? 對學習率相對不那么敏感
? 收斂通常較快
? 適合許多神經網絡任務
? 初學和實驗階段使用方便
一個完整示例如下:
這里要注意:Adam 只是參數更新算法,不改變模型結構,也不改變損失函數的定義。
在很多入門實驗中,Adam 是一個穩妥的默認選擇;但在一些任務中,SGD、AdamW 或帶學習率調度的方案可能表現更好。
九、AdamW:帶解耦權重衰減的 Adam
AdamW 是 Adam 的常用改進版本,尤其在 Transformer、視覺模型和大規模預訓練模型中非常常見。
常見寫法如下:
AdamW 中的 weight_decay 通常用于控制模型參數不要過大,從而起到一定的正則化作用。
需要注意的是,weight_decay 并不等同于學習率。它控制的是參數衰減強度,而學習率控制的是梯度更新步長。
可以簡單理解為:
? lr 決定每次更新走多遠
? weight_decay 限制參數不要無限變大
? AdamW 把權重衰減與 Adam 的梯度更新機制更清晰地分離
在現代深度學習實踐中,如果模型比較復雜,尤其是使用 Transformer 類結構,AdamW 往往比普通 Adam 更常見。
十、RMSprop 與其他優化器
除了 SGD、Adam 和 AdamW,PyTorch 還提供了其他優化器,例如:
? optim.RMSprop
? optim.Adadelta
? optim.Adagrad
? optim.NAdam
? optim.LBFGS
其中,RMSprop 也是一種自適應學習率優化器,曾在循環神經網絡等任務中較常見。
示例:
不同優化器的差異,主要體現在如何利用梯度歷史信息、如何調整不同參數的更新幅度,以及是否引入動量、權重衰減等機制。
初學階段不必一次掌握所有優化器。更重要的是先理解:
? 優化器接收模型參數
? 反向傳播得到梯度
? 優化器根據梯度更新參數
? 不同優化器只是更新策略不同
十一、參數組:為不同參數設置不同學習率
有時,一個模型中不同部分需要使用不同的學習率。例如,在遷移學習中,預訓練主干網絡可以使用較小學習率,新加的分類頭可以使用較大學習率。
這時可以使用參數組:
完整示例如下:
這里可以理解為:
? feature 部分學習率較小,避免大幅改變已有特征表示
? classifier 部分學習率較大,使新任務頭更快適應當前任務
參數組還可以為不同部分設置不同的 weight_decay、momentum 等選項。
十二、凍結參數與優化器
在遷移學習中,經常需要凍結部分參數,只訓練某些層。
例如:
凍結后,最好只把仍然需要訓練的參數交給優化器:
這樣做可以讓訓練目標更清晰,也避免優化器持有不需要更新的參數。
完整示例:
需要注意的是,設置 requires_grad=False 會阻止梯度計算,但如果優化器早已創建,仍可能持有舊參數引用。因此,凍結參數后,通常應重新創建優化器或確認優化器中的參數組是否正確。
十三、學習率調度器:動態調整學習率
訓練過程中,學習率不一定始終固定。有時訓練前期需要較大學習率快速下降,后期需要較小學習率精細調整。
PyTorch 提供了學習率調度器,例如:
? StepLR
? MultiStepLR
? ExponentialLR
? CosineAnnealingLR
? ReduceLROnPlateau
一個簡單示例:
在訓練循環中通常寫成:
這里的含義是:每經過 10 個 epoch,學習率乘以 0.1。
學習率調度器的作用不是直接更新模型參數,而是調整優化器中的學習率,從而影響后續參數更新幅度。
需要注意的是,不同調度器的調用時機可能不同。有些按 epoch 調整,有些按 batch 調整,有些根據驗證集指標調整。實際使用時應根據調度器設計選擇合適的位置。
十四、保存與恢復優化器狀態
訓練模型時,有時不僅要保存模型參數,還要保存優化器狀態。因為 Adam、AdamW、RMSprop 等優化器內部會維護梯度歷史信息。
保存方式如下:
恢復訓練時:
如果只是推理,通常只需要加載模型參數;如果要從中斷處繼續訓練,最好同時恢復優化器狀態。
原因是:優化器狀態會影響后續參數更新。如果只恢復模型參數而不恢復優化器狀態,訓練雖然可以繼續,但優化過程可能與中斷前不完全一致。
十五、一個完整示例:用優化器訓練簡單分類模型
![]()
圖 3:優化器驅動的神經網絡訓練閉環
下面用一個完整示例,把模型、損失函數、自動求導和優化器串起來:
這個訓練閉環可以概括為:
前向傳播 → 計算損失 → 清空梯度 → 反向傳播 → 參數更新
其中:
? torch.nn 負責模型結構和損失函數
? torch.autograd 負責自動計算梯度
? torch.optim 負責根據梯度更新參數
優化器并不是單獨工作的,它必須與模型、損失函數和自動求導機制配合,才能完成真正的訓練過程。
十六、使用優化器時應注意的問題
1、不要忘記 zero_grad()
訓練循環中通常要寫:
optimizer.step()如果忘記 zero_grad(),梯度會不斷累積,導致參數更新異常。
2、不要把 step() 放在 backward() 前面
錯誤寫法:
loss.backward()正確寫法:
optimizer.step()因為優化器需要根據已經計算好的梯度更新參數。
3、優化器只會更新傳入的參數
如果某些參數沒有傳給優化器,它們不會被更新。
例如:
optimizer = optim.Adam(model.classifier.parameters(), lr=0.001)這表示只更新 classifier 部分,而不是整個模型。
4、凍結參數后要檢查優化器
如果先創建優化器,再凍結參數,優化器中可能仍然保存了原來的參數組。更穩妥的做法是:凍結參數后重新創建優化器。
5、學習率過大或過小都會影響訓練
學習率過大,損失可能震蕩或發散;學習率過小,訓練可能非常緩慢。遇到訓練不穩定時,學習率通常是最先需要檢查的超參數之一。
6、Adam 不等于一定更好
Adam 使用方便,收斂速度通常較快,但并不意味著在所有任務中都優于 SGD。不同優化器適合不同任務,實際效果需要通過實驗驗證。
7、權重衰減不是學習率
lr 控制參數更新步長,weight_decay 控制參數衰減強度。二者作用不同,不應混淆。
8、繼續訓練時應保存優化器狀態
如果要從中斷處繼續訓練,建議同時保存:
optimizer.state_dict()否則模型參數雖然恢復了,但優化器內部狀態沒有恢復,后續訓練軌跡可能發生變化。
小結
torch.optim 模塊負責根據梯度更新模型參數,是神經網絡訓練閉環中的關鍵環節。學習優化器,重點是理解 zero_grad() 清空舊梯度、backward() 計算新梯度、step() 執行參數更新,以及學習率、權重衰減、參數組和學習率調度器如何影響訓練過程。
“點贊有美意,贊賞是鼓勵”
特別聲明:以上內容(如有圖片或視頻亦包括在內)為自媒體平臺“網易號”用戶上傳并發布,本平臺僅提供信息存儲服務。
Notice: The content above (including the pictures and videos if any) is uploaded and posted by a user of NetEase Hao, which is a social media platform and only provides information storage services.