轻量化 | 如何让笨重的深度学习模型在移动设备上跑起来?看它!

2023-12-18 12:58

本文主要是介绍轻量化 | 如何让笨重的深度学习模型在移动设备上跑起来?看它!,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

点击上方“AI算法修炼营”,选择加星标或“置顶”

标题以下,全是干货

概述

卷积神经网络依靠神经网络中数以千万计的网络参数共同参与计算,存在网络结构复杂,运算量大,速度慢的缺点,并且很难移植到嵌入式设备中。随着网络模型层数越来越深,参数越来越多,减少他们的大小和计算损耗至关重要,特别是对于在线学习和增强学习这样的实时应用来说。不仅如此,近年来 VR,AR 以及智能可穿戴设备的高速发展,需要研究者们解决将大规模学习系统部署到移动设备上的问题。而达到这个目标需要从很多方面获取整合的解决方案,包括但不限于机器学习,优化方法,计算机结构,数据压缩,索引和硬件设计。这是一个重要且十分活跃的领域。

近年来,许多学者在深度学习压缩和加速方面已经做出许多成果, 出了多种神经网络轻量化的算法,主要可分为以下几类。

1. 网络剪枝

网络剪枝目的在于找出这些冗余连接并将其移除,使其不再参与网络的前向或后向运算过程中,起到减少网络计算量的作用,如下图所示。

移除的神经元及相应连接也不再存储,减少了模型的存储量。在这个过程中,一个原本稠密的神经网络由于部分连接的移除而变得稀疏。

由于全连接层冗余度远远高于卷积层,传统的网络剪枝方法多用于全连接层中。网络剪枝往往针对已经训练好的模型进行,在得到训练好的模型后,根据某种评价标准定义每条连接的重要程度。一种广泛使用的评价标准是连接权重的绝对值大小,该值越小,说明对应的神经元对网络输出结果影响越小,属于不重要的神经元,应该被移除,对应的连接也应被剪除。

虽然移除的连接不那么重要,但随着网络计算过程中的错误累积,网络性能和准确度依然会受到较大的影响,为了消除这些影响,一个很重要的步骤是对剪枝后的网络进行微调训练来恢复网络性能。整个网络剪枝和调优交替进行,直至达到模型大小与模型性能间的最佳平衡。

 

权重剪枝主要有两种方式:

 

(1)后剪枝:拿到一个模型直接对权重进行剪枝,不需要其他条件。

 

(2)训练时剪枝:训练迭代时边剪枝,使网络在训练过程中权重逐渐趋于0,但是由于训练时权重动态调整,使得剪枝操作对网络精度的影响可以减少,所以训练时剪枝比后剪枝更加稳定。

在剪枝结束后,权值矩阵由稠密矩阵变为稀疏矩阵

为了减少参数的存储量,通常使用存储稀疏矩阵的压缩存储方式存储参数,代表方式为稀疏行压缩方法 (Compressed Sparse Row,CSR)和稀疏列压缩方法 (Compressed SparseColumn,CSC)

TF官方提供了详尽的Keras剪枝教程和Python API文档,以及训练稀疏模型等高级用法的指导。

2. 权值量化  

量化概念

网络量化通过减少表示每个权重的比特数的方法来压缩神经网络。

 

量化的思想非常简单,就是对权重数值进行聚类。统计网络权重和激活值的取值范围,找到最大值最小值后进行min-max映射把所有的权重和激活映射到到INT8整型范围(-127~128)。

 

在卷积神经网络中,参数所分布在的数值空间,通过一定的划分方法,总是可以划分为 k 个类别,因此可以通过存储这 k 个类别的中心值或者映射值从而压缩网络的存储

 

通过全值量化方法,一方面可以减少模型的存储占用的空间。模型的权值参数往往以 32 位浮点数的形式保存,神经网络中往往具有数以千万计的参数,会占据极大的存储空间,因此,如果在存储模型参数时将 32 位浮点数量化为 8 位的定点数,可以把参数大小缩小为原来的 1/4,整个模型的大小也可以缩小为原来的 1/4,不仅如此,随着参数量化后模型的减小,网络前向运算阶段所需要的计算资源也会大大减少。

 

量化步骤

量化具体步骤如下:

 

