4.PyTorch——优化器
import numpy as np
import pandas as pd
import torch as t
PyTorch将深度学习中常用的优化方法全部封装在torch.optim
中,其设计十分灵活,能够很方便的扩展成自定义的优化方法。
所有的优化方法都是继承基类optim.Optimizer
,并实现了自己的优化步骤。下面就以最基本的优化方法——随机梯度下降法(SGD)举例说明。这里需重点掌握:
- 优化方法的基本使用方法
- 如何对模型的不同部分设置不同的学习率
- 如何调整学习率
# 定义一个LeNet网络
class Net(t.nn.Module):
def __init__(self):
super(Net, self).__init__()
self.features = t.nn.Sequential(
t.nn.Conv2d(3, 6, 5),
t.nn.ReLU(),
t.nn.MaxPool2d(2, 2),
t.nn.Conv2d(6, 16, 5),
t.nn.ReLU(),
t.nn.MaxPool2d(2, 2)
)
self.classifier = t.nn.Sequential(
t.nn.Linear(16*5*5, 120),
t.nn.ReLU(),
t.nn.Linear(120, 84),
t.nn.ReLU(),
t.nn.Linear(84, 10)
)
def forward(self, x):
x = self.features(x)
x = x.view(-1, 16*5*5)
x = self.classifier(x)
return x
net = Net()
optimizer = t.optim.SGD(params=net.parameters(), lr=1)
optimizer.zero_grad() # 梯度清零
input = t.randn(1, 3, 32, 32)
output = net(input)
output.backward(output)
optimizer.step() # 执行优化
# 为不同子网络设置不同的学习率,在finetune中经常用到
# 如果对某个参数不指定学习率,就使用最外层的默认学习率
optimizer = t.optim.SGD([
{'params': net.features.parameters()}, # 学习率为1e-5
{'params': net.classifier.parameters(), 'lr': 1e-2}
], lr=1e-5)
optimizer
SGD (
Parameter Group 0
dampening: 0
differentiable: False
foreach: None
lr: 1e-05
maximize: False
momentum: 0
nesterov: False
weight_decay: 0
Parameter Group 1
dampening: 0
differentiable: False
foreach: None
lr: 0.01
maximize: False
momentum: 0
nesterov: False
weight_decay: 0
)
# 只为两个全连接层设置较大的学习率,其余层的学习率较小
special_layers = t.nn.ModuleList([net.classifier[0], net.classifier[3]])
special_layers_params = list(map(id, special_layers.parameters()))
base_params = filter(lambda p: id(p) not in special_layers_params, net.parameters())
optimizer = t.optim.SGD([{'params':base_params},
{'params':special_layers.parameters(), 'lr':0.01}], lr=0.001)
optimizer
SGD (
Parameter Group 0
dampening: 0
differentiable: False
foreach: None
lr: 0.001
maximize: False
momentum: 0
nesterov: False
weight_decay: 0
Parameter Group 1
dampening: 0
differentiable: False
foreach: None
lr: 0.01
maximize: False
momentum: 0
nesterov: False
weight_decay: 0
)
对于如何调整学习率,主要有两种做法。一种是修改optimizer.param_groups中对应的学习率,另一种是更简单也是较为推荐的做法——新建优化器,由于optimizer十分轻量级,构建开销很小,故而可以构建新的optimizer。但是后者对于使用动量的优化器(如Adam),会丢失动量等状态信息,可能会造成损失函数的收敛出现震荡等情况。
# 方法1: 调整学习率,新建一个optimizer
old_lr = 0.1
optimizer1 = t.optim.SGD([
{'params': net.features.parameters()},
{'params': net.classifier.parameters(), 'lr': old_lr*0.1}
], lr=1e-5)
optimizer1
SGD (
Parameter Group 0
dampening: 0
differentiable: False
foreach: None
lr: 1e-05
maximize: False
momentum: 0
nesterov: False
weight_decay: 0
Parameter Group 1
dampening: 0
differentiable: False
foreach: None
lr: 0.010000000000000002
maximize: False
momentum: 0
nesterov: False
weight_decay: 0
)
# 方法2: 调整学习率, 手动decay, 保存动量
for param_group in optimizer.param_groups:
param_group['lr'] *= 0.1 # 学习率为之前的0.1倍
optimizer
SGD (
Parameter Group 0
dampening: 0
differentiable: False
foreach: None
lr: 0.0001
maximize: False
momentum: 0
nesterov: False
weight_decay: 0
Parameter Group 1
dampening: 0
differentiable: False
foreach: None
lr: 0.001
maximize: False
momentum: 0
nesterov: False
weight_decay: 0
)