【深度学习笔记】优化算法——小批量随机梯度下降

小批量随机梯度下降

到目前为止,我们在基于梯度的学习方法中遇到了两个极端情况:
:numref:sec_gd中使用完整数据集来计算梯度并更新参数,
:numref:sec_sgd中一次处理一个训练样本来取得进展。
二者各有利弊:每当数据非常相似时,梯度下降并不是非常“数据高效”。
而由于CPU和GPU无法充分利用向量化,随机梯度下降并不特别“计算高效”。
这暗示了两者之间可能有折中方案,这便涉及到小批量随机梯度下降(minibatch gradient descent)。

向量化和缓存

使用小批量的决策的核心是计算效率。
当考虑与多个GPU和多台服务器并行处理时,这一点最容易被理解。在这种情况下,我们需要向每个GPU发送至少一张图像。
有了每台服务器8个GPU和16台服务器,我们就能得到大小为128的小批量。

当涉及到单个GPU甚至CPU时,事情会更微妙一些:
这些设备有多种类型的内存、通常情况下多种类型的计算单元以及在它们之间不同的带宽限制。
例如,一个CPU有少量寄存器(register),L1和L2缓存,以及L3缓存(在不同的处理器内核之间共享)。
随着缓存的大小的增加,它们的延迟也在增加,同时带宽在减少。
可以说,处理器能够执行的操作远比主内存接口所能提供的多得多。

首先,具有16个内核和AVX-512向量化的2GHz CPU每秒可处理高达 2 ⋅ 1 0 9 ⋅ 16 ⋅ 32 = 1 0 12 2 \cdot 10^9 \cdot 16 \cdot 32 = 10^{12} 21091632=1012个字节。
同时,GPU的性能很容易超过该数字100倍。
而另一方面,中端服务器处理器的带宽可能不超过100Gb/s,即不到处理器满负荷所需的十分之一。
更糟糕的是,并非所有的内存入口都是相等的:内存接口通常为64位或更宽(例如,在最多384位的GPU上)。
因此读取单个字节会导致由于更宽的存取而产生的代价。

其次,第一次存取的额外开销很大,而按序存取(sequential access)或突发读取(burst read)相对开销较小。
有关更深入的讨论,请参阅此维基百科文章。

减轻这些限制的方法是使用足够快的CPU缓存层次结构来为处理器提供数据。
这是深度学习中批量处理背后的推动力。
举一个简单的例子:矩阵-矩阵乘法。
比如 A = B C \mathbf{A} = \mathbf{B}\mathbf{C} A=BC,我们有很多方法来计算 A \mathbf{A} A。例如,我们可以尝试以下方法:

  1. 我们可以计算 A i j = B i , : C : , j ⊤ \mathbf{A}_{ij} = \mathbf{B}_{i,:} \mathbf{C}_{:,j}^\top Aij=Bi,:C:,j,也就是说,我们可以通过点积进行逐元素计算。
  2. 我们可以计算 A : , j = B C : , j ⊤ \mathbf{A}_{:,j} = \mathbf{B} \mathbf{C}_{:,j}^\top A:,j=BC:,j,也就是说,我们可以一次计算一列。同样,我们可以一次计算 A \mathbf{A} A一行 A i , : \mathbf{A}_{i,:} Ai,:
  3. 我们可以简单地计算 A = B C \mathbf{A} = \mathbf{B} \mathbf{C} A=BC
  4. 我们可以将 B \mathbf{B} B C \mathbf{C} C分成较小的区块矩阵,然后一次计算 A \mathbf{A} A的一个区块。

如果我们使用第一个选择,每次我们计算一个元素 A i j \mathbf{A}_{ij} Aij时,都需要将一行和一列向量复制到CPU中。
更糟糕的是,由于矩阵元素是按顺序对齐的,因此当从内存中读取它们时,我们需要访问两个向量中许多不相交的位置。
第二种选择相对更有利:我们能够在遍历 B \mathbf{B} B的同时,将列向量 C : , j \mathbf{C}_{:,j} C:,j保留在CPU缓存中。
它将内存带宽需求减半,相应地提高了访问速度。
第三种选择表面上是最可取的,然而大多数矩阵可能不能完全放入缓存中。
第四种选择提供了一个实践上很有用的方案:我们可以将矩阵的区块移到缓存中然后在本地将它们相乘。
让我们来看看这些操作在实践中的效率如何。

