AI-深度学习

嵌入式视觉模型进阶 05:ShuffleNet:通道拆分与重排

展开合集目录
  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:综合项目:检测、跟踪与云台跟随验收
嵌入式视觉模型进阶 05:ShuffleNet:通道拆分与重排的封面

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

分组减少连接,也限制交流

分组卷积把通道分到不同组,每组独立计算。如果连续两层始终使用相同分组,各组之间就缺少直接信息交流。channel shuffle 通过重新排列通道,让下一层分组接收到来自不同旧组的特征。

假设通道为 [0,1,2,3,4,5,6,7],分两组后 reshape 为 [2,4],交换组轴和组内轴再展平,得到 [0,4,1,5,2,6,3,7]。这些数字表示原通道身份,不是计算后的新特征值。

拆分、处理和拼接

ShuffleNetV2 的 stride=1 代表模块把通道拆成两半,一半直通,一半处理,然后拼接并 shuffle。降采样模块需要另行设计两个分支,不能把直通半支直接与缩小后的另一半拼接。

本课代码实现同形状的教学模块,重点是追踪信息流。它没有覆盖完整网络所有 stage 和降采样细节。

连续内存与真实成本

transpose 往往只是改变步幅。后续展平若需要连续内存,contiguous() 可能触发真实复制。参数为零的操作仍可能消耗带宽。部署时检查 compiler 是否能消除重排,或者是否产生额外 CPU 与 NPU 交接。

练习

先手算 groups=4 时的通道顺序,再运行代码核对。把 channels 改成奇数,看拆分分支为什么不能直接复用。

参考结论:groups=4 得到 [0,2,4,6,1,3,5,7]。本教学块要求偶数通道,以保持两支等宽;不同设计可以取消这个限制,但需要同步修改卷积与拼接契约。

配套实验与运行记录

实验:ShuffleNet:通道拆分与重排

查看完整代码:main.py
"""通过通道编号追踪 shuffle 的作用,观察分组之间的信息交换与复制成本。"""

import torch
from torch import nn


def shuffle(x, groups):
    n, c, h, w = x.shape
    if c % groups:
        raise ValueError("channels must be divisible by groups")
    return (
        x.reshape(n, groups, c // groups, h, w)
        .transpose(1, 2)
        .contiguous()
        .reshape(n, c, h, w)
    )


class SplitBlock(nn.Module):
    def __init__(self, c):
        super().__init__()
        if c % 2:
            raise ValueError("even channels required")
        half = c // 2
        self.branch = nn.Sequential(
            nn.Conv2d(half, half, 1),
            nn.ReLU(),
            nn.Conv2d(half, half, 3, padding=1, groups=half),
            nn.Conv2d(half, half, 1),
            nn.ReLU(),
        )

    def forward(self, x):
        a, b = x.chunk(2, dim=1)
        return shuffle(torch.cat([a, self.branch(b)], 1), 2)


def main():
    torch.manual_seed(7)
    torch.set_num_threads(2)
    ids = torch.arange(8).reshape(1, 8, 1, 1)
    assert shuffle(ids, 2).flatten().tolist() == [0, 4, 1, 5, 2, 6, 3, 7]
    print(
        "groups2:",
        shuffle(ids, 2).flatten().tolist(),
        "groups4:",
        shuffle(ids, 4).flatten().tolist(),
    )
    x = torch.randn(1, 8, 16, 16)
    y = SplitBlock(8)(x)
    assert y.shape == x.shape
    print("output", list(y.shape))


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

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

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

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

参考与进一步阅读

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


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