读 ncnn 源码(XXIX):`fuse_innerproduct_dropout`——推理时移除 Dropout
读 ncnn 源码(XXIX):fuse_innerproduct_dropout——推理时移除 Dropout
在本系列的图优化篇章中,我们已经分析了多种针对线性计算链(如 Conv/InnerProduct 后接 BN/Scale/Add/Mul)的融合优化。
Dropout层是神经网络训练时常用的一种正则化手段,但在推理阶段,它的行为通常是恒等映射(Identity)或者乘以一个固定的缩放因子。fuse_innerproduct_dropout正是利用了 Dropout 在推理时的这一特性,将其与前驱的InnerProduct层进行融合或直接消除。本篇,我们将剖析
fuse_innerproduct_dropout的源码,理解其如何处理 Dropout 层以优化推理性能。
TL;DR
- 目标: 将
InnerProduct(全连接) 层后紧随的Dropout层进行融合或消除,因为 Dropout 在推理时通常是恒等操作或固定缩放。 - 模式匹配: 遍历网络
layers,查找InnerProduct -> Dropout的直接连接模式。 - 数学原理:
- Dropout 在训练时以概率
p将部分输出置零,并将其余输出乘以1/(1-p)(Inverted Dropout)或在推理时乘以(1-p)。 - 在推理阶段,Dropout 的操作等效于:,其中
scale通常是1.0(如果训练时采用 Inverted Dropout) 或1-p(如果训练时未做缩放)。 - 这个固定的缩放操作可以像融合
Mul操作一样,被合并到前驱的InnerProduct层中。 - 如果
scale == 1.0(常见情况),则Dropout层在推理时完全是恒等操作,可以直接消除。
- Dropout 在训练时以概率
- 代码实现:
- 获取
Dropout层的缩放因子scale = dropout->scale。 - 如果
scale != 1.f:- 遍历
InnerProduct层的每个输出单元i。 - 将连接到该输出单元的所有权重乘以
scale。 - 如果
InnerProduct层有偏置,则将偏置也乘以scale。
- 遍历
- 如果
scale == 1.f: 代码中没有显式处理,但融合逻辑(图结构修改)依然执行,效果等同于直接消除 Dropout 层。
- 获取
- 图结构修改: 将
InnerProduct层的top指向原Dropout层的top,更新blob的producer,并将Dropout层标记为"ncnnfused"。 - 效果: 完全消除了
Dropout层在推理时的计算(无论是恒等映射还是缩放)和内存访问,降低了模型复杂度。

1. 融合动机:Dropout 在推理时的“失效”
Dropout 是一种强大的正则化技术,它在训练过程中随机将一部分神经元的输出置零,以防止过拟合并增强模型的泛化能力。为了在训练和推理时保持期望输出值的一致性,通常采用以下两种策略之一:
- Inverted Dropout (常用): 在训练时,将未被置零的输出乘以
1 / (1 - p)(p是置零概率)。这样,在推理时,Dropout 层完全是一个恒等操作,直接将输入传递给下一层即可,scale因子为1.0。 - Standard Dropout: 在训练时只置零,不进行缩放。那么,在推理时,需要将所有输出乘以
(1 - p),以补偿训练时丢失的“能量”,此时scale因子为1-p。
无论哪种情况,在推理阶段,Dropout 的行为都是确定的、线性的(要么乘以 1,要么乘以一个固定的 scale)。这个线性操作可以被前驱的线性层(如 InnerProduct)吸收。
2. 代码实现:吸收缩放因子或直接消除
fuse_innerproduct_dropout 的代码逻辑相对简单,核心就是处理这个 scale 因子。
1 | int NetOptimize::fuse_innerproduct_dropout() |
关键点:
- 模式匹配: 查找
InnerProduct -> Dropout的直接连接。 - 参数更新: 获取
dropout->scale。仅当scale != 1.0时,才将InnerProduct的权重矩阵W和偏置向量b(如果存在)的所有元素都乘以scale。 - 图修改: 无论
scale是否为 1,都执行标准的图结构修改:重定向InnerProduct的输出,并将Dropout层标记为"ncnnfused"。这有效地从计算图中移除了Dropout层。
3. 意义:移除推理阶段的冗余操作
Dropout 是一个只在训练阶段有意义的层。在推理时保留它(即使是作为恒等映射)也会带来不必要的开销(至少有一次函数调用和可能的数据拷贝)。fuse_innerproduct_dropout Pass 通过将 Dropout 的(通常为 1 的)缩放效果合并到前驱的 InnerProduct 层中,或者直接消除它,确保了推理计算图中不包含任何冗余的 Dropout 操作。
虽然 ncnnoptimize 工具中还有一个更通用的 eliminate_dropout Pass(我们将在后续篇章分析),但 fuse_innerproduct_dropout 的存在可能是为了更早地处理这种常见的线性组合,或者有历史原因。
4. 结语
fuse_innerproduct_dropout 是 ncnn 图优化中一个相对简单的 Pass,它利用了 Dropout 层在推理阶段行为固定的特性,将其与前驱的 InnerProduct 层进行融合或直接消除。虽然 Dropout 本身计算量不大,但移除冗余层有助于简化计算图,减少调度开销和潜在的内存操作。这一优化与其他针对线性计算链的融合 Pass 一起,共同提升了 ncnn 模型在部署时的最终性能。
该封面图片由Bar Elimelech在Pixabay上发布





