AI-深度学习

嵌入式视觉模型进阶 03:MobileNet:深度可分离卷积与倒残差

展开合集目录
  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:综合项目:检测、跟踪与云台跟随验收
嵌入式视觉模型进阶 03:MobileNet:深度可分离卷积与倒残差的封面

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

从减少空间卷积成本开始

V1 式模块先对各通道做 depthwise,再用 pointwise 交换通道信息。以 16→24、3×3 为例,普通卷积有 3456 个权重,可分离结构有 16×9+16×24=528 个,均忽略 BN 和偏置。这是结构选择,不是对原卷积的无损压缩。

为什么先变宽再变窄

V2 的代表模块先用 1×1 把通道扩展为隐藏维度,在较宽空间执行 depthwise,再用 1×1 投影回输出维度。扩展系数为 6 时,16 通道变为 96。中间特征更大,因此较少参数并不保证较少激活内存。

“倒残差”对应窄输入、宽中间、窄输出的残差结构。线性瓶颈强调最后的窄投影后不再接 ReLU6,以免进一步截断低维信息。它不是说整个模块没有非线性。

残差连接有条件

只有 stride=1 且输入输出通道相同时,示例模块才直接加回输入。16→24 或 stride=2 时不能直接相加。若另外添加投影捷径,就改变了本课要复现的模块约定。

运行代码分别检查同形状和降采样情形。参数初始化固定,但没有训练,因此本课只验证计算结构。

动手修改与部署关注点

把扩展系数从 6 改为 2,统计权重及隐藏激活元素。再在最后投影后错误地添加 ReLU6,对包含负值的输入比较输出分布。

参考结论:缩小扩展系数会降低中间通道与成本,但不能直接断言任务精度保持;修改激活意味着修改模型,必须重训或重新验证。实际部署还要检查 depthwise 内核效率及通道对齐要求。

配套实验与运行记录

实验:MobileNet:深度可分离卷积与倒残差

查看完整代码:main.py
"""实现 MobileNetV1 与 V2 的代表模块,理解扩展、深度卷积和线性投影。"""

import torch
from torch import nn


class V1(nn.Sequential):
    def __init__(self, cin, cout):
        super().__init__(
            nn.Conv2d(cin, cin, 3, padding=1, groups=cin, bias=False),
            nn.BatchNorm2d(cin),
            nn.ReLU(),
            nn.Conv2d(cin, cout, 1, bias=False),
            nn.BatchNorm2d(cout),
            nn.ReLU(),
        )


class InvertedResidual(nn.Module):
    def __init__(self, cin, cout, stride=1, expand=6):
        super().__init__()
        hidden = cin * expand
        self.use_skip = stride == 1 and cin == cout
        self.layers = nn.Sequential(
            nn.Conv2d(cin, hidden, 1, bias=False),
            nn.BatchNorm2d(hidden),
            nn.ReLU6(),
            nn.Conv2d(hidden, hidden, 3, stride, 1, groups=hidden, bias=False),
            nn.BatchNorm2d(hidden),
            nn.ReLU6(),
            nn.Conv2d(hidden, cout, 1, bias=False),
            nn.BatchNorm2d(cout),
        )

    def forward(self, x):
        y = self.layers(x)
        return x + y if self.use_skip else y


def main():
    torch.manual_seed(7)
    torch.set_num_threads(2)
    x = torch.randn(2, 16, 24, 24)
    for net, shape in [
        (V1(16, 24), (2, 24, 24, 24)),
        (InvertedResidual(16, 16), (2, 16, 24, 24)),
        (InvertedResidual(16, 24, 2), (2, 24, 12, 12)),
    ]:
        y = net.eval()(x)
        assert tuple(y.shape) == shape
        y.square().mean().backward()
        print(
            type(net).__name__, list(y.shape), sum(p.numel() for p in net.parameters())
        )


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

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

cd 'content/posts/AI-深度学习/嵌入式视觉模型进阶/03-MobileNet/code' && python3 main.py

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

参考与进一步阅读

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


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