在主流卷积神经网络模型中Conv+BN+Relu
是一种常见的模型结构。在模型推理和训练中,BN层往往与其他层合并,以减少计算量。
模型解析
node_of_325
[TRT] Parsing node: node_of_325 [Conv]
[TRT] Searching for input: 324
[TRT] Searching for input: layer1.0.conv1.weight
[TRT] node_of_325 [Conv] inputs: [324 -> (-1, 64, 56, 56)[FLOAT]], [layer1.0.conv1.weight -> (128, 64, 1, 1)[FLOAT]],
[TRT] Convolution input dimensions: (-1, 64, 56, 56)
[TRT] Registering layer: node_of_325 for ONNX node: node_of_325
[TRT] Using kernel: (1, 1), strides: (1, 1), prepadding: (0, 0), postpadding: (0, 0), dilations: (1, 1), numOutputs: 128
[TRT] Convolution output dimensions: (-1, 128, 56, 56)
[TRT] Registering tensor: 325 for ONNX tensor: 325
[TRT] node_of_325 [Conv] outputs: [325 -> (-1, 128, 56, 56)[FLOAT]],
node_of_326
[TRT] Parsing node: node_of_326 [BatchNormalization]
[TRT] Searching for input: 325
[TRT] Searching for input: layer1.0.bn1.weight
[TRT] Searching for input: layer1.0.bn1.bias
[TRT] Searching for input: layer1.0.bn1.running_mean
[TRT] Searching for input: layer1.0.bn1.running_var
[TRT] node_of_326 [BatchNormalization] inputs: [325 -> (-1, 128, 56, 56)[FLOAT]], [layer1.0.bn1.weight -> (128)[FLOAT]], [layer1.0.bn1.bias
n1.running_mean -> (128)[FLOAT]], [layer1.0.bn1.running_var -> (128)[FLOAT]],
[TRT] Registering layer: node_of_326 for ONNX node: node_of_326
[TRT] Registering tensor: 326 for ONNX tensor: 326
[TRT] node_of_326 [BatchNormalization] outputs: [326 -> (-1, 128, 56, 56)[FLOAT]],
node_of_327
[TRT] Parsing node: node_of_327 [Relu]
[TRT] Searching for input: 326
[TRT] node_of_327 [Relu] inputs: [326 -> (-1, 128, 56, 56)[FLOAT]],
[TRT] Registering layer: node_of_327 for ONNX node: node_of_327
[TRT] Registering tensor: 327 for ONNX tensor: 327
[TRT] node_of_327 [Relu] outputs: [327 -> (-1, 128, 56, 56)[FLOAT]],
优化
在TensorRT中会对网络结构进行垂直整合,即将 Conv、BN、Relu 三个层融合为了一个层,即CBR融合
Scale fusion
[TRT] Fusing convolution weights from node_of_325 with scale node_of_326
在BN层中,首先对输入进行归一化(输入张量的均值,输入张量的方差),然后对归一化的结果进行比例缩放和位移。[1][2]
展开可得:
带入替换后可得:
此时可以将BN层视为一个1x1卷积层。
BN层的输入特征(Conv层的输出特征)的形状为,对于Conv层:
- 卷积核大小为
- 权重为,偏置为
- 通道数为
,因此BN与Conv融合之后
融合之后:
- 权重为
- 偏置为
ConvRelu fusion
[TRT] ConvReluFusion: Fusing node_of_325 with node_of_327
线性整流函数(Rectified Linear Unit, ReLU)即:,又称修正线性单元,是一种人工神经网络中常用的激活函数(activation function)。
在神经网络中,线性整流作为神经元的激活函数,定义了该神经元在线性变换之后的非线性输出结果。换言之,对于来自上一层卷积层的输入向量,使用线性整流激活函数可以得到输出:
在Int8量化模型中,Conv+ReLU 一般也可以合并成一个Conv进行运算[3]。
对于Int8ReLU,其计算公式可以写为 :
由于ReLU的输入(数值范围为)和输出(数值范围为)的数值范围不同,因此需要保证和 、和 是一致的。由于ReLU的截断操作,因此需要使用 和 ,即对于ReLU的输入,使用输出对应的 和 保证其对小于0截断的输入进行截断,对大于等于0的输入映射至[0,255]范围内。
在Int8Conv的计算过程中,首先使用量化计算公式 对输入和权重值进行量化计算,将其转换为数值范围为(0,255)的整数,在完成卷积计算后再将计算结果进行反量化计算。而 ReLU 本身没有做任何的数学运算,只是一个截断函数。假设Int8Conv的卷积输出为 122(),则对应反量化输出 -0.3,经过Int8ReLU(),对该值进行Int8量化,对应的输出为0。因此在ReLU层对输入进行截断之前,即可得到需要截断的数值。
因此,通过在完成卷积计算后直接使用 ReLU 后的 scale 和 zeropoint进行反量化,实现了将 ConvReLU融合。
-
BatchNormalization https://hub.fastgit.org/onnx/onnx/blob/master/docs/Operators.md#BatchNormalization ↩
-
Speeding up model with fusing batch normalization and convolution http://learnml.today/speeding-up-model-with-fusing-batch-normalization-and-convolution-3 ↩
-
神经网络量化入门--Folding BN ReLU https://zhuanlan.zhihu.com/p/176982058 ↩