随着我们对深度学习网络认知的加深,在实际应用过程中,我们一定会厌倦深度网络训练时间长,参数规模太大而感到非常痛苦.那么今天我给大家带来的是如何实现网络剪枝.也就是在不降低准确度的情况下减少训练参数的数量,加快训练时间.从而大大节约我们得成本,这在实际应用中至关重要.
我们将演示如何使用TensorFlow模型优化将Keras模型的大小缩小5倍,这对于在资源受限的环境中进行部署特别重要。其次我们需要清楚剪枝剪去的是什么,剪枝刚开始的时候大家认为减去的是权重, 但是18年有好几篇论文提出剪枝更像是一种模型结构搜索, 跟权重关系不大. 如rethinking the value of network pruning.剪枝是一种结构搜索, 减去的是一个filter,神经元. 剪完后的结构需要删除对应的特征图,还有权重,但本质上网络结构的改变. 思路源头都是来自于Oracle pruning 的方法,即挑选出模型中不重要的参数,将其剔除而不会对模型的效果造成太大的影响。在剔除不重要的参数之后,通过一个retrain的过程来恢复模型的性能。如何找到一个有效的对参数重要性的评价手段,在这个方法中就尤为重要,我们也可以看到,这种评价标准花样百出,各有不同,也很难判定那种方法更好。目前,基于模型裁剪的方法是最为简单有效的模型压缩方式.
权重修剪意味着消除权重张量中不必要的值。将不必要的神经网络参数的值设置为零,以消除我们估计的神经网络各层之间不必要的连接。这是在训练过程中完成的,这样会让神经网络产生自适应变化。下面让我们来介绍它的原理以及实战操作!
我们的实战步骤将分程一下几部分:像往常一样训练Keras模型以达到可接受的精度。
准备好修剪Keras图层或模型。
创建修剪计划并增加多次迭代模型时间。
通过从模型中删除剪枝包来导出修剪后的模型。
通过可选的量化操作将Keras模型转换为TensorFlow Lite。
我们在之前的预训练模型已经达到了理想的精度,想要在保持性能的同时减小网络尺寸。修剪API接口可以帮助实现这一目标.在这里我们需要安装tensorflow-model-optimization和tf-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 modelloaded_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 tensorboardcallbacks = [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_modelmodel = load_model(final_model)import numpy as npfor 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 Mbimport 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 npinterpreter = 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 = 0num_correct = 0for img, label in zip(x_test, y_test):inp = img.reshape((1, 28, 28, 1))total_seen += 1interpreter.set_tensor(input_index, inp)interpreter.invoke()predictions = interpreter.get_tensor(output_index)if np.argmax(predictions) == np.argmax(label):num_correct += 1if 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也将将提供此类功能。