这是tensorflow生成的各种模型文件:
其中关注的主要三种文件格式:
.pb文件,保存的是图模型的计算流程图,包括图中的常量,但不保存变量,可通过以下两个方法获取:
(1):tf.train.write_graph(sess.graph_def,'','graph.pb',as_text=False) #直接保存图模型,但没有图中变量的值
(2):graph = convert_variables_to_constants(sess, sess.graph_def, ["output_image"])
tf.train.write_graph(graph, '.', 'graph.pb', as_text=False)
#这样通过将模型里面的所有变量都变为常量,那么就可以直接使用.pb文件做成接口,无需.ckpt文件再次导入变量的值.
.ckpt文件,保存的是图模型中的变量的值,要使用.ckpt文件的话,要重构图的结构和初始化图中变量.可通过以下方式获取:
saver=tf.train.Saver()
saver.save(sess,"model.ckpt")
.lite文件:里面是包含图模型的计算流程图和图模型中的变量的值,可以直接给android系统或者ios系统的tensorflowLite调用读取.
接下来是生成.lite文件的方法:
首先是,生成lite文件支持的操作和不支持的操作:(如若图中有不支持的操作,将在生成.lite文件时会报错)
https://github.com/tensorflow/tensorflow/blob/master/tensorflow/lite/g3doc/tf_ops_compatibility.md
生成未量化的.lite文件有两种方式(工具生成跟代码生成):
第一种,是通过.pb文件和.ckpt文件,进行图中的变量的固化,再生成.lite文件.执行方法如下
(1).要先安装tensorflow源码和bazel方法,编译tensorflow源码生成tensorflow
(2).cd到源码目录下:
(3).编译生成freeze_graph跟toco工具.最新版本toco替换成tflite_convert工具
bazel build tensorflow/python/tools:freeze_graph
bazel build tensorflow/lite/toco:toco
(4).
bazel-bin/tensorflow/python/tools/freeze_graph \
--input_graph=./model.pb \
--input_checkpoint=./model.ckpt \
--output_graph=./frozen_model.pb \
--input_binary=true \
--output_node_names=result
#--output_node_names 对应的是输出tensor的name
(5).
./bazel-bin/tensorflow/contrib/lite/toco/toco
--input_file=frozen_model.pb \
--output_file=model.tflite \
--input_format=TENSORFLOW_GRAPHDEF \
--output_format=TFLITE \
--inference_type=FlOAT \
--input_shape="1,626,361,3" \
--input_array=input_image \
--output_array=result \
--std_value=127.5 \
--mean_value=127.5 \
--default_ranges_min=-1.0 \
--default_ranges_max=1.0 \
--allow_custom_ops
# --input_arrays 和 --output_arrays 对应的是输入输出tensor的name
# 注意--input_shapes 必须确定,不可以填None
# --allow_custom_ops 是允许一些传统方法
可参考:https://github.com/tensorflow/tensorflow/blob/master/tensorflow/lite/g3doc/convert/cmdline_examples.md 里面包含了混合输入等多种方法及方法的更新.
第二种.是直接在代码中通过代码直接生成.lite文件.
如果途中没有变量
import tensorflow as tf
img = tf.placeholder(name="img", dtype=tf.float32, shape=(1, 64, 64, 3))
val = img + tf.constant([1., 2., 3.]) + tf.constant([1., 4., 4.])
out = tf.identity(val, name="out")
with tf.Session() as sess:
tflite_model = tf.lite.toco_convert(sess.graph_def, [img], [out])
open("converteds_model.tflite", "wb").write(tflite_model)
如果图中有变量的话,需要将变量固化
frozen_graphdef = tf.graph_util.convert_variables_to_constants(sess, sess.graph_def, ['output']) #这里 ['output']是输出tensor的名字
tflite_model = tf.lite.toco_convert(frozen_graphdef, [input], [out]) #这里[input], [out]这里分别是输入tensor或者输出tensor的集合,是变量实体不是名字
open("model.tflite", "wb").write(tflite_model)
生成量化的.lite文件也有两种,分为工具生成跟代码生成
第一种方法:
(1).首先要想要生成量化的lite文件,在训练graph过程中,就要先伪量化计算图
在loss后面增加这段代码:
loss = FLAGS.style_weight * style_loss + FLAGS.content_weight * content_loss + FLAGS.tv_weight * tv_loss
tf.contrib.quantize.create_training_graph(quant_delay=get_quant_delay()) #它会自动将计算图伪量化
(2).在生成pb文件的前面增加一段代码:
tf.contrib.quantize.create_eval_graph() #增加这段代码
eval_graph_file = 'graph.pb'
with open(eval_graph_file, 'w') as f:
f.write(str(g.as_graph_def()))
(3).使用freeze_graph将ckpt跟pb文件冻结成一个图pb文件
bazel build tensorflow/python/tools:freeze_graph && \
bazel-bin/tensorflow/python/tools/freeze_graph \
--input_graph=./graph.pb \
--input_checkpoint=./model.ckpt-200 \
--output_graph=./frozen_eval_graph_test.pb \
--output_node_names=result
(4).使用toco工具量化冻结后的pb文件
./bazel-bin/third_party/tensorflow/lite/toco/toco \
./bazel-bin/tensorflow/contrib/lite/toco/toco
--input_file=frozen_eval_graph_test.pb \
--output_file=tflite_model.tflite \
--input_format=TENSORFLOW_GRAPHDEF
--output_format=TFLITE \
--inference_type=QUANTIZED_UINT8 \
--input_shape="1,626,361,3" \
--input_array=input_image \
--output_array=result \
--std_value=127.5 --mean_value=127.5 --default_ranges_min=-1.0 --default_ranges_max=1.0
(5).使用python脚本测试调用生成的lite文件.
import numpy as np
import tensorflow as tf
import scipy
# Load TFLite model and allocate tensors.
interpreter = tf.contrib.lite.Interpreter(model_path="tflite_model.tflite")
interpreter.allocate_tensors()
# Get input and output tensors.
input_details = interpreter.get_input_details()
output_details = interpreter.get_output_details()
image=scipy.misc.imread("test.jpg")
image_=np.array([image.astype('uint8')])
print(image_.shape)
print(type(image_))
print(input_details)
interpreter.set_tensor(input_details[0]['index'], image_)
interpreter.invoke()
output_data = interpreter.get_tensor(output_details[0]['index'])
scipy.misc.imsave('res.jpg',output_data)
注意事项:
(1).调用toco时:--inference_type参数当前只支持QUANTIZED_UINT8跟FLOAT,当为FLOAT时,生成的lite文件是未量化的,只有在设置为QUANTIZED_UINT8,生成的lite文件才是量化文件,大小约为未量化的1/4.
(2).生成的量化的lite文件,输入的Tensor数据类型Type必须为uint8.不然会出现传入类型错误.未量化的lite文件可传入FLOAT类型的Tensor.
(3).调用toco时.--default_ranges_min= --default_ranges_max=必须传入
(4).当前有些操作不支持量化,当出现不支持量化操作的时候,调用toco工具的时候会出现这个报错,这时候要不去掉这个操作又不找别的操作代替.
F tensorflow/contrib/lite/toco/graph_transformations/quantize.cc:474] Unimplemented: this graph contains an operator of type Cast for which the quantized form is not yet implemented. Sorry, and patches welcome (that's a relatively fun patch to write, mostly providing the actual quantized arithmetic code for this op).
生成量化的.lite文件第二种方式(亲测有用):
可以参考google的一个官方例子:https://github.com/tensorflow/tensorflow/tree/master/tensorflow/examples/speech_commands
主要参考里面的train.py代码跟freeze.py代码,分别用于训练跟生成固化的pb文件.
train.py文件的代码顺序如下:
(1),对输入数据进行量化:fingerprint_input = tf.fake_quant_with_min_max_args(input_placeholder, fingerprint_min, fingerprint_max) 我自己训练自己的图时没使用这一步.
(2).定义loss
(3).创建量化训练图:tf.contrib.quantize.create_training_graph(quant_delay=0)
(4).定义optimizer优化器
(5).saver = tf.train.Saver(tf.global_variables())
(6).变量初始化
(7).check_point载入
(8).保存pbtxt文件:tf.train.write_graph(sess.graph_def, FLAGS.train_dir,FLAGS.model_architecture + '.pbtxt')
(9).循环训练
freeze.py文件的代码顺序如下:
(1).创建graph
(2).创建量化eval图:create_eval_graph()
(3).载入模型的各个变量的参数
(4).保存pb文件:
input_saver_def = saver.as_saver_def()
frozen_graph_def = freeze_graph.freeze_graph_with_def_protos(input_graph_def=tf.get_default_graph().as_graph_def(),input_saver_def=input_saver_def,input_checkpoint = FLAGS.model_file,output_node_names='result',restore_op_name='save/restore_all', filename_tensor_name='save/Const:0',clear_devices=True,output_graph='',initializer_nodes='')
binary_graph = 'tflite_graph.pb'
with tf.gfile.GFile(binary_graph, 'wb') as f:
f.write(frozen_graph_def.SerializeToString())
如果使用以下方法生成pb文件,在android上运行时会报错如下:
#将图中变量转变成常量:
frozen_graph_def = graph_util.convert_variables_to_constants(sess, sess.graph_def, ['labels_softmax'])
#保存pb文件:
tf.train.write_graph(frozen_graph_def,os.path.dirname(FLAGS.output_file),os.path.basename(FLAGS.output_file),as_text=False)
那么会报以下错:
Caused by: java.lang.IllegalArgumentException: ByteBuffer is not a valid flatbuffer model at org.tensorflow.lite.NativeInterpreterWrapper.createModelWithBuffer(Native Method)
这是因为生成的pb文件不是lite专用的flatbuffer格式.
还有,可以通过Netron查看pb文件或者lite文件里面的graph结构,可以看里面是否存在FakeQuantWithMinMaxVars操作来确实是否存在量化操作,Netron网页版如下: https://lutzroeder.github.io/netron/
生成后的pb文件通过如下代码可生成lite文件,分别为两种方法:
(1).通过以下代码可以生成完全量化的lite文件,通过Netron可以看到模型里面的变量,计算过程都是uint8.在移动端运行速度会快一些.
import tensorflow as tf
import pathlib2 as pathlib
# converter = tf.contrib.lite.TocoConverter.from_frozen_graph('model.pb',["input_image"],["result"], input_shapes={"input_image":[1,626,361,3]}) #Python 2.7.6版本,但测试量化后模型大小不会变小
converter = tf.lite.TFLiteConverter.from_frozen_graph('model.pb',["input_image"],["result"], input_shapes={"input_image":[1,626,361,3]}) #python3.4.3--nightly版本,测试量化后模型大小会变小
converter.inference_type = tf.contrib.lite.constants.QUANTIZED_UINT8
converter.quantized_input_stats = {"input_image" : (127, 2.)}
converter.default_ranges_stats=(0, 6)
tflite_quantized_model=converter.convert()
open("quantized_model.tflite", "wb").write(tflite_quantized_model)
注意:
<1>.其中的quantized_input_stats传入的参数为mean均值跟std方差,这两个值可以通过训练数据进行统计获得.
<2>.其中的default_ranges_stats的作用是对于伪量化后模型中不存在min跟max的激化函数设置默认的min跟max.具体的值设置使用自己估算的activation范围activation.
(2).通过以下代码可以生成伪量化的lite文件,通过Netron可以看到模型里面的变量,计算过程都还是float.在移动端运行速度会比较慢.
import tensorflow as tf
import pathlib2 as pathlib
# converter = tf.contrib.lite.TocoConverter.from_frozen_graph('model.pb',["input_image"],["result"], input_shapes={"input_image":[1,626,361,3]}) #Python 2.7.6版本,但测试量化后模型大小不会变小
converter = tf.lite.TFLiteConverter.from_frozen_graph('model.pb',["input_image"],["result"], input_shapes={"input_image":[1,626,361,3]}) #python3.4.3--nightly版本,测试量化后模型大小会变小
converter.post_training_quantize = True
tflite_quantized_model=converter.convert()
open("quantized_model.tflite", "wb").write(tflite_quantized_model)
以上方法经过测试,生成的lite文件是可以正常在android上运行的.
可参考
https://github.com/tensorflow/tensorflow/blob/master/tensorflow/contrib/lite/toco/g3doc/python_api.md
https://blog.csdn.net/computerme/article/details/80699671
https://tensorflow.juejin.im/performance/quantization.html