Home 【WIP】推理图优化方法小结
Post
Cancel

【WIP】推理图优化方法小结

在神经网络推理过程中,图优化可以达到降低运算量、减少算子invoke数目与降低缓存开销等效果,提升推理效率。本文对常见的推理图优化方法进行小结,并不断增补中。

符号定义

卷积、全连接层运算

设卷积或全连接层$Layer(W, b)$的权重和偏置为\(W\)和\(b\),输入和输出分别为\(X\)和\(X^{\prime}\),有

\(X^{\prime} = X \otimes W + b\) 其中$\otimes$代表卷积或矩阵乘法操作。$X \in \mathbf{R}^{c\times h \times w}, \ \ X^{\prime} \in \mathbf{R}^{c^{\prime} \times h^{\prime} \times w^{\prime}}, \ \ b \in \mathbf{R}^{c^{\prime}}.$

当$Layer$为卷积层时,$W \in \mathbf{R}^{c^{\prime} \times c \times k \times k}$,当$Layer$为全连接层时,$W \in \mathbf{R}^{c^{\prime} \times c}.$

BN运算

设BN层($\gamma, \beta$)输出和输出分别为\(X\)和\(X^{\prime}\),有 \(X^{\prime} = \gamma \frac{X - \mu}{\sqrt{\delta^2 + \epsilon}} + \beta\)

其中${X, X^{\prime}}\in \mathbf{R}^{c\times h \times w}$. $\mu, \sigma, \gamma, \beta$均为逐特征通道的变量,在推理时为固定值,${\mu, \sigma, \gamma, \beta}\in \mathbf{R}^c$.

关于BN层的更多细节介绍,可参见关于Batch Normalization

网络层融合

缩放/偏置算子融合

BN + scale (逐通道)

对应ncnn中的NetOptimize::fuse_batchnorm_scale()

1
2
融合前:X --> [BN] --> X' --> [S] --> Y
融合后:X --> [BN'] --> Y

BN层后接一个沿特征通道维度的缩放算子$S\in\mathbf{R}^{c}$,将对应系数逐通道融合进BN层的系数$\gamma, \beta$中即可。

Conv + scale (逐通道)

对应ncnn中的NetOptimize::NetOptimize::fuse_convolution_mul()

1
2
融合前:X --> [Conv] --> X' --> [S] --> Y
融合后:X --> [Conv'] --> Y

卷积层后接一个沿特征通道维度的缩放算子$S\in\mathbf{R}^{c}$,将对应系数逐输出通道(乘积)融合进卷积层的系数$W, b$中即可。

Conv + Add (逐通道)

对应ncnn中的NetOptimize::NetOptimize::fuse_convolution_add()

1
2
融合前:X --> [Conv] --> X' --> [Add] --> Y
融合后:X --> [Conv'] --> Y

卷积层后接一个沿特征通道维度的偏置算子$A\in\mathbf{R}^{c}$,将对应系数逐输出通道(加和)融合进卷积层的系数$b$中即可。

对应ncnn中的NetOptimize::fuse_innerproduct_add()

1
2
融合前:X --> [FC] --> X' --> [Add] --> Y
融合后:X --> [FC'] --> Y

FC + Add 和 Conv + Add 原理非常相似。将对应系数逐输出通道(加和)融合进全连接层的系数$b$中即可。

FC + Add (逐通道)

FC + Act 和 Conv + Act 原理非常相似。此处不再赘述。

BN层融合

Conv + BN

对应ncnn中的NetOptimize::fuse_convolution_batchnorm()

1
2
融合前:X --> [Conv] --> X' --> [BN] --> Y
融合后:X --> [Conv'] --> Y

参考

To execute neural network inference, kernels are invoked for neural network layers in order to compute the output tensors given the input tensors. Each kernel call will bring some overhead time….To achieve high throughput and low latency for neural network inference, the rule of thumb is to have fewer large kernel calls instead of many small kernel calls.

卷积输出\(X^{\prime}\)紧接进入BN层,设BN层的输出为\(Y\),则有

\(\begin{aligned} Y &= \gamma \frac{X^{\prime} - \mu}{\sqrt{\delta^2 + \epsilon}} + \beta= \gamma \frac{X * W + b - \mu}{\sqrt{\delta^2 + \epsilon}} + \beta \\ &= X * (\frac{\gamma}{\sqrt{\delta^2 + \epsilon}}W) + \beta + \frac{\gamma}{\sqrt{\delta^2 + \epsilon}}(b - \mu) \\ &= X * W^{\prime} + b^{\prime} \end{aligned}\) 即 conv + BN 等效于调用一个权重为\(W^{\prime} = \frac{\gamma}{\sqrt{\delta^2 + \epsilon}}W\),偏置为\(b^{\prime} = \beta + \frac{\gamma}{\sqrt{\delta^2 + \epsilon}}(b - \mu)\)的新卷积。该步融合减少了kernel的invoke次数,降低了计算量,且省去了原Conv层与BN层之间的缓存开销。

FC + BN

对应ncnn中的NetOptimize::fuse_innerproduct_batchnorm()

1
2
融合前:X --> [FC] --> X' --> [BN] --> Y
融合后:X --> [FC'] --> Y

FC + BN 和 Conv + BN 原理非常相似。将对应系数逐输出通道(乘积)融合进全连接层的系数$W, b$中即可。

激活函数融合

Conv + Act (逐元素)

对应ncnn中的NetOptimize::NetOptimize::fuse_convolution_activation()

1
2
融合前:X --> [Conv] --> X' --> [Act] --> Y
融合后:X --> [Conv'] --> X'(inplace Act) -> Y

卷积层紧跟逐元素的激活函数,可以直接将卷积层的输出的激活操作原地(inplace)进行,节约一次缓存开销。ncnn目前支持融合的激活函数有ReLU, Clip, Sigmoid, Mish, HardSwish.

基于此,Conv + BN + ReLU可先将Conv和BN融合成Conv’,再将ReLU融合inplace操作,从而在省去BN层计算量的同时,减少两次访存。参考

FC + Act (逐元素)

对应ncnn中的NetOptimize::fuse_innerproduct_activation()

1
2
融合前:X --> [FC] --> X' --> [Act] --> Y
融合后:X --> [FC'] --> X'(inplace Act) -> Y

冗余算子消除

消除dropout算子

对应ncnn中的NetOptimize::eliminate_dropout()

1
2
消除前:X --> [dropout] --> Y
消除后:X --> Y

dropout算子在推理时不作用,因此可消除。

消除identity算子

对应ncnn中的NetOptimize::eliminate_noop()

1
2
消除前:X --> [identity] --> Y
消除后:X --> Y

啥不做的算子为啥留着?消除它。

消除只有一个输出分支的split算子

对应ncnn中的NetOptimize::eliminate_split()

1
2
消除前:X --> [split] --> Y (split只有一个输出分支)
消除后:X --> Y

当split只有一个输出分支时,该split节点时是冗余的,可以消除。

消除global pooling后的flatten算子

对应ncnn中的NetOptimize::eliminate_flatten_after_global_pooling()

1
2
消除前:X --> [global pooling] --> [flatten] --> Y 
消除后:X --> [global pooling] --> Y

在ncnn中,global pooling后特征的形状是大小为$C$的一维向量,再做flatten操作无实质变化,因此flatten是冗余的,可以消除。

常量折叠

参考

This post is licensed under CC BY 4.0 by the author.

Flask项目部署记录(uwsgi)

gdb学习