总结那些常用的优化方法
2020-02-27 本文已影响0人
施主请留步_a5d7
知识点
- 基础的损失函数优化算法为梯度下降算法SGD(根据每次参与计算的样本数又分为了普通梯度下降算法,随机梯度下降算法,批量梯度下降算法)
- 牛顿法在梯度下降方法的基础上,利用了二阶导的信息,相比梯度下降算法,损失函数能更快的收敛。缺点是只适合有二阶导的损失函数,而且计算海森矩阵较为费时
- ill-conditioned problem:海森矩阵的条件数过大,导致梯度下降难以优化。
条件数:,其中表示海森矩阵中的最大特征值,表示海森矩阵中的最小特:征值
两种解决思路:- 预处理梯度向量:Adam, RMSProp, AdaGrad, Adelta, KFC均采用了这种思路
- 平均历史梯度:momentum,Adam, RMSProp都有采用这种思路
- Momentum:设时间步 t 的自变量为 ,学习率为 。 在时间步 t=0 ,动量法创建速度变量 ,并将其元素初始化成 0。在时间步 t>0 ,动量法对每次迭代的步骤做如下修改:
- AdaGrad:动量法依赖指数加权移动平均使得自变量的更新方向更加一致,从而降低发散的可能。本节我们介绍AdaGrad算法,它根据自变量在每个维度的梯度值的大小来调整各个维度上的学习率,从而避免统一的学习率难以适应所有维度的问题。具体原理如下:
使用一个小批量随机梯度 按元素平方的累加变量 。在时间步0,AdaGrad将 中每个元素初始化为0。在时间步 t ,首先将小批量随机梯度 按元素平方后累加到变量 :
其中是按元素相乘。接着将目标函数自变量中每个元素的学习率通过按元素运算重新调整一下:
每次更新时,学习率都由除以之前时间步对应自变量梯度的累计平方和的开方,这样梯度越大,学习率越小。但是由于时逐渐变大的值,导致最后学习率趋于0,不再更新参数。 - RMSProp:对AdaGrad算法的做了修改。不同于AdaGrad算法里状态变量 是截至时间步 t 所有小批量随机梯度 按元素平方和,RMSProp算法将这些梯度按元素平方做指数加权移动平均。具体来说,给定超参数计算:
RMSProp算法将目标函数自变量中每个元素的学习率通过按元素运算重新调整,然后更新自变量
- AdaDelta:与RMSProp类似,也是在AdaGrad基础上进行优化。首先使用了小批量随机梯度 按元素平方的指数加权移动平均变量 。在时间步0,它的所有元素被初始化为0。给定超参数,同RMSProp算法一样计算:
AdaDelta算法还维护一个额外的状态变量 ,其元素同样在时间步0时被初始化为0。我们使用 来计算自变量的变化量:
接着更新自变量: 最后,我们使用 Δxt 来记录自变量变化量 g′t 按元素平方的指数加权移动平均:
- Adam:在RMSProp算法基础上对小批量随机梯度也做了指数加权移动平均 。
首先使用了动量变量 和RMSProp算法中小批量随机梯度按元素平方的指数加权移动平均变量 ,并在时间步0将它们中每个元素初始化为0。给定超参数 0≤β1<1 (算法作者建议设为0.9),时间步 t 的动量变量 即小批量随机梯度 的指数加权移动平均:
和RMSProp算法中一样,给定超参数 0≤β2<1 (算法作者建议设为0.999), 将小批量随机梯度按元素平方后的项 做指数加权移动平均得到 :
在时间步 t 我们得到 ,有.当 t 较小时,过去各时间步小批量随机梯度权值之和会较小.例如,当 β1=0.9 时, 。为了消除这样的影响,对于任意时间步 t ,我们可以将再除以 ,从而使过去各时间步小批量随机梯度权值之和为1。这也叫作偏差修正。
我们对变量 和 均作偏差修正:
Adam算法使用以上偏差修正后的变量 和 ,将模型参数中每个元素的学习率通过按元素运算重新调整:
最后更新参数:
从零开始实现
- SGD
def sgd(params, states, hyperparams):
for p in params:
p.data -= hyperparams['lr'] * p.grad.data
def train_ch7(optimizer_fn, states, hyperparams, features, labels,
batch_size=10, num_epochs=2):
# 初始化模型
net, loss = d2l.linreg, d2l.squared_loss
w = torch.nn.Parameter(torch.tensor(np.random.normal(0, 0.01, size=(features.shape[1], 1)), dtype=torch.float32),
requires_grad=True)
b = torch.nn.Parameter(torch.zeros(1, dtype=torch.float32), requires_grad=True)
def eval_loss():
return loss(net(features, w, b), labels).mean().item()
ls = [eval_loss()]
data_iter = torch.utils.data.DataLoader(
torch.utils.data.TensorDataset(features, labels), batch_size, shuffle=True)
for _ in range(num_epochs):
start = time.time()
for batch_i, (X, y) in enumerate(data_iter):
l = loss(net(X, w, b), y).mean() # 使用平均损失
# 梯度清零
if w.grad is not None:
w.grad.data.zero_()
b.grad.data.zero_()
l.backward()
optimizer_fn([w, b], states, hyperparams) # 迭代模型参数
if (batch_i + 1) * batch_size % 100 == 0:
ls.append(eval_loss()) # 每100个样本记录下当前训练误差
# 打印结果和作图
print('loss: %f, %f sec per epoch' % (ls[-1], time.time() - start))
d2l.set_figsize()
d2l.plt.plot(np.linspace(0, num_epochs, len(ls)), ls)
d2l.plt.xlabel('epoch')
d2l.plt.ylabel('loss')
- Momentum
def init_momentum_states(features):
v_w = torch.zeros((features.shape[1], 1), dtype=torch.float32)
v_b = torch.zeros(1, dtype=torch.float32)
return (v_w, v_b)
def sgd_momentum(params, states, hyperparams):
for p, v in zip(params, states):
v.data = hyperparams['momentum'] * v.data + hyperparams['lr'] * p.grad.data
p.data -= v.data
- AdaGrad
def init_adagrad_states(features):
s_w = torch.zeros((features.shape[1], 1), dtype=torch.float32)
s_b = torch.zeros(1, dtype=torch.float32)
return (s_w, s_b)
def adagrad(params, states, hyperparams):
eps = 1e-6
for p, s in zip(params, states):
s.data += (p.grad.data**2)
p.data -= hyperparams['lr'] * p.grad.data / torch.sqrt(s + eps)
- RMSProp
def init_rmsprop_states(features):
s_w = torch.zeros((features.shape[1], 1), dtype=torch.float32)
s_b = torch.zeros(1, dtype=torch.float32)
return (s_w, s_b)
def rmsprop(params, states, hyperparams):
gamma, eps = hyperparams['beta'], 1e-6
for p, s in zip(params, states):
s.data = gamma * s.data + (1 - gamma) * (p.grad.data)**2
p.data -= hyperparams['lr'] * p.grad.data / torch.sqrt(s + eps)
-AdaDelta
def init_adadelta_states(features):
s_w, s_b = torch.zeros((features.shape[1], 1), dtype=torch.float32), torch.zeros(1, dtype=torch.float32)
delta_w, delta_b = torch.zeros((features.shape[1], 1), dtype=torch.float32), torch.zeros(1, dtype=torch.float32)
return ((s_w, delta_w), (s_b, delta_b))
def adadelta(params, states, hyperparams):
rho, eps = hyperparams['rho'], 1e-5
for p, (s, delta) in zip(params, states):
s[:] = rho * s + (1 - rho) * (p.grad.data**2)
g = p.grad.data * torch.sqrt((delta + eps) / (s + eps))
p.data -= g
delta[:] = rho * delta + (1 - rho) * g * g
-Adam
def init_adam_states(features):
v_w, v_b = torch.zeros((features.shape[1], 1), dtype=torch.float32), torch.zeros(1, dtype=torch.float32)
s_w, s_b = torch.zeros((features.shape[1], 1), dtype=torch.float32), torch.zeros(1, dtype=torch.float32)
return ((v_w, s_w), (v_b, s_b))
def adam(params, states, hyperparams):
beta1, beta2, eps = 0.9, 0.999, 1e-6
for p, (v, s) in zip(params, states):
v[:] = beta1 * v + (1 - beta1) * p.grad.data
s[:] = beta2 * s + (1 - beta2) * p.grad.data**2
v_bias_corr = v / (1 - beta1 ** hyperparams['t'])
s_bias_corr = s / (1 - beta2 ** hyperparams['t'])
p.data -= hyperparams['lr'] * v_bias_corr / (torch.sqrt(s_bias_corr) + eps)
hyperparams['t'] += 1