赞
踩
在深度学习算法优化系列三 | Google CVPR2018 int8量化算法 这篇推文中已经详细介绍了Google提出的Min-Max量化方式,关于原理这一小节就不再赘述了,感兴趣的去看一下那篇推文即可。今天主要是利用tflite
来跑一下这个量化算法,量化一个最简单的LeNet-5模型来说明一下量化的有效性。tflite
全称为TensorFlow Lite
,是一种用于设备端推断的开源深度学习框架。中文官方地址我放附录了,我们理解为这个框架可以把我们用tensorflow训练出来的模型转换到移动端进行部署即可,在这个转换过程中就可以自动调用算法执行模型剪枝,模型量化了。由于我并不熟悉将tflite
模型放到Android
端进行测试的过程,所以我将tflite
模型直接在PC上进行了测试(包括精度,速度,模型大小)。
导入一些需要用到的头文件。
- #coding=utf-8
- import re
- import time
- import numpy as np
- import tensorflow as tf
- import tensorflow.contrib.slim as slim
- from tensorflow.contrib.slim import get_variables_to_restore
- import tensorflow.examples.tutorials.mnist.input_data as input_data
设置一些超参数,分别为dropout
层的丢弃比率,学习率,批量大小,模型需要保存的路径以及训练的迭代次数。
- # 参数设置
- KEEP_PROB = 0.5
- LEARNING_RATE = 1e-5
- BATCH_SIZE = 30
- PARAMETER_FILE = "./checkpoint/variable.ckpt-100000"
- MAX_ITER = 100000
构建我们的训练网络,这里使用LeNet,想使用其他网络或者自己的网络相应修改即可。注意一下这里使用了tensorflow
中的变量重用函数,方便的控制在测试阶段不使用Dropout
。关于Lenet
可以详细的看一下我之前的推文,地址如下:卷积神经网络学习路线(六)| 经典网络回顾之LeNet 同时在LeNet类中已经定义好了损失函数和优化器,所以接下来我们就可以直接启动训练啦。
- # Build LeNet
- class Lenet:
- def __init__(self, is_train=True):
- self.raw_input_image = tf.placeholder(tf.float32, [None, 784], "inputs")
- self.input_images = tf.reshape(self.raw_input_image, [-1, 28, 28, 1])
- self.raw_input_label = tf.placeholder("float", [None, 10], "labels")
- self.input_labels = tf.cast(self.raw_input_label, tf.int32)
- self.dropout = KEEP_PROB
- self.is_train = is_train
-
- with tf.variable_scope("Lenet") as scope:
- self.train_digits = self.build(True)
- scope.reuse_variables() #变量重用
- self.pred_digits = self.build(False) #测试的时候不用Dropout
-
- self.loss = slim.losses.softmax_cross_entropy(self.train_digits, self.input_labels)
- self.lr = LEARNING_RATE
- self.train_op = tf.train.AdamOptimizer(self.lr).minimize(self.loss)
-
- self.predictions = tf.arg_max(self.pred_digits, 1, name="predictions")
- self.correct_prediction = tf.equal(tf.argmax(self.pred_digits, 1), tf.argmax(self.input_labels, 1))
- self.train_accuracy = tf.reduce_mean(tf.cast(self.correct_prediction, "float"))
-
- def build(self, is_trained=True):
- with slim.arg_scope([slim.conv2d], padding='VALID',
- weights_initializer=tf.truncated_normal_initializer(stddev=0.01),
- weights_regularizer=slim.l2_regularizer(0.0005)):
- net = slim.conv2d(self.input_images, 6, [5, 5], 1, padding='SAME', scope='conv1')
- net = slim.max_pool2d(net, [2, 2], scope='pool2')
- net = slim.conv2d(net, 16, [5, 5], 1, scope='conv3')
- net = slim.max_pool2d(net, [2, 2], scope='pool4')
- net = slim.conv2d(net, 120, [5, 5], 1, scope='conv5')
- net = slim.flatten(net, scope='flat6')
- net = slim.fully_connected(net, 84, scope='fc7')
- net = slim.dropout(net, self.dropout, is_training=is_trained, scope='dropout8')
- digits = slim.fully_connected(net, 10, scope='fc9')
- return digits

