Tensorflow Lite之编译生成tflite文件

这是tensorflow生成的各种模型文件:

GraphDef (.pb) - a protobuf that represents the TensorFlow training and or computation graph. This contains operators, tensors, and variables definitions.
CheckPoint (.ckpt) - Serialized variables from a TensorFlow graph. Note, this does not contain the graph structure, so alone it cannot typically be interpreted.
FrozenGraphDef - a subclass of GraphDef that contains no variables. A GraphDef can be converted to a frozen graphdef by taking a checkpoint and a graphdef and converting every variable into a constant with the value looked up in the checkpoint.
SavedModel - A collection of GraphDef and CheckPoint together with a signature that labels input and output arguments to a model. A GraphDef and Checkpoint can be extracted from a saved model.
TensorFlow lite model (.lite) - a serialized flatbuffer, containing TensorFlow lite operators and Tensors for the TensorFlow lite interpreter. This is most analogous to TensorFlow frozen GraphDefs.
其中关注的主要三种文件格式:

.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

 


--------------------- 
作者:程序猿也可以很哲学 
来源:CSDN 
原文:https://blog.csdn.net/qq_16564093/article/details/78996563 
版权声明:本文为博主原创文章,转载请附上博文链接!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值