Ultralytics:解读CBLinear模块

Ultralytics:解读CBLinear模块

前言

相关介绍

Ultralytics 简介

Ultralytics 基于多年的计算机视觉和人工智能基础研究,创建了最先进的 (SOTA) YOLO 模型。我们的模型不断更新性能和灵活性,快速、准确且易于使用。他们擅长对象检测、跟踪、实例分割、语义分割、图像分类和姿势估计任务。

前提条件

  • 熟悉Python、Pytorch

实验环境

bash 复制代码
Package                  Version
------------------------ ------------
Python                   3.11.8
absl-py                  2.4.0
accelerate               1.13.0
annotated-doc            0.0.4
anyio                    4.13.0
calflops                 0.3.2
certifi                  2026.4.22
charset-normalizer       3.4.7
click                    8.3.3
colorama                 0.4.6
contourpy                1.3.3
cycler                   0.12.1
filelock                 3.29.0
flatbuffers              25.12.19
fonttools                4.62.1
fsspec                   2026.4.0
grpcio                   1.80.0
h11                      0.16.0
hf-xet                   1.5.0
httpcore                 1.0.9
httpx                    0.28.1
huggingface_hub          1.14.0
idna                     3.15
Jinja2                   3.1.6
kiwisolver               1.5.0
Markdown                 3.10.2
markdown-it-py           4.2.0
MarkupSafe               3.0.3
matplotlib               3.10.9
mdurl                    0.1.2
ml_dtypes                0.5.0
mpmath                   1.3.0
networkx                 3.6.1
numpy                    1.26.4
nvidia-cublas-cu12       12.8.3.14
nvidia-cuda-cupti-cu12   12.8.57
nvidia-cuda-nvrtc-cu12   12.8.61
nvidia-cuda-runtime-cu12 12.8.57
nvidia-cudnn-cu12        9.7.1.26
nvidia-cufft-cu12        11.3.3.41
nvidia-cufile-cu12       1.13.0.11
nvidia-curand-cu12       10.3.9.55
nvidia-cusolver-cu12     11.7.2.55
nvidia-cusparse-cu12     12.5.7.53
nvidia-cusparselt-cu12   0.6.3
nvidia-nccl-cu12         2.26.2
nvidia-nvjitlink-cu12    12.8.61
nvidia-nvtx-cu12         12.8.55
onnx                     1.19.0
onnxruntime-gpu          1.26.0
onnxslim                 0.1.94
opencv-python            4.6.0.66
packaging                26.2
pillow                   12.2.0
pip                      24.0
polars                   1.40.1
polars-runtime-32        1.40.1
protobuf                 7.34.1
psutil                   7.2.2
pycocotools              2.0.11
Pygments                 2.20.0
pyparsing                3.3.2
python-dateutil          2.9.0.post0
PyYAML                   6.0.3
regex                    2026.5.9
requests                 2.34.1
rich                     15.0.0
safetensors              0.7.0
scipy                    1.16.0
setuptools               65.5.0
shellingham              1.5.4
six                      1.17.0
sympy                    1.14.0
tabulate                 0.10.0
tensorboard              2.20.0
tensorboard-data-server  0.7.2
tokenizers               0.22.2
torch                    2.7.1+cu128
torchaudio               2.7.1+cu128
torchvision              0.22.1+cu128
tqdm                     4.67.3
transformers             5.8.1
triton                   3.3.1
typer                    0.25.1
typing_extensions        4.15.0
ultralytics              8.4.58
ultralytics-thop         2.0.19
urllib3                  2.7.0
Werkzeug                 3.1.8

CBLinear(多分支卷积模块)

CBLinear 是一个用于生成 多个不同通道数的特征图 的卷积模块。它通过一个共享的卷积层同时输出总通道数为 sum(c2s) 的特征图,然后按指定的通道尺寸列表 c2s 沿通道维度拆分为多个张量。这种设计在需要 多任务输出多尺度特征分支 的网络中非常有用,例如 YOLOv9 中的某些跨层连接结构。


