发新帖

tensorflowLite的量化使用问题

[复制链接]
3847 32

快来加入 TensorFlowers 大家庭!

您需要 登录 才可以下载或查看,没有帐号?加入社区

x
我想问下,现在很多企业都说使用了tensorflow lite,但他们使用的tensorflow lite是经过量化的吗?因为现在好多操作都不支持量化操作,比如tf.Abs,tf.tf.Square等等.如果是使用未量化的tensorflow lite,那这样速度比起量化后的pb文件在android手机上使用,速度有提高吗?提高多少?        还有一个问题.就是要使用toco工具生成量化的lite文件,是不是要求在训练过程中得先使用tf.contrib.quantize.create_training_graph和tf.contrib.quantize.create_eval_graph()进行伪量化训练?
麻烦各位大佬解答下.有没有大佬量化自己模型真正测试成功过的,回答下疑惑.或者能写下量化具体步骤教程.这还有一个我的相同的贴,可以一并回答tensorflowLite的量化使用问题https://www.tensorflowers.cn/t/7100
(出处: TensorFlow中文开发者社区)


我知道答案 回答被采纳将会获得10 金币 + 50 金币 已有32人回答
已有1人评分金币 理由
段德璋 + 27

查看全部评分 总评分:金币 +27 

本楼点评(0) 收起

精彩评论32

Zongjun  TF豆豆  发表于 2018-10-24 05:38:19 | 显示全部楼层
本帖最后由 Zongjun 于 2019-4-20 02:19 编辑

以下是我的个人理解,如果是在android 上,Tensorflow Lite (tflite) 可以利用hardware accelerator,应该是更快的。量化只是一个float32到uint8的过程,本质上是weights\bias大小的变化(是原来的25%),有的microcontroller太小,不量化根本就放不进去,并且mircocontroller大部分是8bit计算,float32非常昂贵,所以需要量化。
用toco生成的量化有两个途径:1.你提到的伪量化,这个确实在training时要调用你说的这两句。具体展开:要生成两个graph,一个用于training,在compute gradients前使用create_training_graph,因为forward和backward都需要模拟量化。这个过程其实是找到需要量化的变量,插入fake quantization nodes 来记录 min-max range information。再具体一点:见(*)。另一个graph用于eval,要在import_graph_def后,saver.restore前插入create_eval_graph。后面如何freeze如何调用toco,按照这个链接上说的即可:https://github.com/tensorflow/te ... ow/contrib/quantize
2. post-training quantization。顾名思义,无需伪量化,是toco拿过来一个pb file,直接把toco的post-training quantization flag设成true完成的。但是注意,这个方法只quantize weights,而1.是一个fully quantized model. 并且2.在inference时,会把uint8的weights再转换回float32来做矩阵乘法。所以,2.这个方法其实依然相当于没做quantization。它的存在应当只是便于用户转移model,计算时依然是个非量化model。链接:https://www.tensorflow.org/performance/post_training_quantization

(*):再往前几个版本,tensorflow其实有自己的量化,就叫做quantize。他不是用伪量化,也无需tflite介入。这个文件在现在的tensorflow github中已经不存在了(半个月前移除的,pete warden决心只搞tflite了)。但是他很好的解释了tensorlow 和 tflite量化的数学原理。如果不加入上述的fake quantize node,tensorflow quantize则需要requantize和dequantize的过程。其中requantize是必须的,因为8bit的两个整数相乘为16bit的数。int8的两个矩阵相乘会得出一个32bit的结果,即一个2d convolution layer的输出结果是int32的。但是下一个quantized op需要一个8bit的输入,这就需要记录float32的min 和max,从而对这个int32的中间结果进行requantization,重新获得8bit的数据继续往下传。当使用了fake quantization后,上述过程在训练过程中被模拟了。于是你会看到训练完了的图,不再存在requantization node和dequantization node。有的只是那些fake quantization nodes。值得注意的是,不加伪量化的量化,不是fully quantized model,因为其涉及到了利用float32 min和max转换。而伪量化不存在该问题。简言之,目前唯一可行的fully quantization method就是在tensorflow 下伪量化训练,再到tflite上转化。
(另外,之前还有一个做移动端的方法,即tensorflow mobile。但是它会在 early 2019 deprecate (应该是被tensorflow lite取代),如图:)
tf mobile.JPG
本楼点评(14) 收起
  • OnceJune您好,请教一个问题,我在使用create_training_graph的时候,出现了Cannot use '%s' as input to '%s' because '%s' is in a while loop.这样的错。想请教一下在加fake quant node的时候,对于op的操作上面会和不加有什么区别吗?同样的结构直接训练是可以一路训通的。
    2019-1-29 18:51 回复
  • Zongjun回复 OnceJune :如果用的是底层tensorflow的api, ops比如conv2d这些layer,它们的写法和以前没有不同。只是在定义loss之后和定义optimizer之前加入create_training_graph()即可。这个函数会自动识别一些常用的op,然后训练时在这些op之前插入fake quant node。这个还和你Model的具体结构有关,比如rcnn有的op就不会被自动识别,需要手动插入fake quant nodes。
    你的这个报错我没有遇见过,估计是model结构不同吧。
    2019-1-30 02:02 回复
  • OnceJune回复 Zongjun :您好,感谢回答。我实际上是想对tacotron2的模型做量化,但是while op好像不被支持。手动插入fake quant nodes这块是在调用create_training_graph之前来操作吗?还是完全重新实现一套插入fake quant nodes的逻辑?或者说我可不可以通过某种方式在CheckInputFromValidContext这一步跳过不被支持的op呢?
    2019-2-1 18:05 回复
  • Zongjun回复 OnceJune :果然是模型的问题。tacotron2是不是还有LSTM的部分啊?这一部分官方的说法是自动插入fake quant nodes还没做到这一步。所以你除了create_training_graph之外(这个部分保留),还要在你定义graph的时候手动插入fake quant nodes。
    还有一种方法,和quantization aware training完全不同了。那就是用Tensorflow自带的 Graph Transform Tool. 链接: https://github.com/tensorflow/tensorflow/tree/master/tensorflow/tools/graph_transforms 重点看一下"Shrinking File Size"这里。还有这个链接:https://github.com/tensorflow/tensorflow/issues/7949,这个是有人问怎么量化LSTM(LSTM是不能自动识别添加fake quant node的),你看一下pete warden的官方回答。
    量化这方面,我的模型一直是自动插入fake quant node的,所以我也只能给这些建议了。希望能帮到你一丢丢哈。Good Luck!
    2019-2-2 06:22 回复
  • OnceJune回复 Zongjun :手动插入fake quant nodes的话是要完全抛弃create_training_graph()是吗?然后eval的时候再把手动插入的也手动删除?我看到有FakeQuantWithMinMaxArgs这个方法,具体操作是通过它来逐层修改还是要到更底层的地方啊?另外GraphTransform这个我也尝试过,模型的size确实和预期一样减小,但是本地运行的速度在avx2指令集下反而是慢了的,我理解这里也是一个fake quant吧,所以可能norm/denorm的运算开销冲抵了8bit带来的提升。
    2019-2-2 16:45 回复
  • Zongjun回复 OnceJune :手动插入我没用过,但是我觉得首先可以尝试保留create_training_graph()和create_eval_graph()。在它们无法检测到的层的地方手动加入fake quant node。看看会不会在create_eval_graph()的时候把所有的fake quant node都自动处理,不行的话再尝试手动删除。
    graph transform应该不是fake quant。所谓fake quant其本质是quantization-aware training,是train的过程中模拟量化。graph transform是你拿着一个训练好的模型给它,它再进行处理。读一下graph transform的Eight-bit Calculations部分,它上面也说了:on many platforms the quantized code may actually be slower than the float equivalents, but this is a way of increasing performance substantially when all the circumstances are right. 下面一段简单讲了一下怎么手动插入fake quant node。这一部分其实就是在说,如果想真正做到fully quantization,那就需要fake quant node,训练时就train好min 和 max,然后用它说的那个指令量化。但是这个看上去又和fake quantization之后在用tensorflow lite fully quanitze并没有什么区别。具体实现要看底层代码了。
    综上,只要你想fully quantize一个模型,那就必须手动插入fake quant node去训练。我建议你先放一放graph transform,手动插入训练成功以后直接用tensorflow lite转化试试。
    2019-2-5 02:09 回复
  • OnceJune回复 Zongjun :看了一下,确实是好简略一段啊……A full guide to optimizing for quantization is beyond the scope of this guide, but one thing that can help is using the FakeQuantWithMinMaxVars op after Conv2D or similar operations during training. This trains the min/max variables that control the range used for quantization, so that the range doesn't have to be calculated dynamically by RequantizationRange during inference.我改改建图的脚本试试吧。多谢回答~~
    2019-2-11 11:26 回复
  • OnceJune回复 Zongjun :今天上班又看了一下,发现有一个experimental_create_training_graph的等价方法,可以定义scope,应该可以作为一个解法。把支持的ops放在一个scope里面,不支持的比如lstm放在另一个里面,然后只transform支持的。
    2019-2-11 15:38 回复
  • Zongjun回复 OnceJune :嗯,你说的这个我也可以尝试一下哈!
    2019-2-12 03:33 回复
  • OnceJune回复 Zongjun :另外看到您下面的回复中提到了quant的论文,可以发一下链接吗?我的理解比较粗,想再看一下细节。另外tacotron我做了performance profiling,cost的大户还是在lstm上面……最后可能还得手动去做quant。
    2019-2-13 14:33 回复
Zongjun  TF豆豆  发表于 2018-11-6 08:49:44 | 显示全部楼层
对了,我补充一点,我debug的时候花了不少时间的一个地方:我用的是在linux下,安装tf.nightly,然后jupyter notebook里调用tf.lite.TFLiteConverter这个东西。因为是quantization,所以要求给出quantized_input_stats,这是一个tuple (med, std)平均值和标准差是要自己手动调的。我的是图片为输入,一开始给(128,128),结果很惨。我调来调去,最后是(128,73)给出的accuracy达到了fake quantization的效果。
本楼点评(2) 收起
  • 杨7277请教一下大神,quantized_input_stats中的mean/stddev的值的设定,有什么规律吗?如何正确 快速的接近合理的值?
    2019-4-15 16:50 回复
  • Zongjun回复 杨7277 :看这个链接:https://stackoverflow.com/questions/54261772/tensorflow-lite-toco-mean-values-std-values
    这个公式是谷歌官方的回答。
    2019-4-18 01:50 回复
九幽  TF荚荚  发表于 2018-11-14 11:32:14 来自手机  | 显示全部楼层
Zongjun 发表于 2018-11-6 08:49
对了,我补充一点,我debug的时候花了不少时间的一个地方:我用的是在linux下,安装tf.nightly,然后jupyter  ...

您好,想请教您一个问题,我现在参照着官方的mobilenet训练例子在mnist里面加入了伪量化训练代码,但只加了Create_training_graph那部分,然后跑出了模型,但是在用工具转化tflite的时候,报错说不知道FakeQuantWithMinMaxVars的数据类型,这个是不是我训练的时候代码写错了?
我看了官方的两篇论文,还有各种社区的各种问题,但还是不确定步骤对不对,您是怎么做的怎么找的资料啊?还有什么我可以找的资料吗?或者您能把你的例子给我看一下吗?非常感谢~
本楼点评(0) 收起
Zongjun  TF豆豆  发表于 2018-11-15 06:19:41 | 显示全部楼层
本帖最后由 Zongjun 于 2018-11-15 06:42 编辑
九幽 发表于 2018-11-14 11:32
您好,想请教您一个问题,我现在参照着官方的mobilenet训练例子在mnist里面加入了伪量化训练代码,但只加 ...

首先,我记得官方的mobilenet是用slim写的吧?slim我没用过,但是fake quantization本质应该是一样的。
我下面写的是用纯tensorflow(非slim)做fake quantization:

1. 你的一个明显错误是,没有使用create_eval_graph。你要创建两个graph,一个只用来training,在这个graph上使用create_training_graph(). 另一个graph,只用来inference,在这个graph上使用create_eval_graph(). 最后,Freeze这个用来inference的graph,再把它传给toco。

2. 使用toco需要linux machine,linux比较stable。你要在这个linux machine上安装 tf.nightly 版本的tensorflow,再使用toco。(你的报错也有可能来自这一步。但是第1步是必须做的,不管用的什么版本的tensorflow。)

3. 我自己的例子不能贴出来,是公司的project。但是,谷歌有一个现成的例子,代码非常基础并且清晰。例子链接。 你打开这个目录以后,先看train.py,第157行(create_training_graph)。再去看freeze.py,第133行(create_eval_graph)。这个相当于,在freeze的文件里包含了create_eval_graph。你可以发现,create_eval_graph()上面是调用create_inference_graph()这个函数,显然,create_inference_graph这个函数重新写了一个graph,而不是用你create_training_graph的那个graph。run完freeze.py以后,生成的就是.pb文件。可以直接把这个.pb文件给toco转化了。(注意区分create_eval_graph()和create_inference_graph()。前者是现成的可以直接调用,后者是你自己写的一个普通function。)


4. 还有一个注意事项就是顺序,这个我在最开始的post里提到过了。create_training_graph,要出现在cross_entropy之后,tf.train.GradientDescentOptimizer之前。而create_eval_graph则是出现在创建完inference graph之后,load checkpoint之前。load checkpoint之后是freeze graph,freeze graph 用的是graph_util.convert_variables_to_constants()。

我就是从这个例子学会怎么做quantization-aware training的。至于谷歌官方的documentation,我实在是不敢恭维。(当然paper我也读了,更多的是从中学到背后的数学原理。)

