tensorflow模型量化实例

1,概述

  模型量化应该是现在最容易实现的模型压缩技术,而且也基本上是在移动端部署的模型的毕竟之路。模型量化基本可以分为两种:post training quantizated和quantization aware training。在pyrotch和tensroflow中都提供了相应的实现接口。

  对于量化用现在常见的min-max方式可以用公式概括为:

    $r = S (q - Z)$

  上面式子中q为量化后的值,r为原始浮点值,S为浮点类型的缩放稀疏,Z为和q相同类型的表示r中0点的值。根据:

    $\frac{q - q_{min}}{q_{max} - q_{min}} = \frac{r - r_{min}}{r_{max} - r_{min}}$

  可以推断得到S和Z的值:

    $S = \frac{r_{max} - r_{min}}{q_{max} - q_{min}}$

    $Z = q_{min} - \frac{r_{min}}{S}$

2,实验部分

  基于tensorflow在LeNet上实验了这两种量化方式,代码见GitHub:https://github.com/jiangxinyang227/model_quantization

  post training quantizated

  在tensorflow中实现起来特别简单,训练后的模型可是选择用savedModel保存的模型作为输入进行量化并转换成tflite,我们将这个版本称为v1版本。

import tensorflow as tf

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"]
tflite_model = converter.convert()
open("tflite_model_v3/eval_graph.tflite", "wb").write(tflite_model)

  但在实际过程中这份代码转换后的tflite模型大小并没有缩小到1/4。所以非常奇怪,目前还不确定原因。在这基础上我们引入了一行代码,将这个版本称为v2:

import tensorflow as tf

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"]  # 保存为v1,v2版本时使用
converter.post_training_quantize = True  # 保存为v2版本时使用
tflite_model = converter.convert()
open("tflite_model_v3/eval_graph.tflite", "wb").write(tflite_model)

  这样模型的大小缩小到了1/4。

  之后再单独转为tflite的模型,这个称为v3:

import tensorflow as tf

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"])
tflite_model = converter.convert()
open("tflite_model_v3/eval_graph.tflite", "wb").write(tflite_model)

  很显然,直接转为tflite,模型大小肯定不会压缩的,我们再来看看推断速度,推断代码再GitHub上,具体结果如下:

  

  上面checkpoint是在cpu上直接加载checkpoint进行预测。在这里看到只有v2版本的模型压缩到了原来的1/4,但是推断速度却不如v1和v3版本,且tflite模型的推断速度明显优于checkpoint。我猜原因可能是:

    1,tflite本身的解释器对tflite模型是有加速的。

    2,至于为什么量化后的模型反而效果不好,是因为post training quantized本质上计算时是将int转换成float计算的,因此中间存在量化和反量化的操作占绝了些时间。

  quantization aware training

  在训练中引入量化的操作要复杂很多,首先在训练时在损失计算后面,优化器定义前面要要引入tf.contrib.quantize.create_training_graph()。如下:

self.loss = slim.losses.softmax_cross_entropy(self.train_digits, self.input_labels)

# 获取当前的计算图,用于后续的量化
self.g = tf.get_default_graph()

if self.is_train:
    # 在损失函数之后,优化器定义之前,在这里会自动选择计算图中的一些operation和activation做伪量化
    tf.contrib.quantize.create_training_graph(self.g, 80000)
    self.lr = cfg.LEARNING_RATE
    self.train_op = tf.train.AdamOptimizer(self.lr).minimize(self.loss)

  训练完之后模型会保存为checkpoint文件,该文件中含有伪量化信息。这个里面的变量还是float类型,我们需要将其转换成只含int类型的模型文件,具体做法如下:

  1,保存为freeze pb文件,并使用tf.contrib.quantize.create_eval_graph()来转换成推断模式

with tf.Session() as sess:
    le_net = Lenet(False)
    saver = tf.train.Saver()  # 不可以导入train graph,需要重新创建一个graph,然后将train graph图中的参数来填充该图
    saver.restore(sess, cfg.PARAMETER_FILE)

    frozen_graph_def = graph_util.convert_variables_to_constants(
        sess, sess.graph_def, [‘predictions‘])
    tf.io.write_graph(
        frozen_graph_def,
        "pb_model",
        "freeze_eval_graph.pb",
        as_text=False)

  注意上面的注释,在这里的saver一定不能用类似tf.train.import_meta_graph的方式导入训练时的计算图,而是通过再次调用Lenet类初始一个计算图,然后将训练图中的参数变量赋给该计算图。

  2,转换成tflite文件