(1)量化:首先对权重矩阵中的所有权值进行 k 均值聚类,以图示为例,将其聚为四类,不同类用不同颜色表示,聚类结束后同一类的权重值用对应的聚类索引和聚类中心值表示。

 

 

(2)反向传播:使用普通的梯度计算方法得到每个权重所对应的梯度,根据之前的权重分组,将同一组的权重梯度值进行累加,得到聚类中心这一轮网络训练中的更新量,聚类中心值减去更新量与学习率的乘积,就得到本轮训练更新后的聚类中心。

 

经过多次聚类-训练-更新步骤后,就可以得到量化后的神经网络模型,每个权重可以仅仅由其对应的聚类中心值和聚类索引来表示。

 

一种更为极端的方式是二值量化,即仅仅使用 (-1) 和 (+1) 来表示权重值,此时一个权重值仅仅只占用一个比特位,参数模型压缩到仅为原来的 1/32,并且-1,+1 相比普通的浮点数权值,在乘加运算中具有天然的计算优势,计算效率极高。

量化为什么有效

首先量化会损失精度,这相当于给网络引入了噪声,但是神经网络一般对噪声是不太敏感的,只要控制好量化的程度,对高级任务精度影响可以做到很小。

 

其次,传统的卷积操作都是使用FP32浮点,浮点运算时需要很多时间周期来完成,但是如果我们将权重参数和激活在输入各个层之前量化到INT8,位数少了乘法操作少了,而且此时做的卷积操作都是整型的乘加运算,比浮点快很多,运算结束后再将结果乘上scale_factor变回FP32,这整个过程就比传统卷积方式快很多。

 

从体系结构的考量角度思考量化带来的另一个好处是节能和芯片面积,每个数使用了更少的位数,做运算时需要搬运的数据量少了,减少了访存开销(节能),同时所需的乘法器数目也减少(减少芯片面积)。

3. 低秩近似

基于低秩分解的方法从分解矩阵运算的角度对模型计算过程进行了优化,具有清晰的数学解释,是减少模型冗余和加速模型运算的一种非常有效的方法,特别是对于全连接层的压缩和加速。

 

大多数的计算量分布在卷积层中。在卷积层结构中,网络层的参数通常以多维矩阵的形式保存。通过使用线性代数的方法将参数矩阵分解为一系列小矩阵的组合,使得小矩阵的组合在表达能力上与原始卷积层基本一致,这就是基于低秩分解方法的本质

 

该方法可以保持模型一定的精度并能够极大地降低参数存储所占用的空间并按照从浅到深的顺序逐层对卷积层做低秩近似处理,在一层经过低秩分解后,固定该层参数,并用一种重构误差进行微调。

 

缺点:

 

1. 低秩分解实现并不容易,且计算成本高昂;

2. 目前没有特别好的卷积层实现方式,而目前研究已知,卷积神经网络计算复杂度集中在卷积层;

3. 低秩近似只能逐层进行,无法执行全局参数压缩

4. 知识蒸馏

使用一个大型预先训练的网络(即教师网络)来训练一个更小的网络(又名学生网络)。一旦对一个繁琐笨重的网络模型进行了训练,就可以使用另外一种训练(一种蒸馏的方式),将知识从繁琐的模型转移到更适合部署的小模型

 

KD算法

 

使用两个目标函数的加权平均。首先以一个较高的温度 T 去训练复杂模型,然后训练简单模型,在同样的温度 T 下,将复杂模型输出的 logits 与简单模型输出的 logits 求交叉熵,将之作为第一个目标函数。然后在 T 1 的情况下,求简单模型输出的 logits 与正确标签的交叉熵,作为第二的目标函数。最后将两个目标函数加权平均作为简单模型总的目标函数,用于网络的训练。

 

TAS算法

 

引入多级知识蒸馏,利用一个中等规模的网络(又称教师助理)来弥补学生和教师之间的鸿沟。

从一个复杂的教师模型中提取一个小型的蒸馏模型(即学生模型),当两个网络模型相差很大时,复杂模型的参数越多,准确度就越高时,反而会比一个较其更小的教师模型那里提取的学生模型差。所以创造一个教师助理网络模型,去填补教师和学生网络之间的空隙。学生模型只能从助理模型中提取知识。

5. 高效网络结构

