内部文档专用

创新模型结构

nc: 80 

scales: 
  # [depth, width, max_channels]
  n: [0.50, 0.25, 1024] # summary: 319 layers, 2624080 parameters, 2624064 gradients, 6.6 GFLOPs
  s: [0.50, 0.50, 1024] # summary: 319 layers, 9458752 parameters, 9458736 gradients, 21.7 GFLOPs
  m: [0.50, 1.00, 512] # summary: 409 layers, 20114688 parameters, 20114672 gradients, 68.5 GFLOPs
  l: [1.00, 1.00, 512] # summary: 631 layers, 25372160 parameters, 25372144 gradients, 87.6 GFLOPs
  x: [1.00, 1.50, 512] # summary: 631 layers, 56966176 parameters, 56966160 gradients, 196.0 GFLOPs
 
backbone:
  # [from, repeats, module, args]
  - [-1, 1, Conv, [64, 3, 2]] # 0-P1/2
  - [-1, 1, Conv, [128, 3, 2]] # 1-P2/4
  - [-1, 2, C3k2, [256, False, 0.25]]
  - [-1, 1, Conv, [256, 3, 2]] # 3-P3/8
  - [-1, 2, C3k2, [512, False, 0.25]]
  - [-1, 1, Conv, [512, 3, 2]] # 5-P4/16
  - [-1, 2, C3k2, [512, True]]
  - [-1, 1, Conv, [1024, 3, 2]] # 7-P5/32
  - [-1, 2, C3k2, [1024, True]]
  - [-1, 1, SPPF, [1024, 5]] # 9
  - [-1, 2, C2PSA, [1024]] # 10
 
head:
  - [-1, 1, nn.Upsample, [None, 2, "nearest"]]
  - [[-1, 6], 1, Concat, [1]] # cat backbone P4
  - [-1, 2, C3k2, [512, False]] # 13
 
  - [-1, 1, nn.Upsample, [None, 2, "nearest"]]
  - [[-1, 4], 1, Concat, [1]] # cat backbone P3
  - [-1, 2, C3k2, [256, False]] # 16 (P3/8-small)
 
  - [-1, 1, Conv, [256, 3, 2]]
  - [[-1, 13], 1, Concat, [1]] # cat head P4
  - [-1, 2, C3k2, [512, False]] # 19 (P4/16-medium)
 
  - [-1, 1, Conv, [512, 3, 2]]
  - [[-1, 10], 1, Concat, [1]] # cat head P5
  - [-1, 2, C3k2, [1024, True]] # 22 (P5/32-large)
 
  - [[16, 19, 22], 1, Detect, [nc]] # Detect(P3, P4, P5)

在这里插入图片描述

使用C2K2模块代替C2f模块

首先了解C2f模块的代码,这个模块结合了跨阶段部分连接(CSPNet)和 Bottleneck 结构,用于有效的特征提取。:
__init__方法:
- 初始化了两个卷积层 cv1和 cv2,并计算出隐藏通道数 self.c
- 使用 ModuleList 创建多个 Bottleneck 模块,作为中间的卷积操作
- 参数包括:输入通道数 c1、输出通道数 c2、Bottleneck 模块数 n、是否使用残差连接 shortcut、卷积分组数 g 和扩展因子 e。

  forward方法:
       - 通过 cv1 卷积层将输入特征分成两部分(使用 chunk() 方法),然后通过 Bottleneck                  层处理后一部分特征。
       - 最后使用 cv2 通过 1x1 卷积层将所有分支的输出拼接在一起,得到最终的输出。

    forward_split方法:
       - 与 forward 类似,但使用了 split() 函数来分割特征,而不是 chunk()。

            其中split() 和 chunk()都是 PyTorch 中用于张量切分的函数,但它们的切分方式有所不同。具体来说:       

chunk()根据指定的数量将张量沿着某个维度分成若干块,每块的大小大致相同(可能不是完全相等)。如果无法均匀分割,则有些块会比其他块略大。

torch.chunk(input, chunks, dim=0)