import tensorflow as tf

path_to_frozen_graphdef_pb = ‘pb_model/freeze_eval_graph.pb‘
converter = tf.contrib.lite.TFLiteConverter.from_frozen_graph(path_to_frozen_graphdef_pb,
                                                              ["inputs"],
                                                              ["predictions"])

converter.inference_type = tf.contrib.lite.constants.QUANTIZED_UINT8
converter.quantized_input_stats = {"inputs": (0., 1.)}
converter.allow_custom_ops = True
converter.default_ranges_stats = (0, 255)
converter.post_training_quantize = True
tflite_model = converter.convert()
open("tflite_model/eval_graph.tflite", "wb").write(tflite_model)

  注意几点:

  1),["inputs"], ["predictions"]是freeze pb中的输入节点和输出节点

  2),quantized_input_states是定义输入的均值和方差,tensorflow lite的文档中说这个mean和var的计算方式是:mean 是 0 到 255 之间的整数值,映射到浮点数 0.0f。std_dev = 255 /(float_max - float_min)但我发现再这里采用0. 和 1.的效果也是不错的。

  3),default_ranges_states是指量化后的值的范围,其中255就是2^8 - 1。

  3,使用tflite预测

import time
import tensorflow as tf
import numpy as np
import tensorflow.examples.tutorials.mnist.input_data as input_data

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

"""
预测的时候需要将输入归一化到标准正态分布
"""
means = np.mean(images, axis=1).reshape([10000, 1])
std = np.std(images, axis=1, ddof=1).reshape([10000, 1])
images = (images - means) / std
"""
需要将输入的值转换成uint8的类型才可以
"""
images = np.array(images, dtype="uint8")

interpreter = tf.contrib.lite.Interpreter(model_path="tflite_model/eval_graph.tflite")
interpreter.allocate_tensors()
input_details = interpreter.get_input_details()
output_details = interpreter.get_output_details()

start_time = time.time()

predictions = []
for image in images:
    interpreter.set_tensor(input_details[0][‘index‘], [image])
    interpreter.invoke()
    score = interpreter.get_tensor(output_details[0][‘index‘])[0][0]
    predictions.append(score)

correct = 0
for prediction, label in zip(predictions, labels):
    if prediction == label:
        correct += 1
end_time = time.time()
print((end_time - start_time) / len(labels) * 1000)
print(correct / len(labels))

  同样要注意两点:

  1),输入要归一化到标准正态分布,这个我认为是和之前设定的quantized_inputs_states保持一致的。

  2),输入要转换成uint8类型,不然会会报错。

  4,性能对比

  

  模型大小降低到之前的1/4,这个是没什么问题的,性能下降2%,可以接受,推断速度提升了3倍左右。

  我们再和之前post training quantized中对比下:大小和v2一样,性能较v2差2%,推断速度快0.02。个人认为原因可能如下:

  1,首先可能LeNet在mnist数据集上算是大模型,因此post training quantized对性能损失不大,因此和quantization aware training比并没有劣势,反而还有些优势。

  2,quantization aware training的推断速度要快一些(注:这个值不是偶然,我测试过很多次,推断速度基本都稳定在一个值,平均上差0.02),但是快的不明显,而且较v1和v3还有所下降,因为在卷积网络中,计算复杂度主要受卷积的影响,而在这里的卷积并不大,量化后对推断速度的影响并不明显,其次引入量化操作还会损耗一些时间,且v2中还有反量化操作,因此时间消耗更多一点。最后就是可能硬件上并没有特别支持int8的计算。

  总之上面只是测试了整个tensorflow中量化的流程。因为选择的网络比较简单,并没有看到在诸如Inception3,mobileNet上那样明显一点的差距。另外tflite确实能加速。

原文地址:https://www.cnblogs.com/jiangxinyang/p/12056209.html

时间: 2024-08-04 23:43:56

tensorflow模型量化实例的相关文章

TensorFlow安装及实例-(Ubuntu16.04.1 & Anaconda3)

TensorFlow安装及实例-(Ubuntu16.04.1 & Anaconda3) Python-pip 和python-dev Pip是python的默认包管理器,直接用pip安装TensorFlow,安装这两个包 命令:apt-get install python-pip python-dev python-virtualenv 可以virtualenv 创建一个隔离的容器, 来安装 TensorFlow. 这是可选的,这样做能使排查安装问题变得更容易. 安装Anaconda3 命令:b

转 tensorflow模型保存 与 加载