代码实现

python 复制代码
import cv2
import math
import torch
import torch.nn as nn
import torch.nn.functional as F
import numpy as np
import matplotlib.pyplot as plt

def autopad(k, p=None, d=1):  # kernel, padding, dilation
    """Pad to 'same' shape outputs."""
    if d > 1:
        k = d * (k - 1) + 1 if isinstance(k, int) else [d * (x - 1) + 1 for x in k]  # actual kernel-size
    if p is None:
        p = k // 2 if isinstance(k, int) else [x // 2 for x in k]  # auto-pad
    return p

class CBLinear(nn.Module):
    """CBLinear."""

    def __init__(self, c1: int, c2s: list[int], k: int = 1, s: int = 1, p: int | None = None, g: int = 1):
        """Initialize CBLinear module.

        Args:
            c1 (int): Input channels.
            c2s (list[int]): List of output channel sizes.
            k (int): Kernel size.
            s (int): Stride.
            p (int | None): Padding.
            g (int): Groups.
        """
        super().__init__()
        self.c2s = c2s
        self.conv = nn.Conv2d(c1, sum(c2s), k, s, autopad(k, p), groups=g, bias=True)

    def forward(self, x: torch.Tensor) -> list[torch.Tensor]:
        """Forward pass through CBLinear layer."""
        return self.conv(x).split(self.c2s, dim=1)

功能

  • 多输出分支 :将输入特征通过一个卷积层,生成总通道数为 sum(c2s) 的特征图,然后按照 c2s 列表中的数值,沿通道维拆分成多个张量。
  • 共享卷积权重:所有分支共享同一个卷积核,减少参数量,同时能够并行计算。
  • 灵活配置 :通过 c2s 可以灵活指定每个分支的输出通道数,适应不同任务需求(如目标检测中的分类头、回归头等)。

初始化参数

参数 类型 说明
c1 int 输入特征图的通道数
c2s listint 各分支的输出通道数列表,长度表示分支数
k int 卷积核大小(默认 1)
s int 卷积步长(默认 1)
p int | None 填充,若为 None 则自动计算
g int 分组卷积的组数(默认 1)
  • 卷积层 self.conv 的输出通道数为 sum(c2s),偏置 bias=True
  • 填充 p 通过 autopad 自动计算(保持输出尺寸不变)。

前向方法

  • forward(x):输入 x 形状 [B, c1, H, W],返回一个张量列表,每个张量形状为 [B, c2_i, H, W],其中 c2_i 对应 c2s[i]

使用示例

python 复制代码
if __name__ == '__main__':
    # 1. 随机输入
    B, c1, H, W = 2, 16, 32, 32
    x = torch.randn(B, c1, H, W)

    # 2. 定义各分支输出通道数
    c2s = [8, 16, 32]  # 三个分支

    # 3. 创建 CBLinear 模块
    cbl = CBLinear(c1=c1, c2s=c2s, k=3, s=1)

    # 4. 前向传播
    with torch.no_grad():
        outputs = cbl(x)  # outputs 是一个长度为3的列表

    print("输入形状:", x.shape)  # [2, 16, 32, 32]
    for i, out in enumerate(outputs):
        print(f"分支 {i} 输出形状:", out.shape)  # [2, 8, 32, 32], [2, 16, 32, 32], [2, 32, 32, 32]
    print("总参数量:", sum(p.numel() for p in cbl.parameters()))

输出示例

复制代码
输入形状: torch.Size([2, 16, 32, 32])
分支 0 输出形状: torch.Size([2, 8, 32, 32])
分支 1 输出形状: torch.Size([2, 16, 32, 32])
分支 2 输出形状: torch.Size([2, 32, 32, 32])
总参数量: 16*56*9 + 56 = 8120

流程示意图

