加速器

  • NNAPI
  • GPU
  • XNNPACK

模型推理速度对比(CPU)

  • 量化模型:TFLITE > MNN
  • F32/F16模型:TFLITE < MNN
  • XNNPACK可加速TFLITE浮点模型
  • TFLITE/MNN:F32=F16,F16仅模型减半,推理时仍然是F32参数

XNNPACK

浮点加速库(高度优化的神经网络算子,**具体怎么优化的**),目的是提高CPU上神经网络推理性能。将XNNPACK库集成到TF Lite中,浮点推理的速度平均提升2.3倍。

目的

几乎不影响AI模型推理准确性的同时,“修剪”模型的大小,以提升模型推理速度。
最好情况下能将AI模型的推理速度提升2倍,模型大小“缩水”一半(???)。

实现

利用AI模型的稀疏性实现加速推理。

AI模型分析

移动端轻量级神经网络架构,如MobileNetEfficientNetLite,主要由深度可分离卷积和1x1卷积组成。其中1x1卷积耗费的推理时间最长,占总计算量65%以上。因此1x1卷积卷积层成为了稀疏化的最优选择。

1x1卷积分析

在现代的推理设备中(如XNNPACK),深度学习模型中1x1卷积的实现以及操作都依赖于HWC张量布局,这种张量配置运行推理引擎并行地处理对应于每个空间位置(即图像的每个像素)的通道(理解:并行的按通道处理)。

然而张量的这种排序并不适合稀疏推理,因为它将通道设置为张量的最内层维,并使访问它的计算成本更高。而Google对XNNPACK更新使得其具有了检测模型是否稀疏的表达能力:从标准的密集推理模式切换到稀疏推理模型,在稀疏推理模型中中XNNPACK使用CHW的张量布局。

为了避免每次操作后在稀疏推理最优的CHW张量布局和标准的HWC张量布局之间来回转换,XNNPACK提供了几种在CHW布局中CNN算子的高效实现。

HWC->CHW张量的重新排序,便可以允许实现加速稀疏的1x1卷积核:

  1. 在单个条件检查之后(什么条件,检查什么),当对应的通道权值为零时(谁的通道权值),可以跳过张量的整个空间切片,而不是逐像素检测;
  2. 当信道权值(什么是信道权值)为非零时,可以通过将相邻的像素加载到同一存储单元来提高计算效率。这样使得使用者能够同时处理多个像素,同时也可以在多个线程中并行执行每个操作。
  3. 当至少80%的权重为零时,会引起1.8~2.3倍加速

image.png

为什么此前利用稀疏化加速推理的方法应用不广泛

  1. 因为神经网络本身难以解释(黑盒),导致稀疏化过程也难以解释,以至于缺乏稀疏化工具。
  2. 端侧设备缺乏对稀疏化操作的支持。

Google开发稀疏化新工具

为移动端设备和Web端,发布了一系列针对TensorFlow Lite、XNNPACK浮点加速库的稀疏化新工具。

  1. XNNPACK库包含了可检测模型否稀疏的方法(如何检测呢)。
  2. TF Lite工具包包含基于幅度的剪枝等让模型“缩水”的方法。

AI模型稀疏化过程(代码实现参考TF Lite模型优化工具包)

影响稀疏网络质量的超参数:训练时间、学习速率、剪枝计划。官方建议运行超参数搜索来找到应用程序的最佳位置。
AI模型稀疏化:改进CPU推理速度。
**

  1. 从原始模型开始训练,在训练过程中逐渐将网络中的部分权重设置为0,即“修剪”模型(剪枝过程);
  2. 适当增加训练时间,提升模型准确度(与修剪前模型相差不会太多, 稀疏度高于70%,模型精度会下降明显);
  3. 获得稀疏AI推理模型,可以以压缩格式有效存储(怎么存储),相较于原始模型减少了1/2;

稀疏性

在神经网络模型中,将部分网络的权重参数设置为0,以加快推理速度。

  1. 需要保证模型精度的情况下,将部分权重参数设置为0;
  2. 为什么设置为0后可以加快推理速度?

因为AI模型进行推理任务时往往涉及到大量矩阵乘法运算,将矩阵中的部分参数设置成0,可以极大地加快矩阵运算速度(乘、加运算),缩短推理时间。
TFLITE加速 - 图2