使用tensorflow过程中,训练结束后我们需要用到模型文件.有时候,我们可能也需要用到别人训练好的模型,并在这个基础上再次训练.这时候我们需要掌握如何操作这些模型数据.看完本文,相信你一定会有收获! 1 Tensorflow模型文件 我们在checkpoint_dir目录下保存的文件结构如下: |--checkpoint_dir | |--checkpoint | |--MyModel.meta | |--MyModel.data-00000-of-00001 | |--MyModel.in

模型量化原理及tflite示例

模型量化 什么是量化 模型的weights数据一般是float32的,量化即将他们转换为int8的.当然其实量化有很多种,主流是int8/fp16量化,其他的还有比如 二进制神经网络:在运行时具有二进制权重和激活的神经网络,以及在训练时计算参数的梯度. 三元权重网络:权重约束为+1,0和-1的神经网络 XNOR网络:过滤器和卷积层的输入是二进制的. XNOR 网络主要使用二进制运算来近似卷积. 现在很多框架或者工具比如nvidia的TensorRT,xilinx的DNNDK,TensorFlow

TensorFlow模型保存和提取方法

TensorFlow模型保存和提取方法 原创 2017年06月01日 11:25:25 标签: TensorFlow / 模型保存 / 模型提取 / tf.train.Saver 7004 一.TensorFlow模型保存和提取方法 1. TensorFlow通过tf.train.Saver类实现神经网络模型的保存和提取.tf.train.Saver对象saver的save方法将TensorFlow模型保存到指定路径中,saver.save(sess,"Model/model.ckpt"

使用tensorflow-serving部署tensorflow模型

使用docker部署模型的好处在于,避免了与繁琐的环境配置打交道.不需要安装Python,更不需要安装numpy.tensorflow各种包,直接一个docker就包含了全部.docker的方式是如今部署项目的第一选择. 一.docker用法初探 1.安装 docker安装需要两个命令: sudo apt-get install docker sudo apt-get install docker.io 好的学习资料不必远求 docker --help docker run --help 2.基

移动端目标识别(3)——使用TensorFlow Lite将tensorflow模型部署到移动端(ssd)之Running on mobile with TensorFlow Lite (写的很乱,回头更新一个简洁的版本)

承接移动端目标识别(2) 使用TensorFlow Lite在移动设备上运行         在本节中,我们将向您展示如何使用TensorFlow Lite获得更小的模型,并允许您利用针对移动设备优化的操作. TensorFlow Lite是TensorFlow针对移动和嵌入式设备的轻量级解决方案.它支持端上的机器学习推理,具有低延迟和小二进制模型大小. TensorFlow Lite使用了许多技术,例如允许更小和更快(定点数学)模型的量化内核. 对于本节,您需要从源代码构建TensorFlow

Ejb—开发模型与实例

1.    开发模型 这里用最简洁的一个Ejb项目模型来简介Ejb的开发过程与调用原理.粗略的开发过程如下: ?  首先,新建Ejb项目,并部署到JBoss服务器(作为服务端). ?  然后,新建普通Java项目作为客户端.(客户端的概念是相对的,它可以是任何类型的项目,包括可以是另一个Ejb项目) ?  最后,将Ejb项目中的接口打包为jar包,并添加到客户端,客户端即可以通过InitialContext来查找到Ejb,并调用Ejb的方法. 如下图所示: 2.    实例源码如下 服务端 新建

结构化TensorFlow模型代码

译自http://danijar.com/structuring-your-tensorflow-models/ 使用TensorFlow构建神经网络模型很容易导致较大的代码量,那么如何以可读和可复用的方式构建代码?(没耐心的可直接参考可直接参考源代码https://gist.github.com/danijar/8663d3bbfd586bffecf6a0094cd116f2) 定义计算图 在每一个模型里面定义一个类是一个较好的选择. 那么,如何定义该类的接口呢? 通常,每个模型会连接到一些输

MVC学习之数据库开发模式:模型优先实例介绍

数据库开发模型之模型优先的步骤如下: 1.在数据库中建立一个空的数据库[不需要建立任何表]     如果不事先创建一个新的数据库,如果在连接数据库那步不选着数据库,默认是在Master数据库中生成表的2.添加ADO.NET实体数据模型     选中Models文件夹.添加--新建项--数据--ADO.NET实体数据模型--取名[文中取名为ModelFirst]--控模型--完成3.修改实体容器名称    双击ModelFirst.edmx,在左边的空白页面上单击右键--属性---修改实体容器名称