阅读(4100) (10)

如何为Halide后端安排网络

2017-10-18 10:21:40 更新

介绍

Halide代码对于我们使用的每个设备都是一样的。但是为了达到满意的效率,我们应该正确地安排计算。在本教程中,我们将介绍如何使用OpenCV深度学习模块中的Halide后端来安排网络。

为了更好地了解Halide计划,您可能需要阅读教程@ http://halide-lang.org/tutorials

如果这是您在OpenCV中与Halide的第一次学习,我们建议从如何启用Halide后端以提高效率

配置文件

您可以通过编写文本配置文件来计划Halide管道的计算。这意味着您可以轻松地矢量化,并行和管理层计算的循环顺序。cv::dnn::Net::setHalideScheduler在第一次cv::dnn::Net::forward调用之前,将特定设备的调度指令传递到文件路径。

调度表示为YAML文件的配置文件,其中每个节点是计划功能或调度指令。

relu1:
  reorder: [x, c, y]
  split: { y: 2, c: 8 }
  parallel: [yo, co]
  unroll: yi
  vectorize: { x: 4 }
conv1_constant_exterior:
  compute_at: { relu1: yi }

考虑使用n批量维度,c渠道,y行和x列的变量。对于变量分裂后都具有相同前缀使用的名称,但o和i后缀对应的外部和内部的变量。例如,对于变量x范围[0, 10)指令split: { x: 2 },xo在范围[0, 5)和xi范围内给出了新的范围指令[0, 2)。变量名称x在同一调度节点中不再可用。

您可以在opencv_extra / testdata / dnn上找到调度示例,并使用它来安排您的网络。

图层融合

由于图层融合,我们只能安排融合集顶层。因为对于每个输出值,我们使用融合公式。例如,如果您有三层卷积+ Scale + ReLU一个一个。

conv(x, y, c, n) = sum(...) + bias(c);
scale(x, y, c, n) = conv(x, y, c, n) * weights(c);
relu(x, y, c, n) = max(scale(x, y, c, n), 0);

融合功能就是这样

relu(x, y, c, n) = max((sum(...) + bias(c)) * weights(c), 0);

所以只有函数调用relu需要调度。

计划模式

有时,使用阻塞结构构建的网络意味着某些层是相同或非常相似的。如果要对不同的层应用相同的调度,精确到平铺或矢量化因子,请在patterns调度文件开头部分中定义调度模式。此外,您的模式可能会使用一些参数变量。

# At the beginning of the file
patterns:
  fully_connected:
    split: { c: c_split }
    fuse: { src: [x, y, co], dst: block }
    parallel: block
    vectorize: { ci: c_split }
# Somewhere below
fc8:
  pattern: fully_connected
  params: { c_split: 8 }

自动调度

您可以让DNN自动安排图层。只是跳过电话cv::dnn::Net::setHalideScheduler。有时它可能比手动调度更有效率。但是,如果需要手动调度特定层次,则可以混合手动和自动调度方式。编写计划文件并跳过要自动计划的图层。