本楼点评(15) 收起
  • 九幽嗯嗯嗯,读论文只是知道个原理,真的不知道具体怎么做的,,,我还刚开始学用tf,好多程序源码可能看了但是并不能自己总结出用法规律啥的,身边还没有人问,就在这个问题上卡了好久,,,
    经过这么多天的查找看代码啥的,我就是隐隐有些思路,知道两个create都要加,但就是不知道怎么加,果然看你说了之后,知道了这里面还是有不少坑的,是我这种小白不能看出来的,,,
    非常感谢您的回答,帮了大忙了,思路瞬间感觉清晰了,我之前也是看了您上面的回答才去找的相关资料,我之前只找到了mobilenet那个,确实里面看到了slim,我也觉得应该不是一种方法,不过也算是参考之后完成了一半的内容。
    您的回答非常清晰,十分感谢!~碰到您这样做过量化又很会讲解描述的人,我这个小白实在幸运,多谢~
    那我就不改那个错误了,先改正代码,照着您说的实现一下,希望可以完成,,再次感谢!
    2018-11-15 12:26 回复
  • 九幽看到了那个例子,演讲的那个,我之前也看到了,也照着写了,但是就是因为着急和迷惑吧,就只看了有create_training_graph的部分,其他的没分析出来,,,我继续看去,,,
    2018-11-15 12:29 回复
  • Zongjun不用客气哈。对,那个例子的create_eval_graph藏的比较深,在freeze里面,相当于两件事儿在一个文件里做。Good luck!
    2018-11-16 01:14 回复
  • 九幽回复 Zongjun :我仔细看了一下train.py和freeze.py,这两个文件一个是训练,一个是推理(验证:test)?就是在train.py里用create_training_graph()生成了训练图,然后freeze用create_inference_graph()重写了一个推理图,又用create_eval_graph()调用,生成最终的推理图?create_inference_graph()这个只是给了一个图的定义是吗?我看它做了一个output 给后面的inference,为什么要加create_inference_graph()呢?可以就直接在保存ckpt之前加上freeze.py里面create_eval_graph()那一部分吗?
    我tf语法比较差,刚接触没多久,,,只大概看懂了整体结构,但内部的关系还没太清楚,,,我做的项目是keras写的物体检测和分类训练,网络和训练是分开的,但训练里面是训练和验证一起的,结构上感觉跟speech这个例子差不多,但是speech是把训练和验证分开了是吧?
    就是训练生成.pb和.ckpt,然后验证推理的部分和frozen一起放到了freeze里是吗?我也可以把两部分放在一起是吧?就像我上面想的,不知道create_inference_graph()这个的含义,一直也不太清楚eval这部分该怎么和我之前写的训练的代码结合起来,,,
    然后我试了个简单的例子,就是mnist,它就两部分,一个是建立网络,然后就是训练,我把create_training_graph放到了建立网络里:
    ........
    finaloutput = tf.nn.softmax(tf.nn.dropout(dense_layer2, keep_prob), name="softmax")
        g = tf.get_default_graph()
        with tf.name_scope('cross_entropy'):
            cross_entropy_mean = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits_v2(labels=y, logits=finaloutput))
        tf.contrib.quantize.create_training_graph(input_graph=g,
                                                  quant_delay=0)
        optimize = tf.train.GradientDescentOptimizer(1e-5).minimize(cross_entropy_mean)
        prediction_labels = tf.argmax(finaloutput, axis=1, name="output")
        correct_prediction = tf.equal(tf.argmax(finaloutput, 1), tf.argmax(y, 1))
        accuracy = tf.reduce_mean(tf.cast(correct_prediction, "float"))
    然后看你说的eval的放置位置注意,就是把eval放在第二部分的训练那里吗?因为模型保存部分在训练那里。
    还有个问题,这个eval就是生成一个推理图吗?推理图和训练图是一样的吧?只是参数不一样,结构一样?这个的作用具体是什么呢?量化推理模型?
    2018-11-19 17:43 回复
  • Zongjun回复 九幽 :freeze.py没有在做实际的testing,它只是生成了用于testing的那个graph(create_inference_graph()),它是一个图的定义。这个图将来可以用来做推理。可以理解为,训练图训练出来的参数保存在checkpoint file里,freeze.py把这些checkpoint file load了进来,传给了用来inference的这个图。这里必须create_inference_graph(),直接在训练图上调用create_eval_graph()是不对的。
    对,你也完全可以把创建推理图和freeze放在一个文件中。eval并不是生成推理图,生成推理图是create_inference_graph(),eval只是把这张推理图转换成了tflite能够识别并量化的格式。eval要出现在Load checkpoint的地方而不是save checkpoint的地方。
    2018-11-20 05:30 回复
  • Zongjun回复 九幽 :所以总结一下就是这样的:
    1.创建训练图,其中加入create_training_graph(),训练。把训练好的数据存在checkpoint file中。
    2. 创建推理图,其中加入create_eval_graph()。把训练好的数据导入推理图中,即load checkpoint。
    3. Freeze 生成.pb file.(演讲的例子把2,3合成了一个文件中做。)
    4. 在linux machine上把这个.pb file转化成.tflite file。
    5. 可以用tflite python api的 interpreter读入这个.tflite模型然后做testing.
    2018-11-20 05:40 回复
  • 九幽回复 Zongjun :恩恩,这样清晰不少了,感谢~我继续写试试~
    这些你都是通过读例子程序看出来的吗?还是程序和论文一起分析出来的,还是找到了很多相关资料呢?我看了好多还是不确定,分析不出来......
    2018-11-20 10:09 回复
  • 九幽回复 Zongjun :还有一个问题,我刚把speech的例子跑出来了,然后用toco工具转化了tflite,freeze运行出来的.pb文件是3.7M,然后toco转化出的tflite大小也是3.7M,这样对吗?还是说生成量化的tflite,是会变成pb文件的1/4?
    我用的指令,bazel的,还是说要按你的方法,下载tf.nightly写程序转化?
    我用你说的Netron工具看了转化前后的两个图,结构一样,只是转化之后出现了fused层,我觉得图应该是对的
    2018-11-20 17:52 回复
  • Zongjun回复 九幽 :对,这些操作过程完全是读这个例子的程序看出来的,我从论文获得的只是它背后的数学原理。应该是1/4,我猜你训练的时候是 python train.py。你应该是python train.py --quantize=True. 你得告诉这个网络你要求它做fake quantization啊,不然它默认是不进行伪量化的。
    2018-11-21 01:17 回复
  • Zongjun回复 九幽 :bazel完全可以用。不用非要我说的那样,我说的是针对用python的玩家们。但是即便用bazel,我也推荐用tf.nightly版本。Netron打开发现结构一样就说明你没用伪量化。fake quantization以后,你会看到.pb文件中会出现FakeQuantWithMinMaxVars node, 而.tflite中是没有的。在Netron中展开每一个node看细节,信息量很大的。我自己写过一个tensorflow的kernel,然后自己手动量化(用Paper中的公式),然后拿过来netron中显示的量化min,max,用我的function输出的结果和netron中显示的量化结果一致。证明量化成功。
    2018-11-21 01:23 回复
快到碗里来  TF豆豆  发表于 2018-11-16 18:18:56 | 显示全部楼层
Zongjun 发表于 2018-11-15 06:19
首先,我记得官方的mobilenet是用slim写的吧?slim我没用过,但是fake quantization本质应该是一样的。
我 ...

你好,我想请教下,我使用toco工具将一个冻结了的pb文件,大概4.3m,进行转换成lite文件./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=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

过程中,我发现,只要我模型里面存在下面这段代码relu = tf.where(tf.less(input, 0),  tf.zeros_like(input), input)或者nan_to_zero = tf.where(tf.equal(relu, relu), relu, tf.zeros_like(relu))   转换出来后的lite文件将会变成100多m,如果去掉的话出来的lite文件就正常4.3m左右 ,我问了下别人,有人说是现在tensorflow-lite的bug,里面有些操作影响到,我想问下你有没有出现过这个问题,会不会是你上面提到的tensorflow版本必须要tf.nightly版本才行,我的版本是从github上下载源码编译的2.11版本.还有一个问题就是,你安装tf.nightly版本的tensorflow是用pip install tf-nightly直接安装的吗?谢谢

本楼点评(0) 收起
Zongjun  TF豆豆  发表于 2018-11-17 01:41:34 | 显示全部楼层
快到碗里来 发表于 2018-11-16 18:18
你好,我想请教下,我使用toco工具将一个冻结了的pb文件,大概4.3m,进行转换成lite文件./bazel-bin/tensorfl ...

我是按照官方教程安装的tf.nightly,基本和你说的一样,建个环境然后pip安装。我是一个Ubuntu-16.04的虚拟机。toco我不是用的bazel,我用的是python api. 在这个tf.nightly的机器上,打开一个jupyter notebook,import tensorflow as tf之后,运行 tf.lite.TFLiteConverter.from_frozen_graph. 如果这句不报错那就是成功了。这句在python里就相当于call了toco。

我读了一下你的toco,你如果是quantization-aware training了,那你的inference type那里应该是QUANTIZED_UINT8而不是FLOAT.

relu的那个我木有碰到过,所以可能帮不上忙了。我推荐你下载Netron.可以直接打开你的.pb和.tflite文件,先看一下.pb文件是不是正常,有可能训练或者freeze的时候就出现问题了。Good luck!
本楼点评(22) 收起
  • 快到碗里来大神,你好,我测试了你说的Netron跟使用tf.lite.TFLiteConverter.from_frozen_graph去生成lite,现在基本成功,只要去除一些操作就可以生成量化的lite文件.现在还有个问题想请教下,如果模型里面存在tf.Square操作,lite文件生成就会报错,我查了下文档,里面有这么一句话不太理解,在https://www.tensorflow.org/lite/tf_ops_compatibility   里面的"Here is a list of TensorFlow operations that are usually removed from the graph",什么叫做从通常从图中移除这些操作
    2018-11-26 16:48 回复
  • Zongjun回复 快到碗里来 :这个列表里面的操作应该是toco自动去删除,替代,融合的。比如tf.relu,它可以fuse进上一层的conv2d layer中,成为上一层的输出。tf.Square应该也是一样的,toco会去处理掉,这样tflite的model中就看不到这个操作了。你报的错是啥啊?toco没有自动处理tf.Square吗?
    2018-11-27 02:02 回复
  • 快到碗里来回复 Zongjun :你好,我上面的问题基本解决了,现在我生成了一个lite文件,用python脚本测试,测试代码如下:
    interpreter = tf.contrib.lite.Interpreter(model_path="quantized_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("timg(11).jpg")
    image_=np.array([image.astype('float32')])
    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)
    是能顺利运行并输出东西来的,但我交给我朋友做android的在android上运行lite文件,他是替换官方的demo
      
    https://github.com/tensorflow/tensorflow/tree/master/tensorflow/lite/java/demo的文件去测试,报了以下错误:
    Caused by: java.lang.IllegalArgumentException: ByteBuffer is not a valid flatbuffer model
                                                                                              at org.tensorflow.lite.NativeInterpreterWrapper.createModelWithBuffer(Native Method)
                                                                                              at org.tensorflow.lite.NativeInterpreterWrapper.<init>(NativeInterpreterWrapper.java:72)
                                                                                              at org.tensorflow.lite.NativeInterpreterWrapper.<init>(NativeInterpreterWrapper.java:52)
                                                                                              at org.tensorflow.lite.Interpreter.<init>(Interpreter.java:114)
                                                                                              at com.example.android.tflitecamerademo.ImageClassifier.<init>(ImageClassifier.java:98)
                                                                                              at com.example.android.tflitecamerademo.ImageClassifierQuantizedMobileNet.<init>(ImageClassifierQuantizedMobileNet.java:38)
                                                                                              at com.example.android.tflitecamerademo.Camera2BasicFragment.onActivityCreated(Camera2BasicFragment.java:334)
                                                                                              at android.app.Fragment.performActivityCreated(Fragment.java:2289)
    我想问下,你有没有遇到这个问题或者知道怎么解决吗?有什么好的建议或者能不能分享下你调用lite文件的android代码.谢谢大神
    2018-12-1 11:52 回复
  • Zongjun回复 快到碗里来 :我训练完的model是直接往单片机上放的,所以我没在android上用过。我看了一下你给的链接里的代码,这个报错是说bytebuffer不是一个flatbuffer model(flatbuffer model是tflite的格式)。但是,我发现bytebuffer在代码中是输入图片的一个buffer,里面存的是image data,并不是model本身。所以你同事android的java代码可能出了问题。
    2018-12-4 02:08 回复
  • 快到碗里来回复 Zongjun :好的,谢谢大神,我看下怎么处理.
    2018-12-4 18:02 回复
  • Zongjun回复 快到碗里来 :嗯嗯。不用客气!大神不敢当,只是碰巧平时工作会一些这方面的东西,尽可能帮忙而已。祝你们好运哈!
    2018-12-5 02:27 回复
  • 快到碗里来回复 Zongjun :哈哈,我觉得你有空可以写篇博客讲下tensorflow-lite,现在tensorflow-lite的博客太少了,而且都不是很详细,很多都是抄来抄去的.这段时间我都会找时间研究下tensorflow-lite,现在生成的lite文件还没能部署在android跟ios上,遇到问题到时还要请教你
    2018-12-6 15:12 回复
  • Zongjun回复 快到碗里来 :嗯嗯,模型都有了,部署在android上应该就不难了,毕竟是java哈。
    2018-12-7 02:32 回复
  • 快到碗里来回复 Zongjun :你好,这几天我终于把lite文件部署到android上,但经过测试,量化后的pb文件比量化后的lite文件速度更快,这是什么原因你知道吗?还有,就是现在官方不是说,现在tensorflow-lite只支持mobilenet及少量模型,但我们可以将我们的模型转换成lite文件,所以,我好奇的这个官方说的支持是不是指对mobilenent等这些模型进行优化,加速计算,而不是指单纯可以使用.
    2018-12-13 20:38 回复
  • Zongjun回复 快到碗里来 :具体哪些能转化成tensorflow lite不是看模型,而是看你的模型里面用的哪些op。如果有tflite不能转化的op,这个模型就不能被转化。能被tflite转化的op如下:https://www.tensorflow.org/lite/tf_ops_compatibility
    量化后的pb文件指的是有fakequant nodes但还没被toco转化的.pb文件吗?这个阶段它还是floating point的model,所以称不上是被量化了。而.tflite文件才是真正被量化了的model。你的这个速度问题,我在网上见过类似的。所以在我最一开始的回答中就避开了速度的事儿,只是分析了大小变化。因为理论上应该是量化了更快,但是涉及到android的内部实现问题,这方面我不懂,所以我不确定哪里出了问题。我能想到的是:tensorflow lite要用最新版本,就是我之前说的用tf.nightly版本的tensorflow。某些kernel(比如conv2d),新版本的tensorflow lite更高效。
    2018-12-14 05:41 回复
