1.Yolov5學習率調整策略:lr_scheduler.LambdaLR
本代碼模擬yolov5的學習率調整,深度決議其中torch.optim.lr_scheduler在yolov5的使用方法,有助于提高我們對該代碼的理解,
??為了簡單實作模擬yolov5的學習率調整策略,在此代碼中我使用resnet18網路,yolov5則使用的是darknet網路骨架,
??在yolov5代碼訓練的程序中,作者對不同的層使用不同的學習率調整方法,分別分為權重層weight,偏執層bais,和BN層,單獨調整不同層的學習率可以使得模型訓練的更好,
??另外,yolov5在對學習率更新也采用了warmp-up的的方法預熱學習率,在warmp-up階段,采用的是一維線性插值來進行對每次迭代的學習率進行更新,在warmp-up階段以后采用的余弦退火演算法來對學習率進行更新,所以接下來我們會分別詳細介紹一下代碼的組成以及其中所用到的方法:
如下是我模擬yolov5訓練代碼學習率調整的方法:
1.我定義了訓練的總epoch是100,樣本總數量是600個,訓練的batch_size是20,
import matplotlib.pyplot as plt
import torch
import torch.optim as optim
from torch.optim.lr_scheduler import LambdaLR
from torchvision.models import resnet18
import seaborn as sns
import math
import torch.nn as nn
import numpy as np
num_epochs = 100
nums = 600
batch_size = 20
n = nums/batch_size
#定義10分類網路
model = resnet18(num_classes=10)
# optimizer parameter groups 設定了個優化組:權重,偏置,其他引數
pg0, pg1, pg2 = [], [], []
for k, v in model.named_parameters():
v.requires_grad = True
if '.bias' in k:
pg2.append(v) # biases
elif '.weight' in k and '.bn' not in k:
pg1.append(v) # apply weight decay
else:
pg0.append(v) # all else
optimizer = optim.SGD(pg0, lr=0.01,momentum=0.937, nesterov=True)
#給optimizer管理的引陣列中增加新的組引數,
#可為該組引數定制lr,momentum,weight_decay 等在finetune 中常用,
optimizer.add_param_group({'params': pg1,'weight_decay':0.0005 }) # add pg2 (biases)
optimizer.add_param_group({'params': pg2}) # add pg2 (biases)
lf = lambda x: ((1 + math.cos(x * math.pi / num_epochs)) / 2) * (1 - 0.2) + 0.2
scheduler = LambdaLR(
optimizer=optimizer,
lr_lambda=lf, #傳入一個函式或一個以函式為元素串列,作為學習率調整的策略
)
start_epoch=0
scheduler.last_epoch = start_epoch - 1
lr0,lr1,lr2, epochs = [], [], [] ,[]
optimizer.zero_grad()
for epoch in range(start_epoch,num_epochs):
for i in range(n):
#訓練的迭代次數
ni = i + n * epoch
# Warmup 熱身的迭代次數
if ni <= 1000:
xi = [0, 1000]
for j, x in enumerate(optimizer.param_groups):
#一維線性插值
x['lr'] = np.interp(ni, xi, [0.1 if j == 2 else 0.0, 0.01 * lf(epoch)])
if 'momentum' in x:
x['momentum'] = np.interp(ni, xi, [0.8, 0.937])
pass # iter and train here
# Scheduler 學習率衰減
lr = [x['lr'] for x in optimizer.param_groups]
lr0.append(lr[0])
lr1.append(lr[1])
lr2.append(lr[2])
#學習率更新
scheduler.step()
epochs.append(epoch)
plt.figure()
plt.subplot(221)
plt.plot(epochs, lr0, color="r",label='l0')
plt.legend()
plt.subplot(222)
plt.plot(epochs, lr1, color="b",label='l1')
plt.legend()
plt.subplot(223)
plt.plot(epochs, lr2,color="g",label='l2')
plt.legend()
plt.show()
如下圖所示我分別繪制出訓練100個epoch時的學習率變化情況,

