以下介绍深度学习的主要几种参数更新的优化方法
1.Adagrad
通过引入二阶动量 v t = ∑ i = 0 t ( g i 2 ) v_t=\sqrt{\sum\limits_{i=0}^t(g_i^2)} vt=i=0∑t(gi2)使得学习率 η v t \frac{\eta} {v_t} vtη的更新可以自适应的进行,对于出现频率较低( v t 较 小 v_t较小 vt较小)参数采用较大的α更新;相反,对于出现频率较高的参数采用较小的α更新。因此,Adagrad非常适合处理稀疏数据。
w t + 1 ← w t − η ∑ i = 0 t ( g i 2 ) + ε g t w_{t+1} \leftarrow w_t - \frac{\eta}{\sqrt{\sum_{i=0}^t(g_i^2)+\varepsilon}}g_t wt+1←wt−∑i=0t(gi2)+εηgt
这里的 ε \varepsilon ε 是为了数值稳定性而加上的,因为初始时有可能 v t v_t vt 的值为 0,那么 0 出现在分母就会出现无穷大的情况,通常 ε \varepsilon ε 取 1 0 − 10 10^{-10} 10−10 ,这样不同的参数由于梯度不同,得到的学习率也就不同,从而实现了自适应的学习率。但Adagrad有个缺点,其引入了二阶动量 v t = ∑ i = 0 t ( g i 2 ) v_t=\sqrt{\sum\limits_{i=0}^t(g_i^2)} vt=i=0∑t(gi2)的概念,由于 v t v_t vt是单调递增的,所以学习率单调递减,而当学习率递减速度过快的时候可能就会导致模型没有完全收敛的情况下提前终止。
核心代码:
def sgd_adagrad(parameters, sqrs, lr):
eps = 1e-10
for param, sqr in zip(parameters, sqrs):
sqr[:] = sqr + param.grad.data ** 2
div = lr / torch.sqrt(sqr + eps) * param.grad.data
param.data = param.data - div
以下栗子为采用Adagrad参数更新方法,利用pytorch实现简单的三层神经网络进行MNIST手写数据集的识别
import numpy as np
import torch
from torchvision.datasets import MNIST # 导入 pytorch 内置的 mnist 数据
from torch.utils.data import DataLoader
from torch import nn
from torch.autograd import Variable
import time
import matplotlib.pyplot as plt
%matplotlib inline
def data_tf(x):
x = np.array(x, dtype='float32') / 255
x = (x - 0.5) / 0.5 # 标准化,这个技巧之后会讲到
x = x.reshape((-1,)) # 拉平
x = torch.from_numpy(x)
return x
train_set = MNIST('./data', train=True, transform=data_tf, download=True) # 载入数据集,申明定义的数据变换
test_set = MNIST('./data', train=False, transform=data_tf, download=True)
# 定义 loss 函数
criterion = nn.CrossEntropyLoss()
train_data = DataLoader(train_set, batch_size=64, shuffle=True)
# 使用 Sequential 定义 3 层神经网络
net = nn.Sequential(
nn.Linear(784, 200),
nn.ReLU(),
nn.Linear(200, 10),
)
# 初始化梯度平方项
sqrs = []
for param in net.parameters():
sqrs.append(torch.zeros_like(param.data))
# 开始训练
losses = []
idx = 0
start = time.time() # 记时开始
for e in range(5):
train_loss = 0
for im, label in train_data:
im = Variable(im)
label = Variable(label)
# 前向传播
out = net(im)
loss = criterion(out, label)
# 反向传播
net.zero_grad()
loss.backward()
sgd_adagrad(net.parameters(), sqrs, 1e-2) # 学习率设为 0.01
# 记录误差
train_loss += loss.data[0]
if idx % 30 == 0:
losses.append(loss.data[0])
idx += 1
print('epoch: {}, Train Loss: {:.6f}'
.format(e, train_loss / len(train_data)))
end = time.time() # 计时结束
print('使用时间: {:.5f} s'.format(end - start))epoch: 0, Train Loss: 0.406752
epoch: 1, Train Loss: 0.248588
epoch: 2, Train Loss: 0.211789
epoch: 3, Train Loss: 0.188928
epoch: 4, Train Loss: 0.172839
使用时间: 54.70610 s
运行的result如下所示:
epoch: 0, Train Loss: 0.406752
epoch: 1, Train Loss: 0.248588
epoch: 2, Train Loss: 0.211789
epoch: 3, Train Loss: 0.188928
epoch: 4, Train Loss: 0.172839
使用时间: 54.70610 s
以下为训练过程中的loss
x_axis = np.linspace(0, 5, len(losses), endpoint=True)
plt.semilogy(x_axis, losses, label='adagrad')
plt.legend(loc='best')
当然 pytorch 也内置了 adagrad 的优化算法,只需要调用 torch.optim.Adagrad() 就可以了,下面是例子
train_data = DataLoader(train_set, batch_size=64, shuffle=True)
# 使用 Sequential 定义 3 层神经网络
net = nn.Sequential(
nn.Linear(784, 200),
nn.ReLU(),
nn.Linear(200, 10),
)
optimizer = torch.optim.Adagrad(net.parameters(), lr=1e-2)
# 开始训练
start = time.time() # 记时开始
for e in range(5):
train_loss = 0
for im, label in train_data:
im = Variable(im)
label = Variable(label)
# 前向传播
out = net(im)
loss = criterion(out, label)
# 反向传播
optimizer.zero_grad()
loss.backward()
optimizer.step()
# 记录误差
train_loss += loss.data[0]
print('epoch: {}, Train Loss: {:.6f}'
.format(e, train_loss / len(train_data)))
end = time.time() # 计时结束
print('使用时间: {:.5f} s'.format(end - start))
2.RMSProp
Adagrad会累加之前所有的梯度平方,而RMSprop这里 α 是一个移动平均的系数,也是因为这个系数,导致了 RMSProp 和 Adagrad 不同的地方,这个系数使得 RMSProp 更新到后期累加的梯度平方较小,从而保证 σ \sigma σ 不会太大,也就使得模型后期依然能够找到比较优的结果
w 1 ← w 0 − η σ 0 + ε g 0 , σ 0 = g 0 w 2 ← w 1 − η σ 1 + ε g 1 , σ 1 = α ( σ 0 ) 2 + ( 1 − α ) ( g 1 ) 2 w 3 ← w 2 − η σ 2 + ε g 2 , σ 2 = α ( σ 1 ) 2 + ( 1 − α ) ( g 2 ) 2 . . . w t ← w t − 1 − η σ t − 1 + ε g t − 1 , σ t − 1 = α ( σ t − 2 ) 2 + ( 1 − α ) ( g t − 1 ) 2 w_1 \leftarrow w_0 - \frac{\eta}{\sqrt{\sigma_0+\varepsilon}}g_0\;,\sigma_0=g_0\\ w_2 \leftarrow w_1 - \frac{\eta}{\sqrt{\sigma_1+\varepsilon}}g_1\;,\sigma_1={\alpha(\sigma_0)^2+(1-\alpha)(g_1)^2}\\ w_3 \leftarrow w_2 - \frac{\eta}{\sqrt{\sigma_2+\varepsilon}}g_2\;,\sigma_2={\alpha(\sigma_1)^2+(1-\alpha)(g_2)^2}\\ ...\\ w_t \leftarrow w_{t-1} - \frac{\eta}{\sqrt{\sigma_{t-1}+\varepsilon}}g_{t-1}\;,\sigma_{t-1}={\alpha(\sigma_{t-2})^2