一、优化器Optimizer 加速神经网络训练(Speed Up Training)
越复杂的神经网络 , 越多的数据 , 使得在训练神经网络时花费的时间也就越多。 原因很简单, 是因为计算量太大了,所以我们需要寻找一些方法, 让神经网络的训练快起来。
1、Stochastic Gradient Descent (SGD)
把数据分成小批小批的,然后再分批进行计算。每次使用批数据,虽然不能反映整体数据的情况,但是也可以很大的程度的加速训练过程,并且不会丢失过多的准确率。
2、Momentum 更新方法
- 传统的参数更新:原始的 W 累加【一个负的学习率 × 校正值(dx)】。这种方法会让学习过程曲折无比。好比一个喝醉的人回家时,摇摇晃晃走了很多弯路。
- Momentum参数更新:把这个人从平地上放到了一个斜坡上, 只要他往下坡的方向走一点点, 由于向下的惯性, 他不自觉地就一直往下走, 走的弯路也变少了。
3、AdaGrad 更新方法
- 在学习率上进行优化, 使得每一个参数更新都会得到不同的学习率。好比给他一双不好走路的鞋子, 使得他一摇晃着走路就脚疼, 鞋子成为了走弯路的阻力, 逼着他往前直着走。
4、RMSProp 更新方法
合并【部分的 Momentum 的惯性原则】以及 【AdaGrad 的对错误方向的阻力】,让其同时具备两种方法的优势。
5、Adam 更新方法
计算m 时有 momentum 下坡的属性, 计算 v 时有 adagrad 阻力的属性, 然后再更新参数时 把 m 和 V 都考虑进去。实验证明, 大多数使用 Adam 都能又快又好的达到目标, 迅速收敛。
二、Optimizer 优化器
上面已经介绍了几种优化器,那么我们现在编写代码来看一下各种优化器的效果图
1、准备伪数据
import torch
import matplotlib.pyplot as plt
# 定义的部分超参数
LR = 0.01 # 学习率
BATCH_SIZE = 32 # 批处理数
EPOCH = 12 # 轮数
# 伪数据
x = torch.unsqueeze(torch.linspace(-1, 1, 1000), dim=1)
y = x.pow(2) + 0.1*torch.normal(torch.zeros(*x.size()))
# 画图
plt.scatter(x.numpy(), y.numpy())
plt.show()
输出结果:
2、创建神经网络
为了更好的对比出每一种优化器,需要给他们各自创建一个神经网络。
# 使用上节内容提到的 data loader
torch_dataset = Data.TensorDataset(x, y)
loader = Data.DataLoader(dataset=torch_dataset, batch_size=BATCH_SIZE, shuffle=True, num_workers=2,)
# 默认的 network 形式
class Net(torch.nn.Module):
def __init__(self):
super(Net, self).__init__()
self.hidden = torch.nn.Linear(1, 20) # 隐含层
self.predict = torch.nn.Linear(20, 1) # 输出层
def forward(self, x):
x = F.relu(self.hidden(x))
x = self.predict(x)
return x
# 为每个优化器创建一个 net
net_SGD = Net()
net_Momentum = Net()
net_RMSprop = Net()
net_Adam = Net()
nets = [net_SGD, net_Momentum, net_RMSprop, net_Adam] # 把创建的网络放在一个列表里面,便于训练
3、优化器 Optimizer
创建不同的优化器,用来训练不同的网络。
# 创建不同的优化器
opt_SGD = torch.optim.SGD(net_SGD.parameters(), lr=LR)
opt_Momentum = torch.optim.SGD(net_Momentum.parameters(), lr=LR, momentum=0.8) # 增加动量参数
opt_RMSprop = torch.optim.RMSprop(net_RMSprop.parameters(), lr=LR, alpha=0.9)
opt_Adam = torch.optim.Adam(net_Adam.parameters(), lr=LR, betas=(0.9, 0.99))
optimizers = [opt_SGD, opt_Momentum, opt_RMSprop, opt_Adam]
loss_func = torch.nn.MSELoss() # 计算误差
losses_his = [[], [], [], []] # 记录 training 时不同神经网络的 loss
4、训练
for epoch in range(EPOCH):
print('Epoch: ', epoch)
for step, (b_x, b_y) in enumerate(loader):
# 对每个优化器, 优化属于他的神经网络
for net, opt, l_his in zip(nets, optimizers, losses_his):
output = net(b_x) # 在net中训练数据 x, 输出预测值
loss = loss_func(output, b_y) # 计算预测值与实际y的误差,(预测值写在前面)
opt.zero_grad() # 把net中参数的梯度都将为0,为了下面的优化神经网络
loss.backward() # 误差反向传播, 计算参数更新值
opt.step() # 将参数更新值施加到 net 的 parameters 上,优化梯度
l_his.append(loss.data.numpy()) # 误差记录
5、代码整合
import torch
import torch.utils.data as Data
import torch.nn.functional as F
import matplotlib.pyplot as plt
# 定义的部分超参数
LR = 0.01 # 学习率
BATCH_SIZE = 32 # 批处理数
EPOCH = 12 # 轮数
# 伪数据
x = torch.unsqueeze(torch.linspace(-1, 1, 1000), dim=1)
y = x.pow(2) + 0.1*torch.normal(torch.zeros(*x.size()))
# 使用上节内容提到的 data loader
torch_dataset = Data.TensorDataset(x, y)
loader = Data.DataLoader(dataset=torch_dataset, batch_size=BATCH_SIZE, shuffle=True, num_workers=2, )
# default network
# 默认的 network 形式
class Net(torch.nn.Module):
def __init__(self):
super(Net, self).__init__()
self.hidden = torch.nn.Linear(1, 20) # 隐含层
self.predict = torch.nn.Linear(20, 1) # 输出层
def forward(self, x):
x = F.relu(self.hidden(x))
x = self.predict(x)
return x
if __name__ == '__main__':
# 为每个优化器创建一个 net
net_SGD = Net()
net_Momentum = Net()
net_RMSprop = Net()
net_Adam = Net()
nets = [net_SGD, net_Momentum, net_RMSprop, net_Adam]
# 创建不同的优化器
opt_SGD = torch.optim.SGD(net_SGD.parameters(), lr=LR)
opt_Momentum = torch.optim.SGD(net_Momentum.parameters(), lr=LR, momentum=0.8)
opt_RMSprop = torch.optim.RMSprop(net_RMSprop.parameters(), lr=LR, alpha=0.9)
opt_Adam = torch.optim.Adam(net_Adam.parameters(), lr=LR, betas=(0.9, 0.99))
optimizers = [opt_SGD, opt_Momentum, opt_RMSprop, opt_Adam]
loss_func = torch.nn.MSELoss()
losses_his = [[], [], [], []] # 记录 training 时不同神经网络的 loss
# training
for epoch in range(EPOCH):
print('Epoch: ', epoch)
for step, (b_x, b_y) in enumerate(loader):
# 对每个优化器, 优化属于他的神经网络
for net, opt, l_his in zip(nets, optimizers, losses_his):
output = net(b_x) # 在net中训练数据 x, 输出预测值
loss = loss_func(output, b_y) # 计算预测值与实际y的误差,(预测值写在前面)
opt.zero_grad() # 把net中参数的梯度都将为0,为了下面的优化神经网络
loss.backward() # 误差反向传播, 计算参数更新值
opt.step() # 将参数更新值施加到 net 的 parameters 上,优化梯度
l_his.append(loss.data.numpy()) # 误差记录
# 画图
labels = ['SGD', 'Momentum', 'RMSprop', 'Adam']
for i, l_his in enumerate(losses_his):
plt.plot(l_his, label=labels[i])
plt.legend(loc='best')
plt.xlabel('Steps')
plt.ylabel('Loss')
plt.ylim((0, 0.2))
plt.show()
输出结果:
1)会输出轮数0-11。
Epoch: 0
Epoch: 1
Epoch: 2
Epoch: 3
Epoch: 4
Epoch: 5
Epoch: 6
Epoch: 7
Epoch: 8
Epoch: 9
Epoch: 10
Epoch: 11
Process finished with exit code -1
2)图示,四种优化器的对比图