几种优化器的讲解,请看莫烦的讲解(SGD,Momentum,RMSprop,Adam)
这一篇主要讲解 SGD,Momentum,RMSprop,Adam的实战
import torch
import torch.utils.data as Data
import torch.nn.functional as F
from torch.autograd import Variable
import matplotlib.pyplot as plt
# 超参数定义
LR = 0.01 #学习率
BATCH_SIZE = 32 #一次要将多少个数据扔进模型去训练
EPOCH = 12 #训练集中的全部样本都在训练模型中走了一遍,并返回一次(有去有回),为一个epoch。
# torch.unsqueeze 对数据维度进行扩充。dim=1给第2个位置加上维数为一的维度
x = torch.unsqueeze(torch.linspace(-1,1,1000),dim=1)
y = x.pow(2) + 0.1*torch.normal(torch.zeros(x.size()))
# 创建数据集
# batch_size设置为5
# shuffle=True每一次epoch打乱数据顺序,False代表每次epoch的数据顺序是一样的
# num_workers= 2使用2个子进程
# batchsize:简单点说,就是我们一次要将多少个数据扔进模型去训练,这个值介于1和训练样本总个数之间。
torch_dataset = Data.TensorDataset(x,y)
loader = Data.DataLoader(
dataset=torch_dataset,
batch_size=BATCH_SIZE,
shuffle=True,
num_workers=2
)
最开始需要定义3个参数
LR = 0.01 学习率定为0.01
BATCH_SIZE = 32 这是批处理的数量,表示一次要将32个数据扔进模型去训练
EPOCH = 12 这里表示全部样本都在训练模型中走了12遍,并返回12次(有去有回)
x = torch.unsqueeze(torch.linspace(-1,1,1000),dim=1)
torch.linspace这个表示从-1到1被分为1000个等距离的数据torch.unsqueeze 对数据维度进行扩充。dim=1给第2个位置加上维数为一的维度
y = x.pow(2) + 0.1torch.normal(torch.zeros(x.size()))*
torch.normal表示里离散正态分布,目的是产生干扰数据
torch_dataset = Data.TensorDataset(x,y)
这里表示创建tensor的数据集
loader = Data.DataLoader(
dataset=torch_dataset,
batch_size=BATCH_SIZE,
shuffle=True,
num_workers=2
)
这里的信息量稍微大了一点
DataLoader 是一个迭代器,方便我们去多线程地读取数据
batch_size 设置为5
shuffle=True 每一次epoch打乱数据顺序,False代表每次epoch的数据顺序是一样的
num_workers= 2 使用2个子进程
为了对比每一种优化器, 我们给他们各自创建一个神经网络, 但这个神经网络都来自同一个 Net 形式.
#定义4个不同的神经网络,这四个神经网络使用不同的优化器优化
net_SGD = Net()
net_Momentum = Net()
net_RMSprop = Net()
net_Adam = Net()
# 将四个不同的神经网络放在一起,使用for循环将其提取出来
nets = [net_SGD, net_Momentum, net_RMSprop, net_Adam]
上面的代码定义了四个神经网络,然后把这四个神经网络放在一个nets中
接下来在创建不同的优化器, 用来训练不同的网络. 并创建一个 loss_func 用来计算误差. 我们用几种常见的优化器, SGD, Momentum, RMSprop, 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_Adam.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 = [[],[],[],[]]
接下来训练和 loss 画图.
#训练集中的全部样本都在训练模型中走了一遍,并返回一次(有去有回),为一个epoch。
for epoch in range(EPOCH):
print(epoch)
for step, (batch_x,batch_y) in enumerate(loader):
#要把传入的数据从tensor的形式转化为variavle
b_x = Variable(batch_x)
b_y = Variable(batch_y)
#把不同的神经网络一个个拿出来训练
for net, opt, l_his in zip(nets, optimizers, losses_his):
output = net(b_x) #得到每一个网络的输出
loss = loss_func(output, b_y) #计算每一个网络的误差
opt.zero_grad() #为下一次训练清空梯度
loss.backward() #反向传播,计算梯度
opt.step() #更新权重参数
l_his.append(loss.item()) #记录误差
解释以下 zip() 函数
zip用于将可迭代的对象作为参数,将对象中对应的元素打包成一个个元组,然后返回由这些元组组成的列表。
a = [1,2,3]
b = [4,5,6]
zip(a,b)
zip的输出值:
[(1, 4), (2, 5), (3, 6)]
另外,在莫烦的代码里有这一行,
l_his.append(loss.data[0])
这里会报错
IndexError: invalid index of a 0-dim tensor. Use tensor.item() to convert a 0-dim tensor to a Python number
改为:
l_his.append(loss.item())
# 画图
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()
SGD 是最普通的优化器, 也可以说没有加速效果, 而 Momentum 是 SGD 的改良版, 它加入了动量原则. 后面的 RMSprop 又是 Momentum 的升级版. 而 Adam 又是 RMSprop 的升级版. 不过从这个结果中我们看到, Adam 的效果似乎比 RMSprop 要差一点. 所以说并不是越先进的优化器, 结果越佳. 我们在自己的试验中可以尝试不同的优化器, 找到那个最适合你数据/网络的优化器.