2.余弦退火演算法
如果想看懂上面的學習率調整演算法,我們得需要理解余弦退火演算法,它的公式和解釋如下:
n
e
w
?
l
r
=
e
t
a
?
m
i
n
+
(
i
n
i
t
i
a
l
?
l
r
?
e
t
a
?
m
i
n
)
?
(
(
1
+
c
o
s
(
c
u
r
?
e
p
o
c
h
T
?
m
a
x
?
π
)
)
/
2
)
new_-lr=eta_-min+(initial_-lr-eta_-min)*((1+cos(\frac{cur_-epoch}{T_-max}*\pi ))/2)
new??lr=eta??min+(initial??lr?eta??min)?((1+cos(T??maxcur??epoch??π))/2)
new_-lr:新得到的學習率,
initial_lr:初始學習率,
eta_min:表示最小學習率,
cur_epoch:代表當前訓練到某個epoch對應的值,
T_max: 代表訓練的總epoch數,
last_epoch:最后一個epoch的index,如果是訓練了很多個epoch后中斷了,繼續訓練,這個值就等于加載的模型的epoch,默認為-1表示從頭開始訓練,即從epoch=1開始,
比如當我們initial_-lr為0.01,eta_-min為0.002,epoch為200,T_max為也就是200時,可以繪制如下的學習率圖,
![[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-b7RxjjtW-1611891207119)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20210128173052003.png)]](https://img.uj5u.com/2021/01/30/219825301013112.png)
余弦退火函式的深度剖析:
那么這個函式的具體含義是什么呢?它的結構為什么是這個形式呢?其實只看函式很簡單,它就是由一個余弦函式cos和一些簡單的加法乘法組合而成,接下來我來深度剖析這個函式的具體含義:
為了方便計算和展示函式的意義,假設初始學習率為1,最小學習率為0.2,我們一層一層的對函式進行決議,它的內部是cos函式,而cos函式的取值范圍為[-1,1],
我們繪制出
c
o
s
(
c
u
r
?
e
p
o
c
h
T
?
m
a
x
?
π
)
cos(\frac{cur_-epoch}{T_-max}*\pi )
cos(T??maxcur??epoch??π)的曲線如圖一所示:
( 1 + c o s ( c u r ? e p o c h T ? m a x ? π ) ) (1+cos(\frac{cur_-epoch}{T_-max}*\pi )) (1+cos(T??maxcur??epoch??π))函式的曲線如圖2所示:
( ( 1 + c o s ( c u r ? e p o c h T ? m a x ? π ) ) / 2 ) ((1+cos(\frac{cur_-epoch}{T_-max}*\pi ))/2) ((1+cos(T??maxcur??epoch??π))/2)函式的曲線繪制如圖3所示:
( i n i t i a l ? l r ? e t a ? m i n ) ? ( ( 1 + c o s ( c u r ? e p o c h T ? m a x ? π ) ) / 2 ) (initial_-lr-eta_-min)*((1+cos(\frac{cur_-epoch}{T_-max}*\pi ))/2) (initial??lr?eta??min)?((1+cos(T??maxcur??epoch??π))/2)如圖4所示:
e t a ? m i n + ( i n i t i a l ? l r ? e t a ? m i n ) ? ( ( 1 + c o s ( c u r ? e p o c h T ? m a x ? π ) ) / 2 ) eta_-min+(initial_-lr-eta_-min)*((1+cos(\frac{cur_-epoch}{T_-max}*\pi ))/2) eta??min+(initial??lr?eta??min)?((1+cos(T??maxcur??epoch??π))/2)如圖5所示,

就這么簡單,我們一步一步的解刨了函式的具體含義,接下類我們來決議一下預熱程序中所使用的線性插值方法來更新學習率
3. 一維線性插值:np.interp(x**,** xp**,** fp)
它的定義是將一維分段線性插值回傳給具有給定離散資料點(xp,fp)且在x處求值的函式,
x:待插入資料的橫坐標.
xp:原始資料點的橫坐標
fp:原始資料點的y坐標,與xp的長度相同,
回傳值
浮點數或復數(對應于fp值)或ndarray. 插入資料的縱坐標,和x形狀相同,
例1:可以看到當x=2.5的時候,得到的y值是1,
import numpy as np
import matplotlib.pyplot as plt
x = 2.5
xp = [1, 2, 3]
fp = [3, 2, 0]
y = np.interp(x, xp, fp) # 1.0
plt.plot(xp, fp,'ro')
plt.plot(x, y, 'x')
plt.show()
如下圖所示,其中藍色的點是插值后的點,

例2:在紅色的十個點之間進行插入50個藍色標記的點如下所示:
import numpy as np
import matplotlib.pyplot as plt
x = np.linspace(0, 2 * np.pi, 10)
y = np.sin(x)
xvals = np.linspace(0, 2 * np.pi, 50)
yinterp = np.interp(xvals, x, y)
plt.plot(x, y, 'ro')
plt.plot(xvals, yinterp, 'x')
plt.show()

轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/254402.html
標籤:python