Zongjun  TF豆豆  发表于 2018-11-26 05:48:49 | 显示全部楼层
九幽 发表于 2018-11-14 11:32
您好,想请教您一个问题,我现在参照着官方的mobilenet训练例子在mnist里面加入了伪量化训练代码,但只加 ...

just in case, 你没有新的问题了对吧?之前那一层楼点评太多了,我无法打开点评的第二页。。应该是卡bug了。所以你说了啥我都看不到。。
本楼点评(9) 收起
  • 九幽是,我刷新了几次,试了别的浏览器也发现看不到第二页,,,之前说的没啥别的问题,就是重新训练了一遍,打开了伪量化的开关,然后pb模型图出现了FakeQuantWithMinMaxVars node。但是用toco工具转化为tflite时报错,说Unimplemented: this graph contains an operator of type (Unsupported TensorFlow op: DecodeWav) for which the quantized form is not yet implemented. 应该是有的层没法量化?还没有完善是吗?还是我这里出问题了?
    另外就是那个create_inference_graph,我自己建立一个推理图的话,就是重新建一个网络结构然后给个分类输出吗?那个create_inference_graph我看感觉含义就是说了几个关键的层,然后给了logits,这个是搭建网络模型的意思?create_model?
    我做的目标检测还有画框,然后显示分类和概率,不太清楚这个输出该怎么给,,,
    2018-11-26 10:33 回复
  • 九幽还有一个,train.py里面create_training_graph()就是建立训练图的意思,然后后面只要get_default_graph()来在这一个图里面实现功能就行了呗?但是freeze.py里的create_eval_graph()不能直接创建出推理图,所以需要自己写一个create_inference_graph()函数来创建推理图,然后create_eval_graph()只相当于get_default_graph的功能来实现你之前说的:给出一个输出作为tflite可接受的类型是吗?
    create_training_graph和create_eval_graph添加的FakeQuantWithMinMaxVars还是只是create_training_graph添加的?那create_eval_graph有添加什么吗?
    如果FakeQuantWithMinMaxVars只是create_training_graph添加的,那为什么train.py里面要加fingerprint_input = tf.fake_quant_with_min_max_args(
            input_placeholder, fingerprint_min, fingerprint_max)这一部分呢?
    2018-11-26 11:13 回复
  • Zongjun回复 九幽 :decodewav这里,你的toco指令里要包含模型的输入和输出名字,这个模型的输入应当是:--input_arrays=Reshape_1,就是从reshape那一层算为进入,这样就不会有decodewav的报错了。create_inference_graph就是create model,我在logits后面手动加了softmax作为输出。所以我的toco输出是--output_arrays=labels_softmax。“labels_softmax”是我给起的名字。
    训练图不是create_training_graph()建立的,而是train.py 中 models.create_model这个函数建立的。其实,create_training_graph和create_eval_graph都不能自己创建一个图出来,它们都必须有另一个函数为它们创造图,然后它们在各自的图中把操作改成伪量化版本的操作。input那里它手动加了fake_quant,我感觉是这个模型的输入需要处理,如果你的输入是图片,那应该不需要这一步,这里我不是很确定。我的模型没有这一步依然是工作的。
    2018-11-27 02:34 回复
  • Zongjun回复 九幽 :我感觉你的误区是看见create_training_graph就认为它是在create graph,这个名字起的很有误导性。其实它根本没有创建一个graph出来,真正的graph,每一层的定义都是在models.py这个文件中定义的。create_training_graph只是让一个普通的graph在训练时加入伪量化而已。
    同理,create_eval_graph也只是把一个普通的inference graph变成伪量化版本,之前训练好的参数会传给这个图。再把这个图给toco。
    2018-11-27 02:38 回复
  • 九幽回复 Zongjun :大神好厉害,每次都回答地直中要害~
    恩,decodewav那里确实换了输入就好了,但是我在程序和用工具来看pb的结构都写着输入是wav_data,你怎么知道这个错误就是要把输入改成Reshape_1呢?......
    我看普通的tf程序创建图是用tf.Graph(),然后speech这个例子创建图是用create_model呗?他这个create_model是自己根据自己要用的网络在models.py先定义出来的呗?就是我现在想像他一样写一个create_inference_graph()加进去,因为我就剩下这部分没写进去,所以,我要是根据mnist网络自己定义一个mnist的create_inference_graph的话,就对照着speech例子的结构来写?
    然后其实还要写一个models.py先定义好要用的网络层?.......总感觉没这么复杂,因为我只是改下mnist的训练代码,让它实现伪量化,我觉得我应该抛掉speech的结构,然后只把它的create_inference_graph拿过来,然后在里面定义好mnist的网络结构,写好后,写logits = tf.Graph(),然后再加上最后的输出: tf.nn.softmax(logits, name='output'),是这样吗?
    我觉得我没有看透create_inference_graph()里面代码的含义,也不知道放到自己的网络里该怎么定义这一部分......
    2018-11-27 11:53 回复
  • 九幽回复 Zongjun :其实,我写mnist的伪量化是想测试一下这整个过程还有我用的工具对不对,测试下这种方法的效果怎么样,成功之后再用到我自己的代码里,,,
    感觉现在speech已经成功测试好了上面那些,我现在是不是直接去想自己的代码该怎么改比较好?mnist就先不要做了,也没什么帮助了是吗?
    我的训练和网络结构代码都是keras写的,用的vggA网络且只有卷积和池化,然后要实现画框和分类
    我现在修改了一部分代码,改了训练的部分,加了Session来执行训练,还有create_training_graph,
    感觉keras写的没有图,它应该不是用图的形式来计算实现的吧?,,,
    你知道keras怎么加伪量化吗?还是说伪量化只能用图的形式来实现,用TF来实现?我需要把keras语句改成tf方法来写吗?
    2018-11-27 12:16 回复
  • Zongjun回复 九幽 :1. 咱们这样盖楼,盖到第10层就另起一楼哈,别又像上次那样后面的评论全落地成盒了。。。
    2.  关于decodewav,一开始这里我和你报错一样,所以我知道是啥问题。我解决的方法就是研究了一下这个例子处理input的代码,我发现给toco reshape_1就行了,不需要前面那些。试了几次才成功的。
    3.  你不直接调用tf.Graph(),其实是用的default graph,所以创建图的原理和以前是一样的。这个例子只是把models.py单独拿出来了,在一个独立的文件中创建model,个人感觉这样设计更清晰。对,你其它都写了的话,只要额外加一个你自己的create_inference_graph()即可,注意去掉dropout(如果训练图有的话)。
    4. speech这个例子你如果吃透了的话,没必要再用mnist重复一遍了,核心思想是一样的。
    5. 重点来了,keras不存在伪量化功能。这个我在stack overflow上和别人也讨论过。我同事一开始是用keras,扔给我量化,我才发现keras没这功能。我们才慢慢用起来了tf的底层代码。
    6.有一种方法你可以试一下,它不用tf的底层代码定义Model。你查一下 tf.kears.layers。这一套相当于在tf中直接调用高层api。因为伪量化的操作出现在loss和optimizer那里,这一部分你要用tf的底层代码实现。这是和keras sequential model的区别。keras sequential model,我是没找到插入create_training_graph()的位置。。
    2018-11-28 03:05 回复
  • Zongjun回复 九幽 :我今天学了一下slim,感觉也很好上手。肯定是比tf的底层代码要简单实用一些。fake quantization的步骤和speech的例子也完全一样。我就拿mobilenet举例吧:1. https://github.com/tensorflow/models/blob/master/research/slim/nets/mobilenet_v1_train.py 第139行,在loss处插入了create_training_graph()
    2. https://github.com/tensorflow/models/blob/master/research/slim/nets/mobilenet_v1_eval.py 第127行在build inference model的函数内部同时插入了create_eval_graph()。这个只是和speech的例子代码顺序不同,本质完全一样。
    这样看来,slim的地位应当是介于tf底层代码和keras之间的那个谷歌官方框架。感觉documentation写的也相当好。推荐你考虑一下哈。(上一层说的tf.keras.layers我也用过,同样可以用,看你个人喜好了。)
    2018-11-28 08:55 回复
  • 九幽回复 Zongjun :我现在就是slim那个和speech的例子一起看,结构大体相似,就是eval的顺序不一样,,,主要我代码基础也比较差,tf又是刚接触,所以要查语法,要学使用,然后代码方面的理解力也差一点,所以学的比较慢,对于keras这边我的代码也是上边给我的,用keras写的,改了一段时间觉得找不到插入点,然后现在把keras写的训练那部分代码改掉,用tf来写了,但是loss = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits_v2(labels=y, logits=finaloutput))这句话要调用那个finaloutput,然后去找另一个文件写的vgg网络定义,还不知道keras写的哪个是输出,,,,
    你说的两种方法我先去学一下吧,这两种你觉得我这种基础的大概要花多久啊?感觉最近可能要加班了,,,
    2018-11-28 10:34 回复
九幽  TF荚荚  发表于 2018-11-28 11:04:02 | 显示全部楼层
Zongjun 发表于 2018-11-26 05:48
just in case, 你没有新的问题了对吧?之前那一层楼点评太多了,我无法打开点评的第二页。。应该是卡bug ...

1.对于decodewav,确实这是输入那里的,就应该去查输入,然后还是要多尝试啊,懂了,对于这种情况,网上查不到,我自己又不确定,判断不出来,又觉得不知道是不是其他地方的错误导致的,就比较迷惑,,,这种情况还是因为踩得坑少,经验少是吗?
2.然后对于create_inference_graph,我还是不太清楚具体什么流程,,,我想写好这部分就仔细研究speech或者slim的例子吗?感觉他们是写了大体框架,这里就是写的模型网络层级的定义吗?比如mnist这里,定义网络是这样的:
def build_network():
    x = tf.placeholder("float", shape=[None, 784], name='input')
    y = tf.placeholder("float", shape=[None, 10], name='labels')
    keep_prob = tf.placeholder("float", name='keep_prob')
    def weight_variable(shape):
       ......
    def bias_variable(shape):
        ......
    def conv2d(x, W):
        return tf.nn.conv2d(x, W, strides=[1, 1, 1, 1], padding='VALID')
    def max_pool_2x2(x):
        return tf.nn.max_pool(x, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding='VALID')
    def lenet5_layer(layer, weight, bias):
        ......
    def dense_layer(layer, weight, bias):
        ......

