本文主要講解pytorch中的optim累以及lr_schdule類和optim優化器的配置,使用方法。
在https://blog.csdn.net/dss_dssssd/article/details/83892824中提及優化算法的使用步驟,
-
optimer = optim.SGD()
先初始化 -
反向傳播更新參數
- 將上次迭代計算的梯度值清0
optimizer.zero_grad()
- 反向傳播,計算梯度值
loss.backward()
- 更新權值參數
optimizer.step()
一. 使用optimizer的步驟
SGD源碼: https://pytorch.org/docs/stable/_modules/torch/optim/sgd.html
- 將上次迭代計算的梯度值清0
1. 構造optimizer
1.1 初始化函數__init__
:
參數:
-
params: 包含參數的可迭代對象,必須爲Tensor
-
其餘的參數來配置學習過程中的行爲
optimizer = optim.SGD(model.parameters(), lr = 0.01, momentum=0.9)
optimizer = optim.Adam([var1, var2], lr = 0.0001)
1.2. per-parameter 選擇
不是傳入Tensor的可迭代對象,而是傳入
dict
的可迭代對象,每一個字典定義一個獨立的參數組,每一個dict
必須包含一個params
鍵,和一系列與該優化函數對應的參數。
optim.SGD([
{'params': model.base.parameters()},
{'params': model.classifier.parameters(), 'lr': 1e-3}
], lr=1e-2, momentum=0.9)
上述代碼,model.base
中的參數更新使用learning rate
爲1e-2
的SGD算法,而model.classifier
中的參數更新使用learning rate
爲 1e-2
, momentum爲0.9
的SGD算法
下面用一個兩層的線性迴歸的例子說明一下:
輸入爲[11, 1], 第一層爲[1, 10], 第二層爲[10, 1],最後的輸出爲[11,1]。在優化算法中,第一層和第二層分別使用不同的優化器配置方案。
class model_(nn.Module):
def __init__(self, in_dim):
super().__init__()
self.first_layer = nn.Sequential(
nn.Linear(in_dim, 10)
)
self.second_layer = nn.Sequential(
nn.Linear(10, 1)
)
def forward(self, x):
x = F.Relu(self.first_layer(x))
x = self.second_layer(x)
return x
x = np.arange(1, 11).reshape(-1, 1)
x = torch.from_numpy(x)
model =model_(1)
使用parameters()
打印出weight和bias,注意函數返回的是迭代器
print("the first params:")
# 返回的是迭代器
for param in model.first_layer.parameters():
print(param)
print("the second params:")
print(list(model.second_layer.parameters()))
out:
the first params:
Parameter containing:
tensor([[ 0.7895],
[ 0.4697],
[-0.0534],
[ 0.8223],
[ 0.9414],
[-0.5877],
[-0.4069],
[-0.6598],
[-0.1982],
[-0.2233]], requires_grad=True)
Parameter containing:
tensor([-0.9338, -0.9721, 0.3418, -0.4599, 0.1251, -0.2313, 0.9735, -0.1804,
0.0935, 0.9205], requires_grad=True)
the second params:
[Parameter containing:
tensor([[-0.0765, -0.1632, 0.0979, 0.2206, -0.0102, -0.0452, -0.3096, 0.0189,
-0.0240, -0.0900]], requires_grad=True), Parameter containing:
tensor([-0.1148], requires_grad=True)]
在接收了外部輸入的參數params以後,優化器會把params存在self.param_groups裏(是一個字典的列表)。每一個字典除了保存參數的[‘params’]鍵以外,Optimizer還維護着其他的優化係數,例如學習率和衰減率等
optimizer = optim.SGD(
[
{'params': model.first_layer.parameters()},
{'params': model.second_layer.parameters(), 'lr': 1e-3}
],
lr=1e-2, momentum=0.9)
print(optimizer.param_groups)
out:
[{‘params’: [Parameter containing:
tensor([[ 0.7895],
[ 0.4697],
[-0.0534],
[ 0.8223],
[ 0.9414],
[-0.5877],
[-0.4069],
[-0.6598],
[-0.1982],
[-0.2233]], requires_grad=True), Parameter containing:
tensor([-0.9338, -0.9721, 0.3418, -0.4599, 0.1251, -0.2313, 0.9735, -0.1804,
0.0935, 0.9205], requires_grad=True)], ‘lr’: 0.01, ‘momentum’: 0.9, ‘dampening’: 0, ‘weight_decay’: 0, ‘nesterov’: False},
{‘params’: [Parameter containing:
tensor([[-0.0765, -0.1632, 0.0979, 0.2206, -0.0102, -0.0452, -0.3096, 0.0189,
-0.0240, -0.0900]], requires_grad=True), Parameter containing:
tensor([-0.1148], requires_grad=True)], ‘lr’: 0.001, ‘momentum’: 0.9, ‘dampening’: 0, ‘weight_decay’: 0, ‘nesterov’: False}]
2. optimizer.zero_grade()
在所有的優化算法的基類Optimizer
https://pytorch.org/docs/stable/_modules/torch/optim/optimizer.html中的zero_grad
函數對self.param_group
中的所有的params清零
3. loss.backward()
4. optimizer.step()
4.1. 直接調用step()而不傳入closure
參數,大多數優化算法會用這種方法調用ok,因爲只涉及到一次調用
這個方法在Optimizer中是未定義,根據具體的優化器來單獨實現。
以下通過SGD類中的step()
函數來說明:
def step(self, closure=None):
"""Performs a single optimization step.
Arguments:
closure (callable, optional): A closure that reevaluates the model
and returns the loss.
"""
loss = None
if closure is not None:
loss = closure()
for group in self.param_groups:
weight_decay = group['weight_decay']
momentum = group['momentum']
dampening = group['dampening']
nesterov = group['nesterov']
for p in group['params']:
if p.grad is None:
continue
d_p = p.grad.data
if weight_decay != 0:
d_p.add_(weight_decay, p.data)
if momentum != 0:
param_state = self.state[p]
if 'momentum_buffer' not in param_state:
buf = param_state['momentum_buffer'] = torch.zeros_like(p.data)
buf.mul_(momentum).add_(d_p)
else:
buf = param_state['momentum_buffer']
buf.mul_(momentum).add_(1 - dampening, d_p)
if nesterov:
d_p = d_p.add(momentum, buf)
else:
d_p = buf
p.data.add_(-group['lr'], d_p)
return loss
取出每個group中的params進行更新,
d_p = p.grad.data
將loss.backward()
計算的梯度取出,通過weight_decay,momentum,learning_rate等優化器配置方法更新d_p
的值,最後賦值給p
。
4.2 對於Conjugate Gradient 和 LBFGS等優化算法,須有多次評估優化函數,因而要傳入closure
來多次計算模型。closure
完成的操作:
- 清楚梯度值
- 計算損失loss
- 返回loss
for input, target in dataset:
def closure():
optimizer.zero_grad()
output = model(input)
loss = loss_fn(output, target)
loss.backward()
return loss
optimizer.step(closure)