除了计算效率之外,Python和深度学习框架本身带来的额外开销也是相当大的。
回想一下,每次我们执行代码时,Python解释器都会向深度学习框架发送一个命令,要求将其插入到计算图中并在调度过程中处理它。
这样的额外开销可能是非常不利的。
总而言之,我们最好用向量化(和矩阵)。

%matplotlib inline
import numpy as np
import torch
from torch import nn
from d2l import torch as d2ltimer = d2l.Timer()
A = torch.zeros(256, 256)
B = torch.randn(256, 256)
C = torch.randn(256, 256)

按元素分配只需遍历分别为 B \mathbf{B} B C \mathbf{C} C的所有行和列,即可将该值分配给 A \mathbf{A} A

# 逐元素计算A=BC
timer.start()
for i in range(256):for j in range(256):A[i, j] = torch.dot(B[i, :], C[:, j])
timer.stop()
1.6609790325164795

更快的策略是执行按列分配。

# 逐列计算A=BC
timer.start()
for j in range(256):A[:, j] = torch.mv(B, C[:, j])
timer.stop()
0.022588253021240234

最有效的方法是在一个区块中执行整个操作。让我们看看它们各自的操作速度是多少。

# 一次性计算A=BC
timer.start()
A = torch.mm(B, C)
timer.stop()# 乘法和加法作为单独的操作(在实践中融合)
gigaflops = [2/i for i in timer.times]
print(f'performance in Gigaflops: element {gigaflops[0]:.3f}, 'f'column {gigaflops[1]:.3f}, full {gigaflops[2]:.3f}')
performance in Gigaflops: element 1.204, column 88.542, full 195.625

小批量

🏷sec_minibatches

之前我们会理所当然地读取数据的小批量,而不是观测单个数据来更新参数,现在简要解释一下原因。
处理单个观测值需要我们执行许多单一矩阵-矢量(甚至矢量-矢量)乘法,这耗费相当大,而且对应深度学习框架也要巨大的开销。
这既适用于计算梯度以更新参数时,也适用于用神经网络预测。
也就是说,每当我们执行 w ← w − η t g t \mathbf{w} \leftarrow \mathbf{w} - \eta_t \mathbf{g}_t wwηtgt时,消耗巨大。其中

g t = ∂ w f ( x t , w ) . \mathbf{g}_t = \partial_{\mathbf{w}} f(\mathbf{x}_{t}, \mathbf{w}). gt=wf(xt,w).

我们可以通过将其应用于一个小批量观测值来提高此操作的计算效率。
也就是说,我们将梯度 g t \mathbf{g}_t gt替换为一个小批量而不是单个观测值

g t = ∂ w 1 ∣ B t ∣ ∑ i ∈ B t f ( x i , w ) . \mathbf{g}_t = \partial_{\mathbf{w}} \frac{1}{|\mathcal{B}_t|} \sum_{i \in \mathcal{B}_t} f(\mathbf{x}_{i}, \mathbf{w}). gt=wBt1iBtf(xi,w).

让我们看看这对 g t \mathbf{g}_t gt的统计属性有什么影响:由于 x t \mathbf{x}_t xt和小批量 B t \mathcal{B}_t Bt的所有元素都是从训练集中随机抽出的,因此梯度的期望保持不变。
另一方面,方差显著降低。
由于小批量梯度由正在被平均计算的 b : = ∣ B t ∣ b := |\mathcal{B}_t| b:=Bt个独立梯度组成,其标准差降低了 b − 1 2 b^{-\frac{1}{2}} b21
这本身就是一件好事,因为这意味着更新与完整的梯度更接近了。

直观来说,这表明选择大型的小批量 B t \mathcal{B}_t Bt将是普遍可行的。
然而,经过一段时间后,与计算代价的线性增长相比,标准差的额外减少是微乎其微的。
在实践中我们选择一个足够大的小批量,它可以提供良好的计算效率同时仍适合GPU的内存。
下面,我们来看看这些高效的代码。
在里面我们执行相同的矩阵-矩阵乘法,但是这次我们将其一次性分为64列的“小批量”。

timer.start()
for j in range(0, 256, 64):A[:, j:j+64] = torch.mm(B, C[:, j:j+64])
timer.stop()
print(f'performance in Gigaflops: block {2 / timer.times[3]:.3f}')
performance in Gigaflops: block 2056.535

显而易见,小批量上的计算基本上与完整矩阵一样有效。
需要注意的是,在 :numref:sec_batch_norm中,我们使用了一种在很大程度上取决于小批量中的方差的正则化。
随着后者增加,方差会减少,随之而来的是批量规范化带来的噪声注入的好处。
关于实例,请参阅 :cite:Ioffe.2017,了解有关如何重新缩放并计算适当项目。

读取数据集

让我们来看看如何从数据中有效地生成小批量。
下面我们使用NASA开发的测试机翼的数据集不同飞行器产生的噪声来比较这些优化算法。
为方便起见,我们只使用前 1 , 500 1,500 1,500样本。
数据已作预处理:我们移除了均值并将方差重新缩放到每个坐标为 1 1 1

#@save
d2l.DATA_HUB['airfoil'] = (d2l.DATA_URL + 'airfoil_self_noise.dat','76e5be1548fd8222e5074cf0faae75edff8cf93f')#@save
def get_data_ch11(batch_size=10, n=1500):data = np.genfromtxt(d2l.download('airfoil'),dtype=np.float32, delimiter='\t')data = torch.from_numpy((data - data.mean(axis=0)) / data.std(axis=0))data_iter = d2l.load_array((data[:n, :-1], data[:n, -1]),batch_size, is_train=True)return data_iter, data.shape[1]-1

从零开始实现

:numref:sec_linear_scratch一节中已经实现过小批量随机梯度下降算法。
我们在这里将它的输入参数变得更加通用,主要是为了方便本章后面介绍的其他优化算法也可以使用同样的输入。
具体来说,我们添加了一个状态输入states并将超参数放在字典hyperparams中。
此外,我们将在训练函数里对各个小批量样本的损失求平均,因此优化算法中的梯度不需要除以批量大小。

def sgd(params, states, hyperparams):for p in params:p.data.sub_(hyperparams['lr'] * p.grad)p.grad.data.zero_()

下面实现一个通用的训练函数,以方便本章后面介绍的其他优化算法使用。
它初始化了一个线性回归模型,然后可以使用小批量随机梯度下降以及后续小节介绍的其他算法来训练模型。

#@save
def train_ch11(trainer_fn, states, hyperparams, data_iter,feature_dim, num_epochs=2):# 初始化模型w = torch.normal(mean=0.0, std=0.01, size=(feature_dim, 1),requires_grad=True)b = torch.zeros((1), requires_grad=True)net, loss = lambda X: d2l.linreg(X, w, b), d2l.squared_loss# 训练模型animator = d2l.Animator(xlabel='epoch', ylabel='loss',xlim=[0, num_epochs], ylim=[0.22, 0.35])n, timer = 0, d2l.Timer()for _ in range(num_epochs):for X, y in data_iter:l = loss(net(X), y).mean()l.backward()trainer_fn([w, b], states, hyperparams)n += X.shape[0]if n % 200 == 0:timer.stop()animator.add(n/X.shape[0]/len(data_iter),(d2l.evaluate_loss(net, data_iter, loss),))timer.start()print(f'loss: {animator.Y[0][-1]:.3f}, {timer.avg():.3f} sec/epoch')return timer.cumsum(), animator.Y[0]

让我们来看看批量梯度下降的优化是如何进行的。
这可以通过将小批量设置为1500(即样本总数)来实现。
因此,模型参数每个迭代轮数只迭代一次。