然后create_inference_graph里面,就把conv2d,max_pool_2x2,lenet5_layer,dense_layer这几层按顺序写一下?然后写logits = ...,和tf.nn.softmax(logits, name='output')?
这个logits是一个建立网络结构的语句?看那两个例子,感觉每个里面都包含了好多东西,要想弄清楚这里怎么写还要看哪些知识呢?
本楼点评(11) 收起
  • Zongjun这样的话,我建议你还是从tf.keras.layers开始吧,因为你已经知道keras怎么用了,这俩换汤不换药的。至于slim嘛,如果tf.keras最后出意料之外的问题了,你可以考虑把slim当做备选,实在不行再选择tf底层代码。(如果你的网络有batch normalization,我还是建议你用slim。)
    对于create_inference_graph,这个和你创建训练图是一样的套路。你看这个函数是不是还是调用了models.create_model()这个函数。其中is_training这个flag,这里是False,意思就是说我这里创建的是推理图。我觉得你还是先仔细研究完speech这个例子再看其他的哈。
    logits就是图的输出,最后把它传给softmax用于inference。
    你把创建图想成张量的流动,上一层的输出是下一次的输入。和keras的sequential model原理是一样的。
    2018-11-29 03:47 回复
  • zyc4me回复 Zongjun :你好,我想请教你一个问题,就是关于卷积之后得到的int32的的结果,量化到uint8的方式。  我在gemmlowp的example中看到的矩阵相乘得到int32之后再根据论文公式(7)得到uint8是看明白了,但是当我把tflite模型放入netron查看的时候,发现没有relu6层,而下一层卷积的输入(也就是上一层卷积的 输出)的范围是(0-5.9997),我自己认为这个输出得到的就是论文中提到的S3 和Z3,但是我按照论文中的公式得到的Q3并不在0-255范围,我觉得是我忽略了有关relu的一些操作,你能指点我一下吗?谢谢!!
    2018-11-29 10:45 回复
  • 九幽回复 Zongjun :好的,keras这块我还得好好看一下,,,说到底还是没看透例子,感觉我的问题就是写程序的问题
    就是要先了解好整个原理和逻辑,然后根据逻辑来写create_inference_graph这部分,可能就是语法的问题了,,,我先看懂写写试试,有问题再说吧,感觉写程序总是不开窍啊,好像是没有理解本质上的东西,,总觉得理解之后编程应该是很简单的一件事,,,,
    2018-11-29 10:51 回复
  • 九幽回复 Zongjun :看了tf.keras.layers,这个就是keras的网络结构定义呗?这个我对比了一下我代码里的vgg.py的网络定义,看起来是一样的,我的都是keras写的,
    Conv2D(16, (3, 3), name='conv1_1', **conv_args),
    Conv2D(16, (3, 3), name='conv1_2', **conv_args),
    MaxPool2D((2, 2), strides=(2, 2), name='pool1'),
    这样的,我的网络都是卷积和池化,没别的层,然后用model.compile来配置学习过程参数的,如果加量化的话,你的意思是loss这里换成tf底层代码吗?就是model.compile这里,因为这里有loss和optimizer
    2018-11-29 15:54 回复
  • 九幽回复 Zongjun :还有就是训练的部分,我的代码因为都是keras写的,所以训练感觉也要改成tf吧?因为伪量化是在图里面加了节点,然后还要保存成pb文件啥的,所以这部分是不是都得按照tf的底层代码来写啊?
    2018-11-29 19:39 回复
  • Zongjun回复 九幽 :嗯嗯。可以考虑强化一下你的python功力哈,对看或者写代码都是有帮助的。
    对,loss这里要换成tf底层代码的,这里要加create_train_graph()。都是conv2d和maxpool的话就好办了,应该问题不大。训练你可以先不改,试一试。要是报错了,看看是啥错,不行再转为底层代码。到了这一步以后就得具体情况具体分析啦。祝你好运!
    2018-11-30 02:01 回复
  • Zongjun回复 zyc4me :relu6应该是属于fuse进上一层的操作范畴吧。你看一下应该出现relu6的那一层的output id 是不是写着relu6?看你的结果0 到 5.9997应该就是这样的了。因为relu6的范围就是0到6啊。所以,你应该是带错变量了。
    2018-11-30 02:07 回复
  • Zongjun回复 zyc4me :我推荐你看一下这一篇paper: A Quantization-Friendly Separable Convolution for MobileNets
    session 2.1 TensorFlow 8-bit Quantization Scheme, 我当时根据这个session的式子在jupyter notebook中手动实现了tensorflow的卷积和量化,亲测可行。
    2018-11-30 02:10 回复
  • Zongjun回复 九幽 :我更正一下,我之前说slim是谷歌官方支持的库,这个好像不对。貌似在contrib文件夹下的都是非官方的?我今天看到一篇文章说slim有自己的所有者。嗯,不过问题不大哈,好多project都在用的,依然可以作为你的B计划。
    2018-11-30 03:07 回复
  • zyc4me回复 Zongjun :感谢回复,大神总是半夜干活啊。。  我看了A Quantization-Friendly Separable Convolution for MobileNets
    session 2.1 TensorFlow 8-bit Quantization Scheme这个,是的,这个session几个公式没有问题,可以计算得到uint8的输出,这和google论文中的公式(7)是一致的,我的疑问是,这个u8输出是卷积层输出的对吧,然后它再经过relu6之后变成什么样子了呢?(我解析了量化训练的tf ckpt模型,发现里边有每个卷积weight的min/max,还有每个relu6的min/max(min都是0,max基本都是5.9997,也有4.x,5.x的,这个符合relu6的输出),但是我要计算卷积层的输出的话,应该是要知道卷积层输出的min/max吧,google论文里说它是在训练过程中统计这个值,但是模型里并没有,只有relu6之后的min/max),所以如何获得这个卷积输出的min/max呢,是类似tensorrt使用一些样本跑这个模型,来自己统计吗?还有google论文里有提到这个fuse acvitation function,这个到底是怎么fuse的呢?(因为netron里tflite确实没看到有relu层,但是conv层的output id 里边是有relu6的),简而言之,就是tflite它是如何前向的呢?
    2018-11-30 09:57 回复
九幽  TF荚荚  发表于 2018-11-30 10:44:19 | 显示全部楼层
Zongjun 发表于 2018-11-26 05:48
just in case, 你没有新的问题了对吧?之前那一层楼点评太多了,我无法打开点评的第二页。。应该是卡bug ...

恩,语法编程能力啥的都得提高,,,我也觉得后面的问题涉及到我代码怎么写的,这都是细节问题,就得具体分析了,后面不好问了啊,,,
至于训练这部分,keras保存的是hdf5模型,要保存成pb应该就要改成tf写的了吧?要用图,,,
keras我之前查没看到直接保存成pb的,只有hdf5转化为pb的,,,
本楼点评(9) 收起
  • 九幽那个create_inference_graph好像知道怎么写了,其实明白过来感觉之前的问答都比较边缘化,,,我是一直没清楚这个推理图该怎么创建,里面该怎么写,然后今天又综合考虑了一下,感觉训练图和推理图应该是一个图啊,也就是你说的他们都用的create_model,所以他们就是一样的网络结构,不是我之前问的说create_inference_graph里面是网络层结构定义,这个定义是在create_model里的,而我不明白的地方其实是create_model之前的那些语句,结果那些语句原来是数据处理,跟网络结构没关系,,,,
    所以我的mnist里面就应该直接调用之前训练用的build_network,那里面是网络定义,所以在推理图的部分应该再次调用build_network,然后直接用训练时用的output给create_eval_graph来用
    2018-11-30 16:01 回复
  • 九幽我还是在mnist里面练习了一下,想验证下上面说的对不对,但是怎么改都报错,我把所有步骤都放到一个文件里了,因为像speech那样分开写报错也一样
    我觉得结构应该是没什么问题,就是细节的语法可能有问题,写好之后一直报的错误都是ValueError: Training op found in graph, exiting {'ApplyGradientDescent'}
    然后我感觉是因为没有创建出两个图来,但是创建了两个图,又报错说数据应该来源于一个图,,,
    不知道大神有没有时间看一下代码,,我是不知道怎么改了,,,不确定我写的对不对,,,先贴在这吧,,
    import tensorflow as tf
    import os.path
    from tensorflow.python.framework import graph_util
    from tensorflow.examples.tutorials.mnist import input_data

    mnist = input_data.read_data_sets("MNIST_data/", one_hot=True)

    x = tf.placeholder("float", shape=[None, 784], name='input')
    y = tf.placeholder("float", shape=[None, 10], name='labels')
    keep_prob = tf.placeholder("float", name='keep_prob')


    def build_network():
        def weight_variable(shape):
            initial = tf.truncated_normal(shape, stddev=0.1)
            return tf.Variable(initial)

        def bias_variable(shape):
            initial = tf.constant(0.1, shape=shape)
            return tf.Variable(initial)

        # convolution and pooling
        def conv2d(x, W):
            return tf.nn.conv2d(x, W, strides=[1, 1, 1, 1], padding='VALID')

        def max_pool_2x2(x):
            return tf.nn.max_pool(x, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding='VALID')

        # convolution layer
        def lenet5_layer(layer, weight, bias):
            W_conv = weight_variable(weight)
            b_conv = bias_variable(bias)
            h_conv = conv2d(layer, W_conv)   b_conv
            return max_pool_2x2(h_conv)

        # connected layer
        def dense_layer(layer, weight, bias):
            W_fc = weight_variable(weight)
            b_fc = bias_variable(bias)
            return tf.matmul(layer, W_fc)   b_fc

        # first layer
        with tf.name_scope('first') as scope:
            x_image = tf.pad(tf.reshape(x, [-1,28,28,1]), [[0,0],[2,2],[2,2],[0,0]])
            firstlayer = lenet5_layer(x_image, [5,5,1,6], [6])

        # second layer
        with tf.name_scope('second') as scope:
            secondlayer = lenet5_layer(firstlayer, [5,5,6,16], [16])

        # third layer
        with tf.name_scope('third') as scope:
            W_conv3 = weight_variable([5,5,16,120])
            b_conv3 = bias_variable([120])
            thirdlayerconv = conv2d(secondlayer, W_conv3)   b_conv3
            thirdlayer = tf.reshape(thirdlayerconv, [-1,120])

        # dense layer1
        with tf.name_scope('dense1') as scope:
            dense_layer1 = dense_layer(thirdlayer, [120,84], [84])

        # dense layer2
        with tf.name_scope('dense2') as scope:
            dense_layer2 = dense_layer(dense_layer1, [84,10], [10])

        finaloutput = tf.nn.softmax(tf.nn.dropout(dense_layer2, keep_prob), name="softmax")
        print('finaloutput:', finaloutput)
        return finaloutput


    def create_training_graph():
        # g = tf.get_default_graph()
        logits = build_network()
        # Create the back propagation and training evaluation machinery in the graph.
        with tf.name_scope('cross_entropy'):
            cross_entropy_mean = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits_v2(labels=y, logits=logits))
            print('cost:', cross_entropy_mean)

        # if FLAGS.quantize:
        tf.contrib.quantize.create_training_graph(quant_delay=0)     # input_graph=g,
        optimize = tf.train.GradientDescentOptimizer(1e-5).minimize(cross_entropy_mean)

        prediction_labels = tf.argmax(logits, axis=1, name="output")
        correct_prediction = tf.equal(tf.argmax(logits, 1), tf.argmax(y, 1))
        accuracy = tf.reduce_mean(tf.cast(correct_prediction, "float"))

        with tf.get_default_graph().name_scope('eval'):
            tf.summary.scalar('cross_entropy', cross_entropy_mean)
            tf.summary.scalar('accuracy', accuracy)

        # if is_training:
        return dict(
            x=x,
            y=y,
            keep_prob=keep_prob,
            optimize=optimize,
            cost=cross_entropy_mean,
            correct_prediction=correct_prediction,
            accuracy=accuracy,
        )


    def train_network(graph):
        init = tf.global_variables_initializer()
        saver = tf.train.Saver()

        with tf.Session() as sess:
            sess.run(init)
            for i in range(200):
                batch = mnist.train.next_batch(50)
                if i % 100 == 0:
                    train_accuracy = sess.run([graph['accuracy']], feed_dict={
                                                                               graph['x']:batch[0],
                                                                               graph['y']:batch[1],
                                                                               graph['keep_prob']: 1.0})
                    print("step %d, training accuracy %g"%(i, train_accuracy[0]))
                sess.run([graph['optimize']], feed_dict={
                                                           graph['x']:batch[0],
                                                           graph['y']:batch[1],
                                                           graph['keep_prob']:0.5})

            test_accuracy = sess.run([graph['accuracy']], feed_dict={
                                                                      graph['x']: mnist.test.images,
                                                                      graph['y']: mnist.test.labels,
                                                                      graph['keep_prob']: 1.0})
            print("Test accuracy %g" % test_accuracy[0])

            saver.save(sess, '/home/angela/tensorflow/tensorflow/Mnist_train/mnist_fakequantize.ckpt')
            tf.train.write_graph(sess.graph_def, '/home/angela/tensorflow/tensorflow/Mnist_train/', 'mnist_fakequantize.pbtxt', True)


    def main():
        g1 = create_training_graph()
        train_network(g1)

        sess = tf.InteractiveSession()
        g2 = tf.Graph()
        with g2.as_default():
            build_network()  # is_training=False
            # if FLAGS.quantize:
            tf.contrib.quantize.create_eval_graph()
            # load_variables_from_checkpoint(sess, '/home/angela/tensorflow/tensorflow/Mnist_train/mnist_fakequantize.ckpt')
            # Turn all the variables into inline constants inside the graph and save it.
            frozen_graph_def = graph_util.convert_variables_to_constants(
                sess, sess.graph_def, ['softmax'])
            tf.train.write_graph(
                frozen_graph_def,
                os.path.dirname('/home/angela/tensorflow/tensorflow/Mnist_train/mnist_frozen_graph.pb'),
                os.path.basename('/home/angela/tensorflow/tensorflow/Mnist_train/mnist_frozen_graph.pb'),
                as_text=False)
            tf.logging.info('Saved frozen graph to %s', '/home/angela/tensorflow/tensorflow/Mnist_train/mnist_frozen_graph.pb')


    main()
    2018-11-30 19:13 回复
  • Zongjun回复 九幽 :感觉你彻底明白了哈。inference用的图是用同一个create_model函数产生出来的,用来inference的图结构当然得和训练的时候的结构一样啊,要是图都不一样,训练了也没用啊。只是create_training_graph和create_eval_graph对各自的图的处理效果不同而已。所以还是必须要创建两个图出来。
    我大体看了一下你的代码,你的这个报错的源代码是这个链接:https://github.com/tensorflow/tensorflow/blob/master/tensorflow/contrib/quantize/python/quantize_graph.py
    显然你这个报错来源于create_training_graph或者create_eval_graph或者两者皆报错。我的建议是和speech那样分开写两个文件,各创造一个图。看看这个报错是从train出来的还是从eval出来的。这样比较好debug。
    2018-12-1 01:49 回复
  • 九幽回复 Zongjun :恩,现在不像之前思路不清无从下手了,至少可以动手写一写了,分开的我也写了,报的错误就是上面提到的,训练节点已经存在的问题
    我看位置是create_eval_graph那里,因为create_eval_graph那部分单独写就是像freeze.py一样的结构,然后调用网络那里它又训练了,我写的if is_training也没用,可能没写对吧,,,我记得分开也是一样的,我再试试吧,应该就是推理图那里调用网络结构有问题,,,
    2018-12-1 10:54 回复
  • 九幽回复 Zongjun :分开写一直是训练不报错,freeze报错,错误:
    ValueError: Tensor("first/Variable:0", shape=(5, 5, 1, 6), dtype=float32_ref) must be from the same graph as Tensor("Pad:0", shape=(?, 32, 32, 1), dtype=float32).
    这个float32_ref的错误,我之前用frozen指令转化pb模型的时候就报过这个错误,这个是训练的数据类型和freeze用的不一样吗?
    2018-12-3 13:01 回复
  • Zongjun回复 九幽 :这个报错我没遇见过,感觉和你的Model有关系。你得确保两个图是完全分开的,然后两个图的结构一致(除了dropout这些只属于training的node)。我简单搜了一下,stackoverflow上有一个类似的问题,看看能不能帮到你,链接:https://stackoverflow.com/questions/42527048/valueerror-tensor-must-be-from-the-same-graph-as-tensor
    2018-12-4 01:53 回复
  • 九幽回复 Zongjun :多谢,改了确实不报错了,我分开写的完全是按照speech的例子写的,顺序结构都一样照搬过去的,但现在就是运行freeze就会重新训练,然后调试了一下,发现到create_eval_graph就不运行了,也保存不出最后的pb文件,,,
    freeze这里我只改了create_inference_graph,写的是
    logits = mnist_fakequantize.build_network(is_training=False)  
    tf.nn.softmax(logits, name='output')
    只调用build_network,build_network改成了去掉后面optimize那部分的,只到finaloutput那句话,就是这样,然后就不知道为啥不保存了,,,,
    至于又训练一遍,难道是训练的开关没加好?但只调用build_network,为什么还会训练呢?想不明白,,
    最后,mnist原来用的是AdamOptimizer,几百次就到了0.9多,但是换成了GradientDescentOptimizer,两万次准确率也很低,,只改这一处,别的没动,我看网上有人换了准确率也是0.9多,就是不知道我的为什么这么低,一直不收敛
    2018-12-4 16:11 回复
  • 九幽回复 Zongjun :重复训练的问题改好了,好开心~原来还是要把build_network单独提出来,我重写了一个文件放进去,然后再调用就好了~查看pb图和tflite转化的图也都是对的了~
    至于那个AdamOptimizer和GradientDescentOptimizer的问题,是不是用哪个都行啊?根据自己的情况选择合适的优化器?
    但是我这里两个出来的准确率相差那么悬殊是什么原因呢?
    2018-12-4 17:44 回复
  • Zongjun回复 九幽 :哈哈,恭喜你走完整个流程了。optimizer还是用mnist原来的吧,具体差别可以看看adam的paper。adam应该用的是moving average of parameters。用gradient descent 那个你要调learning rate这个参数的,learning rate过大就不会收敛。你用adam准确略正常的话就还是用adam吧,毕竟mnist默认的。
    2018-12-5 02:25 回复
