AI-深度学习

嵌入式视觉模型进阶 06:结构重参数化:训练分支怎样合为一个卷积

展开合集目录
  1. 嵌入式视觉模型进阶 01:怎样读懂一个实际模型
  2. 嵌入式视觉模型进阶 02:卷积进阶:分组、深度、逐点与空洞卷积
  3. 嵌入式视觉模型进阶 03:MobileNet:深度可分离卷积与倒残差
  4. 嵌入式视觉模型进阶 04:通道注意力与 MobileNetV3
  5. 嵌入式视觉模型进阶 05:ShuffleNet:通道拆分与重排
  6. 嵌入式视觉模型进阶 06:结构重参数化:训练分支怎样合为一个卷积
  7. 嵌入式视觉模型进阶 07:Backbone、Neck、Head 与多尺度特征
  8. 嵌入式视觉模型进阶 08:FPN 与 PAN:怎样融合不同尺度的信息
  9. 嵌入式视觉模型进阶 09:检测头:分类、位置与置信度
  10. 嵌入式视觉模型进阶 10:检测训练:标签分配与损失计算
  11. 嵌入式视觉模型进阶 11:完整 YOLOv8 推理:从图片到原图目标框
  12. 嵌入式视觉模型进阶 12:训练自己的检测器与错误分析
  13. 嵌入式视觉模型进阶 13:检测器导出对照与其他结构路线
  14. 嵌入式视觉模型进阶 14:跟踪基础:运动预测、匹配与轨迹生命周期
  15. 嵌入式视觉模型进阶 15:ByteTrack 思路:用低分框恢复已有轨迹
  16. 嵌入式视觉模型进阶 16:外观特征与 ReID:怎样比较两个目标
  17. 嵌入式视觉模型进阶 17:目标跟随:从轨迹到云台控制接口
  18. 嵌入式视觉模型进阶 18:视频系统:队列、时间戳与异步流水线
  19. 嵌入式视觉模型进阶 19:人脸处理流程:检测、关键点与对齐
  20. 嵌入式视觉模型进阶 20:特征识别:人脸向量、相似度与阈值
  21. 嵌入式视觉模型进阶 21:人体关键点与姿态估计
  22. 嵌入式视觉模型进阶 22:OCR 检测:从文字区域到矫正图片
  23. 嵌入式视觉模型进阶 23:OCR 识别:序列模型与 CTC
  24. 嵌入式视觉模型进阶 24:完整 OCR 流程与识别错误分析
  25. 嵌入式视觉模型进阶 25:轻量语义分割与实例分割
  26. 嵌入式视觉模型进阶 26:工业异常检测:缺陷样本少时怎么办
  27. 嵌入式视觉模型进阶 27:深度估计与图像恢复:两个专题入口
  28. 嵌入式视觉模型进阶 28:面向 NPU 的计算图与算子兼容
  29. 嵌入式视觉模型进阶 29:量化、蒸馏与剪枝的实际取舍
  30. 嵌入式视觉模型进阶 30:综合项目:检测、跟踪与云台跟随验收
嵌入式视觉模型进阶 06:结构重参数化:训练分支怎样合为一个卷积的封面

第 6 / 30 课。 前置:上一课。本篇的运行范围以实验说明和验证记录为准,构造样例不代表真实任务精度或板端性能。

先把 BatchNorm 写成仿射变换

推理模式使用固定 running_mean、running_var。令 a=gamma/sqrt(var+eps),则 BN(z)=a×z+beta−a×mean。对于 z=Wx+b,融合权重为 aW,偏置为 a(b−mean)+beta,a 按输出通道广播。

训练模式依赖当前批次统计,不能用固定融合权重代替。eval() 后进行融合,并核对统计量是否确实经过训练;未训练的统计量也能证明代数等价,但不能证明任务质量。

多分支为什么可以相加

本课包含 3×3+BN、1×1+BN 和 identity+BN 三支,先相加再激活。把 1×1 核放到 3×3 核中心;把 identity 表示为中心位置的通道单位矩阵;再把各支融合后的核和偏置相加。

这个推导依赖分支之间没有非线性。如果每个分支先做 ReLU,通常不能把它们合成一个线性卷积。各支的 stride、padding、分组和输出尺寸也必须一致。

实验的数值对照

脚本先运行几批数据更新 BN 统计,再切换 eval,融合成一个带 bias 的 3×3 卷积。对多个固定随机输入比较完整输出,使用绝对加相对容差而不是要求浮点逐位一致。

修改任务与验收

故意在某一支相加前加入 ReLU,观察误差;恢复后把输入宽高设为奇数,再验证。交付融合前后结构、参数文件、最大误差和实际耗时。

参数文件可能变大或变小,取决于训练结构;主要收益往往是执行图更简单。是否变快需在目标设备测量,不能仅凭分支减少给出 FPS。

配套实验与运行记录

实验:结构重参数化:训练分支怎样合为一个卷积

查看完整代码:main.py
"""推导 Conv-BN 融合,并验证三分支结构与单卷积的数值一致性。"""

import torch
from torch import nn
import torch.nn.functional as F


def fuse(conv, bn):
    scale = bn.weight / torch.sqrt(bn.running_var + bn.eps)
    bias = conv.bias if conv.bias is not None else torch.zeros_like(scale)
    return (
        conv.weight * scale[:, None, None, None],
        bn.bias + (bias - bn.running_mean) * scale,
    )


def main():
    torch.manual_seed(7)
    torch.set_num_threads(2)
    c = 4
    c3 = nn.Conv2d(c, c, 3, padding=1, bias=False)
    b3 = nn.BatchNorm2d(c)
    c1 = nn.Conv2d(c, c, 1, bias=False)
    b1 = nn.BatchNorm2d(c)
    bi = nn.BatchNorm2d(c)
    for _ in range(5):
        x = torch.randn(4, c, 12, 12)
        b3(c3(x))
        b1(c1(x))
        bi(x)
    for b in (b3, b1, bi):
        b.eval()
    identity = nn.Conv2d(c, c, 1, bias=False)
    with torch.no_grad():
        identity.weight.copy_(torch.eye(c).reshape(c, c, 1, 1))
    w3, p3 = fuse(c3, b3)
    w1, p1 = fuse(c1, b1)
    wi, pi = fuse(identity, bi)
    merged = nn.Conv2d(c, c, 3, padding=1)
    with torch.no_grad():
        merged.weight.copy_(w3 + F.pad(w1, (1, 1, 1, 1)) + F.pad(wi, (1, 1, 1, 1)))
        merged.bias.copy_(p3 + p1 + pi)
    for size in [12, 17, 23]:
        x = torch.randn(2, c, size, size)
        expected = torch.relu(b3(c3(x)) + b1(c1(x)) + bi(x))
        actual = torch.relu(merged(x))
        torch.testing.assert_close(actual, expected, rtol=1e-5, atol=2e-6)
        print(size, "max error", float((actual - expected).abs().max().detach()))


if __name__ == "__main__":
    main()
下载代码

在项目根目录执行;下载包解压后,在解压目录执行 python3 main.py。

cd 'content/posts/AI-深度学习/嵌入式视觉模型进阶/06-结构重参数化/code' && python3 main.py

在本篇代码目录执行 python3 main.py。依赖安装、可选入口见包内 README.md;实际运行结果见 verification.md。下载包包含本课使用的辅助模块与样例。

参考与进一步阅读

原始论文或官方文档用于核对结构、接口和适用范围。本篇代码是围绕教学目标编写的最小实验;除明确注明的完整入口外,不视为对应论文模型的完整复现。


完整进阶目录 · 入门系列 · 上一篇 · 下一篇