Open ysh329 opened 4 years ago
关于如何在TF提供的演示应用的APP里使用安卓和 iOS (metal) 的 GPU 委托代理,参考安卓使用 GPU 的委托代理和 iOS 如何使用 GPU的委托代理。
实际 APP 中,多使用 C++ API,下面以 Android 系统的 C++ API 添加 GPU 代理为例。
// 加载FlatBuffer模型
auto model = FlatBufferModel::BuildFromFile(model_path);
if (!model) return false;
// 初始化使用 GPU 代理的解释器
ops::builtin::BuiltinOpResolver op_resolver;
std::unique_ptr<Interpreter> interpreter;
InterpreterBuilder(*model, op_resolver)(&interpreter);
// 基于 GPU 代理的信息,通过解释器修改模型执行Graph
const TfLiteGpuDelegateOptions options = {
.metadata = NULL,
.compile_options = {
.precision_loss_allowed = 1, // FP16
.preferred_gl_object_type = TFLITE_GL_OBJECT_TYPE_FASTEST,
.dynamic_batch_enabled = 0, // Not fully functional yet
},
};
auto* delegate = TfLiteGpuDelegateCreate(&options);
if (interpreter->ModifyGraphWithDelegate(delegate) != kTfLiteOk) return false;
// 写入输入数据、执行推理、获取输出
WriteToInputTensor(interpreter->typed_input_tensor<float>(0));
if (interpreter->Invoke() != kTfLiteOk) return false;
ReadFromOutputTensor(interpreter->typed_output_tensor<float>(0));
// 代理资源单独释放
TfLiteGpuDelegateDelete(delegate);
从TfLiteGpuDelegateCreate
这个API就看出,GPU 代理是单独的接口,感觉不是很好看。
关于输入和输出这里,TFLite有个优点,用户可以直接获取opengl的纹理数据作为输入,传给TFLite解释器,避免从opengl->cpu->tflite解释器这个过程的数据拷贝,只需要将输入转换为OpenGL着色器存储缓冲区对象(SSBO)。例如,包含相机传输的GPU纹理),那么可以直接保留在GPU内存中而无需进入到CPU内存,。TFLite有提供这样的接口。
除了输入,还有输出过程,如果网络的输出采用可渲染图像的格式(例如, image style transfer的输出,那么它可以直接显示在屏幕上。关于相关转换的TFLiteAPI可以参考gpu_advanced#android。
由于历史依赖库都是v7的原因,安卓平台在实际中多为armv7。
由于TensorFlow官网文档不提供ADB Shell环境的性能测试方法,但在TensorFlow的仓库有提TFLite Model Benchmark Tool,并在readme里有写道如何使用和编译,下面以ADB shell环境交叉编译Android-ARMv7版本的TFLite,详细步骤可以见readme的To build/install/run
小节:
# 拉去tensorflow代码,并切到最新release分支,略
# 假设当前在tensorflow目录下
# 配置tensorflow lite的编译安装第三方等环境
# android ndk、SDK需提前装好下载好
# 其他走默认选择
./confgure
# 编译android-armv7的benchmark_model
bazel build -c opt --verbose_failures \
--config=android_arm \
tensorflow/lite/tools/benchmark:benchmark_model
# 编译android-armv8的benchmark_model
#bazel build -c opt \
--config=android_arm64 \
tensorflow/lite/tools/benchmark:benchmark_model
serial_num=<your-phone-seriral-num>
TFMODEL_PATH=<your-tflite-model-dir-on-device>
adb -s ${serial_num} shell /data/local/tmp/tflite/benchmark_model \
--graph=${TFMODEL_PATH} \
--warmup_runs=20 \
--num_runs=1000 \
--use_gpu=true
benchmark的这个tool有一些公用参数,我们能得到一些信息:
该benchmark工具除提供共用参数外,也针对特定硬件的代理如GPU,有对应的参数:
下面是刚那个ADB shell命令的执行log,我讲摘录关键部分,并结合TFLite Delegate Registrar的说明,做内容上的补充:
# 的确是和API的名字一样,对原模型Graph做了修改
# 使用了GPU,另外是高性能的fp16推理,即FP16对应android C++的option设置
Max number of delegated partitions : [0]
Use gpu : [1]
Allow lower precision in gpu : [1]
# 这里看到在初始化TFLite的运行时
# 创建GPU的委托代理并找到squeeze这个算子不支持GPU
# 还记得我前文说的嘛,squeeze2->reshape2这些模型尾巴没啥意义
# 但看来是TFLite没有优化完
INFO: Initialized TensorFlow Lite runtime.
INFO: Created TensorFlow Lite delegate for GPU.
ERROR: Next operations are not supported by GPU delegate:
SQUEEZE: Operation is not supported.
First 29 operations will run on the GPU, and the remaining 2 on the CPU.
# 初始化的是OpenCL的API
# 并使用了gpu-opencl这个委托代理
INFO: Initialized OpenCL-based API.
Applied GPU delegate.
# 最后是性能,mobielnetv1,1000次均值12ms
# 相比mace、mnn的性能都差不多
Running benchmark for at least 1000 iterations and at least 1 seconds but terminate if exceeding 150 seconds.
count=1000 first=14341 curr=14632 min=7771 max=20203 avg=12198.5 std=2614
除GPU外,TFLite Delegate Utilies for Tooling还有NNAPI、Hexagon、XNNPACK、CoreML的委托代理。
其实委托代理对Delegate的翻译听起来就比较别扭,更准确的说法,如GPU可能是“委托Mali GPU硬件,去代理子图执行”,更合适一些。
点赞, 看代码代理应该是比较纯粹的代理模式。
TensorFlow Lite的 GPU 委托代理(Delegate)是什么
1. 什么是委托代理及其优点
TFLite的委托代理是一种将部分或全部的模型运算委托予另一线程执行的方法。
不过从我对文档的理解来看,感觉更像是添加的一种硬件后端(代理我想应该只是调用调用层面,不是底层实现,另外在Hexagon DSP的委托代理部分,文档坦言说Hexagon DSP的代理就是为了补充NNAPI,特别是针对那些NNAPI不可用DSP加速的、老旧驱动的设备,毕竟这些老旧设备也没有NNAPI这个东西,但有DSP硬件),交给模型的子图来去执行。比方原始模型的CPU执行Graph如上图。交给GPU的委托代理后,原Graph变为下面这样:
可以看到TFLite将原模型Graph做子图融合,将Conv2D和Mean结点的计算都交给了代理,前后的输入和输出都是一样的。中间的结点被代理处理,就成为黑盒。这个过程也可以理解成是 TFLite 对模型做了“翻译”,将其”翻译”为将执行后端的黑盒子图。
不过一般来说,该过程存在内存交换,若原有Graph模型中的“翻译转换”并不完全,那么将会有很多计算落在CPU上,原有Graph会拆分成很多子图交给委托代理执行。这种情况性能会因为来回的数据拷贝带来性能损耗。而且据我的一个小伙伴反馈,拿CPU和GPU来说(端侧),跑完CPU再接着跑GPU,或者相反,性能都会带来不稳定波动(下降)。
委托代理的优点:综合移动设备的算力和功耗,在CPU上做高算力计算不划算,但其他设备如 GPU 或 DSP 等硬件加速器或者如华为NPU,联发科APU、三星VPU之类的却可以获取更佳的性能与功耗表现。
2. 如何添加一个代理
TFLite的文档有说明(下面内容复制,粘贴一下原文档并做适当调整):
class MyDelegate
)。为了使用代码进行说明,下面将定义一个可执行 Conv2D 和 Mean 算子的代理,并将其命名为“MyDelegate”。
其实正如我前面所想,翻译原有Graph中的op为代理的实现,也需要针对该backend实现,或许如果是第三方XPU、APU、NPU之类的话,即调用其实现的过程,并做异常处理如找不到其实现退回其CPU实现。
3. TensorFlow LIte 的 GPU 代理
没说安卓的其他设备,而是 GPU 作为委托代理的一个经典示例,是因为 GPU 其三个优点:
TFLite在端侧 GPU 推理的支持方面,最早便支持了 OpenGL 的推理,在2020年5月中旬,基于委托代理方式也支持了 OpenCL 。
4. GPU委托代理对模型和算子的支持情况
目前TFLite GPU 支持的模型主要是CV类的:
1, MobileNetv1(224x224):图像份额里;
支持的算子有19个(忽略版本v1、v2),其中:
当模型执行到 GPU 不支持的算子时,会切到 CPU 上运行并同时给出警告
WARNING: op code #42 cannot be handled by this delegate.
,这个过程会有内存拷贝开销。因此,针对算子支持的情况,也有如下的优化建议,其实下面建议也不仅限于GPU,其它后端也是适用的:这点上 TensorFlow MobileNetV1和V2的共同结构(见上图,分别是MobileNetV1的TensorFlow原始模型、TFLite模型、Caffe模型可视化)就是模型最后有conv2d->squeeze2->reshape2->softmax->reshape2结构。其中squeeze2和reshape2的来回折腾,实际在Netron里可视化对于维度并没有什么本质上的变化,反而因此引入了3个算子耗时。完全可以在端侧部署的时候优化掉。Caffe的MobileNetV1结构是没有reshape2和squeeze2操作的,其实在做端侧框架性能调研时,源自不同训练框架的模型会有不同,结合本身推理框架的底层实现上,对性能可能有不小的影响;