TFLITE + XNNPACK

TFLITE于TF2.3及后续版本中,将XNNAPCK Delegate集成到源码中。

使用方法

  1. XNNCPAC后端已包含在针对Android、iOS的TF Lite预构建二进制文件中,只需修改单行代码即可启用。下载预构建的TF Lite2.3 Android归档(AAR)已包含XNNPACK,只需一行代码即可在Interpreter.Options对象中启用:

    1. // JAVA版本,C++版本不支持
    2. Interpreter.Options interpreterOptions = new Interpreter.Options();
    3. interpreterOptions.setUseXNNPACK(true);
    4. Interpreter interpreter = new Interpreter(model, interpreOptions)
  2. Windows、macOS和Linux版TF Lite也支持XNNPACK,可通过构建时的选择加入机制启用。使用Bazel构建TF Lite时,只需要添加--define tflite_with_xnnpack=true即可启用XNNPACK后端,默认情况下TF Lite将使用此后端。

Support

  1. 支持F32、F16格式模型;
  2. 对于不支持的浮点算子将以透明方式回退到TF Lite默认实现;
  3. 不支持对权重或激活定点量化的模型;
  4. 对小型神经网络模型和低端手机上的TF Lite助益最大;
  5. 支持检测模型是否稀疏化。
  6. Dynamically allocated (with kTfLiteDynamic allocation type) inputs and outputs are not supported.
  7. Resizing model inputs (via Interpreter::ResizeInputTensor) is supported, but cause a complete reinitialization of the delegate instance, which has considerable overhead.
  8. 计划在后续版本中,将所以平台的XNNPACK后端设置为默认启用。

使用方法实践版(TF2.4.2)

1、自行编译包含xnnpack的库,测试里面是否包含XNNPACK;

方法一(无效)
bazel选项添加--define tflite_with_xnnpack=true,TF Lite interpreter默认使能xnnpack库。
该方法中Interpreter::SetNumThreads并不能对xnnpack的线程数生效,需要手动指定xnnpack线程数,否则more使用单线程推理。

  1. // Load model
  2. tflite::Model* model;
  3. ...
  4. // Construct the interprepter
  5. tflite::ops::builtin::BuiltinOpResolver resolver;
  6. std::unique_ptr<tflite::Interpreter> interpreter;
  7. TfLiteStatus res = tflite::InterpreterBuilder(model, resolver, num_threads);

方法二
修改BUILD文件,链接//tensorlfow/lite:tflite_with_xnnpack,通过代码使能xnnpcak。

  1. // add xnnpack
  2. TfLiteXNNPackDelegateOptions xnnpack_options = TfLiteXNNPackDelegateOptionsDefault();
  3. // 重新设置线程数
  4. xnnpack_options.num_threads = info->param.number_of_threads;
  5. xnnpack_delegate = TfLiteXNNPackDelegateCreate(&xnnpack_options);
  6. TfLiteInterpreterOptionsAddDelegate(options, xnnpack_delegate);

3、使用low-level delegate API使能xnnpack(不推荐,除非需要判断是否使能xnnpack)

  1. // Build the interpreter
  2. std::unique_ptr<tflite::Interpreter> interpreter;
  3. ...
  4. // IMPORTANT: initialize options with TfLiteXNNPackDelegateOptionsDefault() for
  5. // API-compatibility with future extensions of the TfLiteXNNPackDelegateOptions
  6. // structure.
  7. TfLiteXNNPackDelegateOptions xnnpack_options =
  8. TfLiteXNNPackDelegateOptionsDefault();
  9. xnnpack_options.num_threads = num_threads;
  10. //
  11. TfLiteDelegate* xnnpack_delegate =
  12. TfLiteXNNPackDelegateCreate(&xnnpack_options);
  13. if (interpreter->ModifyGraphWithDelegate(xnnpack_delegate) != kTfLiteOk) {
  14. // Report error and fall back to another delegate, or the default backend
  15. }
  16. ...
  17. // Run inference using XNNPACK
  18. interpreter->Invoke()
  19. ...
  20. // IMPORTANT: release the interpreter before destroying the delegate
  21. interpreter.reset();
  22. TfLiteXNNPackDelegateDelete(xnnpack_delegate);