TFLite



  • TFLite

    TFLite 是 Google I/O 2017 推出的面向移动端和嵌入式的神经网络计算框架。相比Tensorflow

    • 更轻量级

      经过压缩后,Android 上的 TensorFlow 动态库的体积大约是 4.5M 左右。如果希望满足 Android 平台下的多种处理器架构,可能需要打包 4 个左右的动态库,加起来体积达到 18M 左右;而 tflite 库的体积在 600K 左右,即便是打包 4 个平台下的链接库,也只需要占用 2.5M 左右的体积。

    • 没有太多依赖

    • 可以用上移动端硬件加速

      TFLite 可以通过 Android Neural Networks API (NNAPI) 进行硬件加速,只要加速芯片支持 NNAPI,就能够为 TFLite 加速。不过目前在大多数 Android 手机上,Tflite 还是运行在 CPU 上的。

    TFLite 不再使用旧的 protobuf 格式(可能是为了减少依赖库),而是改用 FlatBuffers 。因此需要把训练好的 protobuf 模型文件转换成 FlatBuffers 格式。

    由于 TFLite 支持的算子比较少,更不支持训练相关的算子,因此需要提前把不需要的算子从模型中移除,即 Freeze Graph ;接着就可以做模型格式转换了,使用的工具是 tensorflow toco。这两个工具也是通过 bazel 编译得到。

    TFLite 目前仅提供有限的算子,主要以 CNN 中使用到的算子为主,如卷积、池化等。例如全卷积神经网络,大部分算子 TFLite 都有提供,但 conv2d_transpose(反向卷积)算子并没有被提供。幸运的该算子出现在网络模型的末端,因此我们可以将反向卷积之前的计算结果取出,自己用 c++ 实现一个反向卷积,从而计算出最终的结果。由于反向卷积的运算量并不大,所以基本没有影响到运行速度。

    如果不巧,你的模型需要但 TFLite 缺少的算子并非出现在网络的末端,该怎么办呢?你可以自定义一个 TFLite 算子,将其注册在 TFLite 的 kernels 列表中,这样编译得到的 TFLite 库就可以处理该算子了。同时,在模型转换时,还需要加上 --allow_custom_ops 选项,将 TFLite 默认不支持的算子也保留在模型中。

    参考:https://baijiahao.baidu.com/s?id=1598155503277825018&wfr=spider&for=pc

    Tensorflow模型文件格式

    GraphDef(.pb

    保存的是图模型的计算流程图,包括图中的常量,但不保存变量。存储格式为protobuffer,所以文件名后缀为pb

    # 直接保存图模型,但没有图中变量的值
    tf.train.write_graph(sess.graph_def, '.', 'graph.pb', as_text=False)
    graph = convert_variables_to_constants(sess, sess.graph_def, ['output_image'])
    # tf.train.write_graph(graph, '.', 'graph.pb', as_text=False)  这样通过将模型里面的所有变量都变为常量,那么就可以直接使用.pb文件做成接口,无需.ckpt文件再次导入变量的值.
    

    CheckPoint(.ckpt

    保存的是图模型中变量的值,要使用.ckpt文件的话,要重构图的结构和初始化图中的变量

    saver = tf.train.Saver()
    saver.save(sess, 'model.ckpt')
    

    Tensorflow lite model(.lite

    里面是包含图模型的计算流程图和图模型中的变量的值,可以直接给android系统或者ios系统的tensorflowLite调用读取。可以直接在TFLite上运行

    生成.lite文件

    1. 在训练脚本中保存图模型文件(GraphDef)和变量文件(CheckPoint)

    利用tensorflow.train中的write_graphsave API来导出GraphDefCheckPoint文件

    saver = tf.train.Saver()
    with tf.Session() as sess:
        sess.run(tf.global_variables_initializer())
        tf.train.write_graph(sess.graph_def, "model/", "nfsw_graph.pb", as_text=False)
        saver.save(sess, "model/nfsw_model.ckpt")
    

    4119701-f8d5b3f607a2ba6e.png
    实际上产生了4个文件。在后续步骤中需要用到的是nsfw_model.ckpt.data-00000-of-00001这个文件,保存了当前神经网络各参数的取值。

    1. 利用freeze_graph工具生成frozengraphdef文件
    2. 利用tocoTensorflow Optimizer Converter)工具,生成最终的tflite文件

    模型量化

    Tensorflow的量化

    Tensorflow自带对八位运算的生产级支持,能把浮点模型转化为等价的使用量化计算进行推断的图。量化之后生成新的模型,执行的操作和原来的模型一样,但内部采用八位计算。文件大小大概是原来的1/4(float占4个字节32bit)。仍旧可以使用一模一样的输入,结果也应该是一致的。

    实现(8bit)

    结构

    对量化的实现是通过把常见操作转化为等价的八位版本达到的,涉及的操作包括卷积,激活函数,池化操作,以及拼接。转化脚本先把每个已知的操作替换为等价的量化版本。然后在操作的前后加上含有转化函数的子图,将input从浮点数转换为8bi,再把output从8bit转换为浮点数。如ReLu:
    选区_009.png

    经过转化后变为:
    选区_010.png

    quantization和dequantization

    之所以这么做,tensorflow的论述是:

    1. 权重、活化张量的数值通常分布在一个相对较小的范围中(weight:-15 ~ 15,activatios:-500 ~ 1000);
    2. 由于神经元只抽取必要信息并给出响应,这样整个网络是抗噪和鲁棒的。训练完成后进行推断时,减低精度的定点化量化也可以考虑为噪声,神经网络对噪音的适应性强,将数量化到一个更小的数集中并不会对整体的结果带来很大的影响;
      3.通 过量化操作,可以有效提高点乘的计算效率。

    裁剪Tensorflow

    第一种裁剪方案

    既然mobile版的tensorflow本来就是pc版本的一个子集,那就意味着可以根据具体的需要,让这个子集变得更小,这也就是达到了裁剪的目的。具体来说,就是修改tensorflow源码中的tensorflow/tensorflow/conrib/makefile/tf_op_files.txt文件,只保留使用到了的模块。
    选区_011.png

    这种操作思路,是针对不同的神经网络结构有不同的裁剪方式,原则就是用到什么模块就保留什么模块

    第二种裁剪方案

    编译级别的strip操作,在连接的时候会自动的把没有调用的函数去除掉。


 

Copyright © 2018 bbs.dian.org.cn All rights reserved.

与 Dian 的连接断开,我们正在尝试重连,请耐心等待