发新帖

tensorflowLite的量化使用问题

[复制链接]
8579 69
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网络结构
本楼点评(15) 收起
  • 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网络不太友好...期待各位大神的意见建议,感谢!
本楼点评(8) 收起
  • 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 回复
  • 龙仔回复 yunxinmengze :您好,请问一下的关于bacth_norml的层的问题你是怎么解决的呢?
    2019-4-20 16:04 回复
yunxinmengze  TF荚荚  发表于 2019-2-26 11:37:09 | 显示全部楼层
C:\Users\gaoyu\Desktop\0ED794D3-3661-4afa-8892-07A66A75BE67.png
本楼点评(0) 收起
人工智障_(:」ㄥ  TF荚荚  发表于 2019-3-7 15:04:37 | 显示全部楼层
Zongjun 发表于 2018-11-15 06:19
首先,我记得官方的mobilenet是用slim写的吧?slim我没用过,但是fake quantization本质应该是一样的。
我 ...

请问一下train graph和eval graph是同时创建还是先训练好了再创建eval graph,加载训练的checkpoint呢?另外我这边量化进行到最后一步用toco convert to tflite的时候总是出现up supported operator 'Cast',请问您知道是怎么回事吗?我猜可能是train时fake quant节点在图里面没有删除掉
本楼点评(6) 收起
  • Zongjun我的项目基本是有一个函数,它叫做create_model,专门用来生成图。训练时调用这个函数,生成训练图然后训练,存出来checkpoint files。然后我在另一个完全独立的文件里,再调用create_model这个函数,生成推理图。然后把刚才训练好的checkpoint导入到这个新的推理图中,然后在这个文件里做freeze生成 .pb文件。
    至于fake quant node是否处理正确了,这个和你模型的具体结构有关。我推荐你下载一个叫Netron的软件,用这个软件直接打开你的 .pb 文件。你可以直接看到你图的结构,在需要量化的层(即,数据类型为float32的层)是有fake quant node出现的。如果没有,可能就需要手动插入fake quant node了。
    2019-3-11 05:41 回复
  • 人工智障_(:」ㄥ回复 Zongjun :谢谢您的回复,我去用netron看看
    2019-3-11 16:34 回复
  • jinyuprincess回复 Zongjun :大神你好,想请问一下,用speech-commands例子跑的模型,训练的时候train.py --quanlize=True,但是用freeze.py生成.pb文件后,用netron看模型,并没有quantize node,想请问会是什么原因呢?
    2019-3-13 20:14 回复
  • Zongjun回复 jinyuprincess :这个不应该啊。你freeze.py的指令也 --quantize=True了嘛?
    2019-3-16 01:31 回复
  • jinyuprincess回复 Zongjun :谢谢大神,解决了,没注意到这里,大神每次发现问题都是一针见血~
    2019-3-20 11:18 回复
  • 小祥我也在最后一步试图转换pb到tflite模型时出现 unsupported operator 'Cast', 请问你最后解决这个问题了吗。
    2019-6-13 11:49 回复
sunyanli  TF荚荚  发表于 2019-3-13 16:40:14 | 显示全部楼层
Zongjun 发表于 2018-11-6 08:49
对了,我补充一点,我debug的时候花了不少时间的一个地方:我用的是在linux下,安装tf.nightly,然后jupyter  ...

大神,想请教您一个问题。我按照伪量化的方式量化了lenet网络,但是量化之后,用Netron查看时发现 conv2D卷积变成了depthconv2D卷积。depthconv2D卷积不是深度可分离卷积吗?不是在inception网络中设计出来的吗?希望大神可以指点一下。谢谢。
本楼点评(6) 收起
  • Zongjun我没有用过Lenet,所以没有深度研究过这个网络。但是,对于你的问题,我有一个猜测,在.tflite里如果conv2d 变成了 depthwise conv2d,你需要看一下你的conv2d 的输入是不是只有一个channel,比如一张黑白图片,它就是一个channel的.如果是的话, toco会自动做一个optimization把它变成depthwise conv2d。官方实现代码:https://github.com/tensorflow/tensorflow/blob/master/tensorflow/lite/toco/graph_transformations/convert_pure_conv_to_depthwise.cc。
    2019-3-16 01:37 回复
  • Zongjun然后就是depthconv2d还不是深度可分离卷积,深度可分离卷积叫做 depthwise separable conv2d.后者是有一个depthconv2d   pointwiseconv2d生成的。pointwise conv2d本质是一个1x1 的 conv2d。depthwise separable conv2d是可以用来替代conv2d的,而 depthwise conv2d自己只能替代 input 为 单个channel的conv2d,就像楼上我说的那样,官方会在后台自动帮你转化。
    还有就是,我感觉一个kernel设计出来,其目的肯定不是只服务于一个网络的。可能是因为Inception搞出来的,但是也完全可以用于其它网络的。嗯,一点个人见解哈。
    2019-3-16 01:43 回复
  • sunyanli回复 Zongjun :谢谢您的回复。您说的我再去理解理解。就是depthwise2D。我之前写错啦!小白对底层代码还不熟,您说的我还得在看看,理解理解。再次谢谢您。
    2019-3-17 22:55 回复
  • sunyanli回复 Zongjun :大神,谢谢您,我这个的lenet的输入通道就是1。今天大致看了一下这个实现过程,对于这个也有点理解啦。既然深度分离卷积可以大大减少计算量,是不是也可由实现当输入通道数为3时,也可以转变成depthwise separable conv2d卷积啊?
    2019-3-18 16:01 回复
  • sunyanli大神,想再请教一个问题,如果我在量化的过程中只量化卷积层,或是只量化全连接层,应该就是在只量化的层加入伪量化节点,请问这个是在哪里改啊?
    是直接改掉对应的这个函数吗_InsertQuantOp(
                post_activation_bypass_context,
                'post_activation_bypass_quant',
                layer_match.post_activation_bypass_op,
                consumers,
                is_training,
                moving_avg=True,
                ema_decay=ema_decay,
                quant_delay=quant_delay,
                vars_collection=vars_collection,
                bits=activation_bits,
                symmetric=symmetric,
                producer_scope=scope)
            quantized_ops.add(layer_match.post_activation_bypass_op)
    2019-3-21 20:53 回复
  • Zongjun回复 sunyanli :只量化某一个层的话。。我觉得肯定是不能用create_training_graph这个api了。要手动在你需要量化的层插入fake quant node。话说你的问题好奇特哈!
    2019-3-27 12:53 回复
sunyanli  TF荚荚  发表于 2019-3-14 16:30:35 | 显示全部楼层
Zongjun 发表于 2018-10-24 05:38
以下是我的个人理解,如果是在android 上,Tensorflow Lite (tflite) 可以利用hardware accelerator,应该 ...

大神,您好,我最近也在在关于量化的东西,看了您的博客以及另外一个大神的博客,感觉对于这个tensorflow的量化,越来越模糊啦。现在我根据我的认知说一下我的理解(底层代码看了一些,但是看得稀里糊涂的)。1、google的论文(Quantization and Training of Neural Networks for Efficient Integer-Arithmetic-Only Inference )和tensorflow的quantization-aware-training的量化方法是相同的。2、我看了您的解答,您说的这个加入伪量化是在原来图中加入fakequantizationnode,记录该节点的最小值与最大值,模拟了quantized—graph.py工具中的逆量化和反量化操作,这个确实也是,我自己也做了这两个实验,确实quantization-aware-training量化之后的tflite文件只有伪量化节点,并且有最大值和最小值,而quantized—graph.py的pb文件里面有requantization和dequantization。但是我在看其他博主在将google量化算法时,都有逆量化这个操作,比如:https://github.com/Ewenwan/MVisi ... on/quantization/IAO。3、现在我在想是不是quantization-aware-training和quantized—graph.py方法是不是内部实现的算法都是一样的,我也去看了他们的代码,可是我这种小白看的都是模模糊糊的,不能从大的框架上看整体。4、有个问题请教您一下,加入伪量化记录节点大小,这个大小是怎么记录的啊?我去看的quantize.py代码,里面只有一个_InsertQuantOp函数,目前我看这个d代码也只是大概知道他的意思,把每一个需要插入伪量化的节点,插入伪量化,更详细的还的看代码。因为自己水平有限,我目前只知道quantization-aware-training方法具体的做法,其中的原理只知道是用的均匀量化器,不知道大神那边有没有其他文档版的资料,可以提供我这个小白参考一下。5、还有一个问题,quantization-aware-training和quantized—graph.py还有那个post-quantization量化方法是不是其量化的方案都是相同的,都是
本楼点评(24) 收起
  • Zongjun你这些问题咱俩如果能语音就好了,几句话就讲明白了。打字就很痛苦了,我慢慢回复哈。
    2019-3-16 01:46 回复
  • Zongjun首先,我没有写过任何这方面的博客,不知道你看了谁的,反正那不是我哦。我这边公司有严格规定的。我属实是不敢和公司打擦边球。。
    然后我尝试着解答一下你的疑惑哈。
    1.底层代码是你最好的老师,我也是一行一行代码研究出来的,我做的还是把这些东西全放进单片机里。所以,你得逼着自己看底层代码,看不懂的话反复看。比如你上面那个问题,答案确实是在底层的c  代码中。
    2. 你可以放下quantized_graph.py这个文件了,现在官方的github中都没有它了。原因就是,它被tensorflow lite完全取代了。你问题中提到的这个github链接,它是7个月之前的方法。
    3. 那么现在就剩下了 quantization-aware training = fake quantization = 伪量化这个方法。它也是现阶段tensorflow唯一能实现fully quantized的方法。其通过训练时加入 fake quant node来学习op的min 和 max。注意,是学习,也就是说你的网络是怎么学习的,你的min 和max就跟着怎么学习,比如gradient descent。
    而之前quantized_graph.py是在你训练后来进行量化,所以它要一路带着requantize和dequantize。所以,我建议还是转到tensorflow lite吧。
    2019-3-16 02:10 回复
  • Zongjun4. 还有一个不是方法的方法,就是你最后提到的post-training quantization,它去年刚出来时让我一度看到了希望,然而它在推理时居然还是用float32 kernel来做计算。。。这一下子就用处不大了,因为在我的微控制器上,float32计算极其昂贵。
    所以这就要看你做量化的最初目的是啥了。我的就是要求完全量化,那伪量化就是我的唯一选择。。除非我完全放弃tensorflow转去Arm的 CMSIS。但是那个的可用性和简洁性肯定是不如tensorflow的(前期选工具时,两者我做过比较)。嗯,还是看你项目的具体要求了。
    2019-3-16 02:21 回复
  • sunyanli回复 Zongjun :非常感谢大神的回答。我是通过博客找到这里的,那个博主也在这里问了很多问题,我入门完成伪量化的量化也是通过他。真的非常感谢你们这些大神。我是一名学生,对这个理解的不是很到位。我看量化的知识,是因为我的论文方向是关于量化相关的。我要以它发论文的。我本来是想将剪枝的方法和量化结合一下发一篇论文。1、不知道把这个伪量化方法加入裁剪操作是否可行?
    2、上文我提到的那个连接里面说的是关于google的那边论文的量化方法,这篇论文我看了,它讲的也是在训练过程中加入的伪量化操作,所以我把这个和quantization-aware-training归为一个方法。
    3、加入 fake quant node学习最大值和最小值,它是自动识别网络中需要量化的节点吗?比如权重和激活值。还有您说他说自己学习最大值与最小值,是在训练过程中每迭代一次,权重更改之后fake quant node都会自动记录他的最大值与最小值,是吗,
    4、在反向传播过程中,论文(量化白皮书)中有提到直通滤波器(straight through estimator),这个操作是在求梯度时,用全精度值,
    5、还有一个问题,关于数据溢出。这个问题是调用了gemmlowp库,解决的吗?比如在卷积操作之后,两个8bit的值相乘,会变成32bit,此处的是如何解决的啊?是通过只取重要的8bit吗?
    6、我用quantization-aware_training量化,生成的pb文件与tflite文件,其中的卷积节点的名称发成了改变,从conv2D变成啦depthconv2D,这个是什么原因啊?
    7、那个quantize_graph.py方法虽然官网上已经溢出,但是我还有一个移位,就是那个量化之后,为什么还要逆量化为float型?
    再次感谢您的回复。
    2019-3-17 15:07 回复
  • sunyanli大神,您说的我那个连接是7个月之前的量化方法,那那个方法原理和quantization—aware-training有什么区别吗?我看了他提供的源码,也看了quantization-aware-training,都是调用了gemmlowp库啊?大神希望有时间可以指点一下我。要是大神感觉我的问题打字回答不方便,可以加微信聊一下吗?我的微信:syl0318521。谢谢您啦。
    2019-3-19 14:44 回复
  • Zongjun回复 sunyanli :这两天在休假,回复的有点迟哈。裁剪的英文是啥啊,pruning吗?如果是pruning的话,伪量化加裁剪我觉得没什么冲突。我也没尝试过,这个肯定是自己上手试一试最好了。
    fake quant node自动识别某些节点,具体识别哪些底层代码中是有的。不被自动识别的需要手动插入fake quant node。学习应该就是back propagation的时候自动更新min max的。
    两个8bit相乘,这个操作本身也是要在tensorflow lite toco转化之后发生了,因为伪量化只是记录min和max,训练过程中全程依然是float32的计算。toco完成fully quantization之后,做推理时才会用到uint8的计算kernel. 此时,我们可以接受int32的数据类型,你打开完全量化好的一个.tflite文件,会发现bias的数据类型就是int32的。
    这个第6点,你看了的话,我就不说啦。
    第7点,你读一遍pete warden的blog就明白啦,他讲得挺清楚的。https://petewarden.com/2016/05/03/how-to-quantize-neural-networks-with-tensorflow/ 这篇讲的就是以前那个方法的具体操作。你可以瞅瞅哈。

    2019-3-20 01:07 回复
  • sunyanli好的大神,pete的这个博客我之前有看过。逆量化是因为在进入下一个操作的输入也是要量化后的,而量化需要最大值和最小值(float),所以才逆量化的。
    还有一个问题,大神,关于那个那个后向传播的直通滤波器(straight through estimator),是怎么理解的呢?我看的白皮书原文:We model the effect of quantization using simulated quantization operations on
    both weights and activations. For the backward pass, we use the straight through estimator (see section 2.4) to model quantization。这个我真的不是很理解。希望大神再指点一下。
    2019-3-21 13:25 回复
  • sunyanli大神,我再请教一个问题,我将模型量化4bit,我该了quantize.py代码里面的Quantize函数里面的bit,将之前的8改成4。但是没有量化成功。我分析了一下,我们之前加入伪量化操作,只是获取需要量化节点的最大值最小值,那我们量化成几bit,主要是看用toco工具里面的操作,是吗?void Transform(const TocoFlags
    2019-3-22 10:20 回复
  • sunyanli回复 Zongjun :大神,不知道您有没有看到我的回复,我之前打开这个网页,看到一闪的回复提醒,可是点进去没有看到。
    2019-3-27 10:59 回复
  • Zongjun回复 sunyanli :嗯。刚度假回来。。整个人都变成烤地瓜了。。量化的话,目前只能是8 bit吧。我目前还没遇到过4 bit量化成功的例子。toco里也只有两个选择 float32和uint8.
    2019-3-27 12:45 回复
您需要登录后才可以回帖 登录 | 加入社区

本版积分规则

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