pytorch学习笔记(三十六):AdaGrad
文章目录
- AdaGrad算法
- 1. 算法
- 2. 特点
- 3. 从零开始实现
- 4. 简洁实现
- 小结
AdaGrad算法
在之前介绍过的优化算法中,目标函数自变量的每一个元素在相同时间步都使用同一个学习率来自我迭代。举个例子,假设目标函数为fff,自变量为一个二维向量[x1,x2]⊤[x_1, x_2]^\top[x1,x2]⊤,该向量中每一个元素在迭代时都使用相同的学习率。例如,在学习率为η\etaη的梯度下降中,元素x1x_1x1和x2x_2x2都使用相同的学习率η\etaη来自我迭代:
x1←x1−η∂f∂x1,x2←x2−η∂f∂x2.x_1 \leftarrow x_1 - \eta \frac{\partial{f}}{\partial{x_1}}, \quad x_2 \leftarrow x_2 - \eta \frac{\partial{f}}{\partial{x_2}}. x1←x1−η∂x1∂f,x2←x2−η∂x2∂f.
当x1x_1x1和x2x_2x2的梯度值有较大差别时,需要选择足够小的学习率使得自变量在梯度值较大的维度上不发散。但这样会导致自变量在梯度值较小的维度上迭代过慢。动量法依赖指数加权移动平均使得自变量的更新方向更加一致,从而降低发散的可能。本节我们介绍AdaGrad算法,它根据自变量在每个维度的梯度值的大小来调整各个维度上的学习率,从而避免统一的学习率难以适应所有维度的问题[1]。
1. 算法
AdaGrad算法会使用一个小批量随机梯度gt\boldsymbol{g}_tgt按元素平方的累加变量st\boldsymbol{s}_tst。在时间步0,AdaGrad将s0\boldsymbol{s}_0s0中每个元素初始化为0。在时间步ttt,首先将小批量随机梯度gt\boldsymbol{g}_tgt按元素平方后累加到变量st\boldsymbol{s}_tst:
st←st−1+gt⊙gt,\boldsymbol{s}_t \leftarrow \boldsymbol{s}_{t-1} + \boldsymbol{g}_t \odot \boldsymbol{g}_t,st←st−1+gt⊙gt,
其中⊙\odot⊙是按元素相乘。接着,我们将目标函数自变量中每个元素的学习率通过按元素运算重新调整一下:
xt←xt−1−ηst+ϵ⊙gt,\boldsymbol{x}_t \leftarrow \boldsymbol{x}_{t-1} - \frac{\eta}{\sqrt{\boldsymbol{s}_t + \epsilon}} \odot \boldsymbol{g}_t,xt←xt−1−st+ϵη⊙gt,
其中η\etaη是学习率,ϵ\epsilonϵ是为了维持数值稳定性而添加的常数,如10−610^{-6}10−6。这里开方、除法和乘法的运算都是按元素运算的。这些按元素运算使得目标函数自变量中每个元素都分别拥有自己的学习率。
2. 特点
需要强调的是,小批量随机梯度按元素平方的累加变量st\boldsymbol{s}_tst出现在学习率的分母项中。因此,如果目标函数有关自变量中某个元素的偏导数一直都较大,那么该元素的学习率将下降较快;反之,如果目标函数有关自变量中某个元素的偏导数一直都较小,那么该元素的学习率将下降较慢。然而,由于st\boldsymbol{s}_tst一直在累加按元素平方的梯度,自变量中每个元素的学习率在迭代过程中一直在降低(或不变)。所以,当学习率在迭代早期降得较快且当前解依然不佳时,AdaGrad算法在迭代后期由于学习率过小,可能较难找到一个有用的解。
下面我们仍然以目标函数f(x)=0.1x12+2x22f(\boldsymbol{x})=0.1x_1^2+2x_2^2f(x)=0.1x12+2x22为例观察AdaGrad算法对自变量的迭代轨迹。我们实现AdaGrad算法并使用和上一节实验中相同的学习率0.4。可以看到,自变量的迭代轨迹较平滑。但由于st\boldsymbol{s}_tst的累加效果使学习率不断衰减,自变量在迭代后期的移动幅度较小。
%matplotlib inline
import math
import torch
from torch import nn
import time
import sys
import numpy as np
import matplotlib.pyplot as plt
sys.path.append("..")
import d2lzh_pytorch as d2l
定义梯度更新函数和线性函数f(x)
def adagrad_2d(x1, x2, s1, s2): # 梯度更新函数g1, g2, eps = 0.2 * x1, 4 * x2, 1e-6 # 前两项为自变量梯度s1 += g1 ** 2s2 += g2 ** 2x1 -= eta / math.sqrt(s1 + eps) * g1x2 -= eta / math.sqrt(s2 + eps) * g2return x1, x2, s1, s2def f_2d(x1, x2):return 0.1 * x1 ** 2 + 2 * x2 ** 2
def train_2d(trainer): # 梯度更新x1, x2, s1, s2 = -5, -2, 0, 0 results = [(x1, x2)]for i in range(20):x1, x2, s1, s2 = trainer(x1, x2, s1, s2)results.append((x1, x2))print('epoch %d, x1 %f, x2 %f' % (i + 1, x1, x2))return resultsdef show_trace_2d(f, results): #结果展示plt.plot(*zip(*results), '-o', color='#ff7f0e')x1, x2 = np.meshgrid(np.arange(-5.5, 1.0, 0.1), np.arange(-3.0, 1.0, 0.1))plt.contour(x1, x2, f(x1, x2), colors='#1f77b4')plt.xlabel('x1')plt.ylabel('x2')
eta = 0.4
show_trace_2d(f_2d, d2l.train_2d(adagrad_2d))
输出:
epoch 20, x1 -2.382563, x2 -0.158591
下面将学习率增大到2。可以看到自变量更为迅速地逼近了最优解。
eta = 2
d2l.show_trace_2d(f_2d, d2l.train_2d(adagrad_2d))
输出:
epoch 20, x1 -0.002295, x2 -0.000000
3. 从零开始实现
同动量法一样,AdaGrad算法需要对每个自变量维护同它一样形状的状态变量。我们根据AdaGrad算法中的公式实现该算法。
features, labels = d2l.get_data_ch7()def init_adagrad_states():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-6for p, s in zip(params, states):s.data += (p.grad.data**2)p.data -= hyperparams['lr'] * p.grad.data / torch.sqrt(s + eps)
定义线性模型和损失函数
def linreg(X, w, b):return torch.mm(X, w) + bdef squared_loss(y_hat, y): # 注意这里返回的是向量, 另外, pytorch里的MSELoss并没有除以 2return ((y_hat - y.view(y_hat.size())) ** 2) / 2
定义训练模型
def train_ch7(optimizer_fn, states, hyperparams, features, labels,batch_size=10, num_epochs=2):# 初始化模型net, loss = linreg, squared_lossw = 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))plt.plot(np.linspace(0, num_epochs, len(ls)), ls)plt.xlabel('epoch')plt.ylabel('loss')
查看训练结果
train_ch7(adagrad, init_adagrad_states(), {'lr': 0.1}, features, labels)
输出:
loss: 0.243675, 0.049749 sec per epoch
4. 简洁实现
通过名称为Adagrad
的优化器方法,我们便可使用PyTorch提供的AdaGrad算法来训练模型。
def train_pytorch_ch7(optimizer_fn, optimizer_hyperparams, features, labels,batch_size=10, num_epochs=2):# 初始化模型net = nn.Sequential(nn.Linear(features.shape[-1], 1))loss = nn.MSELoss()optimizer = optimizer_fn(net.parameters(), **optimizer_hyperparams)def eval_loss():return loss(net(features).view(-1), labels).item() / 2ls = [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):# 除以2是为了和train_ch7保持一致, 因为squared_loss中除了2l = loss(net(X).view(-1), y) / 2 optimizer.zero_grad()l.backward()optimizer.step()if (batch_i + 1) * batch_size % 100 == 0:ls.append(eval_loss())# 打印结果和作图print('loss: %f, %f sec per epoch' % (ls[-1], time.time() - start))plt.plot(np.linspace(0, num_epochs, len(ls)), ls)plt.xlabel('epoch')plt.ylabel('loss')
train_pytorch_ch7(torch.optim.Adagrad, {'lr': 0.1}, features, labels)
输出:
loss: 0.243147, 0.040675 sec per epoch
小结
- AdaGrad算法在迭代过程中不断调整学习率,并让目标函数自变量中每个元素都分别拥有自己的学习率。
- 使用AdaGrad算法时,自变量中每个元素的学习率在迭代过程中一直在降低(或不变)。
pytorch学习笔记(三十六):AdaGrad相关推荐
- 【Unity 3D】学习笔记三十六:物理引擎——刚体
物理引擎就是游戏中模拟真是的物理效果.如两个物体发生碰撞,物体自由落体等.在unity中使用的是NVIDIA的physX,它渲染的游戏画面很逼真. 刚体 刚体是一个很很中要的组件. 默认情况下,新创的 ...
- pytorch学习笔记(十六):Parameters
文章目录 1. 访问模型参数 2. 初始化模型参数 3. 自定义初始化方法 4. 共享模型参数 小结 本节将深入讲解如何访问和初始化模型参数,以及如何在多个层之间共享同一份模型参数. 我们先定义含单隐 ...
- Jenkins +maven+tomcat自动构建部署(学习笔记三十六)
https://my.oschina.net/denglz/blog/524154 摘要: jenkins + maven + svn + tomcat 自动部署 jenkins 是做什么用的,如果 ...
- opencv学习笔记三十六:AKAZE特征点检测与匹配
KAZE是日语音译过来的 , KAZE与SIFT.SURF最大的区别在于构造尺度空间,KAZE是利用非线性方式构造,得到的关键点也就更准确(尺度不变性 ): Hessian矩阵特征点检测 ,方向指定, ...
- Mr.J-- jQuery学习笔记(三十二)--jQuery属性操作源码封装
扫码看专栏 jQuery的优点 jquery是JavaScript库,能够极大地简化JavaScript编程,能够更方便的处理DOM操作和进行Ajax交互 1.轻量级 JQuery非常轻巧 2.强大的 ...
- python数据挖掘学习笔记】十六.逻辑回归LogisticRegression分析鸢尾花数据
但是很多时候数据是非线性的,所以这篇文章主要讲述逻辑回归及Sklearn机器学习包中的LogisticRegression算法 #2018-03-28 16:57:56 March Wednesday ...
- python分析鸢尾花数据_python数据挖掘学习笔记】十六.逻辑回归LogisticRegression分析鸢尾花数据...
但是很多时候数据是非线性的,所以这篇文章主要讲述逻辑回归及Sklearn机器学习包中的LogisticRegression算法 #2018-03-28 16:57:56 March Wednesday ...
- tensorflow学习笔记(三十二):conv2d_transpose (解卷积)
tensorflow学习笔记(三十二):conv2d_transpose ("解卷积") deconv解卷积,实际是叫做conv_transpose, conv_transpose ...
- OpenCV学习笔记(十六)——CamShift研究 OpenCV学习笔记(十七)——运动分析和物体跟踪Video OpenCV学习笔记(十八)——图像的各种变换(cvtColor*+)imgproc
OpenCV学习笔记(十六)--CamShift研究 CamShitf算法,即Continuously Apative Mean-Shift算法,基本思想就是对视频图像的多帧进行MeanShift运算 ...
- OpenCV学习笔记(十六):直方图均衡化:equalizeHist()
OpenCV学习笔记(十六):直方图均匀化:equalizeHist() 参考博客: 直方图均衡化的数学原理 直方图匹配的数学原理 直方图均衡化广泛应用于图像增强中: 直方图均衡化处理的"中 ...
最新文章
- 最近开发的一个项目的一些感想
- python如何打印时间,在python2.7中,如何提取和打印日期、时间和m
- js和jQuery 获取屏幕高度、宽度
- J2ME开发及JBuilder工具的应用
- jquery.imageScroller实现图片滚动
- 2020-我的后端开发秋招之路
- 数据通信技术初级工程师证题库
- tex 表格内容换行_LaTex表格内单元格内容强制换行
- 数据库分库分表之后如何查询统计?
- 【计算机网络 (谢希仁) 习题题解】第5章 运输层 (5)——TCP的运输连接管理
- python怎么读write_python怎么读
- ewebeditor 2.8.0目录遍历漏洞——漏洞复现
- 微生物群落组装过程(assembly processes)
- 【安卓开机启动】安卓JVM加载so库流程
- 支付宝小程序动态绑定样式
- 困惑与破题:人人喊打的屏幕时间究竟对孩子做了什么?
- Gulp插件之gulp-babel和gulp-uglify关于js代码的转换和压缩
- 计算机考试报名不了怎么办,2017年国家公务员考试报名系统繁忙怎么办?
- rj45 千兆接口定义_千兆以太网RJ45接口连线引脚定义?
- java初级工作总结_【Java初级程序员工作总结_Java初级程序员个人年终总结】-看准网...