九幽  TF荚荚  发表于 2018-12-5 13:00:10 | 显示全部楼层
Zongjun 发表于 2018-11-26 05:48
just in case, 你没有新的问题了对吧?之前那一层楼点评太多了,我无法打开点评的第二页。。应该是卡bug ...

哦哦,也是,想了解具体的用法就得好好查查论文啥的了,用GradientDescentOptimizer的learning rate感觉也不大,之前是1e-5,我看别人也是这样写的,结果却是准确率差不多,,,
mnist这一块基本感觉差不多了,剩下的就是语法用法啥的了,,,
感觉大神你很适合做老师啊,又有耐心又会讲解,做领导肯定能把手下的人带的很好,我这几天都觉得提升不少,还得有名师点拨啊~再次感谢~

现在量化知道怎么用了,接下来就是改keras的程序了,vgg网络结构要改,训练保存pb也要改,还要再加一个eval.
1.loss和optimize这部分:
(1)我看这几个例子,都放在了单独写好的网络定义之后,比如create_model之后,然后跟训练的代码放在一起。我的mnist也是把build_network和loss处理、添加伪量化语句分开来写的,所以keras这里我也把它跟训练代码写一起比较好吧?
keras写的vgg模型定义这里,包括了loss处理,就是:
model = Model(inputs=img, outputs=olayer)
model.compile(loss=loss_cls,     # yolo_loss
                      optimizer=SGD(lr=0.01, momentum=0.9, clipnorm=2.0),
                      metrics=['mse', ])
如果是这样的话,那伪量化需要的训练图那部分的处理就要加在这里了呗?是把loss和optimize的处理加在model.compile里面(我不知道怎么加进去,还要看语法,感觉固定的参数添加了其他语句会报错的吧?,,,)还是要去掉这部分,直接用tf底层语句来写啊?像speech例子和我改的mnist那样写?,,,
综述:loss这部分是跟模型写在一起(因为vgg带了model.compile,如果在这里面改的话就放在这),还是拿走去跟训练写在一起(去掉model.compile,改成tf底层代码)?
【从我修改mnist的过程来看,处理loss这部分,也就是加了create_training_graph的这部分,不能跟模型定义放在一起,因为会出问题,后面eval的部分还要调用模型,所以检测到create_training_graph就会报错,所以,从这来看,还是改成tf代码,然后跟训练执行放到一起去?】
(2)我的vgg模型也就相当于create_model里面的东西,但是loss用的是yolo的loss,普通的loss是交叉熵,那我这里可以改成自己要用的yolo_loss吗?,,,除了create_training_graph和create_eval_graph是必须用的,他们的位置是必须考虑的,其他的是不是都是根据自己的需求来用就好啊?比如之前说的Adam和GradientDescent,还有这里的loss?
2.训练图和保存:

创建训练图和保存pb文件,这里应该也得用tf来写,这一部分感觉又是具体问题,也不知道怎么问,,,
3.create_inference_graph
我的mnist里面修改的很简单,logits = build_network(is_training=False),f.nn.softmax(logits, name='output'),就这两句话,,因为我的输出不只是分类,还有画框的tensor,所以就不知道这里给输出该给什么,我的这里应该是画框和分类都放在一起了,有一个14位的一维数组,4个:类别one-hot,2个bbox:(1,1),8个:(x,y,w,h)*2。所以觉得输出是不是就是这个一位数组了呢?
我先看看keras的example还有语法,看看能不能解决一些问题,,,

本楼点评(3) 收起
  • Zongjun不敢当不敢当,略知一二而已。不要客气哈。
    1. (1)Exactly. Loss的定义不能和模型定义放一起,不然你eval的时候call同一个function就会报错。Loss要单独写,就是把kears的这一套换成tf底层代码或者slim代码的loss和optimizer。(2)对的,loss就是你训练用的那个loss,所以你的如果是yolo的,那就换成yolo的。不过我不确定这样简单的直接替换会不会有什么意外,可能还需要后续调试啥的。顺序的话,你按照例子的顺序来即可。
    2. 创建训练图和推理图跟以前那两个例子是一样的啊,推荐用tf.kears.layers或者tf.slim写。如果有batch normliazation的话,优先用slim吧。freeze的话可以考虑用例子里的方法,也可以用tensorflow的freeze_graph.py文件。推荐你先用例子的,毕竟那个你已经成功了。
    3. 输出我不确定,你可以先按照你说的试试,看看toco转化成.tflite的时候报不报错吧。
    为啥还要看keras的语法呢,其实所有的code都可以用tf.keras.layers或者tf.slim或者tf底层代码搞定了。跟例子是一样的。
    2018-12-6 05:00 回复
  • 九幽回复 Zongjun :恩,我今天看了model.compile的使用,可以在前面做loss和optimize的处理,然后在里面调用,但是create_training_graph肯定是没地方塞进去了,所以只能是把model.compile整个去掉,然后用tf来写了,因为这个我还算熟悉一点,slim要再去看,我先把slim排在后面,tf弄不好再说。
    训练那部分是keras写的,你的意思是训练图可以用tf.kears.layers来建?或者我要是把训练和推理的部分都直接用tf底层重写呢?这个工作量会比在keras里面改大吗?
    看keras语法因为我之前没用过keras,只是看了下代码,但没有完全理解keras的整个过程,就是没把我的代码每一句都看懂,我只看懂了整体流程和每部分是什么功能,感觉它跟我今天去keras中文手册里面看的用法不太一样啊,你说的tf.keras.layers和keras是不一样的吗?
    比如,keras网络定义是:model.add(layers.Dense(16, activation='relu', input_shape=(10,))),然后我的vgg定义是:
    feat_layers = [Conv2D(16, (3, 3), name='model_weights/conv1_1/conv1_1', **conv_args), ......],没有model.add,,,
    感觉现在的问题依旧很边缘化,对keras我又不开窍了,,,
    2018-12-6 18:42 回复
  • Zongjun回复 九幽 :对,我的意思就是用tf.keras.layers来建训练图和推理图,这个是tensorflow官方对keras的实现。这个不行,你再尝试底层代码写图吧,底层代码写图应该是最麻烦的。Model.add是keras sequential model的用法,我个人不是很喜欢这个方式。
    loss 和optimizer你就直接用tf底层代码即可,这个和例子是一样的,代码也不多。
    2018-12-7 02:38 回复
Zongjun  TF豆豆  发表于 2018-12-15 02:21:08 | 显示全部楼层
本帖最后由 Zongjun 于 2018-12-15 02:22 编辑
快到碗里来 发表于 2018-11-16 18:18
你好,我想请教下,我使用toco工具将一个冻结了的pb文件,大概4.3m,进行转换成lite文件./bazel-bin/tensorfl ...

那层楼盖到第二页了,我怕卡bug,就另起了一楼。
1. 对,quantize_graph就是我说的旧版本的那个quantization,现在的tf github中已经不存在了。注意,我没说过它是完全量化工具,因为它并不是。google官方的意思是要放弃这个方法,以后都用tflite。我在最一开始的回答中的(*)部分提到过,这个旧方法涉及到requantize 和 dequantize nodes 这些都是含有float32计算的。目前我所知道的fully quantization应该只有伪量化加tflite。
2. 你生成的lite文件之所以有float32,是因为你的指令中缺少了关键的一句:converter.inference_type = tf.contrib.lite.constants.QUANTIZED_UINT8.这一句告诉tflite你要做fully quantization。你选择了使用post_training= True,这个我也在最一开始的回答中说过了,这个是一种独立的方法,它的计算kernel依然是float32.既然伪量化了,你就不能再用post_training这个指令。我建议你再从头到尾读一遍我最一开始的回答哈。
3. 关于速度问题,我想说,量化的目的并不只是为了速度。如果你的要求就是速度,那完全可以选择旧方法,毕竟经过你的测试,那个更快。但是,有的硬件,float32计算会非常昂贵,比这两种方法本身的速度差异更昂贵,那么.tflite的fully quantize模型就是更优解了。
4. 关于速度慢的问题,应该是tflite还在开发之中,像你所说,有的操作速度就是慢。但是估计官方会优化吧,不然完全放弃旧方法,新方法速度又跟不上,拭目以待吧。

本楼点评(0) 收起
快到碗里来  TF豆豆  发表于 2018-12-15 15:02:20 | 显示全部楼层
Zongjun 发表于 2018-12-15 02:21
那层楼盖到第二页了,我怕卡bug,就另起了一楼。
1. 对,quantize_graph就是我说的旧版本的那个quantizati ...

大神,因为之前不是还不是很清楚,所以之前看你最开始的回复,没搞太明白,现在基本搞清楚了,谢谢大神,然后,我按你说的办法:
converter = tf.contrib.lite.TocoConverter.from_frozen_graph('tflite_graph.pb',["input_image"],["result"], input_shapes={"input_image":[1,350,202,3]})
converter.allow_custom_ops = True
converter.inference_type = tf.contrib.lite.constants.QUANTIZED_UINT8
converter.quantized_input_stats = {"input_image" : (0., 2.)}
converter.default_ranges_stats=(0, 6)
tflite_quantized_model=converter.convert()
open("model.tflite", "wb").write(tflite_quantized_model)

现在确实生成uint8的方法,但现在主要报错
2018-12-15 14:54:43.601740: F tensorflow/lite/toco/graph_transformations/quantize.cc:490] Unimplemented: this graph contains an operator of type Square 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).
Square这个操作不支持量化,现在也只能这样了.等新版本更新或者大神有什么好办法,
还有,我想问下:
converter.quantized_input_stats = {"input_image" : (0., 2.)}
converter.default_ranges_stats=(0, 6)

这个 输入的mean跟std,还有最小值跟最大值,这些值要怎么确定,比如我现在输入的是一张普通,未经过处理的图片,那么最小值是0,最大值是255?mean跟std呢?请大神指导下.
挺佩服大神你这些资料是哪里获取到的,我看官方文档,google上也没人详细介绍
本楼点评(1) 收起
  • 九幽回复了两个人,问问题和说个事件,但除了对自己有用的信息之外,别人的一概不理,我觉得这样就算有人给自己解答,也显得太势力了,,,我还是删了吧,,,
    2018-12-18 11:03 回复
Zongjun  TF豆豆  发表于 2018-12-17 02:23:03 | 显示全部楼层
快到碗里来 发表于 2018-12-15 15:02
大神,因为之前不是还不是很清楚,所以之前看你最开始的回复,没搞太明白,现在基本搞清楚了,谢谢大神,然后, ...

1.请问你用的是什么版本的tensorflow?你要去下载tf.nightly版本,这个我跟你建议过两三次了,你好像没给我反馈,所以我再问一遍哈。我个人认为square应该是支持的,你看这个链接:https://www.tensorflow.org/lite/tf_ops_compatibility 这个是谷歌官方说tflite支持的op,里面确实有tf.square啊。还有一个可能就是,如果你去tensorflow github看底层代码,会发现你调用的这句converter = tf.contrib.lite.TocoConverter.from_frozen_graph,其中TocoConverter 属于deprecate了,你改成:converter = tf.contrib.lite. TFLiteConverter.from_frozen_graph试一试。如果改完了报错,说明你可能还停留在1.10版本。。

2. 我感觉你确实没用最新版本的tensorflow,最新版本的tensorflow中,lite这个文件夹已经从contrib移出去了,你调用tf.contrib.lite会报错的。现在都是直接tf.lite...。所以还是先检查一下你的版本哈。

3. 你看我这个贴子的第二个回答,mean和std我是人工调试的,这个原理我也没搞清楚。。官方的说法是mean 和std指的是input的mean和std,但也没给个具体怎么求的例子。。这个input是data augmentation之前的还是之后的?官方也没有个说法,底层代码那里也是一句注释就带过了。你要是找到了详细解释,还希望跟我分享一下哈!converter.default_ranges_stats=(0, 6) 这一句是说你有的op没有min和max,则用这一句给的参数自动补上。比如relu6就是默认min=0, max=6. 你用Netron打开你的伪量化以后的.pb文件,看看里面有没有relu6,有的话看看它跟没跟着fakequant node,如果没有的话,就得在toco这里加上default_ranges_stats(0,6)了。

4. 这些资料90%都是我平时工作自己总结的,你这些bug我基本全都碰到过,见招拆招罢了。谷歌官方的documentation有时候确实让我感到很无奈,我的建议是,多去tensorflow github里研究底层代码,帮助会很大。
本楼点评(0) 收起
快到碗里来  TF豆豆  发表于 2018-12-17 20:31:23 | 显示全部楼层
Zongjun 发表于 2018-12-17 02:23
1.请问你用的是什么版本的tensorflow?你要去下载tf.nightly版本,这个我跟你建议过两三次了,你好像没给 ...

