当前位置:   article > 正文

tensorflow contrib_深度学习算法优化系列五 | 使用TensorFlow-Lite对LeNet进行训练后量化

tensorflow.contrib.slim 推理tflite模型

v2-f304bf73abcb2c9036a52027ac810be6_1440w.jpg?source=172ae18b

前言

深度学习算法优化系列三 | Google CVPR2018 int8量化算法 这篇推文中已经详细介绍了Google提出的Min-Max量化方式,关于原理这一小节就不再赘述了,感兴趣的去看一下那篇推文即可。今天主要是利用tflite来跑一下这个量化算法,量化一个最简单的LeNet-5模型来说明一下量化的有效性。tflite全称为TensorFlow Lite,是一种用于设备端推断的开源深度学习框架。中文官方地址我放附录了,我们理解为这个框架可以把我们用tensorflow训练出来的模型转换到移动端进行部署即可,在这个转换过程中就可以自动调用算法执行模型剪枝,模型量化了。由于我并不熟悉将tflite模型放到Android端进行测试的过程,所以我将tflite模型直接在PC上进行了测试(包括精度,速度,模型大小)。

环境配置

  • tensorflow 1.31.1
  • python3.5

代码实战

导入一些需要用到的头文件。

  1. #coding=utf-8
  2. import re
  3. import time
  4. import numpy as np
  5. import tensorflow as tf
  6. import tensorflow.contrib.slim as slim
  7. from tensorflow.contrib.slim import get_variables_to_restore
  8. import tensorflow.examples.tutorials.mnist.input_data as input_data

设置一些超参数,分别为dropout层的丢弃比率,学习率,批量大小,模型需要保存的路径以及训练的迭代次数。

  1. # 参数设置
  2. KEEP_PROB = 0.5
  3. LEARNING_RATE = 1e-5
  4. BATCH_SIZE = 30
  5. PARAMETER_FILE = "./checkpoint/variable.ckpt-100000"
  6. MAX_ITER = 100000

构建我们的训练网络,这里使用LeNet,想使用其他网络或者自己的网络相应修改即可。注意一下这里使用了tensorflow中的变量重用函数,方便的控制在测试阶段不使用Dropout。关于Lenet可以详细的看一下我之前的推文,地址如下:卷积神经网络学习路线(六)| 经典网络回顾之LeNet 同时在LeNet类中已经定义好了损失函数和优化器,所以接下来我们就可以直接启动训练啦。

  1. # Build LeNet
  2. class Lenet:
  3. def __init__(self, is_train=True):
  4. self.raw_input_image = tf.placeholder(tf.float32, [None, 784], "inputs")
  5. self.input_images = tf.reshape(self.raw_input_image, [-1, 28, 28, 1])
  6. self.raw_input_label = tf.placeholder("float", [None, 10], "labels")
  7. self.input_labels = tf.cast(self.raw_input_label, tf.int32)
  8. self.dropout = KEEP_PROB
  9. self.is_train = is_train
  10. with tf.variable_scope("Lenet") as scope:
  11. self.train_digits = self.build(True)
  12. scope.reuse_variables() #变量重用
  13. self.pred_digits = self.build(False) #测试的时候不用Dropout
  14. self.loss = slim.losses.softmax_cross_entropy(self.train_digits, self.input_labels)
  15. self.lr = LEARNING_RATE
  16. self.train_op = tf.train.AdamOptimizer(self.lr).minimize(self.loss)
  17. self.predictions = tf.arg_max(self.pred_digits, 1, name="predictions")
  18. self.correct_prediction = tf.equal(tf.argmax(self.pred_digits, 1), tf.argmax(self.input_labels, 1))
  19. self.train_accuracy = tf.reduce_mean(tf.cast(self.correct_prediction, "float"))
  20. def build(self, is_trained=True):
  21. with slim.arg_scope([slim.conv2d], padding='VALID',
  22. weights_initializer=tf.truncated_normal_initializer(stddev=0.01),
  23. weights_regularizer=slim.l2_regularizer(0.0005)):
  24. net = slim.conv2d(self.input_images, 6, [5, 5], 1, padding='SAME', scope='conv1')
  25. net = slim.max_pool2d(net, [2, 2], scope='pool2')
  26. net = slim.conv2d(net, 16, [5, 5], 1, scope='conv3')
  27. net = slim.max_pool2d(net, [2, 2], scope='pool4')
  28. net = slim.conv2d(net, 120, [5, 5], 1, scope='conv5')
  29. net = slim.flatten(net, scope='flat6')
  30. net = slim.fully_connected(net, 84, scope='fc7')
  31. net = slim.dropout(net, self.dropout, is_training=is_trained, scope='dropout8')
  32. digits = slim.fully_connected(net, 10, scope='fc9')
  33. return digits

