三、網絡優化方法
1.梯度下降算法
-
梯段下降算法是一種尋找使損失函數最小化的方法,從數學上的角度來看,梯度的方向是函數增長速度最快的方向,那么梯度的反方向就是函數減少最快的方向,所以有Wijnew=Wijold?η?E?WijW_{ij}^{new} = W_{ij}^{old}-\eta\frac{\partial E}{\partial W_{ij}}Wijnew?=Wijold??η?Wij??E?
- 其中,η\etaη是學習率,如果學習率太小,那么每次訓練之后得到的效果都太小,增大訓練的時間成本;如果學習率太大,那就有可能直接跳過最優解,進入無限的訓練中。解決的方法就是,學習率也需要隨著訓練的進行而變化
-
在進行模型訓練的時候,有三個基礎的概念
- EpochEpochEpoch:使用全部數據對模型進行一次完整訓練,訓練輪次
- Batch_sizeBatch\_sizeBatch_size:使用訓練集中的小部分樣本對模型權重進行以此反向傳播的參數更新,每次訓練每批次樣本的數量(越大越好)
- IteraionIteraionIteraion:使用一個Batch數據對模型進行一次參數更新的過程
- eg.eg.eg.:假設數據集有500005000050000個訓練樣本,現在選擇BatchSize=256Batch Size= 256BatchSize=256對模型進行訓練,每個EpochEpochEpoch要訓練的圖片數量為500005000050000,訓練集具有BatchBatchBatch個數為50000/256+1=19650000/256 + 1 = 19650000/256+1=196;每個EpochEpochEpoch具有的IterationIterationIteration個數為196196196;101010個EpochEpochEpoch具有的IterationIterationIteration個數為196019601960
-
在深度學習中,梯度下降的幾種方式的根本區別就在于Batch Size不同
梯度下降方式 Training Set Size Batch Size Number of Batch BGD N N 1 SGD N 1 N Mini-Batch N B N / B + 1 - 注:N/ B+ 1是針對未整除的情況。整除則是 N/ B
- 在工作的時候通常使用的是 Mini-Batch
2.反向傳播算法過程
(1)反向傳播(BP算法)
- 前向傳播:指的是數據輸入的神經網絡中,逐層向前傳輸,一直運算到輸出層為止
- 反向傳播(BackPropagation):利用損失函數ERROR,從后往前,結合梯度下降算法,依次求各個參數的偏導并進行參數更新
- 解釋:
- 前向傳播:獲取預測結果
- 計算損失:交叉熵/MSE(先計算W3W_3W3?,再計算W2W_2W2?,最后計算W1W_1W1?)
- 反向傳播:利用梯度下降算法對參數進行更新
for _ in range(epochs):for train_x, train_y in dataloader:# 將一個batch的訓練數據送入模型y_pred = model(train_x.type(torch.float32))# 計算損失值loss = criterion(y_pred, train_y, reshape(-1,1).type(torch.float32))total_loss += loss.item()train_sample += len(train_y)# 梯度清零optimizer.zero_grad()# 自動微分loss.backward()# 更新參數optimizer.step()
3.梯度下降的優化方法
-
梯度下降優化算法中,可能會碰到以下情況:
-
- 碰到平緩區域,梯度值較小,參數優化變慢
- 碰到鞍點,梯度為0,參數無法優化
- 碰到局部最小值,參數不是最優
-
對于這些問題,出現了一些對梯度下降算法的優化方法,例如:Momentum, AgaGrad,PMSprop,Adam等
(1)指數加權平均
- 指數移動加權平均:參考各數值,并且各數值的權重都不同,距離越遠的數字對平均數計算的貢獻就越小(權重較小),距離越近則對平均數的計算貢獻就越大(權重越大)
- 計算公式:st={Y1,t=0β?st?1+(1?β)?Yt,t>0{s}_t = \begin{cases} \displaystyle Y_1, & t = 0 \\ \displaystyle \beta \cdot {s}_{t-1} + (1 - \beta) \cdot Y_t, & t > 0 \end{cases}st?={Y1?,β?st?1?+(1?β)?Yt?,?t=0t>0?
-
- StS_tSt?表示指數加權平均值
- YtY_tYt?表示ttt時刻的值
- β\betaβ調整權重參數,該值越大平均數越平緩
(2)動量算法Momentum
- 梯度計算公式:Dt=β?St?1+(1?β)?WtD_t = \beta \cdot S_{t - 1} + (1-\beta) \cdot W_tDt?=β?St?1?+(1?β)?Wt?
- St?1S_{t - 1}St?1?表示;表示梯度移動加權平均值
- WtW_tWt?表示當前時刻的梯度值
- DtD_tDt?為當前時刻的梯度值
- β\betaβ為權重系數
- 示例:權重β\betaβ為0.9, 則第一次梯度值S1=D1=W1S_1 = D_1 = W_1S1?=D1?=W1?;第二次梯度值D2=S2=0.9×S1+W2×0.1D_2 = S_2 = 0.9 \times S_1 + W_2 \times0.1D2?=S2?=0.9×S1?+W2?×0.1;第三次梯度值D3=S3=0.9×S2+W3×0.1D_3 = S_3 = 0.9 \times S_2 + W_3 \times 0.1D3?=S3?=0.9×S2?+W3?×0.1;第四次梯度值D4=S4=0.9×S3+W4×0.1D_4 = S_4 = 0.9 \times S_3 + W_4 \times 0.1D4?=S4?=0.9×S3?+W4?×0.1
- 梯度下降公式中梯度的計算,就不再是當前時刻t的梯度值,而是歷史梯度值的指數移動加權平均值,公式修改為:Wt+1=Wt?α?DtW_{t + 1} = W_t - \alpha \cdot D_tWt+1?=Wt??α?Dt?
拓展:Monmentum優化方法是如何一定程度上克服“平緩”,“鞍點”的問題呢?
- 當處于鞍點位置時,由于當前的梯度為0,參數無法更新。但是Momentum動量梯度下降算法已經在先前累計了一些梯度值,很有可能使得跨過鞍點
- 由于mini-batch普通的梯度下降算法,每次選取少數的樣本梯度確定前進方向,可能會出現震蕩,使得訓練時間變長。Momentum使用移動加權平均,平滑了梯度的變化,使得前進方向更加平緩,有利于加快訓練過程
"""加了動量之后的結果"""import torchw = torch.tensor([1.0], requires_grad=True, dtype=torch.float32)loss = ((w ** 2)*0.5).sum()optimizer = torch.optim.SGD([w], lr = 0.01, momentum=0.9)optimizer.zero_grad()
loss.backward()
optimizer.step()print("第一次更新(加了動量之后)梯度:",w.grad)
print("第一次更新(加了動量之后)w:",w.detach())loss = ((w ** 2)*0.5).sum()
optimizer.zero_grad()
loss.backward()
optimizer.step()print("第二次更新(加了動量之后)梯度:",w.grad)
print("第二次更新(加了動量之后)w:",w.detach()) # tensor([0.9711])改變了,沒加動量時 tensor([0.9801])"""tensor([0.9900])--->tensor([0.9711]) 減少得更多,更新得更加快一點"""
輸出結果:
第一次更新(加了動量之后)梯度: tensor([1.])
第一次更新(加了動量之后)w: tensor([0.9900])
第二次更新(加了動量之后)梯度: tensor([0.9900])
第二次更新(加了動量之后)w: tensor([0.9711])
(3)AdaGrad
-
AdaGrad通過對不同的參數分量使用不同的學習率,AdaGrad的學習率總體會逐漸減小,計算步驟如下
-
初始化學習率α\alphaα,初始化參數θ\thetaθ(weight&bias)小常數σ=1e?6\sigma = 1e - 6σ=1e?6(放在分母上,防止分母為0)
-
初始化梯度累計變量s=0s = 0s=0
-
從訓練集中采樣mmm個樣本的小批量,計算梯度ggg
-
累積平方梯度s=s+g⊙g{s} = {s} + {g} \odot {g}s=s+g⊙g,⊙\odot⊙表示各個分量相乘
-
學習率α\alphaα的計算公式如下:α=αs+σ\alpha = \frac{\alpha}{\sqrt s + \sigma}α=s?+σα?
-
參數更新公式如下:θ=θ?αs+σ?g\theta = \theta - \frac{\alpha}{\sqrt s + \sigma} \cdot gθ=θ?s?+σα??g
-
重復2-4步驟,即可完成網絡訓練
- AdaGrad缺點:可能會使得學習率過早,過量的降低(學習率太小了,則更新速度變慢了,迭代相同次數就不能夠到最優解),導致訓練后期學習率大小較難找到最優解
import torchw = torch.tensor([1.0], requires_grad=True, dtype=torch.float32)
loss = ((w ** 2)*0.5).sum()optimizer = torch.optim.Adagrad([w], lr = 0.01)optimizer.zero_grad()
loss.backward()print("第一次更新 梯度:",w.grad) # 獲取梯度的值
print("第一次更新 w:",w.detach()) # 獲取w的值# 再次更新參數
loss = ((w ** 2)*0.5).sum()
optimizer.zero_grad()
loss.backward()
optimizer.step()print("第二次更新 梯度:",w.grad) # 獲取梯度的值
print("第二次更新 w:",w.detach()) # 獲取w的值
輸出結果:
第一次更新 梯度: tensor([1.])
第一次更新 w: tensor([1.])
第二次更新 梯度: tensor([1.])
第二次更新 w: tensor([0.9900])
(4)RMSProp
-
RMSProp優化算法是對AdaGrad的優化,最主要的不同是,其使用指數移動加權平均梯度替換歷史梯度的平方和。計算過程如下:
- 初始化學習率α\alphaα,初始化參數θ\thetaθ,小常數σ=1e?6\sigma = 1e-6σ=1e?6
- 初始化參數θ\thetaθ
- 初始化梯度累計變量sss
- 從訓練集中采樣mmm個樣本的小批量,計算梯度ggg
- 使用指數移動平均累積歷史梯度,公式如下:s=β?s+(1?β)g⊙gs = \beta \cdot s + (1 - \beta)g \odot gs=β?s+(1?β)g⊙g
- 學習率α\alphaα的計算公式如下:α=αs+σ\alpha = \frac{\alpha}{\sqrt s + \sigma}α=s?+σα?
- 參數更新公式如下:θ=θ?αs+σ?g\theta = \theta - \frac{\alpha}{\sqrt s + \sigma} \cdot gθ=θ?s?+σα??g
import torchw = torch.tensor([0.1], requires_grad=True, dtype = torch.float32)
loss = ((w ** 2)*0.5).sum()optimizer = torch.optim.RMSprop([w], lr = 0.01, alpha = 0.9)optimizer.zero_grad()
loss.backward()print("第一次更新 梯度:", w.grad)
print("第一次更新 w:", w.detach())# 再次更新參數
loss = ((w ** 2)*0.5).sum()
optimizer.zero_grad()
loss.backward()
optimizer.step()print("第二次更新 梯度:",w.grad) # 獲取梯度的值
print("第二次更新 w:",w.detach()) # 獲取w的值
輸出結果:
第一次更新 梯度: tensor([0.1000])
第一次更新 w: tensor([0.1000])
第二次更新 梯度: tensor([0.1000])
第二次更新 w: tensor([0.0684])
4.學習率衰減方法
- 通常是和 動量算法Momentum 組合在一起
- 后面學習中,通常使用指定間隔學習率衰減
(1)等間隔學習率衰減
import torch
import matplotlib.pyplot as plot# 參數初始化
LR = 0.1
iteration = 100
epochs = 200
# 網絡數據初始化
x = torch.tensor([1.0])
w = torch.tensor([1.0], requires_grad = True)
y = torch.tensor([1.0])
# 優化器
optimizer = torch.optim.SGD([w], lr = LR, momentum=0.9)
# 學習率策略
scheduler_lr = torch.optim.lr_scheduler.StepLR(optimizer, step_size=20, gamma = 0.8)
# 遍歷輪次
epoch_list = []
lr_list = []
for epoch in range(epochs):lr_list.append(scheduler_lr.get_last_lr())epoch_list.append(epoch)# 遍歷batchfor i in range(iteration):# 計算損失loss = ((w*x-y)**2)*0.5# 更新參數optimizer.zero_grad()loss.backward()optimizer.step()# 更新lrscheduler_lr.step()
# 繪制結果
plt.plot(epoch_list, lr_list)
plt.grid()
plt.show()
(2)指定間隔學習率衰減
import torch
import matplotlib.pyplot as plot# 參數初始化
LR = 0.1
iteration = 100
epochs = 200
# 網絡數據初始化
x = torch.tensor([1.0])
w = torch.tensor([1.0], requires_grad = True)
y = torch.tensor([1.0])
# 優化器
optimizer = torch.optim.SGD([w], lr = LR, momentum=0.9)
# 學習率策略
scheduler_lr = torch.optim.lr_scheduler.MultiStepLR(optimizer, milestones = [20, 60, 90, 135, 180], gamma = 0.8)
# 遍歷輪次
epoch_list = []
lr_list = []
for epoch in range(epochs):lr_list.append(scheduler_lr.get_last_lr())epoch_list.append(epoch)# 遍歷batchfor i in range(iteration):# 計算損失loss = ((w*x-y)**2)*0.5# 更新參數optimizer.zero_grad()loss.backward()optimizer.step()# 更新lrscheduler_lr.step()
# 繪制結果
plt.plot(epoch_list, lr_list)
plt.grid()
plt.show()
(3)按指數學習率衰減
- 這種策略用得很少,一般不會選擇
- gamma值通常是小于1,它是指 指數的底
- 調整方式:lr=lr?gammaepochlr = lr \cdot gamma^{epoch}lr=lr?gammaepoch
import torch
import matplotlib.pyplot as plot# 參數初始化
LR = 0.1
iteration = 100
epochs = 200
# 網絡數據初始化
x = torch.tensor([1.0])
w = torch.tensor([1.0], requires_grad = True)
y = torch.tensor([1.0])
# 優化器
optimizer = torch.optim.SGD([w], lr = LR, momentum=0.9)
# 學習率策略
scheduler_lr = torch.optim.lr_scheduler.ExponentialLR(optimizer, gamma = 0.8)
# 遍歷輪次
epoch_list = []
lr_list = []
for epoch in range(epochs):lr_list.append(scheduler_lr.get_last_lr())epoch_list.append(epoch)# 遍歷batchfor i in range(iteration):# 計算損失loss = ((w*x-y)**2)*0.5# 更新參數optimizer.zero_grad()loss.backward()optimizer.step()# 更新lrscheduler_lr.step()
# 繪制結果
plt.plot(epoch_list, lr_list)
plt.grid()
plt.show()