你好,大神,实在抱歉,我忘记跟你说了,我之前就听从你的建议用了最新版的tf-nightly.然后我一一回复下问题:
(1).我用了pip安装了tf-nightly 1.13.0.dev20181216,我发现,他有tensorflow/lite文件夹,也有tensorflow/contrib/lite文件夹,所以tf.lite跟tf.contrib.lite(应该是还没移除)都没有报错,我看了tensorflow 1.12.0就只有tf.contrib.lite.我看了官方github上最新版,确实就只有tensorflow/lite文件夹了,tensorflow/contrib/lite文件夹已经为空了,但我不知道怎么看github上的tensorflow是什么版本,求大神指教下
(2).我用tf-nightly 1.13.0版本,然后使用tf.lite.TFLiteConverter.from_frozen_graph跟使用tf.contrib.lite.TFLiteConverter.from_frozen_graph还有tf.contrib.lite.TocoConverter.from_frozen_graph,都还是报Unimplemented: this graph contains an operator of type Square for which the quantized form is not yet implemented.
然后我使用tensorflow 1.12.0,使用tf.contrib.lite.TFLiteConverter.from_frozen_graph还有tf.contrib.lite.TocoConverter.from_frozen_graph方法,报的错是
Traceback (most recent call last):
  File "test.py", line 24, in <module>
    tflite_quantized_model=converter.convert()
  File "/home/zhoushaohuang/Virtualenv/py3/lib/python3.4/site-packages/tensorflow/contrib/lite/python/lite.py", line 453, in convert
    **converter_kwargs)
  File "/home/zhoushaohuang/Virtualenv/py3/lib/python3.4/site-packages/tensorflow/contrib/lite/python/convert.py", line 342, in toco_convert_impl
    input_data.SerializeToString())
  File "/home/zhoushaohuang/Virtualenv/py3/lib/python3.4/site-packages/tensorflow/contrib/lite/python/convert.py", line 135, in toco_convert_protos
    (stdout, stderr))
RuntimeError: TOCO failed see console for info.

我查了下,网上人家也说是使用了不支持的操作,建议使用tf-nightly版本我把模型中Square操作去掉了,就不会报错了.
(3).我看了https://www.tensorflow.org/lite/tf_ops_compatibility这个链接,上面标题Straightforward Conversions, Constant-Folding and Fusing下面写着会报某些操作移除或融合进复杂模型里面,这个你前面有跟我解释过,但他下面有句话Note that many of those operations don't have TensorFlow Lite equivalents and the corresponding model will not be convertible if they can't be elided or fused. 这是不是说明,如果你那个操作没办法融合,那么就会转换失败.而且我试了,如果只是伪量化converter.post_training_quantize=True转换成lite,Square是支持的不会报错,但如果converter.inference_type = tf.contrib.lite.constants.QUANTIZED_UINT8 完全量化,就会报错,所以我怀疑官方说的支持会不会是指伪量化而已.
(4).我们老大认识google的人,让我收集现在tensorflowlite现在的问题要去询问,等我得到input的mean和std的回复我再回复大神你.或者大神有什么需要询问的问题,我没注意到的问题也可以跟我说下.


本楼点评(18) 收起
  • Zongjun是tf.nightly的话,问题就不是出在版本上了。那应该是你模型的问题了,你这个模型长啥样,方便放上来吗,我可以看看为啥square不给融合。或者你可以把你的问题发到github issue上去问问看。(如果你的model去掉了square还工作的话,那干脆把square去掉算了。)
    mean和std那里就等你的回复啦,多谢!
    2018-12-18 01:50 回复
  • 九幽回复 Zongjun :大神,这里三个人我都问过问题,回复了东西,但除了你都没人回我,他们也只跟你说话,看来,其他人都是只关注对自己有用的啊,我在别的地方问也是这种情况,,难道是我问问题的方式不对,内容不对?要么就是我太小白了,跟我说也没意义,,,大神,再次深刻地感受到你这种人太少了,,希望我以后也可以成为大神,变成你这样乐于助人的人~我把之前做的写了个博客发出去了,但是感觉写的好像还不够细致,应该是不够深刻吧,你要有时间可以看看我写的有没有啥错误,不过不知道为什么在百度查不到,只有链接能查到:https://blog.csdn.net/angela_12/article/details/85000072 ,看不看都行,我会继续努力的,谢啦~,,,,(不过keras的我还没写出来,,,)
    2018-12-18 11:12 回复
  • 九幽回复 Zongjun :对啦,看你们在说--std_value=127.5 --mean_value=127.5这两个东西,我看Quantization-aware training的git上toco这部分的指令给出了那两个,但我之前转化的时候没用,这个是必须的嘛?我看百度上说是标准差和均值,这个是处理输入数据的吗?
    2018-12-18 12:41 回复
  • 快到碗里来回复 九幽 :哈哈哈,之前有看你的提问,不是不想回复你,是觉得自己也不是很了解所以不敢误导你,所以等大神回复你,至于你说的--std_value=127.5 --mean_value=127.5,伪量化才不需要,如果完全量化的话,我都会报需要这两个参数的错误,所以不知道为什么你可以成功不使用这两个参数,还有,我发现你引用我的博客,哈哈哈,https://blog.csdn.net/qq_16564093/article/details/78996563.有缘有缘
    2018-12-18 14:57 回复
  • 九幽回复 快到碗里来 :噗......我没想着你能回答我问题的事,毕竟这个东西可能知道也可能不知道.....我之前就在跟你说博客的事,,写了一堆发现你的过程,,然后你回复了大神,没回我,然后我才又说没人理我的,,,然后我就把那条回复你的删了,,,
    2018-12-18 17:48 回复
  • 九幽回复 快到碗里来 :我的mnist量化训练,然后再freeze之后生成pb,把这个转化成tflite的指令是这样写的:
    bazel-bin/tensorflow/contrib/lite/toco/toco --
    input_file=/home/angela/tensorflow/tensorflow/Mnist_train/mnist_frozen_graph.pb
    --output_file=/home/angela/tensorflow/tensorflow/Mnist_train/mnist_frozen_graph.tflite
    --input_format=TENSORFLOW_GRAPHDEF
    --output_format=TFLITE
    --inference_type=QUANTIZED_UINT8
    --input_shapes=1,28,28,1 --input_arrays=input --output_arrays=output
    --allow_custom_ops
    然后什么报错都没有,用netron看图也都和speech那个例子跑出来的一样,,就像我博客里贴的图片那样,但是以前的pb和tflite图里面都有input,就比如speech.tflite的倒数第二层(除了Reshape_1和labels_softmax层其他的都有input),是softmax input,两个长方块连在一起的,但今天又弄了一下上面的指令,结果图里就没有input了,不知道这个是什么情况,,
    2018-12-18 17:58 回复
  • 九幽回复 快到碗里来 :刚看了一下,speech那个例子也是上面那个指令,但是运行出来前面有点不一样的东西,写的是: Converting unsupported operation: DecodeWav,Converting unsupported operation: AudioSpectrogram, Converting unsupported operation: Mfcc后面都是正常的。
    然后感觉应该是指令的问题吧,因为我试着加了另外的指令:--inference_input_type=QUANTIZED_UINT8 --input_type=QUANTIZED_UINT8
    然后就会报std和mean的问题,不过如果你指令对的话,我就不知道什么问题了
    2018-12-18 18:04 回复
  • 九幽回复 快到碗里来 :我还是再说一下博客的事吧,,白写那么多了,,,,,
    我最开始弄量化,就看了你的博客,然后照着把指令弄对了,但结果只是float型的好用,然后没看懂里面你说的生成量化的tflite的步骤,因为照着弄不出来,,现在知道了,原来是里面坑太多,,,,
    然后又找了很久,才确定原来你说的就是量化训练的方法,然后到处去找怎么实现,就发现了这里,然后才解决了我的问题
    然后开始想写博客,又去看了你的,然后发现你更新了,最开始看你写的语气还以为你是Zongjun大神,不过点开你的头像发现原来你就是这里问问题的楼主。。。
    所以就跑来跟你说这件事了,然后你也没理我。。。
    最后的感觉就是全网量化训练这块也就你写的有用了,又通过你找到了Zongjun大神,也很谢谢你啊,写了启发我的博客,也让我找到可以问的人~
    2018-12-18 18:10 回复
  • Zongjun回复 九幽 :别客气哈,互帮互助应该的嘛。我有时间一定去看你的博客!mean 和std我也不是很清楚,你看我这个贴子的第二个回答,我的mean和std是手动试出来的。。这个应该和你具体的input, data augmentation有关。楼主也在问谷歌这个问题,希望他能得到答案回来分享一下哈。
    2018-12-19 04:33 回复
  • Zongjun回复 九幽 :对了,其实完整过程中,还有一步,我一直没说。。。就是生成了freeze的.pb文件以后,你可以用optimize_for_inference.py这个文件处理一下你的.pb文件,把新生成的这个opt.pb文件传给toco。有的toco不认的操作就会被opt文件干掉。。
    2018-12-19 04:42 回复
快到碗里来  TF豆豆  发表于 2018-12-18 15:06:28 | 显示全部楼层
Zongjun 发表于 2018-12-17 02:23
1.请问你用的是什么版本的tensorflow?你要去下载tf.nightly版本,这个我跟你建议过两三次了,你好像没给 ...

import tensorflow as tf

