purple5252

随着我们对深度学习网络认知的加深,在实际应用过程中,我们一定会厌倦深度网络训练时间长,参数规模太大而感到非常痛苦.那么今天我给大家带来的是如何实现网络剪枝.也就是在不降低准确度的情况下减少训练参数的数量,加快训练时间.从而大大节约我们得成本,这在实际应用中至关重要.

 

 

我们将演示如何使用TensorFlow模型优化将Keras模型的大小缩小5倍,这对于在资源受限的环境中进行部署特别重要。其次我们需要清楚剪枝剪去的是什么,剪枝刚开始的时候大家认为减去的是权重, 但是18年有好几篇论文提出剪枝更像是一种模型结构搜索, 跟权重关系不大. 如rethinking the value of network pruning.剪枝是一种结构搜索, 减去的是一个filter,神经元. 剪完后的结构需要删除对应的特征图,还有权重,但本质上网络结构的改变. 思路源头都是来自于Oracle pruning 的方法,即挑选出模型中不重要的参数,将其剔除而不会对模型的效果造成太大的影响。在剔除不重要的参数之后,通过一个retrain的过程来恢复模型的性能。如何找到一个有效的对参数重要性的评价手段,在这个方法中就尤为重要,我们也可以看到,这种评价标准花样百出,各有不同,也很难判定那种方法更好。目前,基于模型裁剪的方法是最为简单有效的模型压缩方式.

权重修剪意味着消除权重张量中不必要的值。将不必要的神经网络参数的值设置为零,以消除我们估计的神经网络各层之间不必要的连接。这是在训练过程中完成的,这样会让神经网络产生自适应变化。下面让我们来介绍它的原理以及实战操作!

我们的实战步骤将分程一下几部分:

 

  1. 像往常一样训练Keras模型以达到可接受的精度。

  2. 准备好修剪Keras图层或模型。

  3. 创建修剪计划并增加多次迭代模型时间。

  4. 通过从模型中删除剪枝包来导出修剪后的模型。

  5. 通过可选的量化操作将Keras模型转换为TensorFlow Lite。

我们在之前的预训练模型已经达到了理想的精度,想要在保持性能的同时减小网络尺寸。修剪API接口可以帮助实现这一目标.在这里我们需要安装tensorflow-model-optimizationtf-nightly软件包

pip uninstall -yq tensorflowpip uninstall -yq tf-nightlypip install -Uq tf-nightly-gpupip install -q tensorflow-model-optimization

然后,我们可以加载先前训练好的模型并将其转换成“可修剪”模式。基于Keras的API可以直接使用单个层或整个模型。先前我们已经对整个模型进行了预训练,因此呢,将修剪应用于整个模型将更加容易。该算法将应用于能够进行权重修剪的所有网络层。对于修剪计划,我们先将稀疏度设置为50%,然后逐步训练模型达到稀疏度90%。X%稀疏度意味着X的权重张量将被修剪掉。

此外,我们给模型一些时间用于每个修剪步骤之后的恢复,因此修剪不会在每个步骤上都发生。我们将修剪的frequency为100。与修剪盆景类似,我们在修减的时候不能一次到位,我们不可能一天内砍掉90%的树枝。

鉴于模型已经达到令人满意的精度,我们可以立即开始修剪。我们在begin_step此处将其设置为0,并且只训练另外四个时期。根据给定训练示例的数量,批处理大小以及训练的总时间,计算出训练迭代次数。


