🐠小批量随机梯度下降
2021-11-5
| 2023-8-6
0  |  阅读时长 0 分钟
type
status
date
slug
summary
tags
category
icon
password
Property

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

向量化和缓存

使用小批量的决策的核心是计算效率。 当考虑与多个GPU和多台服务器并行处理时,这一点最容易被理解,我们需要向每个GPU发送至少一张图像。 有了每台服务器8个GPU和16台服务器,就能得到大小为128的小批量。
 
当涉及到单个GPU甚至CPU时,事情会更微妙一些: 这些设备有多种类型的内存、通常情况下多种类型的计算单元以及在它们之间不同的带宽限制。 例如,一个CPU有少量寄存器(register),L1和L2缓存,以及L3缓存(在不同的处理器内核之间共享)。 随着缓存的大小的增加,它们的延迟也在增加,同时带宽在减少。 可以说,处理器能够执行的操作远比主内存接口所能提供的多得多。
首先,具有16个内核和AVX-512向量化的2GHz CPU每秒可处理高达 个字节。 同时,GPU的性能很容易超过该数字100倍。 而另一方面,中端服务器处理器的带宽可能不超过100Gb/s,即不到处理器满负荷所需的十分之一。 更糟糕的是,并非所有的内存入口都是相等的:内存接口通常为64位或更宽(例如,在最多384位的GPU上)。 因此读取单个字节会导致由于更宽的存取而产生的代价。
其次,第一次存取的额外开销很大,而按序存取或突发读取相对开销较小。
 
减轻这些限制的方法是使用足够快的CPU缓存层次结构来为处理器提供数据。 这是深度学习中批量处理背后的推动力。 举一个简单的例子:矩阵-矩阵乘法。 比如 ,有很多方法来计算 。例如可以尝试以下方法:
  1. 可以计算 ,也就是说,可以通过点积进行逐元素计算
  1. 可以计算 ,也就是说,可以一次计算一列。同样,可以一次计算 一行
  1. 可以简单地计算
  1. 可以将分成较小的区块矩阵,然后一次计算的一个区块
如果使用第一个选择,每次计算一个元素 时,都需要将一行和一列向量复制到CPU中。 更糟糕的是,由于矩阵元素是按顺序对齐的,因此当从内存中读取它们时,需要访问两个向量中许多不相交的位置。 第二种选择相对更有利:能够在遍历 的同时,将列向量 保留在CPU缓存中。 它将内存带宽需求减半,相应地提高了访问速度。 第三种选择表面上是最可取的,然而大多数矩阵可能不能完全放入缓存中。 第四种选择提供了一个实践上很有用的方案:可以将矩阵的区块移到缓存中然后在本地将它们相乘。
 
除了计算效率之外,Python和深度学习框架本身带来的额外开销也是相当大的。 回想一下,每次执行代码时,Python解释器都会向深度学习框架发送一个命令,要求将其插入到计算图中并在调度过程中处理它。 这样的额外开销可能是非常不利的。 总而言之,最好用向量化(和矩阵)。
 

小批量

之前我们会理所当然地读取数据的小批量,而不是观测单个数据来更新参数,现在简要解释一下原因。
处理单个观测值需要执行许多单一矩阵-矢量(甚至矢量-矢量)乘法,这耗费相当大,而且对应深度学习框架也要巨大的开销。 这既适用于计算梯度以更新参数时,也适用于用神经网络预测。 也就是说,每当执行 时,消耗巨大。其中
可以通过将其应用于一个小批量观测值来提高此操作的计算效率。 也就是说,将梯度 替换为一个小批量而不是单个观测值
让我们看看这对 的统计属性有什么影响:由于 和小批量 的所有元素都是从训练集中随机抽出的,因此梯度的期望保持不变。 另一方面,方差显著降低。 由于小批量梯度由正在被平均计算的个独立梯度组成,其标准差降低了 。 这本身就是一件好事,因为这意味着更新与完整的梯度更接近了。
直观来说,这表明选择大型的小批量 将是普遍可行的。 然而,经过一段时间后,与计算代价的线性增长相比,标准差的额外减少是微乎其微的。 在实践中我们选择一个足够大的小批量,它可以提供良好的计算效率同时仍适合GPU的内存。
下面来看看这些高效的代码,在里面执行相同的矩阵-矩阵乘法,这次我们将其一次性分为64列的“小批量”。
显而易见,小批量上的计算基本上与完整矩阵一样有效。 需要注意的是,在batch_norm中,使用了一种在很大程度上取决于小批量中的方差的正则化。 随着后者增加,方差会减少,随之而来的是批量规范化带来的噪声注入的好处。
 

读取数据集

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

从零开始实现

下面实现一个通用的训练函数,以方便其他优化算法使用。 它初始化了一个线性回归模型,然后可以使用小批量随机梯度下降。
让我们来看看批量梯度下降的优化是如何进行的。 这可以通过将小批量设置为1500(即样本总数)来实现。 因此,模型参数每个迭代轮数只迭代一次。
notion image
当批量大小为1时,优化使用的是随机梯度下降。 为了简化实现,选择了很小的学习率。 在随机梯度下降的实验中,每当一个样本被处理,模型参数都会更新。 在这个例子中,这相当于每个迭代轮数有1500次更新。 可以看到,目标函数值的下降在1个迭代轮数后就变得较为平缓。 尽管两个例子在一个迭代轮数内都处理了1500个样本,但实验中随机梯度下降的一个迭代轮数耗时更多。 这是因为随机梯度下降更频繁地更新了参数,而且一次处理单个观测值效率较低。
notion image
最后,当批量大小等于100时,我们使用小批量随机梯度下降进行优化。 每个迭代轮数所需的时间比随机梯度下降和批量梯度下降所需的时间短。
notion image
将批量大小减少到10,每个迭代轮数的时间都会增加,因为每批工作负载的执行效率变得更低。
notion image
现在可以比较前四个实验的时间与损失。 可以看出,尽管在处理的样本数方面,随机梯度下降的收敛速度快于梯度下降,但与梯度下降相比,它需要更多的时间来达到同样的损失,因为逐个样本来计算梯度并不那么有效。 小批量随机梯度下降能够平衡收敛速度和计算效率。 大小为10的小批量比随机梯度下降更有效; 大小为100的小批量在运行时间上甚至优于梯度下降。
notion image
 

简洁实现

下面用深度学习框架自带算法实现一个通用的训练函数。
下面使用这个训练函数,复现之前的实验。
 
  • PyTorch
  • 随机梯度下降与动态学习率动量法
    目录