美颜美妆

Tensorflow学习

2023-06-18  本文已影响0人  samychen

TensorFlow 是一个采用数据流图(data flow graphs),用于数值计算的开源软件库。节点(Nodes)在图中表示数学操作,图中的线(edges)则表示在节点间相互联系的多维数据数组,即张量(tensor),Tensor可认为是一个高维数组。它可以是一个数(标量)、一维数组(向量)、二维数组(矩阵)以及更高维的数组

什么是数据流图(Data Flow Graph)?

数据流图用“结点”(nodes)和“线”(edges)的有向图来描述数学计算。“节点” 一般用来表示施加的数学操作,但也可以表示数据输入(feed in)的起点/输出(push out)的终点,或者是读取/写入持久变量(persistent variable)的终点。“线”表示“节点”之间的输入/输出关系。这些数据“线”可以输运“size可动态调整”的多维数据数组,即“张量”(tensor)。张量从图中流过的直观图像是这个工具取名为“Tensorflow”的原因。一旦输入端的所有张量准备好,节点将被分配到各种计算设备完成异步并行地执行运算


image.png

计算模型Graph

TensorFlow 中的所有计算都会被转化为Graph上的节点。TensorFlow中的每个计算都是计算图的一个节点,而节点之间的边描述了计算之间的依赖关系。

模拟(a + b) * c 数据流图的数学计算

import tensorflow as tf
 
def func_of_python():
  a = 2
  b = 17
  c = 91
  add_res = a + b
  print("   [Debug] 此处,你可以打印中间结果的信息。add_res = {}".format(add_res))
  mul_res = add_res * c
  return mul_res
 
def func_of_tf():
  a = tf.constant(2) # 此处 a 不是基本的 int 类型,而是一个由 const Tensor 创建的类实例
  b = tf.constant(17)
  c = tf.constant(91)
  add_op = a + b
  print("   [Debug] 此处,你如果想打印 a + b 的值,控制台输出肯定不是你期望的结果。add_op = {}".format(add_op))
  mul_op = add_op * c
    # 上面的几行,仅仅画了一个数据的流程图,并没有真正的做数学计算
  init = tf.global_variables_initializer()
  sess = tf.Session() # 打开了一个与 Tensorflow 会话的 Session
  sess.run(init)
  # 执行完run,tensorflow 才真正的开始做数学计算
  mul_op_res = sess.run([mul_op])
  return mul_op_res
 
if __name__ == '__main__':
  print("调用 python 版 (a + b) * c ")
  result = func_of_python()
  print("(a + b) * c = {}".format(result))
  print("调用 tensorflow 版 (a + b) * c ")
  result = func_of_tf()
  print("(a + b) * c = {}".format(result))
  pass

输出结果

调用 python 版 (a + b) * c 
   [Debug] 此处,你可以打印中间结果的信息。add_res = 19 (a + b) * c = 1729
调用 tensorflow 版 (a + b) * c 
   [Debug] 此处,你如果想打印 a + b 的值,控制台输出肯定不是你期望的结果。add_op = Tensor("add:0", shape=(), dtype=int32)
(a + b) * c = [1729]

于 python 语言来说,当机器执行add_res = a + b,已经计算出了 add_res 的值
而对于Tensorflow,a、b、c 均是 Tensor 类的实例,且绑定了常量数据分别为 2、17、91。要想获得 (a + b) * c 的值,需要调用 sess.run() 方法获得。
Tensorflow 函数首先在 Graph 画板上画出了一个数据流图,如下

image.png

数据模型Tensor

Tensor的属性值主要有三个:名字(name)、维度(shape)和类型(type)
add_op = Tensor("add:0", shape=(), dtype=int32)
张量和计算图上的每一个节点所有代表的结果是对应的。
TensorFlow支持14种类型:实数(tf.float32, tf.float64)、整数(tf.int8, tf.int16, tf.int32, tf.int64, tf.uint8)、布尔型(tf.bool)、复数(tf.complex64, tf.complex128)

Tensor的用途

对中间结果的引用,提高代码可读性。
当计算图构造完成后,张量可用来获取计算结果。
通过run计算结果或者在会话(Session())中运行。

运行模型Session

执行已经定义好的运算

什么是TFlite ?

TensorFlow Lite 是一种用于设备端推断的开源深度学习框架。
TFLite是为了将深度学习模型部署在移动端和嵌入式设备的工具包,可以把训练好的TF模型通过转化、部署和优化三个步骤,达到提升运算速度,减少内存、显存占用的效果。

TFlite主要由Converter(左)和Interpreter(右)组成。Converter负责把TensorFlow训练好的模型转化,并输出为.tflite文件(FlatBuffer格式)。转化的同时,还完成了对网络的优化,如量化。Interpreter则负责把.tflite部署到移动端,嵌入式(embedded linux device)和microcontroller,并高效地执行推理过程,同时提供API接口给Python,Objective-C,Swift,Java等多种语言。简单来说,Converter负责打包优化模型,Interpreter负责高效易用地执行推理。

image.png

tflite打印信息

