[译]GPU加持,TensorFlow Lite更快了

4,222 阅读8分钟

虽然手机的发展日新月异,我们更换手机的频率也加快,但不可否认的是,手机性能相比PC还是有很大的差距。对于机器学习来说,我们可以在电脑上训练模型,在手机上应用模型,但某些复杂的模型,在手机上进行推断,依然很慢。前一段时间曾经研究过在手机浏览器中加入鉴黄的功能,使用yahoo的open_nsfw模型,实际测试下来,速度仍然太慢,只得放弃。

手机是人工智能应用的绝佳载体,我一直在关注着机器学习在移动端的最新进展,特别是TensorFlow Lit。这是一篇翻译的文章,原文标题:TensorFlow Lite Now Faster with Mobile GPUs (Developer Preview),点击阅读原文,可以跳转到原文链接,需要翻墙哦!

由于处理器性能和电池容量有限,在移动设备上使用计算密集的机器学习模型进行推断是非常耗资源的。 虽然可以采用一种加速途径:转换为定点数模型,但用户已经要求作为一种选项,为加速原始浮点模型推理提供GPU支持,而不会产生额外的复杂性和潜在的量化精度损失。

我们听取了用户的心声,很高兴地宣布,您现在可以使用最新发布的TensorFlow Lite GPU端开发人员预览版,利用移动GPU为特定模型(在后面列出)加速; 对于不支持的部分模型,则回退到CPU推断。在接下来的几个月中,我们将继续增加额外的操作(ops)并整体改进GPU端。

这个新的后端利用了:

今天,我们发布了新GPU后端的预编译二进制预览版,使开发人员和机器学习研究人员有机会尝试这种令人兴奋的新技术。我们计划在2019年晚些时候发布一个完整的开源版本,包含从您们试验中收集的反馈。

今天我们使用TensorFlow Lite CPU浮点推断进行面部轮廓检测(并非面部识别),未来利用新的GPU后端,在Pixel 3和Samsung S9上的推理速度可以提升~4倍,iPhone7上可以加速到~6倍。

GPU与CPU性能对比

在谷歌,我们已经在产品中使用了好几个月的新GPU后端,加速了计算密集型网络,为我们的用户提供了重要的用例。

在Pixel 3上的纵向模式下,Tensorflow Lite GPU推理相比具有浮点精度的CPU推断,将前景 - 背景分割模型加速4倍以上新的深度估计模型加速10倍以上。 在YouTube StoriesPlayground Stickers中,我们的实时视频分割模型在各种手机上的速度提高了5-10倍。

我们发现,对于各种各样的深度神经网络模型,新的GPU后端通常比浮点CPU实现快2-7倍。下面,我们对4个公开模型和2个内部模型进行了基准测试,涵盖了开发人员和研究人员在Android和Apple设备中遇到的常见用例:

公开模型:

Google私有案例:

表1. 与各种Android和Apple设备上的6种模型的基准CPU性能相比,GPU的平均性能加倍。倍数越高,性能越佳。

神经网络模型越复杂,GPU加速越重要,这些模型可以更好地利用GPU,例如计算密集的预测、分段或分类任务。在非常小的模型上,可能没什么加速,使用CPU反而更有利,可以避免内存传输中固有的延迟代价。

我该如何使用它?

教程

最简单的入门方法是按照我们的教程,使用带GPU支持的TensorFlow Lite演示应用程序。以下简要概述它们的使用。更多的信息,请参阅我们的完整文档

手把手的指导教程,请观看视频:

使用Java for Android

我们准备了一个完整的Android档案(AAR),其中包括带有GPU后端的TensorFlow Lite。 编辑gradle文件以包含此AAR而不是当前的发布版本,并将以下代码段添加到Java初始化代码中。

// Initialize interpreter with GPU delegate.
GpuDelegate delegate = new GpuDelegate();
Interpreter.Options options = (new Interpreter.Options()).addDelegate(delegate);
Interpreter interpreter = new Interpreter(model, options);

// Run inference.
while (true) {
  writeToInputTensor(inputTensor);
  interpreter.run(inputTensor, outputTensor);
  readFromOutputTensor(outputTensor);
}

