一文看懂深度学习模型压缩和加速
作者:知乎-扬易(@xieyangyi) 简枫 千瞳
一
前言
近年来深度学习模型在计算机视觉、自然语言处理、搜索推荐广告等各种领域,不断刷新传统模型性能,并得到了广泛应用。随着移动端设备计算能力的不断提升,移动端AI落地也成为了可能。相比于服务端,移动端模型的优势有:
减轻服务端计算压力,并利用云端一体化实现负载均衡。特别是在双11等大促场景,服务端需要部署很多高性能机器,才能应对用户流量洪峰。平时用户访问又没那么集中,存在巨大的流量不均衡问题。直接将模型部署到移动端,并在置信度较高情况下直接返回结果,而不需要请求服务端,可以大大节省服务端计算资源。同时在大促期间降低置信度阈值,平时又调高,可以充分实现云端一体负载均衡。 实时性好,响应速度快。在feed流推荐和物体实时检测等场景,需要根据用户数据的变化,进行实时计算推理。如果是采用服务端方案,则响应速度得不到保障,且易造成请求过于密集的问题。利用端计算能力,则可以实现实时计算。 稳定性高,可靠性好。在断网或者弱网情况下,请求服务端会出现失败。而采用端计算,则不会出现这种情况。在无人车和自动驾驶等可靠性要求很高的场景下,这一点尤为关键,可以保证在隧道、山区等场景下仍能稳定运行。 安全性高,用户隐私保护好。由于直接在端上做推理,不需要将用户数据传输到服务端,免去了网络通信中用户隐私泄露风险,也规避了服务端隐私泄露问题
算法层压缩加速。这个维度主要在算法应用层,也是大多数算法工程师的工作范畴。主要包括结构优化(如矩阵分解、分组卷积、小卷积核等)、量化与定点化、模型剪枝、模型蒸馏等。 框架层加速。这个维度主要在算法框架层,比如tf-lite、NCNN、MNN等。主要包括编译优化、缓存优化、稀疏存储和计算、NEON指令应用、算子优化等 硬件层加速。这个维度主要在AI硬件芯片层,目前有GPU、FPGA、ASIC等多种方案,各种TPU、NPU就是ASIC这种方案,通过专门为深度学习进行芯片定制,大大加速模型运行速度。
二
算法层压缩加速
分解前:矩阵参数量为 (M * N) 分解后:参数量为 (M*K + K*N) 压缩量:(M * N) / (M*K + K*N), 由于M远大于N,故可近似为 N / k,当N=2014,k=128时,可以压缩8倍
分组前:参数量 (M*N*dk*dk) 分组后:参数量 (M*dk*dk + M*N*1*1) 压缩量:(M*dk*dk + M*N*1*1) / (M*N*dk*dk),近似为 1/(dk*dk)。dk的常见值为3,也就是3*3卷积,故可缩小约9倍
使用两个串联小卷积核来代替一个大卷积核。InceptionV2中创造性的提出了两个3x3的卷积核代替一个5x5的卷积核。在效果相同的情况下,参数量仅为原先的 3x3x2 / 5x5 = 18/25 使用两个并联的非对称卷积核来代替一个正常卷积核。InceptionV3中将一个7x7的卷积拆分成了一个1x7和一个7x1, 卷积效果相同的情况下,大大减少了参数量,同时还提高了卷积的多样性。
2.1.5 其他
全局平均池化代替全连接层。这个才是大杀器!AlexNet和VGGNet中,全连接层几乎占据了90%的参数量。inceptionV1创造性的使用全局平均池化来代替最后的全连接层,使得其在网络结构更深的情况下(22层,AlexNet仅8层),参数量只有500万,仅为AlexNet的1/12 1x1卷积核的使用。1x1的卷积核可以说是性价比最高的卷积了,没有之一。它在参数量为1的情况下,同样能够提供线性变换,relu激活,输入输出channel变换等功能。VGGNet创造性的提出了1x1的卷积核 使用小卷积核来代替大卷积核。VGGNet全部使用3x3的小卷积核,来代替AlexNet中11x11和5x5等大卷积核。小卷积核虽然参数量较少,但也会带来特征面积捕获过小的问题。inception net认为越往后的卷积层,应该捕获更多更高阶的抽象特征。因此它在靠后的卷积层中使用的5x5等大面积的卷积核的比率较高,而在前面几层卷积中,更多使用的是1x1和3x3的卷积核。
将大小相近的参数聚在一起,分为一类。 每一类计算参数的平均值,作为它们量化后对应的值。 每一类参数存储时,只存储它们的聚类索引。索引和真实值(也就是类内平均值)保存在另外一张表中 推理时,利用索引和映射表,恢复为真实值。
训练一个performance较好的大模型。 评估模型中参数的重要性。常用的评估方法是,越接近0的参数越不重要。当然还有其他一些评估方法,这一块也是目前剪枝研究的热点。 将不重要的参数去掉,或者说是设置为0。之后可以通过稀疏矩阵进行存储。比如只存储非零元素的index和value。 训练集上微调,从而使得由于去掉了部分参数导致的performance下降能够尽量调整回来。 验证模型大小和performance是否达到了预期,如果没有,则继续迭代进行。
2.3.3 神经元剪枝
老师和学生可以是不同的网络结构,比如BERT蒸馏到BiLSTM网络。但一般相似网络结构,蒸馏效果会更好。 总体loss为 soft_label_loss + hard_label_loss。soft_label_loss可以用KL散度或MSE拟合 soft label为teacher模型的要拟合的对象。可以是模型预测输出,也可以是embeddings, 或者hidden layer和attention分布。
Teacher 12层,student 6层,每两层去掉一层。比如student第二层对应teacher第三层 Loss= 5.0 * Lce+2.0 * Lmlm+1.0 * Lcos
Lce: soft_label 的KL散度 Lmlm: mask LM hard_label 的交叉熵 Lcos:hidden state 的余弦相似度
三
框架层加速
基于基本的C++编译器优化。 打开编译器的优化选项,选择O2等加速选项。 小函数内联,概率大分支优先,避免除法,查表空间换时间,函数参数不超过4个等。 利用C,而不是C++,C++有不少冗余的东西。 缓存优化 小块内存反复使用,提升cache命中率,尽量减少内存申请。比如上一层计算完后,接着用作下一层计算。 连续访问,内存连续访问有利于一次同时取数,相近位置cache命中概率更高。比如纵向访问数组时,可以考虑转置后变为横向访问。 对齐访问,比如224*224的尺寸,补齐为256*224,从而提高缓存命中率。 缓存预取,CPU计算的时候,preload后面的数据到cache中。 多线程 为循环分配线程。 动态调度,某个子循环过慢的时候,调度一部分循环到其他线程中。 稀疏化 稀疏索引和存储方案,采用eigen的sparseMatrix方案。 内存复用和提前申请 扫描整个网络,计算每层网络内存复用的情况下,最低的内存消耗。推理刚开始的时候就提前申请好。避免推理过程中反复申请和释放内存,避免推理过程中因为内存不足而失败,复用提升内存访问效率和cache命中率。 ARM NEON指令的使用,和ARM的深度融合。NEON可以单指令多取值(SIMD),感兴趣可针对学习,这一块水也很深。 手工汇编,毕竟机器编译出来的代码还是有不少冗余的。可以针对运行频次特别高的代码进行手工汇编优化。当然如果你汇编功底惊天地泣鬼神的强,也可以全方位手工汇编。 算子支持:比如支持GPU加速,支持定点化等。有时候需要重新开发端侧的算子。
四
硬件层加速
五
总结
end
评论