#mermaid-svg-TrFRx0DEs44YgqHK{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-TrFRx0DEs44YgqHK .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-TrFRx0DEs44YgqHK .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-TrFRx0DEs44YgqHK .error-icon{fill:#552222;}#mermaid-svg-TrFRx0DEs44YgqHK .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-TrFRx0DEs44YgqHK .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-TrFRx0DEs44YgqHK .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-TrFRx0DEs44YgqHK .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-TrFRx0DEs44YgqHK .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-TrFRx0DEs44YgqHK .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-TrFRx0DEs44YgqHK .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-TrFRx0DEs44YgqHK .marker{fill:#333333;stroke:#333333;}#mermaid-svg-TrFRx0DEs44YgqHK .marker.cross{stroke:#333333;}#mermaid-svg-TrFRx0DEs44YgqHK svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-TrFRx0DEs44YgqHK p{margin:0;}#mermaid-svg-TrFRx0DEs44YgqHK .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-TrFRx0DEs44YgqHK .cluster-label text{fill:#333;}#mermaid-svg-TrFRx0DEs44YgqHK .cluster-label span{color:#333;}#mermaid-svg-TrFRx0DEs44YgqHK .cluster-label span p{background-color:transparent;}#mermaid-svg-TrFRx0DEs44YgqHK .label text,#mermaid-svg-TrFRx0DEs44YgqHK span{fill:#333;color:#333;}#mermaid-svg-TrFRx0DEs44YgqHK .node rect,#mermaid-svg-TrFRx0DEs44YgqHK .node circle,#mermaid-svg-TrFRx0DEs44YgqHK .node ellipse,#mermaid-svg-TrFRx0DEs44YgqHK .node polygon,#mermaid-svg-TrFRx0DEs44YgqHK .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-TrFRx0DEs44YgqHK .rough-node .label text,#mermaid-svg-TrFRx0DEs44YgqHK .node .label text,#mermaid-svg-TrFRx0DEs44YgqHK .image-shape .label,#mermaid-svg-TrFRx0DEs44YgqHK .icon-shape .label{text-anchor:middle;}#mermaid-svg-TrFRx0DEs44YgqHK .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-TrFRx0DEs44YgqHK .rough-node .label,#mermaid-svg-TrFRx0DEs44YgqHK .node .label,#mermaid-svg-TrFRx0DEs44YgqHK .image-shape .label,#mermaid-svg-TrFRx0DEs44YgqHK .icon-shape .label{text-align:center;}#mermaid-svg-TrFRx0DEs44YgqHK .node.clickable{cursor:pointer;}#mermaid-svg-TrFRx0DEs44YgqHK .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-TrFRx0DEs44YgqHK .arrowheadPath{fill:#333333;}#mermaid-svg-TrFRx0DEs44YgqHK .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-TrFRx0DEs44YgqHK .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-TrFRx0DEs44YgqHK .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-TrFRx0DEs44YgqHK .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-TrFRx0DEs44YgqHK .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-TrFRx0DEs44YgqHK .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-TrFRx0DEs44YgqHK .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-TrFRx0DEs44YgqHK .cluster text{fill:#333;}#mermaid-svg-TrFRx0DEs44YgqHK .cluster span{color:#333;}#mermaid-svg-TrFRx0DEs44YgqHK div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-TrFRx0DEs44YgqHK .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-TrFRx0DEs44YgqHK rect.text{fill:none;stroke-width:0;}#mermaid-svg-TrFRx0DEs44YgqHK .icon-shape,#mermaid-svg-TrFRx0DEs44YgqHK .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-TrFRx0DEs44YgqHK .icon-shape p,#mermaid-svg-TrFRx0DEs44YgqHK .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-TrFRx0DEs44YgqHK .icon-shape .label rect,#mermaid-svg-TrFRx0DEs44YgqHK .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-TrFRx0DEs44YgqHK .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-TrFRx0DEs44YgqHK .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-TrFRx0DEs44YgqHK :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 输入 x (B, c1, H, W)
Conv2d: c1 → sum(c2s), k, s, p
输出 (B, sum(c2s), H, W)
split(dim=1, split_sizes=c2s)
分支 0 (B, c2s0, H, W)
分支 1 (B, c2s1, H, W)
...
分支 n-1 (B, c2s-1, H, W)