import numpy as npimport tensorflow as tffrom tensorflow_model_optimization.sparsity import keras as sparsity
#Backend agnostic way to save/restore models
#_, keras_file = tempfile.mkstemp(\'.h5\')
#print(\'Saving model to: \', keras_file)
#tf.keras.models.save_model(model, keras_file, include_optimizer=False)
#Load the serialized model
loaded_model = tf.keras.models.load_model(keras_file)
epochs = 4end_step = np.ceil(1.0 * num_train_samples / batch_size).astype(np.int32) * epochsprint(end_step)
new_pruning_params = { \'pruning_schedule\': sparsity.PolynomialDecay(initial_sparsity=0.50, final_sparsity=0.90,                                                               begin_step=0,end_step=end_step, frequency=100)}
new_pruned_model = sparsity.prune_low_magnitude(loaded_model, new_pruning_params)new_pruned_model.summary()new_pruned_model.compile(     loss=tf.keras.losses.categorical_crossentropy,     optimizer=\'adam\',     metrics=[\'accuracy\'])

现在我们开始训练和修剪模型。

#Add a pruning step callback to peg the pruning step to the optimizer\'s
#step. Also add a callback to add pruning summaries to tensorboard
callbacks = [ sparsity.UpdatePruningStep(), sparsity.PruningSummaries(log_dir=logdir, profile_batch=0)]
new_pruned_model.fit(x_train, y_train, batch_size=batch_size, epochs=epochs, verbose=1, callbacks=callbacks, validation_data=(x_test, y_test))
score = new_pruned_model.evaluate(x_test, y_test, verbose=0)print(\'Test loss:\', score[0])print(\'Test accuracy:\', score[1])

修剪后的模型的测试损失和准确性应与原始Keras模型相似。

这些修剪包可以像下面这样轻松地删除,之后参数的总数应与原始模型相同。

final_model = sparsity.strip_pruning(pruned_model)final_model.summary()

现在,可以通过将权重与零进行比较来检查权重删减的百分比。

下图是修剪了90%的卷积,密集度和批处理规范层的权重。

from tensorflow.keras.models import load_model
model = load_model(final_model)import numpy as np
for i, w in enumerate(model.get_weights()): print( "{} -- Total:{}, Zeros: {:.2f}%".format( model.weights[i].name, w.size, np.sum(w == 0) / w.size * 100 ) )

 

现在,仅使用通用文件压缩算法(例如zip),Keras模型将减少5倍。

压缩前修剪的模型的大小:12.52 Mb 压缩后修剪的模型的大小:2.51 Mb

import tempfileimport zipfile
_, new_pruned_keras_file = tempfile.mkstemp(".h5")print("Saving pruned model to: ", new_pruned_keras_file)tf.keras.models.save_model(final_model, new_pruned_keras_file, include_optimizer=False)
#Zip the .h5 model file
_, zip3 = tempfile.mkstemp(".zip")with zipfile.ZipFile(zip3, "w", compression=zipfile.ZIP_DEFLATED) as f: f.write(new_pruned_keras_file)print( "Size of the pruned model before compression: %.2f Mb" % (os.path.getsize(new_pruned_keras_file) / float(2 ** 20)))print( "Size of the pruned model after compression: %.2f Mb" % (os.path.getsize(zip3) / float(2 ** 20))

 

 

Tensorflow Lite是可用于部署到移动设备的示例格式。要转换为Tensorflow Lite图,必须使用TFLiteConverter以下代码:

 

#Create the .tflite filetflite_model_file = "/tmp/sparse_mnist.tflite" converter = tf.lite.TFLiteConverter.from_keras_model_file(pruned_keras_file) tflite_model = converter.convert() with open(tflite_model_file, "wb") as f:     f.write(tflite_model)

 

然后,我们可以使用类似的技术来压缩tflite文件,并将文件大小减小5倍。训练后量化使权重转换为8位精度,这是从keras模型到TFLite的平面缓冲区的模型转换的一部分,从而使模型大小又减小了4倍。只需在调用之前将以下行添加到上一个代码段即可:

 

converter.optimizations = [tf.lite.Optimize.OPTIMIZE_FOR_SIZE]

 

与原始Keras模型的12.52 Mb相比,压缩的8位tensorflow lite模型仅耗费0.60 Mb,同时保持了相当的测试精度。尺寸总共减少了16倍。

我们可以像这样评估转换后的TensorFlow Lite模型的准确性,并在eval_model其中向测试数据集输入。

import numpy as np
interpreter = tf.lite.Interpreter(model_path=str(tflite_model_file))interpreter.allocate_tensors()input_index = interpreter.get_input_details()[0]["index"]output_index = interpreter.get_output_details()[0]["index"]
def eval_model(interpreter, x_test, y_test): total_seen = 0 num_correct = 0
for img, label in zip(x_test, y_test): inp = img.reshape((1, 28, 28, 1)) total_seen += 1 interpreter.set_tensor(input_index, inp) interpreter.invoke() predictions = interpreter.get_tensor(output_index) if np.argmax(predictions) == np.argmax(label): num_correct += 1
if total_seen % 1000 == 0: print("Accuracy after %i images: %f" % (total_seen, float(num_correct) / float(total_seen)))
return float(num_correct) / float(total_seen)
print(eval_model(interpreter, x_test, y_test))

在本教程中,我们展示了如何使用TensorFlow模型优化工具包,权重修剪API 创建稀疏模型。现在可以创建占用磁盘空间少得多的模型。生成的模型也可以更有效地实现以避免复杂计算;将来,TensorFlow Lite也将将提供此类功能。

分类:

技术点:

相关文章: