Path: blob/master/site/zh-cn/tutorials/optimization/compression.ipynb
25118 views
Copyright 2022 The TensorFlow Compression Authors.
可扩展的模型压缩
概述
本笔记本展示了如何使用 TensorFlow Compression 压缩模型。
在下面的示例中,我们将 MNIST 分类器的权重压缩到比其浮点表示小得多的大小,同时保持分类准确率。这是通过基于论文 Scalable Model Compression by Entropy Penalized Reparameterization 的两步过程完成的:
在训练期间使用显式熵惩罚来训练“可压缩”模型,这鼓励了模型参数的可压缩性。此惩罚的权重 ,能够持续控制压缩模型大小和其准确率之间的权衡。
使用与惩罚相匹配的编码方案将可压缩模型编码为压缩模型,这意味着惩罚是对模型大小的良好预测指标。这确保了该方法不需要多次迭代训练、压缩和重新训练模型以进行微调。
这种方法会严格考虑压缩模型的大小,而不是计算复杂度。它可以与模型剪枝等技术相结合,以减少大小和复杂度。
各种模型的压缩结果示例:
模型(数据集) | 模型大小 | 压缩率 | Top-1 错误压缩(解压缩) |
---|---|---|---|
LeNet300-100 (MNIST) | 8.56 KB | 124x | 1.9% (1.6%) |
LeNet5-Caffe (MNIST) | 2.84 KB | 606x | 1.0% (0.7%) |
VGG-16 (CIFAR-10) | 101 KB | 590x | 10.0% (6.6%) |
ResNet-20-4 (CIFAR-10) | 128 KB | 134x | 8.8% (5.0%) |
ResNet-18 (ImageNet) | 1.97 MB | 24x | 30.0% (30.0%) |
ResNet-50 (ImageNet) | 5.49 MB | 19x | 26.0% (25.0%) |
应用包括:
大规模部署/广播模型到边缘设备,节省传输带宽。
在联合学习中向客户端传达全局模型状态。模型架构(隐藏单元的数量等)相较于初始模型没有变化,客户端可以在解压缩的模型上继续学习。
在内存极其有限的客户端上执行推断。在推断过程中,可以按顺序解压缩每一层的权重,并在计算激活后立即丢弃。
设置
通过 pip
安装 TensorFlow Compression。
导入库依赖项。
定义和训练一个基本的 MNIST 分类器
为了高效压缩密集层和卷积层,我们需要定义自定义层类。这些类似于 tf.keras.layers
下的层,但我们稍后将对它们进行子类化以高效实现熵惩罚重参数化 (EPR)。为此,我们还添加了一个复制构造函数。
首先,我们定义一个标准的密集层:
类似地,定义一个 2D 卷积层:
在继续模型压缩之前,我们来检查一下是否可以成功地训练一个常规分类器。
定义模型架构:
加载训练数据:
最后,训练模型:
成功!该模型训练良好,在 5 个周期内的验证集上的准确率达到了 98% 以上。
训练可压缩分类器
熵惩罚重参数化(EPR)有两个主要组成部分:
在训练期间对模型权重施加惩罚,该惩罚对应于概率模型下的熵,并与权重的编码方案相匹配。下面,我们定义一个实现此惩罚的 Keras
Regularizer
。重新参数化权重,即将它们带入更具可压缩性的潜在表示中(在可压缩性和模型性能之间达成更好的权衡)。对于卷积核,已经证明傅里叶域是一个很好的表示。对于其他参数,以下示例仅使用具有不同量化步长的标量量化(舍入)。
首先,定义惩罚。
下面的示例使用在 tfc.PowerLawEntropyModel
类中实现的代码/概率模型,灵感来自论文 Optimizing the Communication-Accuracy Trade-off in Federated Learning with Rate-Distortion Theory。惩罚定义为: 其中 是模型参数或其潜在表示的一个元素, 是一个数值稳定性在 0 附近小常量。
这种惩罚实际上是一种正则化损失(有时称为“权重损失”)。它是凹形的,顶点为零,这一事实鼓励权重稀疏。用于压缩权重的编码方案是 Elias gamma 码,它为元素大小产生长度为 比特的编码。也就是说,它与惩罚相匹配,并应用惩罚从而最小化预期的代码长度。
其次,定义 CustomDense
和 CustomConv2D
的子类,它们具有以下附加功能:
它们接受上述 Regularizer 的一个实例,并将其应用于训练期间的内核和偏差。
它们将内核和偏差定义为
@property
,每当访问变量时,它们都会使用直通梯度执行量化。这准确地反映了稍后在压缩模型中执行的计算。它们定义了额外的
log_step
变量,代表量化步长的对数。量化越粗,模型越小,但准确率越低。每个模型参数的量化步长都可训练,因此对惩罚损失函数执行优化将确定最佳量化步长。
量化步长定义如下:
有了它,我们可以定义密集层:
卷积层类似。此外,只要设置了卷积核,就会将卷积核作为其实值离散傅里叶变换 (RDFT) 存储,并且每当使用该核时,变换都会被反转。由于内核的不同频率分量往往或多或少是可压缩的,因此其中的每个分量都被分配了自己的量化步长。
按如下方式定义傅里叶变换及其逆变换:
这样,将卷积层定义为:
使用与上面相同的架构定义分类器模型,但使用以下修改后的层:
并训练模型:
可压缩模型已达到与普通分类器相似的准确率。
但是,该模型实际上还没有被压缩。为此,我们定义了另一组子类,它们以压缩形式存储内核和偏差(作为位序列)。
压缩分类器
下面定义的 CustomDense
和 CustomConv2D
的子类将可压缩密集层的权重转换为二进制字符串。此外,它们以半精度存储量化步长的对数以节省空间。每当通过 @property
访问内核或偏差时,它们就会从其字符串表示中解压缩并去量化。
首先,定义函数来压缩和解压缩模型参数:
有了这些,我们可以定义 CompressedDense
:
卷积层类与上面类似。
要将可压缩模型转换为压缩模型,我们可以方便地使用 clone_model
函数。compress_layer
可以将任何可压缩层转换为压缩层,并简单地传递给任何其他类型的层(例如 Flatten
等)。
现在,我们来验证压缩模型是否仍按预期执行:
压缩模型的分类准确率与训练期间达到的分类准确率相同!
此外,压缩后的模型权重的大小远小于原始模型的大小:
将模型存储在磁盘上需要一些开销来存储模型架构、函数图等。
ZIP 等无损压缩方法擅长压缩此类数据,但不擅长压缩权重本身。这就是为什么在应用了 ZIP 压缩之后,当计算模型大小(包括开销)时,EPR 仍然具有显著优势:
正则化效果和大小-准确度权衡
上面, 超参数被设置为 2(通过模型中的参数数量进行标准化)。随着我们增加 ,模型权重的可压缩性受到越来越严重的惩罚。
对于较低的值,惩罚可以起到权重调节器的作用。它实际上对分类器的泛化性能有有益的影响,并且可以在验证数据集上产生略高的准确率:
对于更高的值,我们看到模型大小越来越小,但准确率也在逐渐降低。为了看到这一点,我们来训练几个模型,并绘制它们的大小与准确率之间的关系图:
理想情况下,该图应显示肘形大小-准确率权衡,但准确率指标有些噪声也正常。根据初始化的不同,曲线可能会出现一些曲折。
由于正则化效应,对于较小的 值,EPR 压缩模型在测试集上比原始模型更准确。即使我们比较附加 ZIP 压缩后的大小,EPR 压缩模型也要小很多倍。
解压缩分类器
CompressedDense
和 CompressedConv2D
在每次前向传递时会解压缩它们的权重。这使得它们非常适合内存有限的设备,但解压缩的计算成本可能很高,尤其是对于小批次。
要将模型解压缩一次,并将其用于进一步的训练或推断,我们可以使用常规层或可压缩层将其转换回模型。这在模型部署或联合学习场景中很有用。
首先,转换回普通模型,我们可以进行推断,和/或继续进行常规训练,而不会有压缩惩罚:
请注意,在训练额外的周期后验证准确率会下降,因为训练是在没有正则化的情况下完成的。
或者,我们可以将模型转换回“可压缩”模型,以进行推断和/或进一步训练,并带有压缩惩罚:
在这里,在训练一个额外的周期后,准确率会提高。