##### LOAD TFLITE FILE: "../portraitsegment/model/portrait_segmentation.tflite"
-----------------------------------------------------------------------------
T E N S O R S
-----------------------------------------------------------------------------
tensors size     : 712
nodes   size     : 267
number of inputs : 1
number of outputs: 1

-----------------------------------------------------------------------------
Input Tensor Dimension
-----------------------------------------------------------------------------
Tensor[319]  3145728,  1(fp32), (  0, 0.000000) ip                               [1x512x512x3]

-----------------------------------------------------------------------------
Output Tensor Dimension
-----------------------------------------------------------------------------
Tensor[275]  2097152,  1(fp32), (  0, 0.000000) conv2d_transpose_4/BiasAdd       [1x512x512x2]

TensorFlow Lite核心类

tflite::ErrorReporter : 错误信息
tflite::FlatBufferModel:读取model,构建训练好的model
tflite::Interpreter:输出张量结点图的解释器
tflite::InterpreterBuilder:初始化model
tflite::OpResolver:抽象接口,在给定操作码或自定义操作名称的情况下返回TfLiteRegistrations。
tflite::TfLiteVerifier:验证给定模型是否合法的抽象接口。

Interpreter

   class Interpreter {
    public:
     // Instantiate an interpreter. All errors associated with reading and
     // processing this model will be forwarded to the error_reporter object.
     // Note, if error_reporter is nullptr, then a default StderrReporter is
     // used. Ownership of 'error_reporter' remains with the caller.
     explicit Interpreter(ErrorReporter* error_reporter = DefaultErrorReporter());
     ~Interpreter();
     // Interpreters are not copyable as they have non-trivial memory semantics.
     Interpreter(const Interpreter&) = delete;
     Interpreter& operator=(const Interpreter&) = delete;
   }

Interpreter常用功能

    AllocateTensors   //申请空间内存等
    Invoke  //调用
    UseNNAPI  //是否使用NNAPI
    SetNumThreads  //设置线程数目
    T* typed_input_tensor(int index)  //输入
    T* typed_output_tensor(int index)  //输出

TensorFlow Lite 使用流程

1、基于文件构建Tensorflow模型

    std::unique_ptr<FlatBufferModel> BuildFromFile(
          const char* filename,
          ErrorReporter* error_reporter = DefaultErrorReporter());

2、通过InterpreterBuilder构建Interpreter

    tflite::ops::builtin::BuiltinOpResolver resolver;
    tflite::InterpreterBuilder builder(*model, resolver);
    std::unique_ptr<tflite::Interpreter> interpreter;
    builder(&interpreter);

将BuiltinOpResolver和FlatBufferModel组合构造出一个解释器建造者InterpreterBuilder,利用这个建造者初始化模型解释器
3、分配线程数量

    interpreter->SetNumThreads(num_threads);

4、分配Tensorflow buffer,大多情况下深度学习模型的运行内存消耗都比较固定,所以提前计算分配有利于减少动态内存分配的资源消耗

    TFLITE_MINIMAL_CHECK(interpreter->AllocateTensors() == kTfLiteOk);

5、注册自定义算子,高级应用,具体使用可以参考mediapipe项目下的mediapipe/util/tflite/目录

resolver->AddCustom(const char* name, const TfLiteRegistration* registration,
                                         int version = 1));
typedef struct TfLiteRegistration {
    void* (*init)(TfLiteContext* context, const char* buffer, size_t length);
    // The pointer `buffer` is the data previously returned by an init invocation.
    void (*free)(TfLiteContext* context, void* buffer);
    // Returns kTfLiteOk on success.
    TfLiteStatus (*prepare)(TfLiteContext* context, TfLiteNode* node);
    // Execute the node (should read node->inputs and output to node->outputs).
    // Returns kTfLiteOk on success.
    TfLiteStatus (*invoke)(TfLiteContext* context, TfLiteNode* node);
}

