1. 程式人生 > 實用技巧 >優化器nn.optim-07

優化器nn.optim-07

在上述介紹中, nn.Module模組提供了網路骨架, nn.functional提供了各式各樣的損失函式, 而Autograd又自動實現了求導與反向傳播機制, 這時還缺少一個如何進行模型優化、 加速收斂的模組, nn.optim應運而生。

nn.optim中包含了各種常見的優化演算法, 包括隨機梯度下降演算法SGDStochastic Gradient Descent, 隨機梯度下降) 、 AdamAdaptive Moment Estimation) 、 AdagradRMSProp, 這裡僅對常用的SGDAdam兩種演算法進行詳細介紹。

1SGD方法

梯度下降(Gradient Descent) 是迭代法中的一種, 是指沿著梯度下降的方向求解極小值, 一般可用於求解最小二乘問題。 在深度學習中,當前更常用的是SGD演算法, 以一個小批次(Mini Batch) 的資料為單位, 計算一個批次的梯度, 然後反向傳播優化, 並更新引數。 SGD的表

達式如式(2-1) 與式(2-2) 所示 .

公式中, gt代表了引數的梯度, η代表了學習率(Learning Rate) ,即梯度影響引數更新的程度, 是訓練中非常重要的一個超引數。 SGD優化演算法的好處主要有兩點:

·分擔訓練壓力: 當前資料集通常數量較多, 尺度較大, 使用較大的資料同時訓練顯然不現實, SGD則提供了小批量訓練並優化網路的方法, 有效分擔了GPU等計算硬體的壓力。

·加快收斂: 由於SGD一次只採用少量的資料, 這意味著會有更多次的梯度更新, 在某些資料集中, 其收斂速度會更快。

當然, SGD也有其自身的缺點:

·初始學習率難以確定: SGD演算法依賴於一個較好的初始學習率,但設定初始學習率並不直觀, 並且對於不同的任務, 其初始值也不固定。

·容易陷入區域性最優: SGD雖然採用了小步快走的思想, 但是容易陷入區域性的最優解, 難以跳出。

有效解決區域性最優的通常做法是增加動量(momentum) , 其概念來自於物理學, 在此是指更新的時候一定程度上保留之前更新的方向,同時利用當前批次的梯度進行微調, 得到最終的梯度, 可以增加優化的穩定性, 降低陷入區域性最優難以跳出的風險。 其函式如式(2-3) 與式(2-4) 所示。

公式中的μ為動量因子, 當此次梯度下降方向與上次相同時, 梯度會變大, 也就會加速收斂。 當梯度方向不同時, 梯度會變小, 從而抑制梯度更新的震盪, 增加穩定性。 在訓練的中後期, 梯度會在區域性極小值周圍震盪, 此時gt接近於0, 但動量的存在使得梯度更新並不是0, 從而有可能跳出區域性最優解。

雖然SGD演算法並不完美, 但在當今的深度學習演算法中仍然取得了大量的應用, 使用SGD有時能夠獲得性能更佳的模型。

2Adam方法

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 x
View 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優化器新的學習率。