前面我們學習過了損失函數,損失函數Loss是衡量模型輸出與真實標簽之間的差異的。有了損失函數Loss,通過Loss根據一定的策略 來更新模型中的參數使得損失函數Loss逐步降低;這便是優化器optimizer的任務。本節優化器optimizer主要包括3方面內容,分別是(1)什么是優化器,優化器的定義和作用是?(2)Pytorch中提供的優化器optimizer的基本屬性是?(3)optimizer的方法是?
了解優化器之前,可以通過機器學習模型訓練步驟簡略了解一下優化器。在損失函數模塊,根據模型輸出與真實標簽之間的差異得到Loss損失函數;有了Loss,通常采用Pytorch中的 autograd自動梯度求導 得到 模型中每個可學習參數的梯度grad;有了梯度grad,優化器optimizer獲取梯度grad,通過一系列優化策略來更新模型參數,該參數就可使得Loss值下降,進而達到模型輸出值與真實標簽之間的差異減小的目的。
什么是優化器
Pytorch的優化器:
管理並更新模型中可學習參數的值,使得模型輸出更接近真實標簽。
分析
其中,可學習參數指 權值 和 偏置bias;
其次,優化器最主要的2大功能:
(1)管理:指優化器管理哪一部分參數;
(2)更新:優化器當中具有一些優化策略,優化器可采用這些優化策略更新模型中可學習參數的值;這一更新策略,在神經網絡中通常都會采用梯度下降法。
解釋
那么,什么是梯度下降法?為什么梯度下降法就可以使得Loss降低呢?
<預備知識>
根據圖2例(1)中的一元函數曲線,可以看出函數\(y=4x^2\)在x=2處的導數(變化率/切線斜率)為16;這說明導數是函數在指定坐標軸上的變化率。由於一元函數只有1個自變量,其方向受限。只有在二元函數或三元函數才考慮方向導數。在圖2例(2)中“紅色線”標注的,發現\(\frac{\partial z}{\partial x}\)是固定y軸,求在x軸方向上的變化率;而\(\frac{\partial z}{\partial y}\)是固定x,求在y軸方向上的變化率。那么,由於方向l是任意的,該紅色點應當具有無窮多個方向導數。由此引出梯度概念。所謂梯度,就是在無窮多個方向導數中,其方向為方向導數取得最大的一個向量。
梯度,是1個向量(應當具有方向/模長2個屬性)。①其方向是方向導數取得最大的方向;②模長為方向導數的值。簡言之,梯度向量的方向使得當前點增長達到最快的方向,模長為增長速度。
采用梯度下降法的原因—Loss損失函數朝着梯度的負方向去變化:由於梯度方向是增長最快的,那么梯度的負方向就是下降最快的。因此,在模型更新參數時,采用“梯度下降法”朝着梯度的負方向去變化,可以使得損失函數Loss逐步降低。
<總結>
Pytorch中優化器optimizer 管理着模型中的可學習參數,並采用梯度下降法 更新着可學習參數的值。
optimizer的屬性
class Optimizer(object)
def_init_(self,params,defaults):
self.defaults=defaults
self.state=defaultdict(dict)
self.param_groups=[ ]
...
param_groups=[{'params':param_groups}]
基本屬性:
(1)defaults:優化器超參數;
(2)state:參數的緩存,如momentum的緩存;
(3)param_groups:管理的參數組;
(4)_step_count:記錄更新次數,學習率調整中使用;
基本屬性:
(1)defaults:優化器超參數;用來存儲學習率、momentum的值等等;
(2)state:參數的緩存,如momentum的緩存;采用momentum時會使用前幾次更新時使用的梯度,也就是前幾次的梯度,把前幾次的梯度值緩存下來,在本次更新中使用;
(3)param_groups:管理的參數組;優化器最重要的屬性,已經知道優化器是管理可學習參數,這一系列可學習參數就放在param_groups這一屬性中,同時,這一參數組定義為list。
param_groups=[{'params':param_groups}]
因此,param_groups是1個list。而在list[ ] 中,每一個元素又是1個字典{ } ,這些字典中有很多key,其中最重要的key是-'params',只有'params'當中才會存儲訓練模型的參數。
(4)_step_count:記錄更新次數,學習率調整中使用;比如更新在第100次,就下降學習率,更新到200次,又下降學習率。
optimizer的方法
一、zero_grad()
1、理論
zero_grad():清空所管理參數的梯度
Pytorch中tensor特性:tensor張量梯度不自動清零
已知參數param是1個特殊的張量,張量當中都會有梯度grad。由於Pytorch中張量tensor的梯度grad是不會自動清零的,它會在每一次backward反向傳播時采用autograd計算梯度,並把梯度值累加到張量的grad屬性中的。
如下圖3所示,由於Pytorch中的grad屬性不自動清零,因此每計算1次梯度就自動累加到grad屬性中造成錯誤;因此,一定要在使用完梯度后或者進行梯度求導(反向傳播)之間通過zero_grad進行清零。
2、操作
Pytorch中優化器對梯度清零zero_grad()的具體實現
二、step()
step():執行一步更新
當計算得到Loss,利用Loss進行backward反向傳播計算各個參數的梯度之后,采用step()進行一步更新,更新權值參數。step()會采用梯度下降的策略,具體方法有很多種,比如隨機梯度下降法、momentum+動量方法、autograd自適應學習率等等一系列優化方法。
三、add_param_group()
add_param_group():添加參數組
add_param_group()添加一組參數到優化器中。已知優化器管理很多參數,這些參數是可以分組;對於不同組的參數,有不同的超參數設置,例如在某一模型中,希望特征提取部分的權值參數的學習率小一點,學習更新慢一點,這時可以把特征提取的參數設置為一組參數,而對於后面全連接層,希望其學習率大一點,學習快一點。這時,可以把整個模型參數設置為兩組,一組為特征提取部分的參數,另一部分是全連接層的參數,對這兩組設置不同的學習率或超參數,這時就需要用到參數組概念。
四、state_dict()、load_state_dict()
state_dict():獲取優化器當前狀態信息字典
load_state_dict():加載狀態信息字典
這兩個方法是一組操作,用於模型斷點的續訓練。例如訓練1個模型需要10天,第5天停電或者意外原因,模型終止了訓練。為了再次訓練可以從斷點開始,可以利用該組操作state_dict()和load_state_dict()保存在第5天模型以及優化器中的信息,在模型訓練時會一定間隔如10epoch保存當前狀態信息,用來在斷點可以恢復訓練。
實驗分析optimizer的方法
(1)step()
//構建1個2×2大小的隨機張量,並開放“梯度”
weight = torch.randn((2, 2), requires_grad=True)
//構建1個2×2大小的全1梯度張量
weight.grad = torch.ones((2, 2))
//將可學習參數[weight]傳入優化器
optimizer = optim.SGD([weight], lr=1) //0.1
# -------------------- step ------------------
flag = 1
if flag:
print("weight before step:{}".format(weight.data))
optimizer.step() # 修改lr=1 0.1觀察結果
print("weight after step:{}".format(weight.data))
實驗結果
(2)zero_grad()
# ------------------------ zero_grad --------------------
flag = 0
# flag = 1
if flag:
print("weight before step:{}".format(weight.data))
optimizer.step() # 修改lr=1 0.1觀察結果
print("weight after step:{}".format(weight.data))
print("weight in optimizer:{}\nweight in weight:{}\n".format(id(optimizer.param_groups[0]['params'][0]), id(weight)))
print("weight.grad is {}\n".format(weight.grad))
optimizer.zero_grad()
print("after optimizer.zero_grad(), weight.grad is\n{}".format(weight.grad))
實驗結果
(3)add_param_group
# ------------------- add_param_group -------------------
flag = 0
# flag = 1
if flag:
//這一優化器當前已經管理了一組參數 weight
print("optimizer.param_groups is\n{}".format(optimizer.param_groups))
//想再添加一組參數,設置這組參數學習率lr=1e-04
//首先設置好要添加的這一組參數 w2
w2 = torch.randn((3, 3), requires_grad=True)
//構建字典,key中 params中放置參數 w2 ,利用方法 add_param_group把這一組參數加進來
optimizer.add_param_group({"params": w2, 'lr': 0.0001})
print("optimizer.param_groups is\n{}".format(optimizer.param_groups))
實驗結果
(4)state_dict和load_state_dict
# ------------------------ state_dict ------------
# flag = 0
flag = 1
if flag:
optimizer = optim.SGD([weight], lr=0.1, momentum=0.9)
opt_state_dict = optimizer.state_dict()
//打印step()更新之前的狀態信息字典
print("state_dict before step:\n", opt_state_dict)
//10步更新
for i in range(10):
optimizer.step()
//打印step()更新之后的狀態信息字典
print("state_dict after step:\n", optimizer.state_dict())
//保存更新之后的狀態信息字典在當前文件夾下名為 optimizer_state_dict.pkl的文件
torch.save(optimizer.state_dict(), os.path.join(BASE_DIR, "optimizer_state_dict.pkl"))
# -------------------------load state_dict --------------
flag = 0
# flag = 1
if flag:
//重新構建優化器
optimizer = optim.SGD([weight], lr=0.1, momentum=0.9)
//創建加載狀態名
state_dict = torch.load(os.path.join(BASE_DIR, "optimizer_state_dict.pkl"))
print("state_dict before load state:\n", optimizer.state_dict())
//利用load_state_dict方法加載狀態信息,接着當前狀態往下訓練
optimizer.load_state_dict(state_dict)
print("state_dict after load state:\n", optimizer.state_dict())
總結:
1、優化器管理並更新模型中的可學習參數;
2、學習優化器中的基本屬性和方法,其中最重要的屬性是param_groups,是1個list,其中的每一個元素是1個字典dict,字典中有很多key—‘params’,'params'才是模型中具體的參數。