开始训练原始的LeNet模型,代码如下。在builder = tf.saved_model.builder.SavedModelBuilder("pb_model")这一行代码之前的都是常规的进行模型训练的步骤。然后后面保存为saved_model是干什么的呢?因为将tensorflow模型转换为tflite模型有多种方法例如将tensorflow模型的checkpoint模型固化为pb模型然后使用toco工具转换为tflite模型,但这个过程稍显麻烦。所以这里我选择使用savedModel来保存模型,这个模型可以直接转换为tflite,在转换工程中调用相关代码进行量化。训练完成后会在checkpoint文件夹下生成这4个文件。

v2-d56364ae42a9e98442100595bb91a66a_b.jpg

并同时生成pb_model文件夹,即用SavedModel保存的模型,如下所示:

v2-4d57c431e2784e9d18a2e22352f0af64_b.jpg
  1. def train():
  2. mnist = input_data.read_data_sets("MNIST_data/", one_hot=True)
  3. test_images = mnist.test.images
  4. test_labels = mnist.test.labels
  5. sess = tf.Session()
  6. batch_size = BATCH_SIZE
  7. paramter_path = PARAMETER_FILE
  8. max_iter = MAX_ITER
  9. lenet = Lenet()
  10. variables = get_variables_to_restore()
  11. save_vars = [variable for variable in variables if not re.search("Adam", variable.name)]
  12. saver = tf.train.Saver(save_vars)
  13. sess.run(tf.initialize_all_variables())
  14. # 用来显示标量信息
  15. tf.summary.scalar("loss", lenet.loss)
  16. # merge_all 可以将所有summary全部保存到磁盘,以便tensorboard显示。如果没有特殊要求,
  17. # 一般用这一句就可一显示训练时的各种信息了。
  18. summary_op = tf.summary.merge_all()
  19. # 指定一个文件用来保存图
  20. train_summary_writer = tf.summary.FileWriter("logs", sess.graph)
  21. for i in range(max_iter):
  22. batch = mnist.train.next_batch(batch_size)
  23. if i % 100 == 0:
  24. train_accuracy, summary = sess.run([lenet.train_accuracy, summary_op], feed_dict={
  25. lenet.raw_input_image: batch[0],
  26. lenet.raw_input_label: batch[1]
  27. })
  28. train_summary_writer.add_summary(summary)
  29. print("step %d, training accuracy %g" % (i, train_accuracy))
  30. if i % 500 == 0:
  31. test_accuracy = sess.run(lenet.train_accuracy, feed_dict={lenet.raw_input_image: test_images,
  32. lenet.raw_input_label: test_labels})
  33. print("n")
  34. print("step %d, test accuracy %g" % (i, test_accuracy))
  35. print("n")
  36. sess.run(lenet.train_op, feed_dict={lenet.raw_input_image: batch[0],
  37. lenet.raw_input_label: batch[1]})
  38. saver.save(sess, paramter_path)
  39. print("saved model")
  40. # 保存为saved_model
  41. builder = tf.saved_model.builder.SavedModelBuilder("pb_model")
  42. inputs = {"inputs": tf.saved_model.utils.build_tensor_info(lenet.raw_input_image)}
  43. outputs = {"predictions": tf.saved_model.utils.build_tensor_info(lenet.predictions)}
  44. prediction_signature = tf.saved_model.signature_def_utils.build_signature_def(inputs=inputs, outputs=outputs,
  45. method_name=tf.saved_model.signature_constants.PREDICT_METHOD_NAME)
  46. legacy_init_op = tf.group(tf.tables_initializer(), name="legacy_init_op")
  47. builder.add_meta_graph_and_variables(sess, [tf.saved_model.tag_constants.SERVING],
  48. signature_def_map={"serving_default": prediction_signature},
  49. legacy_init_op=legacy_init_op, saver=saver)
  50. builder.save()

