发新帖

tensorflowLite的量化使用问题

[复制链接]
14874 79
九幽  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)
复制代码
但是就在测试的时候报错:

本帖子中包含更多资源

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

x
本楼点评(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 回复
人工智障_(:」ㄥ  TF荚荚  发表于 2019-3-17 12:03:22 | 显示全部楼层
本帖最后由 人工智障_(:」ㄥ 于 2019-3-17 12:05 编辑

我这边卡了两周bug以后走通了,讲一下我遇到的问题吧,tensorflow挺坑的,而且一些琐碎问题非常non-sense。我是做卷及网络的量化,基于mobilenet v2做的一些改进,操作基本上还是mobilenet v2的那些操作(conv2d, separate_conv2d, relu6, batch_norm),版本是tf1.12和tf1.13(这两个版本tf1.12里是tf.contrib.lite,tf1.13就直接tf.lite了),用的api是tf.contrib.slim(2.0以后contrib没有了,也不知道该怎么办)
为了成功量化,网络不能按常规方法定义没,而是要写成这样,我举个例子:

with slim.arg_scope([slim.separable_convolution2d],
                            normalizer_fn=slim.batch_norm,
                            activation_fn=tf.nn.relu6):
                with slim.arg_scope([slim.batch_norm],
                                      is_training=is_training,
                                      center=True, scale=True):
                            x = slim.separable_convolution2d(x, out_channel, [3, 3],
                                                                 depth_multiplier=1, stride=stride,
                                                                 biases_initializer=None, biases_regularizer=None)

而且我每一层要这样写,最后在整体网络上还要加上类似的with slim.arg_scope来定义卷积层的activation和norm,但是在中间一层调用backbone的时候却不用写这个,我也不知道为什么。试了很多次不同的排列组合,最后玄学成功了。

后面的训练和量化就是直接调用creat_training_graph和creat_eval_graph,然后用python api的tflite_converter。最开始在mac上面出问题,在linux上面不能直接用python api的convert_from_session,必须要导出frozen graph再用命令行工具转换。后来mac上可以成功转换了,但是linux好像还是得用命令行covnert from frozen graph。

说了这么多,总结一句话就是tensorflow太坑了,caffe2的量化工具开源以后大家可以去试试fb的那一套。
本楼点评(0) 收起
shenyichen105  TF荚荚  发表于 2019-3-23 07:22:35 | 显示全部楼层
本帖最后由 shenyichen105 于 2019-3-23 07:27 编辑

同卡在这个坑里两个星期, 今天终于爬出来了。 我网络是用keras写的一维mobilenetV2。  上来先花了一个半星期研究如何让create_training_graph和create_eval_graph两个api创建正确的fake quantized graph。读了半天源码最后发现那个api没法识别keras batch normalization层, 只能自己用tf的api重写了一个网络然后把keras网络里用float32预训练好的权重转进去, 因为有bn层的存在,training和eval的图还不一样,只能分别建图然后互相转weights。试了很多错以后终于成功转换 。 不过在第二步convert成tflite全量化模型之后发现精度完全不行。整整debug了一星期之后最后发现是input_stats没调好。这个参数相当重要!差一点点最后inference结果就差很多。 最后是手动先把我的数据量化成(0,255)然后设置input_stats = (0,1)终于成功。感觉tf的工具真的挺难用的,不过似乎目前在量化的方向没有其他的选择。
本楼点评(4) 收起
  • ZongjunExactly...我当时调input_stats也调了好长好长时间,然后终于搞明白了。因为它决定了 float32如何量化至uint8,所以差一点结果就会差很多。。
    2019-3-27 12:49 回复
  • shenyichen105回复 Zongjun :没错。。btw还是要感谢大神你的之前的回复提醒了我 否则我可能还不知道要同时创建training和eval两个不同的图才能正确转换,估计现在还陷在坑里呢。。
    2019-3-28 08:34 回复
  • Zongjun回复 shenyichen105 :不谢不谢!互相帮助哈!
    2019-3-29 01:39 回复
  • 九幽您好,我之前是keras写的yolo,但是到了量化阶段就完全不知道怎么写那个output了,,,想问下您的keras那个版本是怎么创建两个图的啊?
    2019-4-8 11:53 回复
Smit  TF荚荚  发表于 2019-3-29 14:44:29 | 显示全部楼层
Zongjun 发表于 2018-12-17 02:23
1.请问你用的是什么版本的tensorflow?你要去下载tf.nightly版本,这个我跟你建议过两三次了,你好像没给 ...

Hi,
@Zongjun 感谢大神给大家耐心的答疑,我最近在做基于ssd的量化训练的东西,您应该是我能求助到的最靠谱的人了
我现在的情况是,将ssd模型改成多输出模式(因为用的第三方的代码,包含了一些不支持的op),已经量化训练,生成了tflite文件,我想求教的问题是:
1.如何在本地测试tflite文件呢?您有什么例子可以提供吗?目前没什么方向。。
2.不太清楚对于多输出的目标检测网络,怎么来写测试脚本……把softmax的结果直接合并就可以吗?
3.我看上面有个人贴出来的代码,有tf.contrib.lite.Interpreter,可是我这里报错'tensorflow.contrib' has no attribute 'lite',难道是版本问题?
4.最后一个问题是十分好奇的了,网上也没有搜到,用toco或者bazel工具时候,--mean_values --std_dev_value这两个参数是根据经验选取的吗?有什么常用参数呢?

谢谢!期待您的回复
本楼点评(16) 收起
  • Zongjun1. 测试.tflite模型,我用过python interpreter,基本就是调用api,你可以试试哈!
    3. tensorflow github里原先lite这个文件夹是存在于contrib文件夹中的,现在lite不在contrib中了,直接调用tf.lite.interpreter试一下?
    4. 这个问题我研究了很长时间,目前得到了确切答案。你看这个链接:https://stackoverflow.com/questions/54261772/tensorflow-lite-toco-mean-values-std-values. 这个问题的答案是谷歌官方的回答。
    2019-4-1 13:08 回复
  • Zongjun2. 我目前没有做过多输出的目标检测网络,所以给不了什么好的建议。感觉可以stackoverflow上搜一下或者问一下?嘿嘿。
    2019-4-1 13:10 回复
  • Smit回复 Zongjun :谢谢!我在网上找到分类模型测试tflite的代码,但是迁移到检测模型上我就有些糊涂了,我想请教一下:
    1.本地测试tflite,并不是使用sess-run的方式?似乎是先allocate_tensors,读取数据后,再set_tensor-invoke-get_tensor,是这样的流程吗?
    2.我再写测试脚本的时候,意识到好像tflite要包含location信息,这样加载tflite脚本中get_output_details的时候,location和classification都要直接获取,不知道我理解的对不对。。
    3.这样就引出我的最不知道如何解决的问题了......我TF使用时间不久,不太熟悉,转tflite的过程中,将inference阶段的图freeze到pb的时候,我只保存了softmax信息,location不知道怎么保存。。。我感觉说的语无伦次,我贴一下代码,麻烦大神帮忙看看
    2019-4-1 17:19 回复
  • Zongjun回复 Smit :1. tflite的测试可以考虑使用python tensorflow lite interpreter api。这个也是在sess下运行的。
    2.如果用上述interpreter的方法,我没有碰到需要location信息的地方,可能我不是很理解何谓location?
    不清楚location的节点名字的话,这一点比较好解决。用Netron,打开你的.pb file,整个网络的细节甚至参数的具体数值都能看到。(当然要先去下载安装Netron,这个比较简单,点两下鼠标就安好啦。)
    2019-4-3 04:43 回复
  • Zongjun回复 Smit :Netron链接:https://github.com/lutzroeder/netron
    2019-4-3 04:43 回复
  • Smit回复 Zongjun :好的好的,我试试看,谢谢~
    2019-4-3 16:17 回复
  • Smit回复 Zongjun :hi, 我想再请教您一个问题,是我在测试tflite文件的时候遇到的。我做的是ssd的量化,一切都成功了但是测试tflite的时候,发现不返回任何box,我debug发现,利用tflite_model.get_tensor(output_details[idx]['index'] 从tflite中获取的box信息,跟我在未freeze的ckpt的debug结果中看到的截然不同:
    ckpt获取的box坐标很正常,有很多小数,但是tflite获取到的,是诸如:[15  0  0  0] [ 0  0  9  9][37 15  4  0],怎么会有这么多0呢,而且跟ckpt的查别也太大了
    我想请教的是,您遇到过这种问题吗?会不会是freeze时候出的问题呢?
    谢谢~
    2019-4-8 21:04 回复
  • Zongjun回复 Smit :如果ckpt的结果是对的,说明training没问题。如果怀疑freeze出问题了,我的想法是freeze完得到的.pb文件,先用Netron看一下,是否有问题。如果没有问题,用Tensorflow load进这个.pb 文件,读入Model,再做测试,看看是不是正确。如果正确,那么freeze也没有问题,那么就剩下toco了,你关掉所有的量化,toco就是直接生成一个float32的tflite模型,看看有没有上述的bug,如果有可能量化出问题了。关掉了量化要是还不行,就考虑用他提供的pre-trained的例子跑一下看看有没有这个bug。嗯。
    2019-4-9 07:27 回复
  • Smit回复 Zongjun :Hi 大神,我尝试另一种方法进行量化,似乎更有效,
    convert = tf.contrib.lite.TFLiteConverter.from_frozen_graph("frozen_graph4_8.pb", input_arrays=input_, output_arrays=output_)
    convert.post_training_quantize = True
    tflite_model = convert.convert()
    open("model.tflite", "wb").write(tflite_model)
    我想请教的是:
    1.这样转化跟toco转化是等价的吗?
    2.为什么toco转出来的,跟上述代码转出来的tflite,用Netron查看,不太一样呢?
    用代码转的tflite中有dequantize这个op,我可否认为这是成功的标识?
    3.最后想请教您一个工程问题,一般量化后模型的精度会下降,您有什么优化技巧或经验吗?
    再次,万分感激!
    2019-4-10 09:13 回复
  • Zongjun回复 Smit :啊,不对,你第二种方法用的是post_training quantization。这个方法只是quanitze weights而不会quantize bias和activations.并且inference的时候是转化回float32进行计算。所以如果你训练时做了fake quantization,那么toco的地方就不要用post_training_quanitzation了,直接用QUANTIZED_UINT8。
    我用toco一直是你的这个第二种形式,即python api。所以第二种这个形式没错,就是你的flag有些问题。这个方法就是toco。只不过叫做 TFLiteConverter而已。
    理论上fake quantize了之后再用tflite fully quantize,你的model的准确率不应该下降很多,除非:
    1. 你的model非常小,非常简单,这个时候可能会有问题。
    2. 你的toco声明的mean 和std是错的,这两个数错一点,最后的inference就会错很多。具体你可以翻一翻这篇以前的评论,我应该有公式。
    2019-4-12 07:36 回复
nick_nie  TF荚荚  发表于 2019-4-7 19:25:12 | 显示全部楼层
@Zongjun, 大神,你好!最近在用腾讯开源的pocketflow做量化,遇到了一个小问题,就是在量化训练结束后,需要将生成的meta文件转pb,然后再转tflite,我原先训练的时候是固定size的,现在做测试时想要任意大小,就将下面代码placeholder的shape改成[1, None, None,3],但是报错:ValueError: None is only supported in the 1st dimension. 所以说有办法测试时是任意大小的吗?
# restore the graph with inputs replaced
    net_input = tf.placeholder(tf.float32, shape=[1, None, None, 3], name=net['input_name'])  # input_name: net_input
    saver = tf.train.import_meta_graph(
      file_path_meta, input_map={net['input_name_ckpt']: net_input})  
    saver.restore(sess, file_path_meta.replace('.meta', ''))
本楼点评(2) 收起
  • Zongjun不好意思,我没用过腾讯的这个pocketflow哎。不过,看了一下你的问题,我感觉这个post可能对你有用,我不确定哈。https://github.com/tensorflow/tensorflow/issues/21986
    2019-4-9 07:38 回复
  • nick_nie回复 Zongjun :没事哈, 感谢大神回复~~
    2019-4-9 10:38 回复
nick_nie  TF荚荚  发表于 2019-4-10 01:21:37 | 显示全部楼层
之前看到论坛里有好多对基于slim框架的模型量化成功了,我最近基于deeplabv3+的模型进行量化遇到了一个问题,首先导入了resnet_v2_101的backbone,deeplabv3+模型后面的部分我在debug时先注释掉了,然后前向传播是没有问题的,就是在开始create_training_graph时报了错:tensorflow.python.framework.errors_impl.InvalidArgumentError: Cannot update edge, incompatible shapes: [?,8,8,512] and [?,16,16,512].

以下是模型部分代码:

  1. if FLAGS.base_architecture not in ['resnet_v2_50', 'resnet_v2_101']:
  2.         raise ValueError("'base_architrecture' must be either 'resnet_v2_50' or 'resnet_v2_101'.")

  3.     if FLAGS.base_architecture == 'resnet_v2_50':
  4.         base_model = resnet_v2.resnet_v2_50
  5.     else:
  6.         base_model = resnet_v2.resnet_v2_101

  7.     with tf.contrib.slim.arg_scope(resnet_v2.resnet_arg_scope(batch_norm_decay=_BATCH_NORM_DECAY)):
  8.         logits, end_points = base_model(inputs,
  9.                                         num_classes=None,
  10.                                         is_training=is_train,
  11.                                         global_pool=False,
  12.                                         output_stride=FLAGS.output_stride) # output_stride=16
  13.      inputs_size = tf.shape(inputs)[1:3]
  14.     net = end_points[ FLAGS.base_architecture + '/block4']  # [None, 16, 16, 2048]
复制代码

输入size是256x256x3,有大神知道是怎么回事吗?
本楼点评(2) 收起
  • 徐建君你好,我也遇到与你一样的问题,请问你解决没?
    2019-5-10 00:39 回复
  • nick_nie回复 徐建君 :感觉create_training_graph 对 dilated convolution 会有bug,没有再往下继续了,不好意思!  
    2019-5-20 13:02 回复
龙仔  TF荚荚  发表于 2019-4-20 14:40:34 | 显示全部楼层
本帖最后由 龙仔 于 2019-4-20 23:14 编辑

@Zongjun,您好,我最近一直在尝试量化训练,但是遇到一个问题一直没有找到解决方案。在我的网络中有batch_normalization 层, 在转tflite的时候一直提示我“FusedBatchNorm_mul_0, which is an input to the Add operator producing the output array kws_model/KWS_Model/tower_0/CNN_V1/Relu, is lacking min/max data, which is necessary for quantization. Either target a non-quantized output format, or change the input graph to contain min/max information, or pass --default_ranges_min= and --default_ranges_max= if you do not care about the accuracy of results." 我尝试将bn层删除但是又紧接着提示我最后一层出现的错误“prediction does not have MinMax information, and is not a constant array. Cannot proceed with quantization.” 请问这个问题可能是什么原因导致的呢?最后一层正常来说需要最量化么??多谢
本楼点评(11) 收起
  • Zongjun这个问题我也碰到了。我目前用batch_normalization唯一成功的套路是,在tf.slim中做mobilenet 的伪量化,其中加入了bn。我freeze模型以后得到.pb文件,用netron打开看,我发现bn是被分解成了一个一个的add/mul 操作而不是一整个叫做fusedbatchnorm的node。
    直接用fusedbatchnorm这个操作,我得到的错误和你是一样的。我没有再往下去debug这里,我的项目要求是计算量尽可能的小。所以我感觉你可以看一下slim的mobilenet那里的batch norm,看看能不能用到你的项目上?
    2019-4-24 01:01 回复
  • 龙仔回复 Zongjun :好的,多谢大佬深夜回复。
    2019-4-24 11:05 回复
  • 龙仔回复 Zongjun :暂时将bn层去掉了,先规避了这个问题。
    2019-4-24 11:10 回复
  • Zongjun回复 龙仔 :不客气不客气,你将来要是debug出了这个问题可以回来分享一下哈!Good Luck!
    2019-4-26 00:42 回复
  • 我本是山下那棵回复 Zongjun :您好,我使用lite模块将训练好的pb模型转换为lite模型后,lite模型在PC端的测试不同分类图片都为同一个结果,output仅包含1.和0.(softmax节点),pb模型运行正常,分类正确。请问您遇到过类似的问题吗?对于这个问题想了好久没有思路,如果您了解这方面的知识希望不吝赐教。
    2019-6-19 21:12 回复
  • Zongjun回复 我本是山下那棵 :如果用的是quantization-aware training,在tflite这里要启用fully-quantized。还需要在toco的指令里给出mean和std这些input stats,这两个值稍微错一点,预测结果就会差非常大。(因为你的pb模型预测正常,我就默认你的训练和模型没什么问题了。)
    首先,我觉得应该检查模型本身。用Netron这个软件打开你的.tflite文件,看看你的模型是否正确。该有的层都在,该量化的都量化了。内部参数看起来比较正常。这些都需要先确保正确。
    然后可以调一下mean/std那两个值,看看有没有什么变化。这两个值具体的公式我记得我在这个帖子里贴过,你可以找找看哈。
    2019-6-20 00:44 回复
  • 我本是山下那棵回复 Zongjun :前边的问题已经解决,是由于网络中存在batch_normal造成的精度问题,请问我的quantization-aware training是正确的吗?虽然结果是正确的,也可以看到mIn 和max但是在pb转tflite的时候出现了这个错误ValueError: Input 0 of node conv2/weights_quant/AssignMinLast was passed float from conv2/weights_quant/min:0 incompatible with expected float_ref.
    2019-6-24 21:23 回复
  • 我本是山下那棵回复 Zongjun :class mobileNet:
        training = True
        def __init__(self, height=160, width=160, channel=1, n_class=100):    # <tf.Tensor 'x:0' shape=(?, 160, 160, 1) dtype=float32>

            self.tfx = tf.placeholder(tf.float32, [None, height, width, channel], name='x')
            self.tfy = tf.placeholder(tf.float32, [None, n_class], name='y')


            layer1_conv1_in = channel
            layer1_conv1_out = 60

            conv1_weights = self.wight_variable([3,3,layer1_conv1_in,layer1_conv1_out])
            conv1_biases = self.bias_variable(shape=[layer1_conv1_out])        

            conv1 = tf.nn.conv2d(self.tfx, conv1_weights, strides=[1, 2, 2, 1],padding="SAME",name="conv1")   conv1_biases
            pool1 = self.max_pool(conv1, 'pool1')

            .....
                    .....
                    .....

            nodes = conv6.shape[1]._value * conv6.shape[2]._value * conv6.shape[3]._value  # nodes 38400
            self.flatten = tf.reshape(conv6, [-1, nodes])
            self.out = tf.layers.dense(self.flatten, n_class, name='out')  # <tf.Tensor 'out/BiasAdd:0' shape=(?, 100) dtype=float32>
            self.softMAX = tf.nn.softmax(self.out, name='softMAX')
            self.sess = tf.Session()
            self.loss = tf.losses.softmax_cross_entropy(onehot_labels=self.tfy, logits=self.out)
            if self.training:
                tf.contrib.quantize.create_training_graph(input_graph=tf.get_default_graph(), quant_delay=100)
            
                    self.train_op = tf.train.AdamOptimizer(0.001).minimize(self.loss)
            self.correct_prediction = tf.equal(tf.argmax(self.tfy, 1), tf.argmax(self.out, 1))
            self.accuracy = tf.reduce_mean(tf.cast(self.correct_prediction, tf.float32))
            
                    if not self.training:
                tf.contrib.quantize.create_eval_graph(input_graph=tf.get_default_graph())
            self.sess.run(tf.global_variables_initializer())
    2019-6-24 21:25 回复
  • Zongjun回复 我本是山下那棵 :我有以下想法:你的代码可能没有真正的创建两个图。代码中多写函数会清楚一些。比如,写一个build_model函数用于创建图。然后写两个script,train_script里只做train,里面创建train graph,然后有create_training_graph。另一个eval_script里,创建一模一样的只做eval的图,调用的是同一个build_model函数。eval_script的图导入train_script训练好的checkpoint files,然后create_eval_graph,最后在eval_script里freeze成.pb文件。这样可以确保把两个图完全分开。
    你的代码好像缺少了存储checkpoint file,然后新建一个graph再导入的这个步骤。
    2019-6-25 01:00 回复
  • 我本是山下那棵回复 Zongjun :您好,我完成了tflite的转化,但是在调用的时候显示Transpose op only supports 1D-4D input arrays.Node number 11 (TRANSPOSE) failed to prepare.错误的原因在于interpreter.allocate_tensors(),大概意思是我的某个张量不是1-4D,关于这点我不太了解,Stack Overflow上有人提出了一样的问题但是没有解答,请问您是否了解这方面的知识?
    2019-6-25 19:59 回复
liangdas  TF荚荚  发表于 2019-4-24 21:05:26 | 显示全部楼层
@Zongjun  
你好,请教个问题:tf.multiply操作在转tflite的时候报错
net = tf.multiply(net_resize, net, name=scope+'_add_regular')

错误信息:is lacking min/max data, which is necessary for quantization.
本楼点评(6) 收起
  • look@Zongjun 大神以及各位朋友你们好。请问我在量化训练superpoint算法时,加入create_eval_graph后,出现错误:ValueError: Training op found in graph,exiting{'ApplyAdam'}是怎么回事?和训练时创建model是一样的。谢谢
    2019-4-25 11:39 回复
  • Zongjun回复 look :你去下载一个叫做Netron的软件,用它打开看看你freeze以后得到的.pb file。tf.multiply那里应该有fake quant node,这个是记录你的min 和max的。看你的第二个问题,请问你在训练的时候用了 create_training_graph()这个函数嘛?这个是必须要用的。
    2019-4-26 00:44 回复
  • look回复 Zongjun :@Zongjun谢谢大神。在加入create_eval_graph时报错"ValueError: Training op found in graph,exiting{'ApplyAdam'}"这个问题解决了,是因为eval建图的时候调用了训练时建model的函数,表征是否训练的参数没设置正确。现在还有一个问题,报错:NotFoundError : Restoring from checkpoint failed. This is most likely due to a Variable name or other graph key that is missing from the checkpoint. Please ensure that you have not altered the graph expected based on the checkpoint. Original error:Key superpoint/eval_tower0/descriptor/conv1/conv/act_quant/max not found in checkpoint.请问大神,是因为只是这一层的max没有加进去,需要手动添加吗?现在freeze没有成功,看不了freeze.pb   谢谢您
    2019-4-26 10:19 回复
  • look百度了一下,有人说是由于使用了tf.train.AdamOptimizer()来更新梯度,所以在保存检查点的时候如果不指定则是全局保存,把优化的变量类似“w_out/Adam”这种命名规则的变量也一并保存了,自然在恢复的时候就会出现找不到XX变量。解决办法,在声明 saver = tf.train.Saver()的时候带上参数,即需要保存的变量。是这样吗?
    2019-4-26 11:30 回复
  • look把AdamOptimizer改成GradientDescentOptimizer之后还是报同样的错:Key superpoint/eval_tower0/descriptor/conv1/conv/act_quant/max not found in checkpoint。
    2019-4-26 14:50 回复
  • Zongjun回复 look :这个错误应该是你的eval graph和training graph不一样导致的。你得确定你的这两个图完全一致(除了batch normalization之外如果有的话)。如果有手动加入的node,那在你的eval也是要有的,否则你的两个图还是不一样。
    这个得看你的具体代码怎么实现的了。
    2019-5-1 00:59 回复
薛俊锋  TF荚荚  发表于 2019-4-30 15:52:43 | 显示全部楼层
大神,你好,请问一下tensorflow能做16位量化吗?我把create_training_graph(quant_delay=0)修改成experimental_create_training_graph(input_graph=None,
                                       weight_bits=16,
                                       activation_bits=16,
                                       quant_delay=2000000,
                                       freeze_bn_delay=None,
                                       scope=None),按照样例步骤,我能得到16位的my_frozen_graph.pb,但是最后一部转化的时候bazel-bin/tensorflow/contrib/lite/toco/toco \
--input_file=my_frozen_graph.pb \
--output_file=speech_my_frozen_graph.tflite \
--input_format=TENSORFLOW_GRAPHDEF \
--output_format=TFLITE \
--inference_type=QUANTIZED_UINT8 \
--input_shapes=1,98,40,1 \
--input_arrays=Reshape_1 \
--output_arrays=labels_softmax \
--allow_custom_ops这个时候的参数--inference_type=QUANTIZED_UINT8不支持16位,相当于我做了16位的伪量化,但是最后转换成8位的,精度估计会有问题,这不算是16位量化吧
本楼点评(1) 收起
  • sunyanli您好,我前一段时间也在做这个,也是这样改的,我个人感觉这个是不对的。用toco转变的时候,好像只支持float和int8,要是量化成16bit,toco这个工具源码估计也得改。
    我是这么想的。这个量化方法不是google发的那篇论文有详细论述吗,里面不是有个量化公式:q=r/s  z 吗,我们要改成其他bit的量化,首先得改量化间隔,如果代码是根据这个量化方案写的,这个代码里面这一部分,改到n不就可以啦?可是目前我还没有找到。你有其他想法吗?
    2019-5-9 22:46 回复
小祥  TF荚荚  发表于 2019-6-13 11:19:26 | 显示全部楼层
你好。我最近在尝试量化 open pose这个项目, 已经成功得到了quantized aware training的模型,并且用 evaluation graph得到了 pb 文件,之后用 freeze_graph.freeze_graph得到了frozen pb,这些步骤都成功了,但是之后将pb 转为 tflite的模型过程中一直报错 ,错误都是类似的:

Unimplemented: this graph contains an operator of type Cast for which the quantized form is not yet implemented.

我尝试修改bug。 第一次报错是 contains an operator FusedBatchNormV3 which the quantized form is not yet implemented, 我在tensorflow12.0 环境下freeze 模型,得到的便是FusedBatchNorm 而不是FusedBatchNormV3 了(之前用的是tensorflow14.0rc版本)
第二次报错是 contains an operator Size which the quantized form is not yet implemented. 我将eval 模型中的 batchsize固定为1,  该错误消失
第三次报错是contains an operator Cast which the quantized form is not yet implemented, 具体的位置如下图所示:

这个错误找了好久都没法解决。不知道成果转换之后的模型 fuse batchnorm这块都是什么样的呢。我现在没法确定是伪量化训练过程中的问题,还是说是eval中的问题。求大神指导。

多谢!

本帖子中包含更多资源

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

x
本楼点评(0) 收起
happy_national  TF荚荚  发表于 2019-7-10 09:42:16 | 显示全部楼层
@Zongjun  
大神您好,请教一下,我目前针对自己的tensorflow模型已经freeze成pb文件并通过toco命令行转换为tflite文件了,现在使用的是Post Training Quantization方式,使用Netron查看生成的tflite模型文件也好像没问题,但是我想在PC端运行测试下该模型的效果,却总是报错:
[{'name': 'Placeholder', 'index': 10, 'shape': array([ 1, 288, 800, 3], dtype=int32), 'dtype': <class>, 'quantization': (0.0, 0)}] self.interpreter.allocate_tensors() File "/home/anaconda3/envs/tensorflow13_gpuenv/lib/python3.7/site-packages/tensorflow/lite/python/interpreter.py", line 73, in allocate_tensors return self._interpreter.AllocateTensors() File "/home/anaconda3/envs/tensorflow13_gpuenv/lib/python3.7/site-packages/tensorflow/lite/python/interpreter_wrapper/tensorflow_wrap_interpreter_wrapper.py", tensorflow/lite/kernels/concatenation.cc:57 t0->dims->size &lt;= 4 was not true.Node number 259 (CONCATENATION) failed to prepare. 希望您看到后有空帮忙解答一下,不胜感激!(目前就想看下直接采用Post Training Quantization的压缩模型前向测试效果咋样,环境是tensorflow-gpu1.13,也正准备安装tf-nightly)

本楼点评(0) 收起
zjq-crystal  TF荚荚  发表于 2019-8-13 16:06:14 | 显示全部楼层
大家使用量化都是在移动端吗?没有在服务器端的么?有使用过Intel 的量化工具的么?
本楼点评(0) 收起
yucheng  TF荚荚  发表于 2019-9-9 18:19:58 | 显示全部楼层
首先先感谢此版各大神,在进行量化训练时能透过此篇文章慢慢摸索出来。
由于我是针对量化后的CNN设计硬体,所以需要将tflite的运算输出打印出来,与硬体的运算结果进行比较,但在进行完一整个layer的运算后,requantize的过程中,输出与scale相乘再返回uint8,总会有几笔输出会产生进位问题(该进位没进位),而我参考的运算是tensorflow/tensorflow/lite/kernels/internal/common.h中的MultiplyByQuantizedMultiplier ,所用到的SaturatingRoundingDoublingHighMul和RoundingDivideByPOT的函式也能在gemmlowp/fixedpoint/fixedpoint.h找到

像是这个实例 x=16809 ,M=0.0008031099569052458
照里面的算法会先将 M变成介于 [1-0.5)的值(M0) 所以M0等于0.8223845958709717, shift -10 在进行相乘后的结果为==>0000000000000000000110101111111110111011001110001101011000000000
接着进行SaturatingRoundingDoublingHighMul运算
由于31bit为0,就算有nudge1<<30也不会进位
==> 000000000000000000011010111111111
最后进行RoundingDivideByPOT,根据其运算,在这边也不会进位
最后得到1101也就是13 但tflite的结果却是14
这边想请问大神,是我在设计上有疏忽了啥运算吗?还是有哪边的运算搞错了?
本楼点评(1) 收起
  • yucheng我找到问题的源头了我直接用浮点数的形式将input,kernel,output的scale相乘,再截乘int32的形式,不过在tflite的运算里,似乎是先各别转成int32再相乘,然后再截成int32的M值
    2019-9-10 14:58 回复
热心市民刘先生  TF荚荚  发表于 2019-10-25 10:26:11 | 显示全部楼层
大神我想问一下,我生成的pb文件的大小大约是原来ckpt文件的三分之一左右。pb文件是将ckpt文件的权重固化到计算图上,这一步应该还是float32类型吧,按道理pb文件的大小应该和ckpt文件差不多大才是啊。
本楼点评(0) 收起
您需要登录后才可以回帖 登录 | 加入社区

本版积分规则

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