由于神经网络对于噪声不敏感,所以许多早期经典模型的参数其实是存在很大冗余度的,这样的模型很难部署在存储有限的边缘设备,嵌入式设备的高需求刺激了高效网络结构的设计。

例如 GoogleNet 使用了Inception 模块而不再是简单的堆叠网络层从而减小了计算量。ResNet 通过引入瓶颈结构取得了极好的图像识别效果。ShuffleNet 结合了群组概念和深度可分离卷积,在 ResNet 上取得了很好的加速效果。MobileNet 采用了深度可分离卷积实现了目前的最好网络压缩效果。

  • 减小卷积核大小

 

使用更小的3x3卷积(加深网络来弥补感受野变小)

 

将大卷积核分解成一系列小的卷积核的操作组合

 

  • 减少通道数


(1)1x1卷积的应用。在大卷积核前应用1X1卷积,可以灵活地缩减feature map通道数(同时1X1卷积也是一种融合通道信息的方式),最终达到减少参数量和乘法操作次数的效果。

 

(2)组卷积的应用。将feature map的通道进行分组,每个filter对各个分组进行操作即可,例如分成两组,每个filter的参数减少为传统方式的二分之一(乘法操作也减少)。

 

(3)深度可分离卷积(depthwise convolution)的应用,是组卷积的极端情况,将卷积的过程分为逐通道卷积与逐点1×1卷积两步,每一个组只有一个通道。虽然深度可分离卷积将一步卷积过程扩展为两步,但减少了冗余计算,因此总体上计算量有了大幅度降低。

 

如果只采用分组卷积操作,相比传统卷积方式,不同组的feature map信息无法交互,因此可以在组卷积之后在feature map间进行Shuffle Operation来加强通道间信息融合

 

或者更巧妙的是像Pointwise convolution那样将原本卷积操作分解成两步,先进行 depthwise convolution,之后得到的多个通道的feature map再用一个1X1卷积进行通道信息融合,拆解成这样两步,卷积参数也减少了。

 

  • 减少filter数目

 

直接减少的filter数目虽然可以减少参数,但是导致每层产生的feature map数目减少,网络的表达能力也会下降不少。一个方法是像DenseNet那样,一方面减少每层filter数目,同时在每层输入前充分重用之前每一层输出的feature map

  • 池化操作


池化操作是操作卷积神经网络的标准操作,池化层没有参数,同时又可以灵活缩减上一级的feature map大小,从而减少下一级卷积的乘法操作。

6. 卷积运算的优化

  • 卷积转化为矩阵乘法


大部分深度学习框架会把卷积操作转化成矩阵乘法操作,这样可以利用现有成熟的矩阵乘法优化方案,比较常见的转化方式是img2col

全连接层可以直接转化为矩阵乘法,因为全连接的操作本质上就是Y=WX的矩阵操作。从另一个角度,全连接的操作其实还可以看作 每个神经元为1x1filter,所进行的卷积操作。而对于卷积层,转化为矩阵乘法后,会引入一定冗余度。

  • 优化矩阵乘法操作


矩阵分块可以更好地匹配各级cache的大小,这样cache局部性更好,访存时的miss率更低。除此之外,对矩阵的内存数据布局进行重排布来增加待计算数据放置的连续性,这样取数的时候cache miss率也会更低,像常见的 NCWH 和 NWHC 排布模式都是这方面考虑。

  • 矩阵乘法的近似计算


使用其它的卷积近似替代算法,可以减少复杂度。不过有些算法有使用条件,像Winograd只是对小卷积核操作有比较大的优化效果。傅里叶变换似乎不太常用,因为要达到理论上的加速,要求卷积核要很大才可以,而实际上深度学习用的都是小卷积核

参考:

1.分布式机器学习:算法、理论与实践 (豆瓣)

2.李沐 Mu Li. Scaling Distributed Machine Learning with System and Algorithm Co-design. https://www.cs.cmu.edu/~muli/file/mu-thesis.pdf

3.深度学习加速综述:算法、编译器、体系结构与硬件设计https://zhuanlan.zhihu.com/p/101544149 

5. https://blog.skymind.ai/distributed-deep-learning-part-1-an-introduction-to-distributed-training-of-neural-networks/

-END-

扫描个人微信号,

拉你进AI算法修炼营学习交友群。

目标检测、图像分割、自动驾驶、机器人、面试经验

福利满满,名额已不多…

▲长按关注我们


觉得好看对你有帮助,就点个在看吧  

                               

这篇关于轻量化 | 如何让笨重的深度学习模型在移动设备上跑起来?看它!的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/508438

相关文章

深度解析Python中递归下降解析器的原理与实现

《深度解析Python中递归下降解析器的原理与实现》在编译器设计、配置文件处理和数据转换领域,递归下降解析器是最常用且最直观的解析技术,本文将详细介绍递归下降解析器的原理与实现,感兴趣的小伙伴可以跟随... 目录引言:解析器的核心价值一、递归下降解析器基础1.1 核心概念解析1.2 基本架构二、简单算术表达

深度解析Java @Serial 注解及常见错误案例

《深度解析Java@Serial注解及常见错误案例》Java14引入@Serial注解,用于编译时校验序列化成员,替代传统方式解决运行时错误,适用于Serializable类的方法/字段,需注意签... 目录Java @Serial 注解深度解析1. 注解本质2. 核心作用(1) 主要用途(2) 适用位置3

Java MCP 的鉴权深度解析

《JavaMCP的鉴权深度解析》文章介绍JavaMCP鉴权的实现方式,指出客户端可通过queryString、header或env传递鉴权信息,服务器端支持工具单独鉴权、过滤器集中鉴权及启动时鉴权... 目录一、MCP Client 侧(负责传递,比较简单)(1)常见的 mcpServers json 配置

Maven中生命周期深度解析与实战指南

《Maven中生命周期深度解析与实战指南》这篇文章主要为大家详细介绍了Maven生命周期实战指南,包含核心概念、阶段详解、SpringBoot特化场景及企业级实践建议,希望对大家有一定的帮助... 目录一、Maven 生命周期哲学二、default生命周期核心阶段详解(高频使用)三、clean生命周期核心阶

深度剖析SpringBoot日志性能提升的原因与解决

《深度剖析SpringBoot日志性能提升的原因与解决》日志记录本该是辅助工具,却为何成了性能瓶颈,SpringBoot如何用代码彻底破解日志导致的高延迟问题,感兴趣的小伙伴可以跟随小编一起学习一下... 目录前言第一章:日志性能陷阱的底层原理1.1 日志级别的“双刃剑”效应1.2 同步日志的“吞吐量杀手”

Unity新手入门学习殿堂级知识详细讲解(图文)

《Unity新手入门学习殿堂级知识详细讲解(图文)》Unity是一款跨平台游戏引擎,支持2D/3D及VR/AR开发,核心功能模块包括图形、音频、物理等,通过可视化编辑器与脚本扩展实现开发,项目结构含A... 目录入门概述什么是 UnityUnity引擎基础认知编辑器核心操作Unity 编辑器项目模式分类工程

深度解析Python yfinance的核心功能和高级用法

《深度解析Pythonyfinance的核心功能和高级用法》yfinance是一个功能强大且易于使用的Python库,用于从YahooFinance获取金融数据,本教程将深入探讨yfinance的核... 目录yfinance 深度解析教程 (python)1. 简介与安装1.1 什么是 yfinance?

Python学习笔记之getattr和hasattr用法示例详解

《Python学习笔记之getattr和hasattr用法示例详解》在Python中,hasattr()、getattr()和setattr()是一组内置函数,用于对对象的属性进行操作和查询,这篇文章... 目录1.getattr用法详解1.1 基本作用1.2 示例1.3 原理2.hasattr用法详解2.

把Python列表中的元素移动到开头的三种方法

《把Python列表中的元素移动到开头的三种方法》在Python编程中,我们经常需要对列表(list)进行操作,有时,我们希望将列表中的某个元素移动到最前面,使其成为第一项,本文给大家介绍了把Pyth... 目录一、查找删除插入法1. 找到元素的索引2. 移除元素3. 插入到列表开头二、使用列表切片(Lis

深度解析Spring Security 中的 SecurityFilterChain核心功能

《深度解析SpringSecurity中的SecurityFilterChain核心功能》SecurityFilterChain通过组件化配置、类型安全路径匹配、多链协同三大特性,重构了Spri... 目录Spring Security 中的SecurityFilterChain深度解析一、Security