- 在解决的是什么问题? 激活值占用显存较多的问题
- 为何成功,标志/准是什么? 不影响收敛性的情况下,能降低4倍显存值,而且是普遍适用的
- 在前人基础上的关键创新是什么? 最牛逼的还是数学证明,如何选取量化后的bit 数字,来保证方差最小。而且证明了梯度的方差可以作为近似(量化)的质量关键指标
- 关键结果有哪些? 能让 batchsize 增加一倍
- 有哪些局限性?如何优化? 计算过程是否比较耗时? 量化策略好像还能比 1bit 还低,比如 product 量化。
- 这个工作可能有什么深远的影响?
Activation compressed training (ACT) 是一个非常有前景的方法,可以减少训练的内存消耗。ACT 通过量化方法压缩激活值到低比特数值精度。首次在 BLPA 里提出,后来又有 TinyScript 扩展到了非均匀的量化策略。这些方法在训练 ResNet-50 时使用了4bit 的激活值。
但是有几个劣势:
- ACT 方法下的收敛性没有完全搞清楚(出了在强 mean-field 假设的限制下)
- 之前的工作聚焦在特定的架构下,例如ResNet的 pre-activation 的定制版本,限制了通用性
- 现存的量化策略并不是专门给 ACT 设计的,压缩率不理想
提出的 ActNN,克服了上述所有问题。它存储随机量化的激活值来计算梯度。理论上,可以把 ActNN计算出的梯度当作全精度下(FP radient)梯度的随机近似。我们展示了 ActNN 梯度时 FP 梯度的无偏见近似。证明了 ActNN 在通用模型架构上的收敛性。所以 ACT 有理论保证可以解决通用问题
本文贡献主要三点:
- ACt的通用收敛理论
- 感知异构性(feature维度,采样,层)的量化策略,达到2bit压缩
- PyTorch 里高效激活值压缩的实现
我们通过梯度偏差上精确的表达式,刻画了量化对收敛的影响。更好的量化策略减少了梯度偏差,可以用低比特达到满意的收敛性。受理论启发,设计了创新的量化策略,可以利用激活值在 feature 维度,数据样例和层上的异构性。这包括了一个 group 粒度的量化器和一个细粒度混合精度的算法,可以近似减少给定内存限制下的梯度偏差。在多种不通任务下,包括图片分类,语义分割和对象检测上,ActNN 把激活值压缩到2bit,精度损失小于 0.5%。即使只有 1.25 bit,也能有很好的收敛和结果。这很大程度提高了前人的工作,只能在4bit 下收敛。
效果:ActNN 减少了12倍的激活值内存,可以允许在 GPU 上训练 6.6到14倍大的batchsize。它允许不增加额外计算资源的情况下,训练更大的模型。比如把 ResNet 扩展到6.4倍深,或3.7倍宽,或 3.1倍更高精度。
量化训练(Quantized Training) :量化感知训练(QAT) 或 完全量化的训练,目标是在推理或训练时减少计算的代价,通过量化来实现。一个副作用是训练的内存开销会减少。但是 QAT 是一个更有挑战的任务,因为需要计算的内核直接支持量化的 tensor。与之对比, ACT 只需要考虑存储,可以利用更灵活的量化策略来做更好的压缩。而且 ACT 和 QT 是互补的。一个利用 QT 来加速训练,ACT 用来进一步减少内存开销
模型/梯度压缩:模型压缩和梯度压缩压缩了权重和梯度,减少了存储和通信的开销。然而,激活值与权重和梯度相比,有不同的属性,它有一个“sample”的轴。而且,ACT对压缩速度更敏感,因为激活值可以在过程中压缩,通常比权重和梯度更大(这个应该是cv里,nlp里不是)。ActNN的压缩策略就是给有这些特定属性量身定制的。
内存高效的训练系统:Gradient checkpointing 妥协了计算来获得更小内存,方法是在前向过程中丢掉一些激活值,在 backward 里需要时重新计算。交换(Swapping)技术利用了 CPU 上的大内存,把 GPU 上的 tensor 交换到 CPU 上。模型并行的训练,把模型拆分到GPU上,所以每个 GPU 只存储部分layers。所有这些方法通过在 GPU 上存储更少的 tensor 来节省内存。与之对比,ActNN 是压缩保存的 tensors,可以和这些方法互补。
看八太懂啊
收敛时的误差会被梯度的方差所限制。推导出了使用量化压缩后,随机梯度下降计算出的方差= mnibatch采样时随机梯度下降的方差 + 有损压缩额外引入的方差。说明有损压缩带来的方差远小于随机梯度下降带来的方差即可,这样不影响收敛性
a) conv_2_2_1这个层在不通分组(per-group) 的范围下直方图,可以看到主要集中在前面 b) 同一层中,样本级别的直方图敏感度,看到在少量层上敏感 c)每个层上各维度的敏感性,看到最敏感的是最后一层?。Y轴是敏感性,缺 dimension 这个维度呢?
上述敏感度,是用它提供的公式来刻画的:w(l,n)
为了解决 feature 的不通维度上数值的范围差异,提出了分组的量化策略。 给定一个激活值 tensor,把它分到 hni 个组里,每个组有 G 个元素。数量会被量化到 b bit,或 B=2^b-1。对于每个元素,可以计算出最小值,最大值和激活值的缩放:
这样把输入缩到 [0, B] 上。利用随机近似来处理成整数。在反向传播时,激活值可以反量化。这里利用了随机近似的非偏向性,所以量化前后的期望是一致的。
已有量化策略对每个 Tensor 使用单个范围和零点,可以看作是单个组。这样 Range 只能取最大的那个 R=max(ni)Rni。而图3 里介绍了,大部分组的 range 是比 R 要小很多的。这样就增大了方差(Variance)。
实际设置 G 为 256 ,用bfloat16来 存储每个组范围和零点,这样每个组额外消耗 32 bit。
想进一步减小方差,ActNN 使用混合精度量化策略:针对每次 sample 和 每层,适应性选取数值精度。sample 里是这一次 iteraion 里的数据特点,而每层是不通类型的 op,对数值精度的敏感性不通。
虽然看不懂,但是它的原理是啥?忽略掉不重要的变量,只研究主要变量对结果的影响?
混合精度可以极大地减小梯度的方差。
为了最好地利用数据的特点,ActNN 在运行时调整混合精度策略。每个 SGD 的一次迭代里,发生两阶段的调优:
- (每个 sample 上分配一次) 每层的forward 时,计算并存储每个 sample 里的敏感性。计算出在当前层的固定bit的预算下,对每次 sample 的最佳桶位数。通过解问题10.
- (每层分配) 在结束 backward 时,对所有的层,解问题10,然后设置整体 b 为 sum(b)
问题十是一个离散优化问题,可以通过 DP 来精确解决。但是代价比较大,所以使用贪心算法,当通过二分堆来选一个最优解,复杂度是 O(NL log2(NL)),N 是batchsize,L 是模型深度
最终,敏感度 wn(l) 可能取决于每个采样里梯度的量级,我们压缩时并不知道。ActNN 用了两个方法来预估梯度的量级。第一个是用上一次 epoch 里的 stale gradient magnitude。第二个是用多个 sample 之间的梯度量级的移动平均。两个在实际中效果都很好。
有 PyTorch 里一些层的实现,用户可以直接用 ActNNModule 来包一下自己的模型即可
实现了高效的 CUDA kernel 来压缩和解压缩,可以把浮点数量化为整数,压缩到bit流里
对比了 CheckMate,MONet,DTR,SwapAdvisor,Capuchin,BLPA 和 ActNN,其中有一些方法需要训练前先解决一波优化问题,有的需要花费小时级别的时间。
而 ActNN 不需要
优化级别:
PyTorch 里使用了 caching allocator,复用了 GPU buffer,速度快但是会有严重的碎片问题。当要分配大的 tensor 时,可以关闭掉。
L1:只给 conv 层做分组量化 L2 : 所有层 L3: L2 + 细粒度混合精度优化 L4: L3 + swapping L5: L4 + defragmentation
ActNN 可以和 AMP 结合,对进度无影响
表格里统计了各类 CV 模型里 activation 的量级,大概占比内存的 90%,所以用 ActNN 非常有用
- 上述 CUDA kernel 里,还可以把数据变成bit stream ?
- 按照不同类 op,可以理解为是固定类型的? 不是的,是根据公式要流式计算敏感性
- 那 sample 是如何做呢?需要先读取一遍sample 数据吗? forward 时记录即可
- 上述三种方法都是处理完一次 iteration,再优化量化策略?还是说每次压缩前都处理一下?前者,只有梯度的量级是逐步更新的
- 为何对 GPU 友好?
- 此算法主要用在 CV 里,因为图片是三维的,activation 数据比较大。而且它的数学公式,跟 SGD 有关?
有趣有趣,与互联网领域的文本压缩或时序数据的压缩对比一下: 共同点:都依赖数据的变化特点,需要考虑压缩后尽量减小决策结果的影响 差异点:ML里具体数值不重要,方差重要,所以用整数桶来表示数据(量化)。而时序数据是通过描述当前数据与前一条/块数据的差异来压缩,需要能还原为准确的原始数值