开始训练原始的LeNet模型,代码如下。在builder = tf.saved_model.builder.SavedModelBuilder("pb_model")
这一行代码之前的都是常规的进行模型训练的步骤。然后后面保存为saved_model
是干什么的呢?因为将tensorflow
模型转换为tflite
模型有多种方法例如将tensorflow
模型的checkpoint
模型固化为pb
模型然后使用toco
工具转换为tflite
模型,但这个过程稍显麻烦。所以这里我选择使用savedModel
来保存模型,这个模型可以直接转换为tflite
,在转换工程中调用相关代码进行量化。训练完成后会在checkpoint文件夹下生成这4
个文件。
并同时生成pb_model
文件夹,即用SavedModel
保存的模型,如下所示:
- def train():
- mnist = input_data.read_data_sets("MNIST_data/", one_hot=True)
- test_images = mnist.test.images
- test_labels = mnist.test.labels
- sess = tf.Session()
- batch_size = BATCH_SIZE
- paramter_path = PARAMETER_FILE
- max_iter = MAX_ITER
-
- lenet = Lenet()
- variables = get_variables_to_restore()
- save_vars = [variable for variable in variables if not re.search("Adam", variable.name)]
-
- saver = tf.train.Saver(save_vars)
- sess.run(tf.initialize_all_variables())
- # 用来显示标量信息
- tf.summary.scalar("loss", lenet.loss)
- # merge_all 可以将所有summary全部保存到磁盘,以便tensorboard显示。如果没有特殊要求,
- # 一般用这一句就可一显示训练时的各种信息了。
- summary_op = tf.summary.merge_all()
- # 指定一个文件用来保存图
- train_summary_writer = tf.summary.FileWriter("logs", sess.graph)
-
- for i in range(max_iter):
- batch = mnist.train.next_batch(batch_size)
- if i % 100 == 0:
- train_accuracy, summary = sess.run([lenet.train_accuracy, summary_op], feed_dict={
- lenet.raw_input_image: batch[0],
- lenet.raw_input_label: batch[1]
- })
- train_summary_writer.add_summary(summary)
- print("step %d, training accuracy %g" % (i, train_accuracy))
-
- if i % 500 == 0:
- test_accuracy = sess.run(lenet.train_accuracy, feed_dict={lenet.raw_input_image: test_images,
- lenet.raw_input_label: test_labels})
- print("n")
- print("step %d, test accuracy %g" % (i, test_accuracy))
- print("n")
- sess.run(lenet.train_op, feed_dict={lenet.raw_input_image: batch[0],
- lenet.raw_input_label: batch[1]})
- saver.save(sess, paramter_path)
- print("saved model")
-
- # 保存为saved_model
- builder = tf.saved_model.builder.SavedModelBuilder("pb_model")
- inputs = {"inputs": tf.saved_model.utils.build_tensor_info(lenet.raw_input_image)}
- outputs = {"predictions": tf.saved_model.utils.build_tensor_info(lenet.predictions)}
- prediction_signature = tf.saved_model.signature_def_utils.build_signature_def(inputs=inputs, outputs=outputs,
- method_name=tf.saved_model.signature_constants.PREDICT_METHOD_NAME)
-
- legacy_init_op = tf.group(tf.tables_initializer(), name="legacy_init_op")
- builder.add_meta_graph_and_variables(sess, [tf.saved_model.tag_constants.SERVING],
- signature_def_map={"serving_default": prediction_signature},
- legacy_init_op=legacy_init_op, saver=saver)
- builder.save()

