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