6、根据Tensorflow输入模型大小缩放待训练图片大小
7、将缩放后的图片像素值填充进Tensorflow输入

  T* input = interpreter->typed_input_tensor<T>(i);`

8、运行深度学习网络模型推断过程,调用Interpreter::Invoke接口

  TFLITE_MINIMAL_CHECK(interpreter->Invoke() == kTfLiteOk);

9、如果模型推断过程没有发生错误,那么网络模型的推断结果就会被放到Interpreter的输出张量上,后续读取并按照业务逻辑进行后处理

  T* output = interpreter->typed_output_tensor<T>(i);

填充Tensor输入数据和获取Tensor输出数据过程中需要注意的问题

Tensorflow内部在进行计算过程,不同模型使用的data_format不一定相同,比如卷积API默认的data_format=NHWC(N数量, H高度, W宽度, C通道数或分类数),比如[1x256x256x3]
还有NCHW类型,仅仅是和NHWC排列方式不同,比如[1x3x256x256]


image.png

其他特殊数据格式,比如人像分割模型munet_mnv3_wm05.tflite中输出Tensor的shape = [1X50176] ,1代表数量,50176=224X224,是单通道指针,指针内容是224X224分辨率的单通道像素值
人像分割模型portrait_segmentation.tflite的输出Tensor shape=[1x512x512x2],1代表数量,512x512代表分辨率,2代表分类数(背景和人像)

关于使用TensorflowLite加速推断

image.png

TensorflowLite可以利用设备上的加速器(例如GPU和DSP)来启用 TensorFlow Lite 模型的硬件加速,

默认情况下,TensorFlow Lite 使用针对ARM Neon指令集优化的 CPU 内核 。但是,CPU 是一种多用途处理器,不一定针对机器学习模型中常见的繁重算术(例如,卷积和密集层中涉及的矩阵数学)进行优化。例如,GPU 可提供高达5倍的延迟加速,而 Qualcomm® Hexagon DSP 在实验中显示可将功耗降低 75%。

image.png

这些加速器中的每一个都有相关的 API,可以支持自定义计算,例如用于嵌入式GPU 的OpenCL或OpenGLES和用于DSP的Hexagon SDK,用于IOS的CoreML加速和用于Android的NNAPI加速

不同平台选择不同的Delegate,这里仅以Ubuntu的GPU委托为例

TfLiteDelegate *delegate = NULL;
const TfLiteGpuDelegateOptionsV2 options = {
        .is_precision_loss_allowed = 1, // 允许丢失精度FP32->FP16
        .inference_preference = TFLITE_GPU_INFERENCE_PREFERENCE_FAST_SINGLE_ANSWER,
        .inference_priority1 = TFLITE_GPU_INFERENCE_PRIORITY_MIN_LATENCY,
        .inference_priority2 = TFLITE_GPU_INFERENCE_PRIORITY_AUTO,
        .inference_priority3 = TFLITE_GPU_INFERENCE_PRIORITY_AUTO,
};
delegate = TfLiteGpuDelegateV2Create(&options);
// 修改Graph,执行成功后相关节点会被替换
if (p->interpreter->ModifyGraphWithDelegate(delegate) != kTfLiteOk)
{
  LOG("ERR: %s(%d)\n", __FILE__, __LINE__);
  return -1;
}

经过测量,普通tflite开启GPU加速相比CPU推断,速度提升5倍左右,符合TensorFlowLite官方给出的GPU可提供5倍延迟加速的结论

遇到问题

1、开启GL加速,使用OpenglES3.1+版本,开启相关宏定义USE_GL_DELEGATE编译失败,和glew冲突

/usr/include/GLES3/gl31.h:1518:88: error: 'void __glewVertexBindingDivisor(GLuint, GLuint)' redeclared as different kind of symbol
 GL_APICALL void GL_APIENTRY glVertexBindingDivisor (GLuint bindingindex, GLuint divisor);

2、人像动漫模型使用GPU加速,函数调用ModifyGraphWithDelegate,内部直接抛异常

ERROR: Following operations are not supported by GPU delegate:
FULLY_CONNECTED: Max version supported: 4. Requested version 9.
MEAN: Mean operation supports only HW plane
64 operations will run on the GPU, and the remaining 3039 operations will run on the CPU.
ERROR: No supported OpenCL platform.
ERROR: Falling back to OpenGL
INFO: Initialized OpenGL-based API.
terminate called after throwing an instance of 'absl::lts_2020_02_25::bad_any_cast'
  what():  Bad any cast
Aborted (core dumped)

关于模型转换

不同深度学习框架模型存储格式不同,要想最终正确转化为我们需要的模型需要并不一定能成功,这时我们需要一种通用的模型去做中间过渡,转化过程为:A模型 -> 通用模型 -> B模型

ONNX模型

ONNX(Open Neural Network Exchange)是一种针对机器学习所设计的开放式的文件格式,用于存储训练好的模型。它使得不同的人工智能框架可以采用相同格式存储模型数据并交互

ONNX模型转换tflite模型
1、ONNX模型转化为pb模型

from onnx_tf.backend import prepare
import onnx

TF_PATH = "tf_model" # where the representation of tensorflow model will be stored
ONNX_PATH = "mobilenet_v2.onnx" # path to my existing ONNX model
onnx_model = onnx.load(ONNX_PATH)  # load onnx model
tf_rep = prepare(onnx_model)  # creating TensorflowRep object
tf_rep.export_graph(TF_PATH)

2、pb模型转换为tflite模型

import tensorflow as tf

TF_PATH = "tf_model" 
TFLITE_PATH = "mobilenet_v2.tflite"
converter = tf.lite.TFLiteConverter.from_saved_model(TF_PATH)
converter.optimizations = [tf.lite.Optimize.DEFAULT]
tf_lite_model = converter.convert()
with open(TFLITE_PATH, 'wb') as f:
    f.write(tf_lite_model)

参考资料

1、https://www.zhuanzhi.ai/document/23d768da567e7eb89424b8be2b6d8dfa

2、https://www.cnblogs.com/vitoyeah/p/10273299.html

3、https://netron.app/

4、https://tensorflow.google.cn/lite/performance/delegates?hl=en

上一篇下一篇

猜你喜欢

热点阅读