優化器nn.optim-07
在上述介紹中, nn.Module模組提供了網路骨架, nn.functional提供了各式各樣的損失函式, 而Autograd又自動實現了求導與反向傳播機制, 這時還缺少一個如何進行模型優化、 加速收斂的模組, nn.optim應運而生。
nn.optim中包含了各種常見的優化演算法, 包括隨機梯度下降演算法SGD(Stochastic Gradient Descent, 隨機梯度下降) 、 Adam(Adaptive Moment Estimation) 、 Adagrad、 RMSProp, 這裡僅對常用的SGD與Adam兩種演算法進行詳細介紹。
1. SGD方法
梯度下降(Gradient Descent) 是迭代法中的一種, 是指沿著梯度下降的方向求解極小值, 一般可用於求解最小二乘問題。 在深度學習中,當前更常用的是SGD演算法, 以一個小批次(Mini Batch) 的資料為單位, 計算一個批次的梯度, 然後反向傳播優化, 並更新引數。 SGD的表
公式中, gt代表了引數的梯度, η代表了學習率(Learning Rate) ,即梯度影響引數更新的程度, 是訓練中非常重要的一個超引數。 SGD優化演算法的好處主要有兩點:
·分擔訓練壓力: 當前資料集通常數量較多, 尺度較大, 使用較大的資料同時訓練顯然不現實, SGD則提供了小批量訓練並優化網路的方法, 有效分擔了GPU等計算硬體的壓力。
·加快收斂: 由於SGD一次只採用少量的資料, 這意味著會有更多次的梯度更新, 在某些資料集中, 其收斂速度會更快。
當然, SGD也有其自身的缺點:
·初始學習率難以確定: SGD演算法依賴於一個較好的初始學習率,但設定初始學習率並不直觀, 並且對於不同的任務, 其初始值也不固定。
·容易陷入區域性最優: SGD雖然採用了小步快走的思想, 但是容易陷入區域性的最優解, 難以跳出。
有效解決區域性最優的通常做法是增加動量(momentum) , 其概念來自於物理學, 在此是指更新的時候一定程度上保留之前更新的方向,同時利用當前批次的梯度進行微調, 得到最終的梯度, 可以增加優化的穩定性, 降低陷入區域性最優難以跳出的風險。 其函式如式(2-3) 與式(2-4) 所示。
公式中的μ為動量因子, 當此次梯度下降方向與上次相同時, 梯度會變大, 也就會加速收斂。 當梯度方向不同時, 梯度會變小, 從而抑制梯度更新的震盪, 增加穩定性。 在訓練的中後期, 梯度會在區域性極小值周圍震盪, 此時gt接近於0, 但動量的存在使得梯度更新並不是0, 從而有可能跳出區域性最優解。
雖然SGD演算法並不完美, 但在當今的深度學習演算法中仍然取得了大量的應用, 使用SGD有時能夠獲得性能更佳的模型。
2. Adam方法
在SGD之外, Adam是另一個較為常見的優化演算法。 Adam利用了梯度的一階矩與二階矩動態地估計調整每一個引數的學習率, 是一種學習率自適應演算法。
Adam的優點在於, 經過調整後, 每一次迭代的學習率都在一個確定範圍內, 使得引數更新更加平穩。 此外, Adam演算法可以使模型更快收斂, 尤其適用於一些深層網路, 或者神經網路較為複雜的場景.
下面通過一個三層感知機的例子來介紹基本的優化過程。 新建一個mlp.py檔案, 內容如下:
1 # from torch import optim 2 # optimizer = optim.SGD(model.parameters(), lr=0.001, momentum= 0.9) 3 # optimizer = optim.Adam([var1, var2], lr=0.0001) 4 5 import torch 6 from torch import nn 7 8 class MLP(nn.Module): 9 10 def __init__(self, in_dim, hid_dim1, hid_dim2, out_dim): 11 super(MLP, self).__init__() 12 13 self.layer = nn.Sequential( 14 nn.Linear(in_dim, hid_dim1), 15 nn.ReLU(), 16 nn.Linear(hid_dim1, hid_dim2), 17 nn.ReLU(), 18 nn.Linear(hid_dim2, out_dim), 19 nn.ReLU(0) 20 ) 21 22 def forward(self, x): 23 x = self.layer(x) 24 return xView Code
1 import torch 2 from mlp import MLP 3 from torch import optim 4 from torch import nn 5 6 # 例項化模型, 並賦予每一層的維度 7 model = MLP(28*28, 300, 200, 10) 8 # 列印model的結構, 由3個全連線層組成 9 print(model) 10 >> MLP( 11 (layer): Sequential( 12 (0): Linear(in_features=784, out_features=300, bias=True) 13 (1): ReLU() 14 (2): Linear(in_features=300, out_features=200, bias=True) 15 (3): ReLU() 16 (4): Linear(in_features=200, out_features=10, bias=True) 17 (5): ReLU() 18 ) 19 ) 20 21 #採用SGD優化器, 學習率為0.01 22 optimizer = optim.SGD(params = model.parameters(), lr=0.01) 23 data = torch.randn(10, 28*28) 24 output = model(data) 25 26 # 由於是10分類, 因此label元素從0到9, 一共10個樣本 27 label = torch.Tensor([1, 0, 4, 7, 9, 3, 4, 5, 3, 2]).long() 28 print(label) 29 >> tensor([1, 0, 4, 7, 9, 3, 4, 5, 3, 2]) 30 31 # 求損失 32 criterion = nn.CrossEntropyLoss() 33 loss = criterion(output, label) 34 print(loss) 35 tensor(2.2808, grad_fn=<NllLossBackward>) 36 37 # 清空梯度, 在每次優化前都需要進行此操作 38 o = optimizer.zero_grad() 39 40 # 損失的反向傳播 41 l = loss.backward() 42 43 # 利用優化器進行梯度更新 44 ol = optimizer.step()View Code
對於訓練過程中的學習率調整, 需要注意以下兩點:
·不同引數層分配不同的學習率: 優化器也可以很方便地實現將不同的網路層分配成不同的學習率, 即對於特殊的層單獨賦予學習率, 其餘的保持預設的整體學習率, 具體示例如下:
1 # 對於model中需要單獨賦予學習率的層, 如special層, 則使用'lr'關鍵字單獨賦予 2 optimizer = optim.SGD( 3 [{'params': model.special.parameters(), 'lr': 0.001}, 4 {'params': model.base.parameters()}, lr=0.0001)View Code
學習率動態調整: 對於訓練過程中動態的調整學習率, 可以在迭代次數超過一定值後, 重新賦予optim優化器新的學習率。