代码解读

  • __init__
    • 存储 c2s 列表供 forward 使用。
    • 创建 nn.Conv2d,输出通道数为 sum(c2s),其他参数(核大小、步长、填充、分组)透传。
    • 使用 autopad 自动计算填充,保证输出空间尺寸不变(除非步长不为 1)。
  • forward
    • 对输入 x 应用卷积,得到总输出。
    • 使用 torch.split 沿通道维(dim=1)按 c2s 拆分,返回张量列表。

注意事项

  1. 输出通道总和sum(c2s) 必须与卷积层的输出通道数一致(实际上代码直接使用该值,因此无冲突)。
  2. 空间尺寸变化 :若步长 s 不为 1,则所有分支的空间尺寸会同步缩小(步长共享),且填充自动调整。
  3. 权重共享:所有分支共享同一个卷积核,这意味着它们提取的是相同的底层特征,只是输出通道数不同。如果希望各分支独立学习,应使用多个卷积层。
  4. 应用场景:常用于需要同时输出不同维度特征(如多个检测头)的网络结构,例如 YOLOv9 中的某些模块。

优缺点

优点
  1. 参数高效:共享卷积权重,相比多个独立卷积层,参数量大幅减少。
  2. 计算高效:一次卷积即可生成所有分支输出,计算量远低于多次卷积。
  3. 灵活配置 :通过 c2s 可任意调整各分支的通道数,适应多任务或多尺度需求。
  4. 易于集成 :与普通 Conv 接口相似,可直接替换标准卷积层。
缺点
  1. 表达能力受限:所有分支共享相同的卷积核,无法针对每个分支学习独立的特征变换。
  2. 输出尺寸绑定:所有分支共享相同的空间尺寸(由步长决定),无法分别控制不同分支的分辨率。
  3. 分组卷积的限制g 参数作用于整个卷积,所有分支共享相同的分组数,若各分支需要不同分组则无法实现。

在 YOLOv9 等网络结构中,CBLinear 常用于高效地生成多个特征分支,以便后续进行不同的处理(如不同尺度的检测)。使用时需权衡共享权重带来的效率提升与表达能力下降之间的关系。若任务对分支独立性要求较高,可考虑使用多个独立卷积代替。

参考文献

1 https://docs.ultralytics.com/

2 https://github.com/ultralytics/ultralytics.git

相关推荐
满怀冰雪1 小时前
24-PaddleClas 模型评估、导出与推理部署入门
大数据·人工智能·python·深度学习·paddle
Mid_search2 小时前
随机排列与Fisher-Yates算法
人工智能·深度学习·强化学习·随机排列·fisher-yates
DeepVisionary7 小时前
谷歌 Gemini Omni 1.1 Flash 正式发布:4K 视频、40 秒场景延伸,视频生成进入按 token 计费时代
python·自动化
ZGIAI7 小时前
ZGI Workflow:条件分支走错时先查哪一层
人工智能·架构
X54先生(人文科技)7 小时前
《元创力》纪实录 · 桥段 《窑变纪元:一份来自星历2227年的深空考古笔记》
人工智能·开源·ai写作·零知识证明
ZGIAI7 小时前
ZGI 文件产物:生成报告后怎样交付
人工智能·架构
东方-教育技术博主7 小时前
自动编码在教育场景中的重要性:一项基于多源证据的深度综述
大数据·人工智能
骥龙7 小时前
模块二:Ollama本地模型部署与OpenCode代理配置
人工智能
阡之尘埃7 小时前
Python数据分析案例85——大模型微调全流程(SFT的LoRA微调)
人工智能·python·深度学习·语言模型·llm·微调·千问