def conv2d(x, input_filters, output_filters, kernel, strides, mode='REFLECT'):
    with tf.variable_scope('conv'):

        shape = [kernel, kernel, input_filters, output_filters]
        weight = tf.Variable(tf.truncated_normal(shape, stddev=0.1), name='weight')
        x_padded = tf.pad(x, [[0, 0], [kernel // 2, kernel // 2], [kernel // 2, kernel // 2], [0, 0]])
        return tf.nn.conv2d(x_padded, weight, strides=[1, strides, strides, 1], padding='VALID', name='conv')

def resize_conv2d(x, input_filters, output_filters, kernel, strides,height , width , training):
    with tf.variable_scope('conv_transpose'):

        new_height = height * strides * 2
        new_width = width * strides * 2

        x_resized = tf.image.resize_images(x, [new_height, new_width])
        return conv2d(x_resized, input_filters, output_filters, kernel, strides)


def instance_norm(x):
    epsilon = 1e-9

    #之前使用这个,但是报SquaredDifference这个操作不支持才自己写方法去方差求均值.SquaredDifference这个应该里面也使用到square原理
    #mean, var = tf.nn.moments(x, [1, 2], keep_dims=True)
    mean = tf.reduce_mean(x,[1,2])
    mean = tf.expand_dims(mean,[1])
    mean = tf.expand_dims(mean,[1])
    s = x.get_shape()
    ###在这里用了square.
    var = tf.reduce_sum(tf.square(tf.subtract(x, mean)), [1,2], keep_dims=True)/(s[1].value*s[2].value)
    #mean = 1.0
    #var = 1.0
    result = tf.div(tf.subtract(x, mean), tf.sqrt(tf.add(var, epsilon)))
    return result

def relu(input):
    relu = tf.nn.relu(input)
    return relu

def residual(x, filters, kernel, strides):
    with tf.variable_scope('residual'):
        conv1 = conv2d(x, filters, filters, kernel, strides)
        conv2 = conv2d(relu(conv1), filters, filters, kernel, strides)
        residual = x + conv2
        return residual

def net(image, heigh_image, width_image ,training):
    image = tf.pad(image, [[0, 0], [10, 10], [10, 10], [0, 0]])
    with tf.variable_scope('conv1'):
        conv1 = relu(instance_norm(conv2d(image, 3, 32, 9, 1)))
    with tf.variable_scope('conv2'):
        conv2 = relu(instance_norm(conv2d(conv1, 32, 64, 3, 2)))
    with tf.variable_scope('conv3'):
        conv3 = relu(instance_norm(conv2d(conv2, 64, 128, 3, 2)))
    with tf.variable_scope('res1'):
        res1 = residual(conv3, 128, 3, 1)
    with tf.variable_scope('res2'):
        res2 = residual(res1, 128, 3, 1)
    with tf.variable_scope('res3'):
        res3 = residual(res2, 128, 3, 1)
    with tf.variable_scope('deconv1'):
        deconv1 = relu(instance_norm(resize_conv2d(res3, 128, 64, 3, 1,(heigh_image+20)//4, (width_image+20)//4, training)))
    with tf.variable_scope('deconv2'):
        deconv2 = relu(instance_norm(resize_conv2d(deconv1, 64, 32, 3, 1, (heigh_image+20)//2 , (width_image+20)//2 ,training)))
    with tf.variable_scope('deconv3'):
        deconv3 = tf.nn.tanh(instance_norm(conv2d(deconv2, 32, 3, 3, 1)))

    y = (deconv3 + 1) * 127.5

    height = tf.shape(y)[1]
    width = tf.shape(y)[2]
    y = tf.slice(y, [0, 10, 10, 0], tf.convert_to_tensor([-1, height - 20, width - 20, -1]))

    return y



大神.代码发给你了,net是主方法,调用上面定义的一些方法,使用square的地方我加了标注了.

本楼点评(0) 收起
快到碗里来  TF豆豆  发表于 2018-12-18 18:03:22 | 显示全部楼层
Zongjun 发表于 2018-12-17 02:23
1.请问你用的是什么版本的tensorflow?你要去下载tf.nightly版本,这个我跟你建议过两三次了,你好像没给 ...

大神,我刚刚又试了下,我大概可以确定是模型融合不了导致那些操作没办法量化,我做了下面的测试,我把square去掉,改为a*a去求,他反而报div不支持量化操作,这说明应该是结构问题导致了量化不了.
(1) 我把var设置为1.0,这个是可以量化成功的
def instance_norm(x):
    epsilon = 1e-9
    mean = tf.reduce_mean(x,[1,2])
    mean = tf.expand_dims(mean,[1])
    mean = tf.expand_dims(mean,[1])
    s = x.get_shape()
    a = tf.subtract(x, mean)
    b = a*a
    var = tf.reduce_sum(b, [1,2], keep_dims=True)/(s[1].value*s[2].value)
    result = tf.div(tf.subtract(x, mean), tf.sqrt(tf.add(1.0, epsilon)))
    return result

2018-12-18 18:03:51屏幕截图.png
(2).我把var要自己求的话,就会报div操作不支持量化
def instance_norm(x):
    epsilon = 1e-9
    mean = tf.reduce_mean(x,[1,2])
    mean = tf.expand_dims(mean,[1])
    mean = tf.expand_dims(mean,[1])
    s = x.get_shape()
    a = tf.subtract(x, mean)
    b = a*a
    var = tf.reduce_sum(b, [1,2], keep_dims=True)/(s[1].value*s[2].value)
    result = tf.div(tf.subtract(x, mean), tf.sqrt(tf.add(var, epsilon)))
    return result

2018-12-18 18:05:37屏幕截图.png
想麻烦大神帮我看下为什么不可以量化,我自己用netron看了,但看不出问题所在.
本楼点评(9) 收起
  • Zongjun不对吧,链接https://www.tensorflow.org/lite/tf_ops_compatibility中说,tf.square_difference是直接可兼容的。不知道你的square difference为何会报错。我觉得解决这个问题,要比解决你现在的这个问题更直接一些。
    看你的netron图,感觉你的操作好多啊。比如conv2d上面有一个pad,用tf.nn.conv2d的话padding是自动在conv2d里的。所以我建议你从已知的谷歌例子入手,以谷歌的例子为框架,把你的代码填上去。
    再就是,生成了freeze的.pb文件以后,你可以用optimize_for_inference.py这个文件处理一下你的.pb文件,把新生成的这个opt.pb文件传给toco。有的toco不认的操作会被optimize_for_inference.py干掉。。文件链接:https://github.com/tensorflow/tensorflow/blob/master/tensorflow/python/tools/optimize_for_inference.py
    2018-12-19 05:04 回复
  • 快到碗里来回复 Zongjun :嗯嗯,大神,我看了square difference确实是说支持的,但就是用了tf-nightly 1.13进行量化出现以下报错,所以我觉得是不是某个结构限制了:
    2018-12-19 15:17:22.162240: I tensorflow/core/platform/cpu_feature_guard.cc:141] Your CPU supports instructions that this TensorFlow binary was not compiled to use: AVX2 FMA
    2018-12-19 15:17:22.165892: I tensorflow/core/platform/profile_utils/cpu_utils.cc:94] CPU Frequency: 3392410000 Hz
    2018-12-19 15:17:22.166176: I tensorflow/compiler/xla/service/service.cc:150] XLA service 0x447daf0 executing computations on platform Host. Devices:
    2018-12-19 15:17:22.166209: I tensorflow/compiler/xla/service/service.cc:158]   StreamExecutor device (0): <undefined>, <undefined>
    Traceback (most recent call last):
      File "test.py", line 24, in <module>
        tflite_quantized_model=converter.convert()
      File "/home/zhoushaohuang/Virtualenv/python3.4/lib/python3.4/site-packages/tensorflow/lite/python/lite.py", line 455, in convert
        **converter_kwargs)
      File "/home/zhoushaohuang/Virtualenv/python3.4/lib/python3.4/site-packages/tensorflow/lite/python/convert.py", line 442, in toco_convert_impl
        input_data.SerializeToString())
      File "/home/zhoushaohuang/Virtualenv/python3.4/lib/python3.4/site-packages/tensorflow/lite/python/convert.py", line 205, in toco_convert_protos
        "TOCO failed. See console for info.\n%s\n%s\n" % (stdout, stderr))
    tensorflow.lite.python.convert.ConverterError: TOCO failed. See console for info.
    2018-12-19 15:17:23.094945: I tensorflow/lite/toco/graph_transformations/graph_transformations.cc:39] Before Removing unused ops: 156 operators, 241 arrays (0 quantized)
    2018-12-19 15:17:23.096349: I tensorflow/lite/toco/graph_transformations/graph_transformations.cc:39] Before general graph transformations: 156 operators, 241 arrays (0 quantized)
    2018-12-19 15:17:23.105274: I tensorflow/lite/toco/graph_transformations/graph_transformations.cc:39] After general graph transformations pass 1: 84 operators, 147 arrays (1 quantized)
    2018-12-19 15:17:23.106310: I tensorflow/lite/toco/graph_transformations/graph_transformations.cc:39] Before pre-quantization graph transformations: 84 operators, 147 arrays (1 quantized)
    2018-12-19 15:17:23.106844: I tensorflow/lite/toco/graph_transformations/graph_transformations.cc:39] After pre-quantization graph transformations pass 1: 78 operators, 141 arrays (1 quantized)
    2018-12-19 15:17:23.107561: I tensorflow/lite/toco/graph_transformations/graph_transformations.cc:39] Before default min-max range propagation graph transformations: 78 operators, 141 arrays (1 quantized)
    2018-12-19 15:17:23.108139: I tensorflow/lite/toco/graph_transformations/graph_transformations.cc:39] After default min-max range propagation graph transformations pass 1: 78 operators, 141 arrays (1 quantized)
    2018-12-19 15:17:23.108795: I tensorflow/lite/toco/graph_transformations/graph_transformations.cc:39] Before quantization graph transformations: 78 operators, 141 arrays (1 quantized)
    2018-12-19 15:17:23.108860: W tensorflow/lite/toco/graph_transformations/quantize.cc:127] Constant array conv1/conv/weight lacks MinMax information. To make up for that, we will now compute the MinMax from actual array elements. That will result in quantization parameters that probably do not match whichever arithmetic was used during training, and thus will probably be a cause of poor inference accuracy.
    2018-12-19 15:17:23.108993: F tensorflow/lite/toco/graph_transformations/quantize.cc:491] Unimplemented: this graph contains an operator of type SquaredDifference 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).
    Aborted (core dumped)

    然后用pad是因为想使用pad操作里面的某个扩大图片的方式.
    然后我使用了optimize_for_inference.py,但还是报一样的错误,我看了,对我现在生成的pb文件感觉没什么变化,就输出变成了一个placehold.其他没什么改变.
    2018-12-19 20:46 回复
  • Zongjun回复 快到碗里来 :问一下,你的squareddifference用的是tf.squared_difference语句吗?如果是的话,我就不知道了,我木有用过squared difference。我感觉可以去github上建一个issue来问了。让官方的人回答一下哈。最好是把你的traceback和代码都贴上去。
    2018-12-20 01:55 回复
  • 快到碗里来回复 Zongjun :大神,我用的是tf.nn.moments,里面应该用到tf.squared_difference,我去把问题丢到github上去问问,谢谢大神。
    2018-12-20 14:53 回复
  • Zongjun回复 快到碗里来 :哦,tf.nn.moments应该是不兼容的。你何不尝试直接用tf.squared_difference。不客气哈,good luck!
    2018-12-21 01:10 回复
  • 快到碗里来回复 Zongjun :
    SquaredDifference这个ops目前没有被支持TF Lite(已经支持的ops可以在这里看到)。不过tf.nn.moments比较简单,可以用其他两个ops reduce_mean和tf_square组合代替
    mean = tf.reduce_mean(x, axis=ax, keepdims=True)
    variance = tf.reduce_mean(tf.square(x-mean), axis=ax)
    当然也可以自己实现一个custom ops
    mean/std_dev使用训练数据输入的统计数据
    min/max使用估算的activation范围
    这是google回复的mean跟std_dev的值,但我min/max有个问题,你说的是这个是如果图中存在多个没有min跟max的activation,那么min跟max是不是应该是所有activation的最小min跟最大的max.还有,你知道怎么自己实现custom ops,这个自己i实现是不是有些难的
    2018-12-29 17:30 回复
  • 快到碗里来回复 快到碗里来 :还有,他说的支持的操作网站在https://www.tensorflow.org/lite/tf_ops_compatibility,但上面不也写着SquaredDifference支持.
    2018-12-29 17:43 回复
  • Zongjun回复 快到碗里来 :好的,“mean/std_dev是使用训练数据输入的统计数据”,这个是在做完data augmentation以后的统计数据吗?比如我的输入是一张图片,我训练前对图片做旋转,左右对称,切割等操作,这个时候我要再多加一步,去求我整个数据集的mean和std?我这里一直不是很清楚。。
    2018-12-30 13:42 回复
  • 快到碗里来回复 Zongjun :我觉得是data augmentation以后的统计数据,而至于求数据集的mean和std,可以重新写个脚本求。
    2018-12-31 20:53 回复
九幽  TF荚荚  发表于 2018-12-19 14:45:41 | 显示全部楼层
快到碗里来 发表于 2018-12-15 15:02
大神,因为之前不是还不是很清楚,所以之前看你最开始的回复,没搞太明白,现在基本搞清楚了,谢谢大神,然后, ...

上面那个又出bug了,回复不了,写在这了,,,
没事了,从大神和你的对话来看,也知道你的性格了,你应该细节的地方注意的少,,,
博客我这也不断修改,趁着最近这段时间,争取把这部分学的透彻一点吧,不过我没有项目,涉及的东西少,看你问的问题我都没考虑过,你做的是什么啊?你要从源码那里来看懂量化的原理吗?还要通过源码的东西来实现一些功能?
本楼点评(6) 收起
  • 九幽感觉你可能又没看到我在上面回你的,所以再在这里写一下吧:
    我的mnist量化训练,然后再freeze之后生成pb,把这个转化成tflite的指令是这样写的:
    bazel-bin/tensorflow/contrib/lite/toco/toco --
    input_file=/home/angela/tensorflow/tensorflow/Mnist_train/mnist_frozen_graph.pb
    --output_file=/home/angela/tensorflow/tensorflow/Mnist_train/mnist_frozen_graph.tflite
    --input_format=TENSORFLOW_GRAPHDEF
    --output_format=TFLITE
    --inference_type=QUANTIZED_UINT8
    --input_shapes=1,28,28,1 --input_arrays=input --output_arrays=output
    --allow_custom_ops
    然后什么报错都没有,用netron看图也都和speech那个例子跑出来的一样,,就像我博客里贴的图片那样,但是以前的pb和tflite图里面都有input,就比如speech.tflite的倒数第二层(除了Reshape_1和labels_softmax层其他的都有input),是softmax input,两个长方块连在一起的,但今天又弄了一下上面的指令,结果图里就没有input了,不知道这个是什么情况,,
    2018-12-19 14:54 回复
  • 九幽刚看了一下,speech那个例子也是上面那个指令,但是运行出来前面有点不一样的东西,写的是: Converting unsupported operation: DecodeWav,Converting unsupported operation: AudioSpectrogram, Converting unsupported operation: Mfcc后面都是正常的。
    然后感觉应该是指令的问题吧,因为我试着加了另外的指令:--inference_input_type=QUANTIZED_UINT8 --input_type=QUANTIZED_UINT8
    然后就会报std和mean的问题,不过如果你指令对的话,我就不知道什么问题了
    2018-12-19 14:54 回复
  • 快到碗里来回复 九幽 :因为业务需要,我们是想把公司一些模型放到手机端运行,而用之前的伪量化的pb文件,会速度跟内存都有一定限制,所以想尝试lite来实现在手机端运行,而对于你说结果图里就没有input了,这个我也不太清楚,但应该是你指令或者哪里改了,应该不会平白无故就输出模型的结构就变了,还有你说的std和mean,我还在测试,等有结果我再告诉你或csdn私信给你说.
    2018-12-19 20:35 回复
  • 九幽回复 快到碗里来 :恩,我们也是放到手机里运行,再加上模型优化,所以要生成tflite文件,但是我只是在我们的代码里面加上伪量化操作,感觉步骤没有你的那么复杂,你提到的问题我也没遇到过,用的工具也没有,应该是你们都习惯写代码来实现功能,我能用工具指令的都用指令来完成了,,,,
    上面那个input的问题,我用的指令就是我给出来的,除了没用std和mean,其余的就是上面写的那样了,跟你用的一样吗?我的意思是你生成的tflite图有input吗?还是也是没有的?或者生成之后你是怎么测试是否生成正确的,直接把tflite放到AndroidStudio然后生成APP测试吗?还是写脚本看识别准确率来试?
    2018-12-20 10:54 回复
  • 快到碗里来回复 九幽 :你好,可能我用的是我自己写的模型,你用的是官方的例子吗?所以我遇到了一些奇葩的坑,只能慢慢摸索,我之前是用toco工具的,但感觉有些麻烦,要编译工具,又怕版本问题,所以上面大神说他是用代码tf.lite.TFLiteConverter.from_frozen_graph生成的,具体代码可以看我博客,我都更新了,我觉得,调用toco跟使用代码其实原理差不多,我测试了也感觉效果是一样的,而且我更偏向使用代码去生成。然后你说的std跟mean,我之前也用toco生成lite,具体生成命令在博客里面,现在我拿不到我的笔记,你可以对比下。我用代码接口生成的lite是有input的,测试是用python脚本本地测试后我再放到android上测试的。
    2018-12-20 15:05 回复
  • 九幽回复 快到碗里来 :恩,这样就差不多了,我就是按照大神说的,照着speech那个例子改的,然后在mnist上面实现的。自己的模型肯定有些不常用的层,我这只有卷积和池化,,,,工具我是之前编译好的,不过不是clone下来的源码,是download的,之后还用pip安装了一遍tensorflow,然后动了下Python,感觉某些地方版本坏了,,,然后现在大神说的有一步之前没说的,就是optimize_for_inference.py,我现在用bazel编译不通过了,估计可以用代码试试,不过等有时间再试吧,这步应该是优化吧?是不是加不加都行啊?
    指令这一块,我是结合你的博客和官方文档用的,感觉应该没什么问题,std和mean我没用,测试之后,生成的tflite是正确的,所以我也暂时不考虑这部分了,,
    等有时间,我也试试用代码来实现转换~
    2018-12-20 16:02 回复