def train_sgd(lr, batch_size, num_epochs=2):data_iter, feature_dim = get_data_ch11(batch_size)return train_ch11(sgd, None, {'lr': lr}, data_iter, feature_dim, num_epochs)gd_res = train_sgd(1, 1500, 10)
loss: 0.243, 0.055 sec/epoch

在这里插入图片描述

当批量大小为1时,优化使用的是随机梯度下降。
为了简化实现,我们选择了很小的学习率。
在随机梯度下降的实验中,每当一个样本被处理,模型参数都会更新。
在这个例子中,这相当于每个迭代轮数有1500次更新。
可以看到,目标函数值的下降在1个迭代轮数后就变得较为平缓。
尽管两个例子在一个迭代轮数内都处理了1500个样本,但实验中随机梯度下降的一个迭代轮数耗时更多。
这是因为随机梯度下降更频繁地更新了参数,而且一次处理单个观测值效率较低。

sgd_res = train_sgd(0.005, 1)
loss: 0.246, 0.102 sec/epoch

在这里插入图片描述

最后,当批量大小等于100时,我们使用小批量随机梯度下降进行优化。
每个迭代轮数所需的时间比随机梯度下降和批量梯度下降所需的时间短。

mini1_res = train_sgd(.4, 100)
loss: 0.243, 0.003 sec/epoch

在这里插入图片描述

将批量大小减少到10,每个迭代轮数的时间都会增加,因为每批工作负载的执行效率变得更低。

mini2_res = train_sgd(.05, 10)
loss: 0.243, 0.013 sec/epoch

在这里插入图片描述

现在我们可以比较前四个实验的时间与损失。
可以看出,尽管在处理的样本数方面,随机梯度下降的收敛速度快于梯度下降,但与梯度下降相比,它需要更多的时间来达到同样的损失,因为逐个样本来计算梯度并不那么有效。
小批量随机梯度下降能够平衡收敛速度和计算效率。
大小为10的小批量比随机梯度下降更有效;
大小为100的小批量在运行时间上甚至优于梯度下降。

d2l.set_figsize([6, 3])
d2l.plot(*list(map(list, zip(gd_res, sgd_res, mini1_res, mini2_res))),'time (sec)', 'loss', xlim=[1e-2, 10],legend=['gd', 'sgd', 'batch size=100', 'batch size=10'])
d2l.plt.gca().set_xscale('log')


在这里插入图片描述

简洁实现

下面用深度学习框架自带算法实现一个通用的训练函数,我们将在本章中其它小节使用它。

#@save
def train_concise_ch11(trainer_fn, hyperparams, data_iter, num_epochs=4):# 初始化模型net = nn.Sequential(nn.Linear(5, 1))def init_weights(m):if type(m) == nn.Linear:torch.nn.init.normal_(m.weight, std=0.01)net.apply(init_weights)optimizer = trainer_fn(net.parameters(), **hyperparams)loss = nn.MSELoss(reduction='none')animator = d2l.Animator(xlabel='epoch', ylabel='loss',xlim=[0, num_epochs], ylim=[0.22, 0.35])n, timer = 0, d2l.Timer()for _ in range(num_epochs):for X, y in data_iter:optimizer.zero_grad()out = net(X)y = y.reshape(out.shape)l = loss(out, y)l.mean().backward()optimizer.step()n += X.shape[0]if n % 200 == 0:timer.stop()# MSELoss计算平方误差时不带系数1/2animator.add(n/X.shape[0]/len(data_iter),(d2l.evaluate_loss(net, data_iter, loss) / 2,))timer.start()print(f'loss: {animator.Y[0][-1]:.3f}, {timer.avg():.3f} sec/epoch')

下面使用这个训练函数,复现之前的实验。

data_iter, _ = get_data_ch11(10)
trainer = torch.optim.SGD
train_concise_ch11(trainer, {'lr': 0.01}, data_iter)
loss: 0.243, 0.015 sec/epoch

在这里插入图片描述