// Clean up.
delegate.close();

使用C++ for iOS

步骤1. 下载TensorFlow Lite的二进制版本。

步骤2. 修改代码,在创建模型后调用ModifyGraphWithDelegate()。

// Initialize interpreter with GPU delegate.
std::unique_ptr<Interpreter> interpreter;
InterpreterBuilder(model, op_resolver)(&interpreter);
auto* delegate = NewGpuDelegate(nullptr);  // default config
if (interpreter->ModifyGraphWithDelegate(delegate) != kTfLiteOk) return false;

// Run inference.
while (true) {
  WriteToInputTensor(interpreter->typed_input_tensor<float>(0));
  if (interpreter->Invoke() != kTfLiteOk) return false;
  ReadFromOutputTensor(interpreter->typed_output_tensor<float>(0));
}

// Clean up.
interpreter = nullptr;
DeleteGpuDelegate(delegate);

目前加速了什么?

GPU后端目前支持选择操作(参见文档)。如果你的模型仅包含这些操作,将运行得最快,而不受支持的GPU操作将自动回退到CPU。

它是如何工作的?

深度神经网络按顺序运行数百个操作,使它们非常适合GPU,这些GPU的设计考虑了面向吞吐量的并行工作负载。

在Objective-C++中调用Interpreter::ModifyGraphWithDelegate()时,或者在Java中带Interpreter.Options调用Interpreter的构造函数,初始化GPU代理。在该初始化阶段,基于从框架接收的执行计划构建输入神经网络的规范表示。使用此新表示,将应用一组转换规则,包括但不限于:

  • 剔除不必要的ops
  • 用其他具有更好性能的等效ops替换ops
  • 合并ops以减少最终生成的着色器程序的数量

基于此优化图,生成并编译计算着色器。目前,我们在Android上使用OpenGL ES 3.1 Compute Shaders,在iOS上使用Metal Compute Shaders。在创建这些计算着色器时,我们还采用了各种特定于体系结构的优化,例如:

  • 应用某些ops的特化而不是它们(较慢)的通用实现
  • 减少寄存器的压力
  • 选择最佳工作组的大小
  • 安全的减少精度
  • 重新排序显式的数学运算

在这些优化结束时,着色器程序被编译,可能需要几毫秒到半秒,就像手机游戏一样。一旦着色器程序编译出来,新的GPU推断引擎就可以工作了。

在推断每个输入时:

  • 如有必要,输入将移至GPU:输入张量,如果尚未存储为GPU内存,可由框架通过创建GL缓冲区/纹理或MTLBuffers进行GPU访问,同时还可能复制数据。由于GPU在4通道数据结构中效率最高,因此通道大小不等于4的张量将重新整形为更加GPU友好的布局。
  • 执行着色器程序:将上述着色器程序插入命令缓冲区队列,GPU将这些程序输出。在此步骤中,我们还为中间张量管理GPU内存,以尽可能减少后端的内存占用。
  • 必要时将输出移动到CPU:一旦深度神经网络完成处理,框架将结果从GPU内存复制到CPU内存,除非网络输出可以直接在屏幕上呈现并且不需要这种传输。

为获得最佳体验,我们建议优化输入/输出的张量复制和/或网络架构。有关此类优化的详细信息,请参阅TensorFlow Lite GPU文档。有关性能的最佳实践,请阅读这篇指南

它有多大?

GPU代理将为Android armeabi-v7a APK添加大约270KB,为iOS的每个所包含架构添加212KB。但是,后端是可选的,因此如果您不使用GPU代理,则无需包含它。

未来的工作

这只是我们GPU支持工作的开始。除了社区反馈,我们还打算进行以下改进:

  • 扩大ops的范围
  • 进一步优化性能
  • 演进并最终固定API

我们鼓励您将您的想法和评论留在我们的GitHubStackOverflow页面上。

注:本文中包含有大量链接,但是微信公众号不能包含外链,请访问我在掘金上的这篇专栏文章。文中的链接需要翻墙才能访问。

image