lishanlu136  TF荚荚  发表于 2019-2-13 11:05:34 | 显示全部楼层
Zongjun 发表于 2018-11-6 08:49
对了,我补充一点,我debug的时候花了不少时间的一个地方:我用的是在linux下,安装tf.nightly,然后jupyter  ...

大神,你好,我最近也在做模型的伪量化训练,在这上面折腾了快两个月了,能转换生成tflite模型,但用python的api调用测试会报错,不知大神碰到过没有,现把我的问题描述一下:我用的是models/research/slim 里面的inception_resnet_v1模型,tensorflow版本是1.12.0

第一步:在train.py中的loss之后,train_op之前加入tf.contrib.quantize.create_training_graph(input_graph=tf.get_default_graph(), quant_delay=20000),训练模型,保存ckpt文件。
第二步:在freeze.py中,构建inception_resnet_v1的inference_graph,加入tf.contrib.quantize.create_eval_graph(input_graph=tf.get_default_graph()),restore之前训练保存的ckpt文件,冻结生成pb文件。

pb文件用netron显示如下:
第三步:用bazel编译tensorflow1.12.0的源码,生成toco,用toco工具转换,转换命令如下:

  1. ./bazel-bin/tensorflow/contrib/lite/toco/toco \
  2. --input_file=inception_resnet_v1_fake_quantized_eval.pb \
  3. --output_file=tflite_model.tflite \
  4. --input_format=TENSORFLOW_GRAPHDEF \
  5. --output_format=TFLITE \
  6. --inference_type=QUANTIZED_UINT8 \
  7. --input_shape="1,160,160,3" \
  8. --input_array=input \
  9. --output_array=embeddings \
  10. --std_value=127.5 \
  11. --mean_value=127.5 \
  12. --default_ranges_min=-1.0 \
  13. --default_ranges_max=1.0
复制代码

最后能生成tflite_model.tflite文件,大小约为转换之前的eval.pb的1/4左右。用netron看结构如下:
第四步:test_tflite.py,调用python的api加载tflite文件,用图片测试。代码如下:

  1. import numpy as np
  2. import tensorflow as tf
  3. import scipy

  4. # Load TFLite model and allocate tensors.
  5. interpreter = tf.contrib.lite.Interpreter(model_path="tflite_model.tflite")
  6. interpreter.allocate_tensors()

  7. # Get input and output tensors.
  8. input_details = interpreter.get_input_details()
  9. output_details = interpreter.get_output_details()
  10. image = scipy.misc.imread("src/151105230861_0_76.536.jpg")
  11. image_ = np.array([image.astype('uint8')])

  12. print(image_.shape)
  13. print(type(image_))
  14. print(input_details)
  15. print(output_details)
  16. interpreter.set_tensor(input_details[0]['index'], image_)
  17. interpreter.invoke()
  18. output_data = interpreter.get_tensor(output_details[0]['index'])
  19. print(output_data)
复制代码
但是就在测试的时候报错:

报错信息

报错信息

eval.pb网络结构

eval.pb网络结构

tflite_model.tflite网络结构

tflite_model.tflite网络结构
本楼点评(12) 收起
  • lishanlu136对了,我还尝试用了slim里面的inception_v3,mobileV2,inception_resnet_v2都会出现类似的错误。
    2019-2-13 11:08 回复
  • lishanlu136自问自答,我用tensorflow1.12.0的python api转tflite,测试成功了,tf.contrib.lite.TFLiteConverter.from_frozen_graph(),最后测试能出来结果,还得进一步测试精度。
    2019-2-13 14:21 回复
  • Zongjun回复 lishanlu136 :对。我用的也是tf 1.12.0版本。Tflite toco这里,我用的一直都是python api 的 tf.contrib.lite.TFLiteConverter.from_frozen_graph()。我不推荐用bazel,有的时候bazel自己都存在bug,github issue上都能查到的。
    其次就是精度,这个就涉及到 quatized_input_stats这个参数的设定了。这个和你训练网络时用的输入和data augmentation有关。你如果测试精度不对,可能就是这里出了问题,可以再来发帖问。正常的话,fully quantized的测试精度和你quantization aware training后的test accuracy应该一致。
    我用过slim的mobilenet v1,调完这两个参数以后是完全成功的哈。
    2019-2-14 05:20 回复
  • Zongjun回复 lishanlu136 :其实,我记得楼上的小伙伴和我都用bazel成功过的,但是仅限于1.12.0版本的tensorflow。我再尝试用tensorflow-master去bazel,结果就不是很开心了。所以我最后就决定所有版本我都用python api,不去管bazel了。。。
    2019-2-14 05:35 回复
  • lishanlu136回复 Zongjun :均值和方差应该就是0和1了,因为我的训练图片在进入训练网络之前都做过标准化的;但这个default_ranges_stats的(min, max) range values不好确定呀,这个好像是用来量化激活后的值的,我用的是relu激活函数;难道要把我的测试集全部跑一次,看看relu激活后的值的分布范围,然后再来确定最大,最小值吗?大神有什么建议呢?
    2019-2-14 10:05 回复
  • Zongjun回复 lishanlu136 :不需要的,如果是quantization aware training,你的.pb文件中的relu后面会跟着fake quant node来自动识别激活范围。所以我转化的时候没有用到default_ranges_stats。
    均值和方差你的理解可能有偏差。需要看training input的float range。如果你标准化做完data augmentation以后的float32 range是(0,1)那么toco这里的mean = 0, std = 255,而不是mean=0, std=1。这个是和tensorflow的量化机制有关的。量化的具体公式论文中有。
    2019-2-15 02:13 回复
  • lishanlu136回复 Zongjun :我用的网络是带有残差模块的,inception_resnet_v1,其中有add和concat操作之后再用relu激活的操作,就这一步无法自动识别激活范围,其它的常规卷积操作加激活确实能自动确定激活范围。
    2019-2-15 21:52 回复
  • Zongjun回复 lishanlu136 :哦哦,这种情况我还是第一次遇到。学习了!
    2019-2-20 01:36 回复
  • sunyanli楼主,您好,我看了您的图,.pb文件里面conv2D转变成.tflite文件,卷积核依然是conv2D。而我的卷积核变成ddepthconv2D。我用的网络本身没有深度卷积。不知道这个是什么问题,希望楼主可以指点一下。谢谢啦。
    2019-3-14 10:38 回复
  • lishanlu136回复 sunyanli :你好好看看你构建网络的代码,应该是用了深度卷积的函数;转换不可能把卷积操作的性质都会改变的,也不可能改名字。
    2019-3-14 13:44 回复
ake123100  TF荚荚  发表于 2019-2-16 11:34:37 | 显示全部楼层
Zongjun 发表于 2018-12-17 02:23
1.请问你用的是什么版本的tensorflow?你要去下载tf.nightly版本,这个我跟你建议过两三次了,你好像没给 ...

各位大神好~有幸在刚接触tensorflow量化工作之初就看到了这篇帖子和相关的博客,避开了很多弯路,非常感谢各位的详细分享!我在这想抛出一个问题并分享下自己量化工作的理解。我的问题是:CNN和RNN网络在使用tfliteconverter量化为INT8类型的tflite后,推断精确度的损失为何相差这么大? (我使用同一套量化和转换api代码,并使用最简单的CNN网络(conv2d+maxpool+dense)和最简单的RNN网络(BasicLSTM+static_rnn)分别进行伪量化训练和冻结、转换,最后在MNIST数据集上调用解释器来验证转换后的INT8类型tflite推断准确度,发现CNN模型量化后精度损失几乎为0(mean和stderr使用0和1即可),但RNN模型量化后精度损失从训练的90%跌到了11%。。。) 是否RNN本身的网络结构特点导致了INT8的量化会非常不稳定?期待各位大神的指点~
然后再分享下自己近期的量化工作理解。我使用tf-nightly二进制版本然后调用tf.lite.TFLiteConverter api函数来进行模型的量化转换。在量化转换过程中关键的几个点分别为:输入输出张量的name,输入张量的shape,输入的mean和stderr。 其中输入张量的shape要求不能存在-1这类值,必须指定具体的维数。 输入的mean和stderr 会直接影响到最终的推断准确度,从tflite官网的解释和tf源码的注释中理解,这两个参数起到了对输入数据的归一化作用,参考BatchNormalization原理(BN层在训练时会对每一个minibatch为单位进行归一化处理加速训练,在推断时对单个的样本则需要我们估算均值和方差来实现归一化)。个人理解此处的两个参数跟BN层的均值方差略有不同,只是对输入数据做一次归一化。并且根据CNN简单模型的量化验证结果来看,如果模型本身不存在BN层的话,均值和方差取0和1即可以不引起精确度损失。如果模型本身有BN层的话,这两个参数就必须要经过估算得到以免精确度损失太大。根据吴恩达的DL课程,这两个参数可以通过对训练集的多个mini-batch进行均值和方差的指数加权平均来估算(具体操作还未理解。。。) 另外default_ranges_stats应该就是针对伪量化过程中没有收集到min和max的ops来手动指定量化范围的。
总的来说感觉TFlite的量化转换工具对RNN网络不太友好...期待各位大神的意见建议,感谢!
本楼点评(7) 收起
  • Zongjun你理解的基本透彻,mean 和std可以直接影响inference accuracy。但是mean和std并不是简单的对输入数据做normalization而得到的。做完normalization,如果输入的值的范围是float32 (0.0, 1.0),那么用toco转化成uint8时,mean = 0, std = 255. 所以要看的是最终输入的极值。
    mean =  the uint8 value in the range [0, 255] that corresponds to floating point 0.0. So if the float range is [0.0, 1.0], then mean_value = 0.
    std = (uint8_max - uint8_min) / (float_max - float_min). So if the float range is [0.0, 1.0], then std_value = 255 / 1.0 = 255.
    (这两个公式来自谷歌,应用在我的项目上也是成立的。)
    没有收集到的min和max,应该使用手动插入的方式进行伪量化吧。default_ranges_stats准确度应该没有那么高。
    RNN的话,这个按照谷歌的说法好像是正在开发中,不知道何时才能完善。
    2019-2-20 01:51 回复
  • ake123100回复 Zongjun :感谢大神的回答~mean和stderr我再继续学习下. RNN的INT8量化业界例子貌似很少...只有TensorRT3.0官方说是可以支持,还需要特定类的GPU支持...Orz
    2019-2-20 09:21 回复
  • Zongjun回复 ake123100 :TensorRT 是NVIDIA的那个吧。我当时觉得用起来有点麻烦就还是选择用传统的tensorflow了。祝你成功哈!
    2019-2-21 02:53 回复
  • yunxinmengze回复 Zongjun :大神,你好!
    我在做全量化的时候,网络模型中加入slim的batch_norm层,然后通过speech_commands里面的freeze.py生成.pb文件
    最后toco工具转换,就会出现下面这个问题
    Array DS-CNN/conv_1/batch_norm/FusedBatchNorm_mul_0, which is an input to the Add operator producing the output array DS-CNN/conv_1/batch_norm/FusedBatchNorm, is lacking min/max data, which is necessary for quantization. If accuracy matters, either target a non-quantized output format, or run quantized training with your model from a floating point checkpoint to change the input graph to contain min/max information. If you don't care about accuracy, you can pass --default_ranges_min= and --default_ranges_max= for easy experimentation.

    要是去掉bn层,就可以正确的转换,请问这是什么原因导致的
    2019-2-25 16:07 回复
  • Zongjun回复 yunxinmengze :你这个报错说的是在伪量化训练的时候,你的这个Node没有训练它的min和max。你用一个叫Netron的软件打开你的.pb文件看一看,伪量化成功的Node是带有fakequant node的,你报错的这个应该没有。
    如果你是在做speech commands这个project的话,batch_norm应该有自己较底层的api来实现而不是用比较高等级的slim api。还不行的话,可以尝试一下在你报错的这个node前面或后面手动插入fakequant node来训练它的min和max,这个理论上一定会成功。
    如果你的project都是用slim写的,别用speech_commands 的freeze,自己用slim写一个freeze比较好,我有一个project完全是用slim写的,然后去tflite做量化,batch_norm是不存在你这个错误的。
    2019-2-26 01:42 回复
  • yunxinmengze回复 Zongjun :通过查看pb文件发现slim.batch_norm层变成了FusedBatchNorm,不明白为什么会出现这个问题?请大神指点迷津

    然后我底层实现了bn层就可以顺利的生成tflite文件。

    还有一点不太明白的是speech_commads里面的freeze.py和您说的自己实现的slim版本的freeze有什么区别,前向网络模型就是用slim写的啊,其他的函数就只有tf.contrib.quantize.create_eval_graph()和graph_util.convert_variables_to_constants这两个函数了。不太明白大神说的用slim实现freeze是怎么的一个改法?
    2019-2-26 11:50 回复
  • Zongjun回复 yunxinmengze :fused batch是tensorflow的优化。具体链接:https://github.com/tensorflow/tensorflow/tree/master/tensorflow/contrib/quantize 看folding batch norm layers那里。
    底层实现顺利的话你可以用interpreter测一测整个模型,看看推理正确率是不是正常。(但是理论上,你的模型是slim写的, slim batch_norm应该可以的。要看你具体的项目和代码细节了。)
    至于freeze,你不是说你用的是speech_commands的freeze.py嘛,那个文件非常的project specific,比如里面包含了对音频的preprocess啥的。既然你模型都是用的slim,那你就不是在做speech_commands这个例子了。那肯定要改一改那个文件再用,我是这个意思。。其实就是你说的那样,需要create_eval_graph()和graph_util..这两个函数。
    2019-2-28 03:31 回复
您需要登录后才可以回帖 登录 | 加入社区

本版积分规则

快速回复 返回顶部 返回列表