Tensorflow学习
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 画板上画出了一个数据流图,如下
数据模型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.pngtflite打印信息
##### 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.pngTensorflowLite可以利用设备上的加速器(例如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
4、https://tensorflow.google.cn/lite/performance/delegates?hl=en