小结

  • 由于减少了深度学习框架的额外开销,使用更好的内存定位以及CPU和GPU上的缓存,向量化使代码更加高效。
  • 随机梯度下降的“统计效率”与大批量一次处理数据的“计算效率”之间存在权衡。小批量随机梯度下降提供了两全其美的答案:计算和统计效率。
  • 在小批量随机梯度下降中,我们处理通过训练数据的随机排列获得的批量数据(即每个观测值只处理一次,但按随机顺序)。
  • 在训练期间降低学习率有助于训练。
  • 一般来说,小批量随机梯度下降比随机梯度下降和梯度下降的速度快,收敛风险较小。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/726608.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

电脑蓝牙在哪里打开?不同系统详解

在现代计算机的多功能性中,蓝牙技术的广泛应用使得我们能够轻松连接各种外部设备,实现无线传输和分享。无论是连接无线耳机、键盘,还是与其他设备快速交换文件,蓝牙在电脑中的角色很重要。然而,对于一些用户而言&#…

centos7 使用rpm包部署filebeat

先决条件参考 虚拟机部署elasticsearch集群-CSDN博客 下载并安装filebeat的rpm包 curl -L -O https://artifacts.elastic.co/downloads/beats/filebeat/filebeat-7.17.18-x86_64.rpmrpm -vi filebeat-7.17.18-x86_64.rpm 修改配置文件 配置文件内容可以参考 Repositories…

5G工业网关是什么?

随着科技的飞速发展,5G技术已经逐渐渗透到我们生活的方方面面。而在工业领域,5G工业网关作为连接工业设备与网络的关键组件,正发挥着越来越重要的作用。HiWoo Box其5G工业网关产品以其卓越的性能和稳定性,正助力企业实现数字化转型…

【异常处理】Vue报错 Component template should contain exactly one root element.

问题描述 启动VUE项目后控制台报错: Component template should contain exactly one root element. If you are using v-if on multiple elements, use v-else-if to chain them instead.翻译为:组件模板应该只包含一个根元素 查看vue代码&#xff0…

【Redis】Redis的应用场景

