深度学习本身是一个不断优化,逼近真实映射函数的过程。而这个过程是需要优化器不断的迭代更新参数,不断降低损失值,这其中最基础也是关键的参数就是学习率。可以说用好学习率策略,准确率至少上升十几个点。
下面依次通过实例来看下相应的学习率策略
import torch
from torch import nn, optim
from torch.autograd import Variable
import numpy as np
import matplotlib.pyplot as plt
x_train = np.array([[3.3], [4.4], [5.5], [6.71], [6.93], [4.168],
[9.779], [6.182], [7.59], [2.167], [7.042],
[10.791], [5.313], [7.997], [3.1]], dtype=np.float32)
y_train = np.array([[1.7], [2.76], [2.09], [3.19], [1.694], [1.573],
[3.366], [2.596], [2.53], [1.221], [2.827],
[3.465], [1.65], [2.904], [1.3]], dtype=np.float32)
x_train = torch.from_numpy(x_train)
y_train = torch.from_numpy(y_train)
# Linear Regression Model
class LinearRegression(nn.Module):
def __init__(self):
super(LinearRegression, self).__init__()
self.linear1 = nn.Linear(1, 5) # input and output is 1 dimension
self.linear2 = nn.Linear(5,1)
def forward(self, x):
out = self.linear1(x)
out = self.linear2(out)
return out
model = LinearRegression()
print(model.linear1)
# 微调:自定义每一层的学习率
# 定义loss和优化函数
criterion = nn.MSELoss()
optimizer = optim.SGD(
[{
"params":model.linear1.parameters(),"lr":0.01},
{
"params":model.linear2.parameters()}],
lr=0.02)
lambda1 = lambda epoch: epoch//100
lambda2 = lambda epoch: 0.95**epoch
step_schedule = optim.lr_scheduler.StepLR(step_size=20,gamma=0.9,optimizer=optimizer)
cosine_schedule = optim.lr_scheduler.CosineAnnealingLR(optimizer=optimizer,T_max=20,eta_min=0.0004)
exponent_schedule = optim.lr_scheduler.ExponentialLR(optimizer=optimizer,gamma=0.9)
reduce_schedule = optim.lr_scheduler.ReduceLROnPlateau(optimizer=optimizer)
multi_schedule = optim.lr_scheduler.MultiStepLR(optimizer=optimizer,milestones=[120,180])
lambda_schedule = optim.lr_scheduler.LambdaLR(optimizer=optimizer,lr_lambda=[lambda1,lambda2])
step_lr_list = []
cosine_lr_list = []
exponent_lr_list = []
reduce_lr_list = []
loss_list = []
multi_list = []
lambda1_list = []
lambda2_list = []
# 开始训练
num_epochs = 240
for epoch in range(num_epochs):
inputs = Variable(x_train)
target = Variable(y_train)
# forward
out = model(inputs)
loss = criterion(out, target)
# backward
optimizer.zero_grad()
loss.backward()
optimizer.step()
step_schedule.step()
cosine_schedule.step()
exponent_schedule.step()
reduce_schedule.step(loss)
multi_schedule.step()
lambda_schedule.step()
# print(schedule.get_lr())
loss_list.append(loss.item())
# print(optimizer.param_groups[0]["lr"])
step_lr_list.append(step_schedule.get_lr()[0])
cosine_lr_list.append(cosine_schedule.get_lr()[0])
exponent_lr_list.append(exponent_schedule.get_lr()[0])
reduce_lr_list.append(optimizer.param_groups[0]["lr"])
multi_list.append(multi_schedule.get_lr()[0])
lambda1_list.append(optimizer.param_groups[0]["lr"])
lambda2_list.append(optimizer.param_groups[1]["lr"])
# print(optimizer.param_groups[0]["lr"])
# print(optimizer.param_groups[1]["lr"])
# if (epoch+1) % 20 == 0:
# print('Epoch[{}/{}], loss: {:.6f}'
# .format(epoch+1, num_epochs, loss.item()))
plt.subplot(121)
plt.plot(range(len(loss_list)),loss_list,label="loss")
plt.legend()
plt.subplot(122)
plt.plot(range(len(step_lr_list)),step_lr_list,label="step_lr")
plt.plot(range(len(cosine_lr_list)),cosine_lr_list,label="cosine_lr")
plt.plot(range(len(exponent_lr_list)),exponent_lr_list,label="exponent_lr")
plt.plot(range(len(reduce_lr_list)),reduce_lr_list,label="reduce_lr")
plt.plot(range(len(multi_list)),multi_list,label="multi_lr")
plt.plot(range(len(lambda1_list)),lambda1_list,label="lambda1_lr")
plt.plot(range(len(lambda2_list)),lambda2_list,label="lambda2_lr")
plt.legend()
plt.show()
使用的是简单的两层全连接网络,不必深究主要是展示学习率的变化。
等步长间隔调整学习率
optim.lr_scheduler.StepLR(optimizer, step_size, gamma=0.1, last_epoch=-1)
step_size是每隔多少个epoch学习率衰减一次,last_epoch=-1时epoch初始值为0。gamma设置的衰减率即 lr = lr*gamma,我这里取step_size=20,gamma=0.9
cosine学习率
optim.lr_scheduler.CosineAnnealingLR(optimizer, T_max, eta_min=0)
T_max表示半个周期( π \pi π)的长度,eta_min表示最小的cos值。这里设置T_max=50,eta_min=0.0004
指数衰减学习率
optim.lr_scheduler.ExponentialLR(optimizer, gamma, last_epoch=-1)
公式 l r = l r ∗ γ e p o c h lr=lr*\gamma^{epoch} lr=lr∗γepoch这里每全部迭代一次数据成为一个epoch
通常 γ \gamma γ值设置会比较大。这是参数 γ \gamma γ = 0.99
自适应调整学习率
optim.lr_scheduler.ReduceLROnPlateau(optimizer, mode='min', factor=0.1, patience=10,
verbose=False, threshold=1e-4, threshold_mode='rel',
cooldown=0, min_lr=0, eps=1e-8)
参数:
mode(str)- 模式选择,有 min 和 max 两种模式, min 表示当指标不再降低(如监测loss), max 表示当指标不再升高(如监测 accuracy)。
factor(float)- 学习率调整倍数(等同于其它方法的 gamma),即学习率更新为 lr = lr * factor
patience(int)- 忍受该指标多少个 step 不变化,当忍无可忍时,调整学习率。
verbose(bool)- 是否打印学习率信息, print(‘Epoch {
:5d}: reducing learning rate of group {
} to {
:.4e}.’.format(epoch, i, new_lr))
threshold_mode(str)- 选择判断指标是否达最优的模式,有两种模式, rel 和 abs。
当 threshold_mode == rel,并且 mode == max 时, dynamic_threshold = best * ( 1 +threshold );
当 threshold_mode == rel,并且 mode == min 时, dynamic_threshold = best * ( 1 -threshold );
当 threshold_mode == abs,并且 mode== max 时, dynamic_threshold = best + threshold ;
当 threshold_mode == rel,并且 mode == max 时, dynamic_threshold = best - threshold;
threshold(float)- 配合 threshold_mode 使用。
cooldown(int)- “冷却时间“,当调整学习率之后,让学习率调整策略冷静一下,让模型再训练一段时间,再重启监测模式。
min_lr(float or list)- 学习率下限,可为 float,或者 list,当有多个参数组时,可用 list 进行设置。
这里使用loss作为检测值,其他参数为默认值
非等间隔调整学习率
optim.lr_scheduler.MultiStepLR(optimizer, milestones, gamma=0.1, last_epoch=-1)
通过参数milestones给定衰减的epoch列表,可以在指定的epoch时期进行衰减。
参数设置 milestones=[120,180]
自定义网络层隔学习率
optim.lr_scheduler.LambdaLR( optimizer, lr_lambda, last_epoch=-1)
这里第一层去全连接设置的是上升的学习率lambda1,第二层是下降的学习率lambda2。lambda匿名函数得出学习率的衰减值,lr=lr*decay,decay有lambda函数得出。
lambda1 = lambda epoch: epoch//100
lambda2 = lambda epoch: 0.95**epoch
参数设置,第一层学习率为0.01,第二层学习率为0.02
经过基础的尝试,发现一些比较骚的操作实际效果一般。反而简单设置的会好一点,像等间隔衰减和非等间隔衰减。非等间隔衰减适合在对损失函数初步了解后进行细致给定衰减epoch。给定每一层的学习率适合迁移学习的微调,多用在卷积网路的全连接层。
炼丹不易,多提意见!
参考博文 https://blog.csdn.net/shanglianlm/article/details/85143614