将模型转为tflite
,调用的tf.lite.TFLiteConverter
。并执行量化操作,这样模型大小被压缩到了之前的1/4
左右。 代码如下:
- # 将Saved_Model转为tflite,调用的tf.lite.TFLiteConverter
- def convert_to_tflite():
- saved_model_dir = "./pb_model"
- converter = tf.lite.TFLiteConverter.from_saved_model(saved_model_dir,
- input_arrays=["inputs"],
- input_shapes={"inputs": [1, 784]},
- output_arrays=["predictions"])
- converter.optimizations = ["DEFAULT"]
- converter.post_training_quantize = True
- tflite_model = converter.convert()
- open("tflite_model/eval_graph.tflite", "wb").write(tflite_model)
最后我们再写两个测试的代码,分别对原始模型和量化后模型的推理速度和精度进行一个测试,代码如下:
- # 使用原始的checkpoint进行预测
- def origin_predict():
- mnist = input_data.read_data_sets("MNIST_data/", one_hot=True)
- sess = tf.Session()
- saver = tf.train.import_meta_graph("./checkpoint/variable.ckpt-100000.meta")
- saver.restore(sess, "./checkpoint/variable.ckpt-100000")
-
- input_node = sess.graph.get_tensor_by_name('inputs:0')
- pred = sess.graph.get_tensor_by_name('predictions:0')
- labels = [label.index(1) for label in mnist.test.labels.tolist()]
- predictions = []
- start_time = time.time()
- for image in mnist.test.images:
- prediction = sess.run(pred, feed_dict={input_node: [image]}).tolist()[0]
- predictions.append(prediction)
- end_time = time.time()
- correct = 0
- for prediction, label in zip(predictions, labels):
- if prediction == label:
- correct += 1
- print(correct / len(labels))
- print((end_time - start_time))
-
- # 使用tflite进行预测
- def tflite_predict():
- mnist = input_data.read_data_sets("MNIST_data/", one_hot=True)
- labels = [label.index(1) for label in mnist.test.labels.tolist()]
- images = mnist.test.images
- #images = np.array(images, dtype="uint8")
- # 根据tflite文件生成解析器
- interpreter = tf.contrib.lite.Interpreter(model_path="tflite_model/eval_graph.tflite")
- # 用allocate_tensors()分配内存
- interpreter.allocate_tensors()
- # 获取输入输出tensor
- input_details = interpreter.get_input_details()
- output_details = interpreter.get_output_details()
-
- predictions = []
- start_time = time.time()
- for image in images:
- # 填充输入tensor
- interpreter.set_tensor(input_details[0]['index'], [image])
- # 前向推理
- interpreter.invoke()
- # 获取输出tensor
- score = interpreter.get_tensor(output_details[0]['index'])[0][0]
- # # 结果去掉无用的维度
- # result = np.squeeze(score)
- # #print('result:{}'.format(result))
- # # 输出结果是长度为10(对应0-9)的一维数据,最大值的下标就是预测的数字
- predictions.append(score)
- end_time = time.time()
- correct = 0
- for prediction, label in zip(predictions, labels):
- if prediction == label:
- correct += 1
- print((end_time - start_time))
- print(correct / len(labels))

最后测试结果如下表所示:
类型模型大小测试集精度推理测试集10轮的时间原始模型242KB97.39%110.72量化后的模型67KB97.34%35.97
可以看到对LeNet量化后模型的大小变为原始模型的近1/4
,并且精度几乎不降,且运行速度也有3-4
倍加快。也说明了训练后量化的有效性。今天暂时就讲到这里了,我把源码放到github上了,地址见附录。
欢迎关注我的微信公众号GiantPandaCV,期待和你一起交流机器学习,深度学习,图像算法,优化技术,比赛及日常生活等。
http://weixin.qq.com/r/L0zRyWTE2jmlrSCq9xk9 (二维码自动识别)
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。