敞开生长之旅!这是我参加「日新方案 12 月更文挑战」的第3天,点击检查活动详情。
卷积网络的紧缩办法
- 一,低秩近似
- 二,剪枝与稀少束缚
- 三,参数量化
- 四,二值化网络
- 五,常识蒸馏
- 六,浅层网络
咱们知道,在必定程度上,网络越深,参数越多,模型越复杂,其终究作用越好。神经网络的紧缩算法是,旨在将一个巨大而复杂的预练习模型(pre-trained model)转化为一个精简的小模型。 依照紧缩进程对网络结构的破坏程度,咱们将模型紧缩技能分为“前端紧缩”和“后端紧缩”两部分。
- 前端紧缩,是指在不改动原网络结构的紧缩技能,首要包含
常识蒸馏
、轻量级网络(紧凑的模型结构规划)以及滤波器(filter)层面的剪枝(结构化剪枝)
等; - 后端紧缩,是指包含
低秩近似
、未加束缚的剪枝(非结构化剪枝/稀少)、参数量化
以及二值网络等,方针在于尽或许削减模型巨细,会对原始网络结构形成极大程度的改造。
总结:前端紧缩简直不改动原有网络结构(只是只是在原模型基础上削减了网络的层数或者滤波器个数),后端紧缩对网络结构有不可逆的大幅度改动,形成原有深度学习库、乃至硬件设备不兼容改动之后的网络。其保护成本很高。
一,低秩近似
简略理解便是,卷积神经网络的权重矩阵往往稠密且巨大,然后核算开支大,有一种办法是采用低秩近似的技能将该稠密矩阵由若干个小规模矩阵近似重构出来,这种办法归类为低秩近似算法。
一般地,行阶梯型矩阵的秩等于其“台阶数”-非零行的行数。
低秩近似算法能减小核算开支的原理如下:
给定权重矩阵 W∈RmnW\in \mathbb{R}^{m\times n} , 若能将其表明为若干个低秩矩阵的组合,即 W=∑i=1niMiW=\sum_{i=1}^{n}\alpha_{i}M_{i} , 其间 Mi∈RmnM_{i}\in \mathbb{R}^{m\times n} 为低秩矩阵,其秩为 rir_{i} , 并满足 ri≪min(m,n)r_{i}\ll min(m,n) ,则其每一个低秩矩阵都可分化为小规模矩阵的乘积,Mi=GiHiTM_{i}=G_{i}H_{i}^{T} ,其间 Gi∈RmriG_{i}\in \mathbb{R}^{m\times r_{i}} ,Hi∈RmriH_{i}\in \mathbb{R}^{m \times r_{i}}。当 rir_{i} 取值很小时,便能大幅下降总体的存储和核算开支。
依据以上想法,Sindhwani
等人提出运用结构化矩阵来进行低秩分化的算法,详细原理可自行参阅论文。另一种比较简洁的办法是运用矩阵分化来下降权重矩阵的参数,如 Denton 等人提出运用奇异值分化
(Singular Value Decomposition,简称 SVD)分化来重构全衔接层的权重。
1.1,总结
低秩近似算法在中小型网络模型上,取得了很不错的作用,但其超参数量与网络层数呈线性改动趋势,跟着网络层数的增加与模型复杂度的提高,其查找空间会急剧增大,目前首要是学术界在研讨,工业界运用不多。
二,剪枝与稀少束缚
给定一个预练习好的网络模型,常用的剪枝算法一般都遵从如下操作:
- 衡量神经元的重要程度
- 移除掉一部分不重要的神经元,这步比前 1 步愈加简洁,灵活性更高
- 对网络进行微调,剪枝操作不可防止地影响网络的精度,为防止对分类功能形成过大的破坏,需求对剪枝后的模型进行微调。关于大规模行图像数据集(如ImageNet)而言,微调会占用大量的核算资源,因而对网络微调到什么程度,是需求斟酌的
- 返回第一步,循环进行下一轮剪枝
依据以上循环剪枝框架,不同学者提出了不同的办法,Han等人提出首先将低于某个阈值的权重衔接全部剪除,之后对剪枝后的网络进行微调以完结参数更新的办法,这种办法的不足之处在于,剪枝后的网络对错结构化的,即被剪除的网络衔接在散布上,没有任何连续性,这种稀少的结构,导致CPU高速缓冲与内存频繁切换,然后束缚了实践的加快作用。
依据此办法,有学者测验将剪枝的粒度提高到整个滤波器等级,即丢掉整个滤波器,可是怎么衡量滤波器的重要程度是一个问题,其间一种战略是依据滤波器权重自身的统计量,如分别核算每个滤波器的 L1 或 L2 值,将相应数值巨细作为衡量重要程度标准。
运用稀少束缚来对网络进行剪枝也是一个研讨方向,其思路是在网络的优化方针中参加权重的稀少正则项,使得练习时网络的部分权重趋向于 0 ,而这些 0 值便是剪枝的目标。
2.1,总结
总体而言,剪枝是一项有用减小模型复杂度的通用紧缩技能,其关键之处在于怎么衡量个别权重关于全体模型的重要程度
。剪枝操作对网络结构的破坏程度极小,将剪枝与其他后端紧缩技能相结合,能够达到网络模型最大程度紧缩,目前工业界有运用剪枝办法进行模型紧缩的案例。
三,参数量化
比较于剪枝操作,参数量化则是一种常用的后端紧缩技能。所谓“量化”,是指从权重中概括出若干“代表”,由这些“代表”来表明某一类权重的详细数值。“代表”被存储在码本(codebook)之中,而原权重矩阵只需记载各自“代表”的索引即可,然后极大地下降了存储开支。这种思维可类比于经典的词包模型(bag-of-words model)。常用量化算法如下:
- 标量量化(
scalar quantization
)。 - 标量量化会在必定程度上下降网络的精度,为防止这个弊端,很多算法考虑结构化的向量办法,其间一种是乘积向量
(Product Quantization, PQ)
,详情咨询查阅论文。 - 以PQ办法为基础,Wu等人规划了一种通用的网络量化算法:
QCNN(quantized CNN)
,首要思维在于Wu等人以为最小化每一层网络输出的重构差错,比最小化量化差错更有用。
标量量化算法基本思路是,关于每一个权重矩阵 W∈RmnW\in \mathbb{R}^{m\times n},首先将其转化为向量办法:w∈R1mnw\in \mathbb{R}^{1\times mn}。之后对该权重向量的元素进行 kk 个簇的聚类,这可借助于经典的 k-均值(k-means)聚类
算法快速完结:
argminc∑imn∑jk∥wi−cj∥22\underset{c}{arg min}\sum_{i}^{mn}\sum_{j}^{k}\begin{Vmatrix}w_{i}-c_{j}\end{Vmatrix}_{2}^{2}
这样,只需将 kk 个聚类中心(cjc_{j},标量)存储在码本中,而原权重矩阵则只负责记载各自聚类中心在码本中索引。假如不考虑码本的存储开支,该算法能将存储空间削减为本来的 log2(k)/32log_{2}(k)/32。依据 kk 均值算法的标量量化在很多运用中十分有用。参数量化与码本微调进程图如下:
这三类依据聚类的参数量化算法,其本质思维在于将多个权重映射到同一个数值,然后完结权重同享,下降存储开支的目的。
3.1,总结
参数量化是一种常用的后端紧缩技能,能够以很小的功能丢失完结模型体积的大幅下降,不足之处在于,量化的网络是“固定”的,很难对其做任何改动,一同这种办法通用性差,需求配套专门的深度学习库来运行网络。
这儿,权重参数从浮点转定点、二值化等办法都是是试图防止浮点核算耗时而引入的办法,这些办法能加快运算速率,一同削减内存和存储空间的占用,并确保模型的精度丢失在可接受的范围内,因而这些办法的运用是有其现实价值的。更多参数量化常识,请参阅此 github仓库。
四,二值化网络
- 二值化网络能够视为量化办法的一种极点情况:所有的权重参数取值只能为 1\pm 1 ,也便是运用
1bit
来存储Weight
和Feature
。在普通神经网络中,一个参数是由单精度浮点数来表明的,参数的二值化能将存储开支下降为本来的1/32
。 - 二值化神经网络以其高的模型紧缩率和在前传中核算速度上的优势,近几年格外受到重视和开展,成为神经网络模型研讨中的十分热门的一个研讨方向。可是,第一篇真正意义大将神经网络中的权重值和激活函数值一同做到二值化的是
Courbariaux
等人 2016 年宣布的名为《Binarynet: Training deep neural networks with weights and activations constrained to +1 or -1》的一篇论文。这篇论文第一次给出了关于怎么对网络进行二值化和怎么练习二值化神经网络的办法。 - CNN 网络一个典型的模块是由卷积(
Conv
)->批标准化(BNorm
)->激活(Activ
)->池化(Pool
)这样的次序操作组成的。关于异或神经网络,规划出的模块是由批标准化(BNorm
)->二值化激活(BinActiv)->二值化卷积(BinConv
)->池化(Pool
)的次序操作完结。这样做的原因是批标准化以后,确保了输入均值为0
,然后进行二值化激活,确保了数据为-1
或者+1
,然后进行二值化卷积,这样能最大程度上削减特征信息的丢失。二值化残差网络结构界说实例代码如下:
def residual_unit(data, num_filter, stride, dim_match, num_bits=1):
"""残差块 Residual Block 界说
"""
bnAct1 = bnn.BatchNorm(data=data, num_bits=num_bits)
conv1 = bnn.Convolution(data=bnAct1, num_filter=num_filter, kernel=(3, 3), stride=stride, pad=(1, 1))
convBn1 = bnn.BatchNorm(data=conv1, num_bits=num_bits)
conv2 = bnn.Convolution(data=convBn1, num_filter=num_filter, kernel=(3, 3), stride=(1, 1), pad=(1, 1))
if dim_match:
shortcut = data
else:
shortcut = bnn.Convolution(data=bnAct1, num_filter=num_filter, kernel=(3, 3), stride=stride, pad=(1, 1))
return conv2 + shortcut
4.1,二值网络的梯度下降
现在的神经网络简直都是依据梯度下降算法来练习的,可是二值网络的权重只要 1\pm 1,无法直接核算梯度信息,也无法进行权重更新。为处理这个问题,Courbariaux 等人提出二值衔接(binary connect)算法,该算法采纳单精度与二值结合的办法来练习二值神经网络,这是第一次给出了关于怎么对网络进行二值化和怎么练习二值化神经网络的办法。进程如下:
- 权重
weight
初始化为浮点 - 前向传达
Forward Pass
:- 运用决定化办法(
sign(x)函数
)把 Weight 量化为+1/-1
, 以0为阈值 - 运用量化后的 Weight (只要+1/-1)来核算前向传达,由二值权重与输入进行卷积运算(实践上只涉及加法),取得卷积层输出。
- 运用决定化办法(
- 反向传达
Backward Pass
:- 把梯度更新到浮点的 Weight 上(依据放松后的符号函数,核算相应梯度值,并依据该梯度的值对单精度的权重进行参数更新)
- 练习完毕: 把 Weight 永久性转化为
+1/-1
, 以便inference
运用
4.1,两个问题
网络二值化需求处理两个问题:怎么对权重进行二值化和怎么核算二值权重的梯度。
1,怎么对权重进行二值化?
权重二值化一般有两种挑选:
- 直接依据权重的正负进行二值化:xb=sign(x)x^{b}=sign(x)。符号函数
sign(x)
界说如下:
- 进行随机的二值化,即对每一个权重,以必定概率取 1\pm 1
2,怎么核算二值权重的梯度?
二值权重的梯度为0,无法进行参数更新。为处理这个问题,需求对符号函数进行放松,即用 Htanh(x)=max(−1,min(1,x))Htanh(x) = max(-1, min(1,x)) 来代替 sinx(x)sinx(x)。当 x 在区间 [-1,1] 时,存在梯度值 1,否则梯度为 0 。
4.3,二值衔接算法改善
之前的二值衔接算法只对权重进行了二值化,可是网络的中心输出值依然是单精度的,所以 Rastegari 等人对此进行了改善,提出用单精度对角阵与二值矩阵之积来近似表明原矩阵的算法,以提高二值网络的分类功能,弥补二值网络在精度上弱势。该算法将原卷积运算分化为如下进程:
IW≈(IB)I \times W\approx (I \times B)\alpha
其间 I∈RcwinhinI\in \mathbb{R}^{c\times w_{in}\times h_{in}} 为该层的输入张量,I∈RcwhI\in \mathbb{R}^{c\times w\times h} 为该层的一个滤波器,B=sign(W)∈{+1,−1}cwhB=sign(W)\in \{+1, -1\}^{c \times w\times h}为该滤波器所对应的二值权重。
这儿,Rastegari 等人以为单靠二值运算,很难达到原单精度卷积元素的成果,所以他们运用了一个单精度放缩因子 ∈R+\alpha \in \mathbb{R}^{+} 来对二值滤波器卷积后的成果进行放缩。而 \alpha 的取值,则可依据优化方针:
min∥W−B∥2min \left \| W -\alpha B \right \|^{2}
得到 =1n∣W∣ℓ1\alpha = \frac{1}{n}\left |W \right |\ell{1}。二值衔接改善的算法练习进程与之前的算法大致相同,不同的地方在于梯度的核算进程还考虑了 \alpha 的影响。由于 \alpha 这个单精度的缩放因子的存在,有用下降了重构差错,并初次在 ImageNet 数据集上取得了与 Alex-Net 相当的精度。如下图所示:
能够看到的是权重二值化神经网络(BWN)
和全精度神经网络的精确度简直相同,可是与异或神经网络(XNOR-Net)比较而言,Top-1 和 Top-5 都有 10+% 的丢失。
比较于权重二值化神经网络,异或神经网络将网络的输入也转化为二进制值,所以,异或神经网络中的乘法加法 (Multiplication and ACcumulation) 运算用按位异或 (bitwise xnor) 和数 1 的个数 (popcount) 来代替。
更多内容,能够看这两篇文章:
- github.com/Ewenwan/MVi…
- 二值神经网络(Binary Neural Network,BNN)
4.4,二值网络规划注意事项
- 不要运用 kernel = (1, 1) 的
Convolution
(包含 resnet 的 bottleneck):二值网络中的 weight 都为 1bit, 假如再是 1×1 巨细, 会极大地下降表达能力 - 增大
Channel
数目 + 增大 activation bit 数 要协同配合:假如一味增大 channel 数, 终究 feature map 由于 bit 数过低, 还是浪费了模型容量。 同理反过来也是。 - 主张运用 4bit 及以下的
activation bit
, 过高带来的精度收益变小, 而会显著提高 inference 核算量
五,常识蒸馏
本文只简略介绍这个领域的开篇之作-Distilling the Knowledge in a Neural Network,这是蒸 “logits”办法,后面还出现了蒸 “features” 的论文。想要更深入理解,中文博客可参阅这篇文章-常识蒸馏是什么?一份入门随笔。
常识蒸馏(knowledge distillation),是迁移学习(transfer learning)的一种,简略来说便是练习一个大模型(teacher)和一个小模型(student),将巨大而复杂的大模型学习到的常识,经过必定技能手段迁移到精简的小模型上,然后使小模型能够取得与大模型附近的功能。
在常识蒸馏的试验中,咱们先练习好一个 teacher
网络,然后将 teacher
的网络的输出成果 qq 作为 student
网络的方针,练习 student
网络,使得 student
网络的成果 pp 挨近 qq ,因而,student
网络的丢失函数为 L=CE(y,p)+CE(q,p)L = CE(y,p)+\alpha CE(q,p)。这儿 CE
是穿插熵(Cross Entropy),yy 是实在标签的 onehot
编码,qq 是 teacher
网络的输出成果,pp 是 student
网络的输出成果。
可是,直接运用 teacher
网络的 softmax 的输出成果 qq,或许不大合适。因而,一个网络练习好之后,关于正确的答案会有一个很高的置信度。例如,在 MNIST 数据中,关于某个 2 的输入,关于 2 的猜测概率会很高,而关于 2 相似的数字,例如 3 和 7 的猜测概率为 10−610^-6 和 10−910^-9。这样的话,teacher
网络学到数据的相似信息(例如数字 2 和 3,7 很相似)很难传达给 student
网络,由于它们的概率值挨近0
。因而,论文提出了 softmax-T
(软标签核算公式),公式如下所示:
qi=zi/T∑jzj/Tq_{i} = \frac{z_{i}/T}{\sum_{j}z_{j}/T}
这儿 qiq_i 是 studentstudent 网络学习的目标(soft targets),ziz_i 是 teacher
模型 softmax
前一层的输出 logit
。假如将 TT 取 1,上述公式变成 softmax,依据 logit 输出各个类别的概率。假如 TT 挨近于 0,则最大的值会越近 1,其它值会挨近 0,近似于 onehot
编码。
所以,能够知道 student
模型终究的丢失函数由两部分组成:
- 第一项是由小模型的猜测成果与大模型的“软标签”所构成的穿插熵(cross entroy);
- 第二项为猜测成果与普通类别标签的穿插熵。
这两个丢失函数的重要程度可经过必定的权重进行调节,在实践运用中,T
的取值会影响终究的成果,一般而言,较大的 T 能够取得较高的准确度,T(蒸馏温度参数) 归于常识蒸馏模型练习超参数的一种。T 是一个可调节的超参数、T 值越大、概率散布越软(论文中的描绘),曲线便越平滑,相当于在迁移学习的进程中添加了扰动,然后使得学生网络在学习学习的时候更有用、泛化能力更强,这其实便是一种按捺过拟合的战略。常识蒸馏的整个进程如下图:
student
模型的实践模型结构和小模型相同,可是丢失函数包含了两部分,分类网络的常识蒸馏 mxnet 代码示例如下::
# -*-coding-*- : utf-8
"""
本程序没有给出详细的模型结构代码,首要给出了常识蒸馏 softmax 丢失核算部分。
"""
import mxnet as mx
def get_symbol(data, class_labels, resnet_layer_num,Temperature,mimic_weight,num_classes=2):
backbone = StudentBackbone(data) # Backbone 为分类网络 backbone 类
flatten = mx.symbol.Flatten(data=conv1, name="flatten")
fc_class_score_s = mx.symbol.FullyConnected(data=flatten, num_hidden=num_classes, name='fc_class_score')
softmax1 = mx.symbol.SoftmaxOutput(data=fc_class_score_s, label=class_labels, name='softmax_hard')
import symbol_resnet # Teacher model
fc_class_score_t = symbol_resnet.get_symbol(net_depth=resnet_layer_num, num_class=num_classes, data=data)
s_input_for_softmax=fc_class_score_s/Temperature
t_input_for_softmax=fc_class_score_t/Temperature
t_soft_labels=mx.symbol.softmax(t_input_for_softmax, name='teacher_soft_labels')
softmax2 = mx.symbol.SoftmaxOutput(data=s_input_for_softmax, label=t_soft_labels, name='softmax_soft',grad_scale=mimic_weight)
group=mx.symbol.Group([softmax1,softmax2])
group.save('group2-symbol.json')
return group
tensorflow
代码示例如下:
# 将类别标签进行one-hot编码
one_hot = tf.one_hot(y, n_classes,1.0,0.0) # n_classes为类别总数, n为类别标签
# one_hot = tf.cast(one_hot_int, tf.float32)
teacher_tau = tf.scalar_mul(1.0/args.tau, teacher) # teacher为teacher模型直接输出张量, tau为温度系数T
student_tau = tf.scalar_mul(1.0/args.tau, student) # 将模型直接输出logits张量student处于温度系数T
objective1 = tf.nn.sigmoid_cross_entropy_with_logits(student_tau, one_hot)
objective2 = tf.scalar_mul(0.5, tf.square(student_tau-teacher_tau))
"""
student模型终究的丢失函数由两部分组成:
第一项是由小模型的猜测成果与大模型的“软标签”所构成的穿插熵(cross entroy);
第二项为猜测成果与普通类别标签的穿插熵。
"""
tf_loss = (args.lamda*tf.reduce_sum(objective1) + (1-args.lamda)*tf.reduce_sum(objective2))/batch_size
tf.scalar_mul
函数为对 tf
张量进行固定倍率 scalar
缩放函数。一般 T 的取值在 1 – 20 之间,这儿我参阅了开源代码,取值为 3。我发现在开源代码中 student
模型的练习,有些是和 teacher
模型一同练习的,有些是 teacher 模型练习好后直接指导 student 模型练习。
六,浅层/轻量网络
浅层网络:经过规划一个更浅(层数较少)结构更紧凑的网络来完结对复杂模型作用的迫临, 可是浅层网络的表达能力很难与深层网络相匹敌。因而,这种规划办法的局限性在于只能运用处理在较为简略问题上。如分类问题中类别数较少的 task
。
轻量网络:运用如 MobilenetV2、ShuffleNetv2
等轻量网络结构作为模型的 backbone
能够大幅削减模型参数数量。
参阅资料
- 神经网络模型紧缩和加快之常识蒸馏
- github.com/chengshengc…
- github.com/dkozlov/awe…
- XNOR-Net
- 解析卷积神经网络-深度学习实践手册
- 常识蒸馏(Knowledge Distillation)简述(一)