📝个人主页:五敷有你 🔥系列专栏:Redis ⛺️稳中求进,晒太阳 Redis的应用场景: 限流 要求10s内只能访问一次 RequestMapping("xian")public String xianLiu(String sign){String sign1 …

前端处理接口直接返回的图片

有时候接口会直接返回图片而不是连接&#xff0c;前端需要处理后才能使用。 首先你可能需要设置responseType: blob’处理响应数据格式。 直接使用 将接口及参数动态拼接成img.src直接使用 <img src"http://test.com/api/img?size50x50" alt"">i…

SpringBoot和Vue 实现增删改查、分页查询、模糊查询

文章目录 前言统一的设置返回类型Vue安装axios&#xff0c;封装request查询所有的用户前端页面请求后端接口编写 条件查询前台请求后台处理请求 分页查询前台发送请求后台接受请求 新增、编辑管理员信息前台后台 删除操作前台请求后台 前言 SpringBoot实现增删改查、分页查询、…

出口内销双循环,诺赛特企业走出发展新路子

在外贸圈这个深水池中&#xff0c;企业的命脉早已与规模、市场地位等因素牢牢绑定。大型外贸企业掌握大资源、大曝光&#xff0c;不用为订单发愁&#xff1b;中型外贸企业&#xff0c;投入相当大的人力、财力、物力&#xff0c;孤注一掷&#xff0c;虎口夺食&#xff1b;小型外…

LLM 大模型框架 LangChain 可观测性最佳实践

LLM&#xff08;Large Language Model&#xff09;大模型的可观测性是指对模型内部运行过程的理解和监控能力。由于LLM大模型通常具有庞大的参数量和复杂的网络结构&#xff0c;因此对其内部状态和运行过程的理解和监控是一个重要的问题。 什么是 LangChain&#xff1f; Lang…

中华财险启动“3·15”金融消费者权益保护教育宣传活动!

2024年中国银行保险业“3•15”消费者权益保护教育宣传活动拉开帷幕。中华财险始终坚持“中华保险•服务中华”&#xff0c;切实履行险企责任&#xff0c;为主动保护金融消费者合法权益&#xff0c;在国家监督管理总局和中华保险集团的指导下&#xff0c;全面开展“3•15” 金融…

吼!原来教师这样发布学生成绩,轻松没烦恼

在教育的日常工作中&#xff0c;发布学生成绩往往是一项繁琐而重要的任务。教师们需要确保每位学生及家长能准确、及时地了解到学习成果。然而&#xff0c;传统的纸质成绩单或逐个通知的方式不仅耗时耗力&#xff0c;还容易出错。那么&#xff0c;有没有一种方法能够让教师们轻…

前端请求的错误处理

全局考虑&#xff1a; 错误需要从三个方面考虑&#xff0c;范围一次减小&#xff1a; 网络层 > 协议层 > 应用层

全球IT外包的趋势与发展

随着全球化进程的不断深化&#xff0c;IT外包已经成为众多企业的关键战略之一。IT外包是将企业的信息技术需求委托给第三方服务提供商&#xff0c;以在成本、效率和核心业务专注方面取得优势。在全球化的大背景下&#xff0c;IT外包的发展呈现出一系列新的趋势。 首先&#xff…

【ChatGPT实践】Claude 3全面升级:多模态+100万Token上下文长度,碾压OpenAI?

最近几天这张图在ai圈很火。 前几天&#xff0c;Anthropic发布了其新一代大语言模型Claude 3系列,包括Claude 3 Opus、Sonnet和Haiku三种规模&#xff0c;分别代表了超大杯、大杯和中杯。其中Sonnet版本在官网可以免费体验&#xff1a;https://claude.ai 最强的Opus版本&#x…

Nodejs web服务器之GET、POST请求初次体验

一、认识http请求 步骤 1.DNS解析域名&#xff0c;找到ip地址&#xff0c;建立TCP连接&#xff0c;发起http请求 2.服务器接收到http请求&#xff0c;进行处理&#xff0c;返回数据 3.客户端接收到返回的数据&#xff0c;处理数据&#xff08;比如渲染页面&#xff09; 二、no…

element多选框select下拉框数据回显的问题value.push is not a function

文章目录 问题描述 问题描述 今天在使用Element UI el-select组件遇到了一个问题&#xff0c;如下图&#xff1a; 下拉框里的值选中了&#xff0c;但是文本框里没有值 这是 el-select组件代码,我这里是用了一个多选框&#xff0c;options的值是在后端查询的&#xff0c;form.we…

Batch Nomalization 迁移学习

Batch Nomalization 1.Batch Nomalization原理 图像预处理过程中通常会对图像进行标准化处理&#xff0c;这样能够加速网络的收敛。就是按照channel去求均值和方差&#xff0c;然后原数据减均值除标准差&#xff0c;使我们的feature map满足均值为0&#xff0c;方差为1的分布…

three.js如何实现简易3D机房?(三)显示信息弹框/标签

接上一篇&#xff1a; three.js如何实现简易3D机房&#xff1f;(二&#xff09;模型加载的过渡动画&#xff1a;http://t.csdnimg.cn/onbWY 目录 七、创建信息展示弹框 1.整体思路 &#xff08;1&#xff09;需求&#xff1a; &#xff08;2&#xff09;思路&#xff1a;…

网康科技 NS-ASG 应用安全网关 SQL注入漏洞复现(CVE-2024-2022)

0x01 产品简介 网康科技的NS-ASG应用安全网关是一款软硬件一体化的产品,集成了SSL和IPSec,旨在保障业务访问的安全性,适配所有移动终端,提供多种链路均衡和选择技术,支持多种认证方式灵活组合,以及内置短信认证、LDAP令牌、USB KEY等多达13种认证方式。 0x02 漏洞概述 …

C语言第三十六弹---文件操作(中)

✨个人主页&#xff1a; 熬夜学编程的小林 &#x1f497;系列专栏&#xff1a; 【C语言详解】 【数据结构详解】 文件操作 1、文件的顺序读写 1.1、顺序读写函数介绍 1.1.1、fgetc 与 fputc 1.1.2、fgets 与 fputs 1.1.3、fscanf 与 fprintf 1.1.4、fread 与 fwrite 1.…