input:输入的张量。

chunks:将张量分割成多少块。

dim:在哪个维度上进行分割。

示例:
x = torch.tensor([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
chunks = torch.chunk(x, 2, dim=0)

chunks 的结果是 [tensor([[1, 2, 3], [4, 5, 6]]), tensor([[7, 8, 9]])]

split() 根据指定的每个块的大小将张量沿着某个维度分割。你可以指定每个块的大小,因此它的切分方式比chunk()更灵活。

torch.split(input, split_size_or_sections, dim=0)

input:输入的张量。

split_size_or_sections:可以是一个整数,表示每块的大小,或者是一个列表,表示每块的大小。

dim:在哪个维度上进行分割。

x = torch.tensor([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
splits = torch.split(x, [1, 2], dim=0)

splits 的结果是 [tensor([[1, 2, 3]]), tensor([[4, 5, 6], [7, 8, 9]])]

C2f模块实现代码:

import torch
import torch.nn as nn

class C2f(nn.Module):
“”“CSP Bottleneck with 2 convolutions.”“”

def __init__(self, c1, c2, n=1, shortcut=False, g=1, e=0.5):
    """
    初始化函数,定义C2f模块的参数和层次结构。
    参数:
    - c1: 输入通道数
    - c2: 输出通道数
    - n: Bottleneck模块的数量
    - shortcut: 是否使用残差连接
    - g: 卷积层中的组数(用于分组卷积)
    - e: 扩展因子,用于计算 Bottleneck 中的隐藏通道数
    """
    super().__init__()
    self.c = int(c2 * e)  # 计算隐藏通道数,通常是输出通道数的某个比例
    self.cv1 = Conv(c1, 2 * self.c, 1, 1)  # 1x1 卷积层,用于减少通道数,输出2倍隐藏通道数
    self.cv2 = Conv((2 + n) * self.c, c2, 1)  # 最终的1x1卷积层,恢复到原输出通道数,可能使用FReLU激活函数
    # 创建多个 Bottleneck 模块,每个模块有一个卷积层,使用shortcut和分组卷积
    self.m = nn.ModuleList(Bottleneck(self.c, self.c, shortcut, g, k=((3, 3), (3, 3)), e=1.0) for _ in range(n))

def forward(self, x):
    """
    前向传播函数,定义C2f模块的计算流程。
    参数:
    - x: 输入的张量(Tensor)
    返回:
    - 输出的张量(Tensor)
    """
    # 使用1x1卷积将输入分成两部分,并沿通道维度进行分割
    y = list(self.cv1(x).chunk(2, 1))  # 将输入切分为两块(沿通道维度)
    # 对每个Bottleneck模块处理y的后一部分,并添加到列表中
    y.extend(m(y[-1]) for m in self.m)  # 将每个 Bottleneck 的输出添加到列表中
    # 将所有分支的输出拼接,经过最后的1x1卷积生成最终输出
    return self.cv2(torch.cat(y, 1))  # 在通道维度上拼接

def forward_split(self, x):
    """
    前向传播的另一种方式,使用 split() 方法代替 chunk() 进行特征切分。
    参数:
    - x: 输入的张量(Tensor)
    返回:
    - 输出的张量(Tensor)
    """
    # 使用1x1卷积将输入分成两部分,并使用 split() 进行分割
    y = list(self.cv1(x).split((self.c, self.c), 1))  # 使用 split 按照指定尺寸切分
    # 对每个Bottleneck模块处理y的后一部分,并添加到列表中
    y.extend(m(y[-1]) for m in self.m)  # 将每个 Bottleneck 的输出添加到列表中
    # 将所有分支的输出拼接,经过最后的1x1卷积生成最终输出
    return self.cv2(torch.cat(y, 1))  # 在通道维度上拼接

 接下来了解C3k2模块的代码,C3k2模块是 CSP Bottleneck 的一种加速实现版本,它结合了卷积和分组卷积的特性,并允许选择使用自定义卷积核大小的 C3k模块,或者标准的 Bottleneck模块。它的主要设计目的是在保持高效特征提取的同时,提供更灵活的配置以适应不同的计算需求:

C3k2 模块

CSP Bottleneck 的加速版本:

CSP (Cross Stage Partial Network)网络最早是在 YOLOv4 中引入的,它通过减少冗余的梯度流动,使得模型的计算更加高效,同时保持了强大的特征表示能力。
C3k2模块继承了 CSP 的基本思想,通过使用 Bottleneck或者 C3k模块来进行特征提取。
两个卷积操作(2 convolutions):

C3k2的命名反映了它的结构,其中的 “k2” 代表两个卷积操作。相比传统的 CSP Bottleneck,它通过减少卷积操作的复杂度来加速特征提取过程。
自定义卷积核大小(C3k):

如果设置 C3k=True,C3k2模块会使用 C3k,这是一个具有可定制卷积核大小的 Bottleneck 模块。通过允许用户选择卷积核的大小,C3k2提供了更大的灵活性,可以针对不同任务和计算资源进行优化。
如果 C3k=False,则使用标准的 Bottleneck 模块。
扩展因子(Expansion Factor):

C3k2使用了扩展因子 e,这意味着模块内部的卷积通道数将根据输出通道数 c2 和 e 的值进行缩放。扩展因子通常用于控制瓶颈层中的特征维度,决定了特征的精简和扩展程度。
分组卷积(Group Convolution):

C3k2中的卷积层可以支持分组卷积(通过参数 g 控制),这种技术通过将卷积操作分成多个组,从而减少计算量并加速推理过程。这在处理高维特征时尤为重要,特别是在需要进行实时推理的应用中。
残差连接(Shortcut):

C3k2还支持残差连接(shortcut参数)。残差连接使得输入可以跳过卷积操作,直接加到输出上,从而避免梯度消失问题,并在深层网络中提高训练效果。
C3k2模块实现代码:

import torch
import torch.nn as nn

class C3k2(nn.Module):
“”"
C3k2 模块是带有 2 个卷积操作的加速版 CSP Bottleneck 模块,并且可以选择性地使用 C3k 块。
“”"

def __init__(self, c1, c2, n=1, c3k=False, e=0.5, g=1, shortcut=True):
    """
    初始化 C3k2 模块。
    参数:
    - c1: 输入通道数
    - c2: 输出通道数
    - n: Bottleneck 模块的重复次数
    - c3k: 是否使用 C3k 模块
    - e: 扩展因子,决定隐藏通道数
    - g: 分组卷积参数
    - shortcut: 是否使用残差连接
    """
    super(C3k2, self).__init__()
    self.c = int(c2 * e)  # 计算隐藏通道数
    # 定义 1x1 卷积层,用于将输入调整到 2 倍的隐藏通道数
    self.cv1 = nn.Conv2d(c1, 2 * self.c, kernel_size=1, stride=1, bias=False)
    self.cv2 = nn.Conv2d(2 * self.c, c2, kernel_size=1, stride=1, bias=False)
    
    # 定义模块列表,使用 C3k 或 Bottleneck 模块
    self.m = nn.ModuleList(
        C3k(self.c, self.c, 2, shortcut, g) if c3k else Bottleneck(self.c, self.c, shortcut, g) for _ in range(n)
    )

def forward(self, x):
    """
    前向传播函数,处理输入张量。
    参数:
    - x: 输入张量
    
    返回:
    - 经过 C3k2 模块处理后的输出张量
    """
    # 将 cv1 的输出分成两部分
    y = list(self.cv1(x).chunk(2, dim=1))  # 使用 chunk 操作将通道维度分成两部分
    # 对每个模块列表中的模块进行处理,并将结果添加到 y 中
    y.extend(m(y[-1]) for m in self.m)
    # 将所有部分拼接并通过 cv2 恢复通道数
    return self.cv2(torch.cat(y, dim=1))

class Bottleneck(nn.Module):
“”"
Bottleneck 模块,用于特征提取,包含 1x1 和 3x3 卷积层,并带有残差连接。
“”"

def __init__(self, in_channels, out_channels, shortcut=True, g=1, e=0.5):
    """
    初始化 Bottleneck 模块。
    参数:
    - in_channels: 输入通道数
    - out_channels: 输出通道数
    - shortcut: 是否使用残差连接
    - g: 分组卷积参数
    - e: 扩展因子,控制 Bottleneck 中的隐藏通道数
    """
    super(Bottleneck, self).__init__()
    hidden_channels = int(out_channels * e)
    
    # 1x1 卷积用于降维
    self.conv1 = nn.Conv2d(in_channels, hidden_channels, kernel_size=1, stride=1, bias=False)
    self.bn1 = nn.BatchNorm2d(hidden_channels)
    
    # 3x3 卷积用于提取特征,使用自定义卷积核大小
    self.conv2 = nn.Conv2d(hidden_channels, out_channels, kernel_size=3, stride=1, padding=1, groups=g, bias=False)
    self.bn2 = nn.BatchNorm2d(out_channels)
    
    # 判断是否需要残差连接
    self.shortcut = nn.Identity() if in_channels == out_channels and shortcut else None

def forward(self, x):
    """
    前向传播函数,处理输入张量。
    参数:
    - x: 输入张量
    
    返回:
    - 经过 Bottleneck 模块处理后的输出张量
    """
    residual = x
    x = self.conv1(x)
    x = self.bn1(x)
    x = torch.relu(x)
    x = self.conv2(x)
    x = self.bn2(x)
    
    if self.shortcut is not None:
        x += residual
    
    return torch.relu(x)

class C3k(nn.Module):
“”"
C3k 模块,提供自定义卷积核大小的 CSP Bottleneck 模块。
“”"

def __init__(self, c1, c2, n=1, shortcut=True, g=1, k=3):
    """
    初始化 C3k 模块。
    参数:
    - c1: 输入通道数
    - c2: 输出通道数
    - n: Bottleneck 模块的重复次数
    - shortcut: 是否使用残差连接
    - g: 分组卷积参数
    - k: 卷积核大小
    """
    super(C3k, self).__init__()
    hidden_channels = int(c2 * 0.5)  # 默认扩展因子为 0.5
    
    # 定义卷积层
    self.cv1 = nn.Conv2d(c1, hidden_channels, kernel_size=1, stride=1)
    self.cv2 = nn.Conv2d(c1, hidden_channels, kernel_size=1, stride=1)
    self.cv3 = nn.Conv2d(2 * hidden_channels, c2, kernel_size=1, stride=1)
    
    # 定义 Bottleneck 模块序列,使用自定义卷积核大小
    self.m = nn.Sequential(
        *[Bottleneck(hidden_channels, hidden_channels, shortcut, g=g, k=(k, k), e=1.0) for _ in range(n)]
    )

def forward(self, x):
    """
    前向传播函数,处理输入张量。
    参数:
    - x: 输入张量
    
    返回:
    - 经过 C3k 模块处理后的输出张量
    """
    return self.cv3(torch.cat((self.m(self.cv1(x)), self.cv2(x)), dim=1))

示例使用

if name == “main”:
# 假设输入张量形状为 [batch_size, channels, height, width]
x = torch.randn(1, 64, 128, 128)
model = C3k2(in_channels=64, out_channels=128, n=3, c3k=False, e=0.5, shortcut=True)
output = model(x)
print(output.shape) # 输出张量的形状应为 [1, 128, 128, 128]

其中CSP Bottleneck 模块通常包含以下几个部分:

分支机制:输入被分为两部分:一个直接传递,另一个通过 Bottleneck 进行处理

Bottleneck 单元:Bottleneck 是由 1x1 卷积层和 3x3 卷积层组成的结构,1x1 卷积层用于减少通道数(降低计算复杂度),3x3 卷积层用于提取局部特征

特征融合:最终通过拼接(concatenation)或逐元素相加(element-wise addition)将两条路径的特征融合在一起,作为输出。

Bottleneck 模块实现代码:

import torch
import torch.nn as nn

class Bottleneck(nn.Module):
“”"
ResNet 中 Bottleneck 模块的实现,包含 1x1、3x3 和 1x1 卷积层,并带有残差连接。
“”"
def init(self, in_channels, out_channels, bottleneck_channels=None, shortcut=True, groups=1, expansion=0.5):
“”"
初始化 Bottleneck 模块。
参数:
- in_channels: 输入通道数
- out_channels: 输出通道数
- bottleneck_channels: Bottleneck 中的隐藏通道数
- shortcut: 是否使用残差连接
- groups: 分组卷积参数
- expansion: 扩展因子,控制 Bottleneck 中的通道数
“”"
super().init()
bottleneck_channels = bottleneck_channels or int(out_channels * expansion)
# 1x1 卷积,降维操作
self.conv1 = nn.Conv2d(in_channels, bottleneck_channels, kernel_size=1, stride=1, bias=False)
# 3x3 卷积,特征提取
self.conv2 = nn.Conv2d(bottleneck_channels, bottleneck_channels, kernel_size=3, stride=1, padding=1, groups=groups, bias=False)
# 1x1 卷积,升维操作
self.conv3 = nn.Conv2d(bottleneck_channels, out_channels, kernel_size=1, stride=1, bias=False)
# Batch Normalization
self.bn1 = nn.BatchNorm2d(bottleneck_channels)
self.bn2 = nn.BatchNorm2d(bottleneck_channels)
self.bn3 = nn.BatchNorm2d(out_channels)
self.relu = nn.ReLU(inplace=True)
self.shortcut = shortcut and in_channels == out_channels # 是否使用残差连接

def forward(self, x):
    """
    前向传播函数。
    参数:
    - x: 输入的张量
    返回:
    - 经过 Bottleneck 处理后的输出张量
    """
    residual = x  # 原始输入,用于残差连接

    # 通过 1x1 卷积进行降维
    x = self.conv1(x)
    x = self.bn1(x)
    x = self.relu(x)

    # 通过 3x3 卷积进行特征提取
    x = self.conv2(x)
    x = self.bn2(x)
    x = self.relu(x)

    # 通过 1x1 卷积升维
    x = self.conv3

2.3.2 YOLO11在SPPF层后新增C2PSA层
C2PSA 模块是基于 PSA(Pyramid Squeeze Attention)注意力机制的卷积模块,负责处理输入张量并通过注意力机制增强特征表示。该模块涉及到卷积操作、分割和多头注意力机制。

PSA 模块:

PSA 模块实现代码:

import torch
import torch.nn as nn
import math

Squeeze-and-Excitation (SE) 模块,用于特征加权

class SEWeightModule(nn.Module):
“”"
Squeeze-and-Excitation (SE) 模块:
通过全局池化和两层全连接网络,对特征通道进行重新加权。
“”"
def init(self, channels, reduction=16):
“”"
初始化 SE 模块。
参数:
- channels: 输入的通道数
- reduction: 降维比率,通常是 16
“”"
super(SEWeightModule, self).init()
# 全局平均池化,将空间维度压缩为 1x1
self.avg_pool = nn.AdaptiveAvgPool2d(1)
# 第一个 1x1 卷积层用于减少通道数,通道数为 channels // reduction
self.fc1 = nn.Conv2d(channels, channels // reduction, kernel_size=1, padding=0)
self.relu = nn.ReLU(inplace=True)
# 第二个 1x1 卷积层用于恢复通道数
self.fc2 = nn.Conv2d(channels // reduction, channels, kernel_size=1, padding=0)
# 使用 sigmoid 激活函数生成注意力权重
self.sigmoid = nn.Sigmoid()

def forward(self, x):
    """
    前向传播函数。
    参数:
    - x: 输入张量 (batch_size, channels, height, width)
    
    返回:
    - weight: 通道权重 (batch_size, channels, 1, 1)
    """
    # 全局平均池化
    out = self.avg_pool(x)
    # 通过全连接层减少通道数
    out = self.fc1(out)
    out = self.relu(out)
    # 恢复通道数并生成权重
    out = self.fc2(out)
    weight = self.sigmoid(out)
    return weight

标准卷积定义函数

def conv(in_planes, out_planes, kernel_size=3, stride=1, padding=1, dilation=1, groups=1):
“”"
定义带有填充和分组卷积的标准卷积层。
参数:
- in_planes: 输入通道数
- out_planes: 输出通道数
- kernel_size: 卷积核大小
- stride: 步长
- padding: 填充
- dilation: 扩张
- groups: 分组卷积数
返回:
- 卷积层
“”"
return nn.Conv2d(in_planes, out_planes, kernel_size=kernel_size, stride=stride,
padding=padding, dilation=dilation, groups=groups, bias=False)

1x1 卷积定义

def conv1x1(in_planes, out_planes, stride=1):
“”"
定义 1x1 卷积层。
参数:
- in_planes: 输入通道数
- out_planes: 输出通道数
- stride: 步长
返回:
- 1x1 卷积层
“”"
return nn.Conv2d(in_planes, out_planes, kernel_size=1, stride=stride, bias=False)

PSA (Pyramid Squeeze Attention) 模块定义

class PSAModule(nn.Module):
“”"
Pyramid Squeeze Attention (PSA) 模块:
通过多种不同卷积核大小和分组卷积提取多尺度特征,并使用 SE 模块进行特征加权。
“”"
def init(self, inplans, planes, conv_kernels=[3, 5, 7, 9], stride=1, conv_groups=[1, 4, 8, 16]):
“”"
初始化 PSA 模块。
参数:
- inplans: 输入通道数
- planes: 输出通道数
- conv_kernels: 使用的卷积核大小列表
- stride: 卷积的步长
- conv_groups: 分组卷积数量列表
“”"
super(PSAModule, self).init()
# 定义四个不同卷积核大小和分组数的卷积层
self.conv_1 = conv(inplans, planes // 4, kernel_size=conv_kernels[0], padding=conv_kernels[0] // 2,
stride=stride, groups=conv_groups[0])
self.conv_2 = conv(inplans, planes // 4, kernel_size=conv_kernels[1], padding=conv_kernels[1] // 2,
stride=stride, groups=conv_groups[1])
self.conv_3 = conv(inplans, planes // 4, kernel_size=conv_kernels[2], padding=conv_kernels[2] // 2,
stride=stride, groups=conv_groups[2])
self.conv_4 = conv(inplans, planes // 4, kernel_size=conv_kernels[3], padding=conv_kernels[3] // 2,
stride=stride, groups=conv_groups[3])
# 使用 SE 模块进行加权
self.se = SEWeightModule(planes // 4)
self.split_channel = planes // 4
self.softmax = nn.Softmax(dim=1)

def forward(self, x):
    """
    前向传播函数。
    参数:
    - x: 输入张量 (batch_size, channels, height, width)
    返回:
    - 加权后的输出张量
    """
    batch_size = x.shape[0]
    # 四个不同卷积核的特征提取
    x1 = self.conv_1(x)
    x2 = self.conv_2(x)
    x3 = self.conv_3(x)
    x4 = self.conv_4(x)

    # 将四个分支特征拼接
    feats = torch.cat((x1, x2, x3, x4), dim=1)
    feats = feats.view(batch_size, 4, self.split_channel, feats.shape[2], feats.shape[3])

    # 使用 SE 模块加权
    x1_se = self.se(x1)
    x2_se = self.se(x2)
    x3_se = self.se(x3)
    x4_se = self.se(x4)

    # 将所有加权后的特征拼接
    x_se = torch.cat((x1_se, x2_se, x3_se, x4_se), dim=1)
    attention_vectors = x_se.view(batch_size, 4, self.split_channel, 1, 1)
    attention_vectors = self.softmax(attention_vectors)

    # 使用注意力权重重新加权特征
    feats_weight = feats * attention_vectors
    for i in range(4):
        x_se_weight_fp = feats_weight[:, i, :, :, :]
        if i == 0:
            out = x_se_weight_fp
        else:
            out = torch.cat((out, x_se_weight_fp), 1)

    return out

C2PSA 模块:

C2PSA 模块实现代码:

import torch
import torch.nn as nn

class C2PSA(nn.Module):
“”"
C2PSA 模块实现,结合了卷积和 Pyramid Squeeze Attention (PSA) 注意力机制,
用于增强特征表示能力。
“”"

def __init__(self, c1, c2, n=1, e=0.5):
    """
    初始化 C2PSA 模块。
    参数:
    - c1: 输入通道数,确保与 c2 相等
    - c2: 输出通道数
    - n: 重复 PSABlock 的次数
    - e: 扩展因子,控制隐藏通道数的大小
    """
    super().__init__()
    assert c1 == c2  # 确保输入通道数与输出通道数相同
    self.c = int(c1 * e)  # 计算隐藏通道数
    # 1x1 卷积层,将输入通道数变为 2 倍的隐藏通道数
    self.cv1 = Conv(c1, 2 * self.c, 1, 1)
    # 最终的 1x1 卷积层,将通道数恢复到原始的输出通道数
    self.cv2 = Conv(2 * self.c, c1, 1, 1)

    # 使用 PSA Block 处理分割后的特征,定义一个包含多个 PSABlock 的序列
    # PSA Block 中的注意力比率设置为 0.5,num_heads 控制多头注意力的数量
    self.m = nn.Sequential(
        *(PSABlock(self.c, attn_ratio=0.5, num_heads=self.c // 64) for _ in range(n))
    )

def forward(self, x):
    """
    前向传播函数,处理输入张量 x,通过 PSA blocks 后返回处理后的张量。
    参数:
    - x: 输入的张量
    返回:
    - 经过处理的输出张量
    """
    # 输入张量 x 经过第一个 1x1 卷积层 self.cv1 处理
    # 使用 split 将通道维度按隐藏通道数 self.c 分割为两个部分 a 和 b
    a, b = self.cv1(x).split((self.c, self.c), dim=1)

    # 通过 PSA blocks 处理 b 分支
    b = self.m(b)

    # 将经过处理的 b 分支和未处理的 a 分支在通道维度拼接
    # 然后经过第二个 1x1 卷积层恢复原通道数
    return self.cv2(torch.cat((a, b), dim=1))

init 方法:

参数:

c1:输入通道数。
c2:输出通道数,代码中通过 assert c1 == c2 确保输入输出通道数相等。
n:模块重复的次数。
e:扩展因子,用于计算隐藏通道数。
变量:

self.c:隐藏通道数,计算方式为 c1 * e。
self.cv1:1x1 卷积操作,用于将输入通道数变为 2 倍的隐藏通道数。该操作减少了特征维度。
self.cv2:最终的卷积操作,将通道数恢复到原始输出通道 c1。
PSA Block:

self.m 是通过 PSABlock 组成的序列,PSABlock 是 PSA 注意力机制的核心部分。每个 PSABlock 中,计算注意力比率 attn_ratio=0.5,以及 num_heads=self.c // 64,表示多头注意力的数量(每 64 个通道作为一组头部)。
forward 方法:

张量分割:

a, b = self.cv1(x).split((self.c, self.c), dim=1):输入的张量首先经过 cv1 处理,然后通过 split 函数将其在通道维度上分割成两部分,每部分具有 self.c 个通道。
PSA Block 处理:

b = self.m(b):通过 PSABlock 模块处理第二部分特征 b,这些模块包含注意力机制,用于增强特征表示。
拼接与输出:

torch.cat((a, b), dim=1):最后,将分割后的两个张量 a 和 b 在通道维度上拼接。
拼接后的张量通过 cv2 进行处理,恢复到输出通道数。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

深度物联网

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值