将模型转为tflite,调用的tf.lite.TFLiteConverter。并执行量化操作,这样模型大小被压缩到了之前的1/4左右。 代码如下:

  1. # 将Saved_Model转为tflite,调用的tf.lite.TFLiteConverter
  2. def convert_to_tflite():
  3. saved_model_dir = "./pb_model"
  4. converter = tf.lite.TFLiteConverter.from_saved_model(saved_model_dir,
  5. input_arrays=["inputs"],
  6. input_shapes={"inputs": [1, 784]},
  7. output_arrays=["predictions"])
  8. converter.optimizations = ["DEFAULT"]
  9. converter.post_training_quantize = True
  10. tflite_model = converter.convert()
  11. open("tflite_model/eval_graph.tflite", "wb").write(tflite_model)

最后我们再写两个测试的代码,分别对原始模型和量化后模型的推理速度和精度进行一个测试,代码如下:

  1. # 使用原始的checkpoint进行预测
  2. def origin_predict():
  3. mnist = input_data.read_data_sets("MNIST_data/", one_hot=True)
  4. sess = tf.Session()
  5. saver = tf.train.import_meta_graph("./checkpoint/variable.ckpt-100000.meta")
  6. saver.restore(sess, "./checkpoint/variable.ckpt-100000")
  7. input_node = sess.graph.get_tensor_by_name('inputs:0')
  8. pred = sess.graph.get_tensor_by_name('predictions:0')
  9. labels = [label.index(1) for label in mnist.test.labels.tolist()]
  10. predictions = []
  11. start_time = time.time()
  12. for image in mnist.test.images:
  13. prediction = sess.run(pred, feed_dict={input_node: [image]}).tolist()[0]
  14. predictions.append(prediction)
  15. end_time = time.time()
  16. correct = 0
  17. for prediction, label in zip(predictions, labels):
  18. if prediction == label:
  19. correct += 1
  20. print(correct / len(labels))
  21. print((end_time - start_time))
  22. # 使用tflite进行预测
  23. def tflite_predict():
  24. mnist = input_data.read_data_sets("MNIST_data/", one_hot=True)
  25. labels = [label.index(1) for label in mnist.test.labels.tolist()]
  26. images = mnist.test.images
  27. #images = np.array(images, dtype="uint8")
  28. # 根据tflite文件生成解析器
  29. interpreter = tf.contrib.lite.Interpreter(model_path="tflite_model/eval_graph.tflite")
  30. # 用allocate_tensors()分配内存
  31. interpreter.allocate_tensors()
  32. # 获取输入输出tensor
  33. input_details = interpreter.get_input_details()
  34. output_details = interpreter.get_output_details()
  35. predictions = []
  36. start_time = time.time()
  37. for image in images:
  38. # 填充输入tensor
  39. interpreter.set_tensor(input_details[0]['index'], [image])
  40. # 前向推理
  41. interpreter.invoke()
  42. # 获取输出tensor
  43. score = interpreter.get_tensor(output_details[0]['index'])[0][0]
  44. # # 结果去掉无用的维度
  45. # result = np.squeeze(score)
  46. # #print('result:{}'.format(result))
  47. # # 输出结果是长度为10(对应0-9)的一维数据,最大值的下标就是预测的数字
  48. predictions.append(score)
  49. end_time = time.time()
  50. correct = 0
  51. for prediction, label in zip(predictions, labels):
  52. if prediction == label:
  53. correct += 1
  54. print((end_time - start_time))
  55. print(correct / len(labels))

最后测试结果如下表所示:

类型模型大小测试集精度推理测试集10轮的时间原始模型242KB97.39%110.72量化后的模型67KB97.34%35.97

可以看到对LeNet量化后模型的大小变为原始模型的近1/4,并且精度几乎不降,且运行速度也有3-4倍加快。也说明了训练后量化的有效性。今天暂时就讲到这里了,我把源码放到github上了,地址见附录。

附录

  • Tensorflow-Lite官方文档:https://tensorflow.google.cn/lite
  • Tensorflow后量化官方实例:https://github.com/tensorflow/tensorflow/blob/d035a83459330c87bbc527e3d480b65f32841997/tensorflow/contrib/lite/tutorials/post_training_quant.ipynb
  • 我的github地址:https://github.com/BBuf/model_quantization

欢迎关注我的微信公众号GiantPandaCV,期待和你一起交流机器学习,深度学习,图像算法,优化技术,比赛及日常生活等。

http://weixin.qq.com/r/L0zRyWTE2jmlrSCq9xk9 (二维码自动识别)

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/weixin_40725706/article/detail/393206
推荐阅读
相关标签
  

闽ICP备14008679号