参考文章:

Pruning Filters for Efficient Convnets

Compressing deep neural nets

压缩神经网络 实验记录(剪枝 + rebirth + mobilenet)

为了在手机上加速运行深度学习模型,目前实现的方式基本分为两类:一是深度学习框架层面的加速,另一个方向是深度学习模型层面的加速。

深度学习模型的加速又可以分为采用新的卷积算子来加速模型,另一个方向是通过对已有模型进行剪枝操作得到一个参数更少的模型来加速模型。

通过观察深度学习模型,可以发现其中很多kernel的权重很小,均在-1~1之间震荡,对于这些绝对值很小的参数,可以视其对整体模型贡献很小,将其删除,然后将剩余的权重构成新的模型,以达到模型压缩,加速,并保证精准度不变的目的。

基本步骤:

1.实现原始网络,并将其训练到收敛,保存权重

2.观察对每一层的权重,判断其对模型的贡献大小,删除贡献较小的kernel,评判标准可以是std,sum(abs),mean等

3.当删除部分kernel后,会导致输出层的channel数变化,需要删除输出层对应kernel的对应channel

4.构建剪枝后的网络,加载剪枝后的权重,与原模型对比精准度。

5.使用较小的学习率,rebirth剪枝后的模型

6.重复第1步

上图展示了conv的kernel剪枝后导致的输出维度变化

对于conv层后面接续全连接层的情况:

conv层在接续全连接层前,会先reshape为一个维度。假设conv层输出为 (h,w,c),其会reshape为 h*w*c, 假设删除的kernel下标为[ 2,5,7],对应的conv输出通道也会减少 [2,5,7] 。reshape后 会减少 [2,5,7,...,h*w*2,h*w*5,h*w*7]。

全连接层接全连接层的逻辑基本和conv接conv层的逻辑一样。

一个使用mnist的简单示例

# 读取保存的权重和所有训练的var
model_path = './checkpoints/net_2018-12-19-10-05-17.ckpt-99900'
reader = tf.train.NewCheckpointReader(model_path)
all_variables = reader.get_variable_to_shape_map()
{'conv1/biases': [16],
 'conv1/weights': [3, 3, 1, 16],
 'conv2/biases': [32],
 'conv2/weights': [3, 3, 16, 32],
 'conv3/biases': [32],
 'conv3/weights': [3, 3, 32, 32],
 'fc1/biases': [128],
 'fc1/weights': [512, 128],
 'fc2/biases': [256],
 'fc2/weights': [128, 256],
 'global_step': [],
 'logits/biases': [10],
 'logits/weights': [256, 10]}
# 分析 conv1 的权重
conv1_weight = reader.get_tensor("conv1/weights")
# 计算每个kernel权重的和 (也可以使用其他指标,如std,mean等)
conv1_weight_sum = np.sum(conv1_weight, (0,1,2))
sort_conv1_weights = np.sort(conv1_weight_sum)
# 绘制conv1的
x = np.arange(0,len(sort_conv1_weights),step=1)
plt.plot(x,sort_conv1_weights)

# 保留权重和最大的8个kernel
pure_conv1_weight_index = np.where(conv1_weight_sum >= sort_conv1_weights[8])
pure_conv1_weight = conv1_weight[:,:,:,pure_conv1_weight_index[0]]
# conv1对应的bias 也做相同处理
conv1_bias = reader.get_tensor("conv1/biases")
pure_conv1_bias = conv1_bias[pure_conv1_weight_index[0]]
# 对后面接续的 conv 层的kernel做相同处理
conv2_weight = reader.get_tensor("conv2/weights")
conv2_bias = reader.get_tensor("conv2/biases")
conv2_weight = conv2_weight[:,:,pure_conv1_weight_index[0],:]

后面层重复以上操作

剪枝后结果对比

原始模型精度

剪枝后模型精度

模型权重大小从400多kb减小到了100多kb

jupyter文件及代码

Logo

学大模型,用大模型上飞桨星河社区!每天8点V100G算力免费领!免费领取ERNIE 4.0 100w Token >>>

更多推荐