背景意义

随着城市化进程的加快,交通管理与车辆监控成为现代城市治理的重要组成部分。传统的车辆检测方法往往依赖于人工监控或固定摄像头,这不仅效率低下,而且难以应对复杂的交通环境。近年来,随着无人机技术的迅猛发展,航拍成为获取交通数据的新兴手段。航拍技术能够从空中全方位观察交通状况,为车辆检测提供了新的视角和可能性。然而,如何高效、准确地从航拍图像中识别和分类车辆,仍然是一个亟待解决的技术难题。

在此背景下,基于改进YOLOv11的航拍车辆检测系统应运而生。YOLO(You Only Look Once)系列模型以其高效的实时检测能力和优异的准确性,广泛应用于目标检测领域。YOLOv11作为该系列的最新版本,进一步优化了检测速度和精度,特别适合于动态变化的航拍场景。通过对YOLOv11进行改进,结合航拍图像的特性,我们能够提升车辆检测的准确性和实时性,从而为交通管理提供更为可靠的数据支持。

本研究所使用的数据集“Aerial Cars”包含153幅航拍图像,专注于车辆的检测任务。尽管图像数量相对较少,但通过精心设计的模型训练和数据增强技术,可以有效提升模型的泛化能力。此外,该数据集的类别设置简单明了,涵盖了多种车辆类型,为后续的检测算法优化提供了良好的基础。通过对该数据集的深入分析与处理,我们希望能够推动航拍车辆检测技术的发展,为智能交通系统的构建提供坚实的技术支持,最终实现城市交通的智能化管理与优化。

图片效果

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

数据集信息

本项目所使用的数据集名为“Aerial Cars”,旨在为改进YOLOv11的航拍车辆检测系统提供高质量的训练数据。该数据集专注于航拍视角下的车辆检测,包含了多种不同类型的车辆样本,旨在提升模型在实际应用中的检测精度和鲁棒性。数据集中共包含四个类别,分别用数字“0”、“1”、“2”和“3”进行标识。这些类别可能代表不同类型的车辆,如小型轿车、SUV、货车和公共交通工具等,具体的类别定义将在后续的研究中进一步明确。

“Aerial Cars”数据集的构建过程经过精心设计,确保数据的多样性和代表性。数据集中的图像均为航拍视角,涵盖了不同的天气条件、时间段和地理位置,以模拟真实世界中可能遇到的各种场景。这种多样性不仅有助于提高模型的泛化能力,还能有效应对不同环境下的车辆检测挑战。此外,数据集中的每一张图像均配有详细的标注信息,确保训练过程中模型能够准确学习到各个类别的特征。

在数据预处理阶段,我们对图像进行了标准化处理,以适应YOLOv11的输入要求。通过对图像进行缩放、裁剪和增强等操作,进一步丰富了数据集的内容,提高了模型的训练效果。最终,我们希望通过“Aerial Cars”数据集的应用,能够显著提升航拍车辆检测系统的性能,使其在实际应用中更具实用性和可靠性。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

核心代码

以下是经过简化和注释的核心代码部分,主要集中在 selective_scan_easy 函数及其相关的类和方法。该代码实现了选择性扫描的功能,通常用于处理序列数据,特别是在深度学习模型中。

import torch
import torch.nn.functional as F

def selective_scan_easy(us, dts, As, Bs, Cs, Ds, delta_bias=None, delta_softplus=False, return_last_state=False, chunksize=64):
“”"
选择性扫描函数
参数:
us: 输入序列,形状为 (B, G * D, L)
dts: 时间增量,形状为 (B, G * D, L)
As: 权重矩阵,形状为 (G * D, N)
Bs: 权重矩阵,形状为 (B, G, N, L)
Cs: 权重矩阵,形状为 (B, G, N, L)
Ds: 可选的偏置项,形状为 (G * D)
delta_bias: 可选的偏置调整项
delta_softplus: 是否对时间增量应用 softplus 函数
return_last_state: 是否返回最后的状态
chunksize: 每次处理的序列长度
返回:
输出序列和(可选)最后状态
“”"

def selective_scan_chunk(us, dts, As, Bs, Cs, hprefix):
    """
    处理每个块的选择性扫描
    参数:
    us: 输入序列块
    dts: 时间增量块
    As, Bs, Cs: 权重矩阵
    hprefix: 前一个状态
    返回:
    ys: 输出序列块
    hs: 状态块
    """
    ts = dts.cumsum(dim=0)  # 计算时间增量的累积和
    Ats = torch.einsum("gdn,lbgd->lbgdn", As, ts).exp()  # 计算 A 的指数
    rAts = Ats  # 归一化处理
    duts = dts * us  # 计算 duts
    dtBus = torch.einsum("lbgd,lbgn->lbgdn", duts, Bs)  # 计算 dtBus
    hs_tmp = rAts * (dtBus / rAts).cumsum(dim=0)  # 计算状态的临时值
    hs = hs_tmp + Ats * hprefix.unsqueeze(0)  # 更新状态
    ys = torch.einsum("lbgn,lbgdn->lbgd", Cs, hs)  # 计算输出
    return ys, hs

# 数据类型设置
dtype = torch.float32
inp_dtype = us.dtype  # 输入数据类型
has_D = Ds is not None  # 检查 Ds 是否存在
if chunksize < 1:
    chunksize = Bs.shape[-1]  # 设置块大小

# 处理时间增量
dts = dts.to(dtype)
if delta_bias is not None:
    dts = dts + delta_bias.view(1, -1, 1).to(dtype)
if delta_softplus:
    dts = F.softplus(dts)  # 应用 softplus 函数

# 数据维度调整
Bs = Bs.unsqueeze(1) if len(Bs.shape) == 3 else Bs
Cs = Cs.unsqueeze(1) if len(Cs.shape) == 3 else Cs
B, G, N, L = Bs.shape
us = us.view(B, G, -1, L).permute(3, 0, 1, 2).to(dtype)
dts = dts.view(B, G, -1, L).permute(3, 0, 1, 2).to(dtype)
As = As.view(G, -1, N).to(dtype)
Bs = Bs.permute(3, 0, 1, 2).to(dtype)
Cs = Cs.permute(3, 0, 1, 2).to(dtype)
Ds = Ds.view(G, -1).to(dtype) if has_D else None
D = As.shape[1]

oys = []  # 输出序列列表
hprefix = us.new_zeros((B, G, D, N), dtype=dtype)  # 初始化前一个状态
for i in range(0, L, chunksize):
    ys, hs = selective_scan_chunk(
        us[i:i + chunksize], dts[i:i + chunksize], 
        As, Bs[i:i + chunksize], Cs[i:i + chunksize], hprefix, 
    )
    oys.append(ys)  # 添加输出序列块
    hprefix = hs[-1]  # 更新前一个状态

oys = torch.cat(oys, dim=0)  # 合并输出序列
if has_D:
    oys = oys + Ds * us  # 添加偏置项
oys = oys.permute(1, 2, 3, 0).view(B, -1, L)  # 调整输出维度

return oys.to(inp_dtype) if not return_last_state else (oys.to(inp_dtype), hprefix.view(B, G * D, N).float())

class SelectiveScanEasy(torch.autograd.Function):
“”"
自定义的 PyTorch 自动求导函数
“”"
@staticmethod
@torch.cuda.amp.custom_fwd(cast_inputs=torch.float32)
def forward(ctx, us, dts, As, Bs, Cs, Ds, delta_bias=None, delta_softplus=False, return_last_state=False, chunksize=64):
# 前向传播逻辑
return selective_scan_easy(us, dts, As, Bs, Cs, Ds, delta_bias, delta_softplus, return_last_state, chunksize)

@staticmethod
@torch.cuda.amp.custom_bwd
def backward(ctx, doys: torch.Tensor, *args):
    # 反向传播逻辑
    pass  # 反向传播的具体实现可以根据需要添加

选择性扫描的外部接口

def selective_scan_easy_fwdbwd(u, delta, A, B, C, D, delta_bias=None, delta_softplus=None,
return_last_state=False, chunksize=64):
outs = SelectiveScanEasy.apply(u, delta, A, B, C, D, delta_bias, delta_softplus, return_last_state, chunksize)
return (outs[0].to(u.dtype), *outs[1:]) if return_last_state else outs[0].to(u.dtype)
代码说明:
selective_scan_easy 函数:这是核心函数,执行选择性扫描操作。它接收输入序列、时间增量和权重矩阵,并返回输出序列。
selective_scan_chunk 函数:处理每个块的选择性扫描,计算输出和状态。
SelectiveScanEasy 类:继承自 torch.autograd.Function,实现了自定义的前向和反向传播逻辑。
selective_scan_easy_fwdbwd 函数:提供了一个接口,使用 SelectiveScanEasy 类进行前向和反向传播。
注意事项:
代码中涉及到的张量操作和维度变换是为了适应深度学习模型的输入格式。
反向传播的具体实现可以根据需求进行补充。
该代码依赖于 PyTorch 库,因此需要在相应的环境中运行。
这个文件 test_selective_scan_easy.py 是一个用于测试和实现选择性扫描(Selective Scan)算法的 Python 脚本,主要使用 PyTorch 库。该算法在处理序列数据时,可以高效地进行状态更新和输出计算,广泛应用于深度学习模型中,尤其是在处理时序数据时。

文件开头包含了一些版权信息和导入的库,包括 torch 和 pytest,后者用于测试框架。接下来定义了多个函数和类,其中最重要的是 selective_scan_easy 函数和 SelectiveScanEasy 类。

selective_scan_easy 函数实现了选择性扫描的核心逻辑。它接受多个参数,包括输入序列 us、时间差 dts、权重矩阵 As、Bs、Cs 和 Ds,以及一些可选参数如 delta_bias 和 delta_softplus。函数内部定义了一个嵌套函数 selective_scan_chunk,用于处理数据块。该函数通过逐步累加的方式计算状态和输出,利用了张量运算来提高效率。

SelectiveScanEasy 类继承自 torch.autograd.Function,实现了前向传播和反向传播的功能。前向传播使用 @torch.cuda.amp.custom_fwd 装饰器来支持混合精度计算,适用于 GPU 加速。反向传播则通过 @torch.cuda.amp.custom_bwd 装饰器实现,确保在计算梯度时的高效性。

文件中还定义了多个辅助函数,如 selective_scan_easy_fwdbwd 和 selective_scan_ref,用于不同版本的选择性扫描实现,以及与原始实现的比较。最后,使用 pytest 进行参数化测试,确保不同输入条件下的函数输出一致性。

整个文件的设计旨在通过高效的张量运算和灵活的参数设置,提供一个强大的选择性扫描实现,同时确保其在不同情况下的正确性和性能。

10.2 revcol.py
以下是经过简化和注释的核心代码部分:

import torch
import torch.nn as nn

定义一个用于反向传播的自定义函数

class ReverseFunction(torch.autograd.Function):
@staticmethod
def forward(ctx, run_functions, alpha, *args):
# 获取运行的函数和缩放因子
l0, l1, l2, l3 = run_functions
alpha0, alpha1, alpha2, alpha3 = alpha

    # 保存上下文信息
    ctx.run_functions = run_functions
    ctx.alpha = alpha
    
    # 确保输入参数数量正确
    assert len(args) == 5
    [x, c0, c1, c2, c3] = args
    
    # 进行前向计算并保存中间结果
    with torch.no_grad():
        c0 = l0(x, c1) + c0 * alpha0
        c1 = l1(c0, c2) + c1 * alpha1
        c2 = l2(c1, c3) + c2 * alpha2
        c3 = l3(c2, None) + c3 * alpha3
    
    # 保存计算的结果以供反向传播使用
    ctx.save_for_backward(x, c0, c1, c2, c3)
    return x, c0, c1, c2, c3

@staticmethod
def backward(ctx, *grad_outputs):
    # 获取保存的张量和函数
    x, c0, c1, c2, c3 = ctx.saved_tensors
    l0, l1, l2, l3 = ctx.run_functions
    alpha0, alpha1, alpha2, alpha3 = ctx.alpha
    
    # 获取梯度
    gx_right, g0_right, g1_right, g2_right, g3_right = grad_outputs
    
    # 进行反向传播计算
    with torch.enable_grad():
        # 反向计算每一层的梯度
        g3_up = g3_right
        g3_left = g3_up * alpha3
        oup3 = l3(c2, None)
        torch.autograd.backward(oup3, g3_up, retain_graph=True)
        c3_left = (1 / alpha3) * (c3 - oup3)
        
        g2_up = g2_right + c2.grad
        g2_left = g2_up * alpha2
        oup2 = l2(c1, c3_left)
        torch.autograd.backward(oup2, g2_up, retain_graph=True)
        
        g1_up = g1_right + c1.grad
        g1_left = g1_up * alpha1
        oup1 = l1(c0, c2_left)
        torch.autograd.backward(oup1, g1_up, retain_graph=True)
        
        g0_up = g0_right + c0.grad
        g0_left = g0_up * alpha0
        oup0 = l0(x, c1_left)
        torch.autograd.backward(oup0, g0_up)
    
    # 返回各层的梯度
    return None, None, gx_up, g0_left, g1_left, g2_left, g3_left

定义一个包含多个子网络的主网络

class RevCol(nn.Module):
def init(self, kernel=‘C2f’, channels=[32, 64, 96, 128], layers=[2, 3, 6, 3], num_subnet=5, save_memory=True) -> None:
super().init()
self.num_subnet = num_subnet
self.channels = channels
self.layers = layers

    # 定义输入层
    self.stem = Conv(3, channels[0], k=4, s=4, p=0)

    # 创建多个子网络
    for i in range(num_subnet):
        first_col = True if i == 0 else False
        self.add_module(f'subnet{str(i)}', SubNet(channels, layers, kernel, first_col, save_memory=save_memory))

def forward(self, x):
    # 初始化各层的输出
    c0, c1, c2, c3 = 0, 0, 0, 0
    x = self.stem(x)  # 通过输入层处理输入
    # 依次通过每个子网络
    for i in range(self.num_subnet):
        c0, c1, c2, c3 = getattr(self, f'subnet{str(i)}')(x, c0, c1, c2, c3)
    return [c0, c1, c2, c3]  # 返回每层的输出

代码说明:
ReverseFunction:自定义的反向传播函数,负责前向和反向计算。前向计算中会保存中间结果以供反向传播使用。
RevCol:主网络类,包含多个子网络。通过输入层处理输入数据,并依次通过每个子网络进行计算,最终返回各层的输出。
这个程序文件 revcol.py 实现了一个名为 RevCol 的深度学习模型,主要用于图像处理任务。该模型使用了反向传播和融合策略,以提高计算效率和节省内存。代码中使用了 PyTorch 框架,包含了多个类和函数,下面是对代码的详细讲解。

首先,文件导入了必要的 PyTorch 模块以及自定义的卷积和块模块。接着定义了一些辅助函数,比如 get_gpu_states 用于获取当前 GPU 的随机数生成状态,get_gpu_device 用于获取输入张量所在的 GPU 设备,set_device_states 用于设置设备的随机数状态,detach_and_grad 用于从计算图中分离张量并设置其需要梯度计算的属性,get_cpu_and_gpu_states 则是获取 CPU 和 GPU 的随机数状态。

ReverseFunction 类是一个自定义的 PyTorch 自动求导函数,包含 forward 和 backward 两个静态方法。在 forward 方法中,模型的前向传播通过多个函数(l0, l1, l2, l3)和缩放因子(alpha)进行计算,并保存中间结果和状态。在 backward 方法中,模型的反向传播通过计算梯度并利用保存的状态来更新参数。

Fusion 类用于实现不同层之间的融合操作,根据层级和是否为第一列的条件选择不同的计算方式。Level 类则是一个包含多个卷积块的模块,负责处理特定层级的输入。SubNet 类则是一个子网络,它包含多个层级的融合和卷积操作,并根据是否需要节省内存选择不同的前向传播策略。

RevCol 类是整个模型的核心,初始化时定义了网络的结构,包括输入通道、层数和子网络的数量。在 forward 方法中,模型首先通过 stem 层处理输入,然后依次通过每个子网络进行计算,最终返回多个输出通道。

总的来说,这个文件实现了一个复杂的深度学习模型,结合了反向传播、内存优化和模块化设计,适用于需要高效计算和内存管理的图像处理任务。

10.3 fast_kan_conv.py
以下是代码中最核心的部分,并附上详细的中文注释:

import torch
import torch.nn as nn

class RadialBasisFunction(nn.Module):
def init(self, grid_min: float = -2., grid_max: float = 2., num_grids: int = 8, denominator: float = None):
super().init()
# 创建一个从 grid_min 到 grid_max 的均匀分布的网格
grid = torch.linspace(grid_min, grid_max, num_grids)
# 将网格参数化,设置为不可训练
self.grid = torch.nn.Parameter(grid, requires_grad=False)
# 计算分母,如果未提供则根据网格范围和数量计算
self.denominator = denominator or (grid_max - grid_min) / (num_grids - 1)

def forward(self, x):
    # 计算径向基函数的输出
    return torch.exp(-((x[..., None] - self.grid) / self.denominator) ** 2)

class FastKANConvNDLayer(nn.Module):
def init(self, conv_class, norm_class, input_dim, output_dim, kernel_size, groups=1, padding=0, stride=1, dilation=1, ndim: int = 2, grid_size=8, base_activation=nn.SiLU, grid_range=[-2, 2], dropout=0.0):
super(FastKANConvNDLayer, self).init()
# 初始化输入和输出维度、卷积参数等
self.inputdim = input_dim
self.outdim = output_dim
self.kernel_size = kernel_size
self.padding = padding
self.stride = stride
self.dilation = dilation
self.groups = groups
self.ndim = ndim
self.grid_size = grid_size
self.base_activation = base_activation()
self.grid_range = grid_range

    # 检查参数的有效性
    if groups <= 0:
        raise ValueError('groups must be a positive integer')
    if input_dim % groups != 0:
        raise ValueError('input_dim must be divisible by groups')
    if output_dim % groups != 0:
        raise ValueError('output_dim must be divisible by groups')

    # 创建基础卷积层和样条卷积层
    self.base_conv = nn.ModuleList([conv_class(input_dim // groups, output_dim // groups, kernel_size, stride, padding, dilation, groups=1, bias=False) for _ in range(groups)])
    self.spline_conv = nn.ModuleList([conv_class(grid_size * input_dim // groups, output_dim // groups, kernel_size, stride, padding, dilation, groups=1, bias=False) for _ in range(groups)])
    self.layer_norm = nn.ModuleList([norm_class(output_dim // groups) for _ in range(groups)])

    # 初始化径向基函数
    self.rbf = RadialBasisFunction(grid_range[0], grid_range[1], grid_size)

    # 初始化 dropout 层
    self.dropout = None
    if dropout > 0:
        if ndim == 1:
            self.dropout = nn.Dropout1d(p=dropout)
        if ndim == 2:
            self.dropout = nn.Dropout2d(p=dropout)
        if ndim == 3:
            self.dropout = nn.Dropout3d(p=dropout)

    # 使用 Kaiming 均匀分布初始化卷积层的权重
    for conv_layer in self.base_conv:
        nn.init.kaiming_uniform_(conv_layer.weight, nonlinearity='linear')
    for conv_layer in self.spline_conv:
        nn.init.kaiming_uniform_(conv_layer.weight, nonlinearity='linear')

def forward_fast_kan(self, x, group_index):
    # 对输入应用基础激活函数,并进行线性变换
    base_output = self.base_conv[group_index](self.base_activation(x))
    if self.dropout is not None:
        x = self.dropout(x)
    # 计算样条基
    spline_basis = self.rbf(self.layer_norm[group_index](x))
    spline_basis = spline_basis.moveaxis(-1, 2).flatten(1, 2)
    # 通过样条卷积层计算输出
    spline_output = self.spline_conv[group_index](spline_basis)
    # 将基础输出和样条输出相加
    x = base_output + spline_output
    return x

def forward(self, x):
    # 将输入分割为多个组进行处理
    split_x = torch.split(x, self.inputdim // self.groups, dim=1)
    output = []
    for group_ind, _x in enumerate(split_x):
        y = self.forward_fast_kan(_x.clone(), group_ind)
        output.append(y.clone())
    # 将所有组的输出拼接在一起
    y = torch.cat(output, dim=1)
    return y

代码核心部分说明:
RadialBasisFunction 类:实现了一个径向基函数,主要用于计算输入与网格之间的距离,并输出基于高斯函数的值。
FastKANConvNDLayer 类:这是一个自定义的卷积层,支持多维卷积(1D、2D、3D),并结合了基础卷积和样条卷积的特性。它包含了参数初始化、激活函数、归一化层和 dropout 层的设置。
forward_fast_kan 方法:实现了快速的卷积计算,首先通过基础卷积层处理输入,然后通过样条卷积层处理经过归一化的输入,最后将两者的输出相加。
forward 方法:将输入分割为多个组,逐组进行处理并拼接输出,支持多组输入的并行处理。
这个程序文件定义了一个用于快速卷积操作的神经网络模块,主要包含了一个径向基函数(Radial Basis Function)和一个快速KAN卷积层(FastKANConvNDLayer),以及针对不同维度的卷积层(1D、2D、3D)的具体实现。

首先,RadialBasisFunction类是一个自定义的神经网络模块,初始化时接收一些参数,如网格的最小值和最大值、网格数量以及分母值。该类生成一个线性空间的网格,并计算基于这些网格的径向基函数。在前向传播中,它接受输入并计算每个输入点与网格点之间的距离,然后通过高斯函数(以分母为标准差)进行平滑处理,输出结果。

接下来,FastKANConvNDLayer类是一个通用的卷积层实现,支持多维卷积(如1D、2D、3D)。在初始化时,它接收卷积类、归一化类、输入和输出维度、卷积核大小、分组数、填充、步幅、扩张、网格大小、基础激活函数、网格范围和丢弃率等参数。该类内部会创建多个基础卷积层和样条卷积层,并为每个组初始化归一化层。它还会根据给定的丢弃率设置相应的丢弃层。权重初始化使用Kaiming均匀分布,以便于更好的训练开始。

在前向传播过程中,forward方法首先将输入数据按组进行分割,然后对每个组应用forward_fast_kan方法。该方法对输入进行基础激活和线性变换,接着通过径向基函数计算样条基,最后将基础卷积输出和样条卷积输出相加,形成最终的输出。

最后,FastKANConv3DLayer、FastKANConv2DLayer和FastKANConv1DLayer类分别继承自FastKANConvNDLayer,并指定了对应的卷积和归一化类,以便于在3D、2D和1D卷积中使用。

整体来看,这个程序文件实现了一个灵活且高效的卷积层结构,结合了径向基函数和分组卷积的思想,适用于各种维度的输入数据处理。

10.4 SwinTransformer.py
以下是代码中最核心的部分,并附上详细的中文注释:

import torch
import torch.nn as nn
import torch.nn.functional as F
from timm.models.layers import DropPath, to_2tuple, trunc_normal_

class Mlp(nn.Module):
“”" 多层感知机 (MLP) 模块 “”"

def __init__(self, in_features, hidden_features=None, out_features=None, act_layer=nn.GELU, drop=0.):
    super().__init__()
    out_features = out_features or in_features  # 输出特征数默认为输入特征数
    hidden_features = hidden_features or in_features  # 隐藏层特征数默认为输入特征数
    self.fc1 = nn.Linear(in_features, hidden_features)  # 第一层线性变换
    self.act = act_layer()  # 激活函数
    self.fc2 = nn.Linear(hidden_features, out_features)  # 第二层线性变换
    self.drop = nn.Dropout(drop)  # Dropout层

def forward(self, x):
    """ 前向传播 """
    x = self.fc1(x)  # 线性变换
    x = self.act(x)  # 激活
    x = self.drop(x)  # Dropout
    x = self.fc2(x)  # 线性变换
    x = self.drop(x)  # Dropout
    return x

class WindowAttention(nn.Module):
“”" 基于窗口的多头自注意力 (W-MSA) 模块 “”"

def __init__(self, dim, window_size, num_heads, qkv_bias=True, attn_drop=0., proj_drop=0.):
    super().__init__()
    self.dim = dim  # 输入通道数
    self.window_size = window_size  # 窗口大小
    self.num_heads = num_heads  # 注意力头数
    head_dim = dim // num_heads  # 每个头的维度
    self.scale = head_dim ** -0.5  # 缩放因子

    # 定义相对位置偏置参数表
    self.relative_position_bias_table = nn.Parameter(
        torch.zeros((2 * window_size[0] - 1) * (2 * window_size[1] - 1), num_heads))  # 位置偏置表

    # 计算每个token的相对位置索引
    coords_h = torch.arange(self.window_size[0])
    coords_w = torch.arange(self.window_size[1])
    coords = torch.stack(torch.meshgrid([coords_h, coords_w]))  # 生成坐标网格
    coords_flatten = torch.flatten(coords, 1)  # 展平坐标
    relative_coords = coords_flatten[:, :, None] - coords_flatten[:, None, :]  # 计算相对坐标
    relative_coords = relative_coords.permute(1, 2, 0).contiguous()  # 重新排列维度
    relative_coords[:, :, 0] += self.window_size[0] - 1  # 位置偏置调整
    relative_coords[:, :, 1] += self.window_size[1] - 1
    relative_coords[:, :, 0] *= 2 * self.window_size[1] - 1
    relative_position_index = relative_coords.sum(-1)  # 计算相对位置索引
    self.register_buffer("relative_position_index", relative_position_index)  # 注册为缓冲区

    self.qkv = nn.Linear(dim, dim * 3, bias=qkv_bias)  # 线性变换生成q, k, v
    self.attn_drop = nn.Dropout(attn_drop)  # 注意力的Dropout
    self.proj = nn.Linear(dim, dim)  # 输出线性变换
    self.proj_drop = nn.Dropout(proj_drop)  # 输出的Dropout
    trunc_normal_(self.relative_position_bias_table, std=.02)  # 初始化相对位置偏置
    self.softmax = nn.Softmax(dim=-1)  # Softmax层

def forward(self, x, mask=None):
    """ 前向传播 """
    B_, N, C = x.shape  # B_: 批量大小, N: token数量, C: 通道数
    qkv = self.qkv(x).reshape(B_, N, 3, self.num_heads, C // self.num_heads).permute(2, 0, 3, 1, 4)  # 计算q, k, v
    q, k, v = qkv[0], qkv[1], qkv[2]  # 分离q, k, v

    q = q * self.scale  # 缩放q
    attn = (q @ k.transpose(-2, -1))  # 计算注意力权重

    # 添加相对位置偏置
    relative_position_bias = self.relative_position_bias_table[self.relative_position_index.view(-1)].view(
        self.window_size[0] * self.window_size[1], self.window_size[0] * self.window_size[1], -1)  # 计算相对位置偏置
    relative_position_bias = relative_position_bias.permute(2, 0, 1).contiguous()  # 重新排列维度
    attn = attn + relative_position_bias.unsqueeze(0)  # 添加偏置

    if mask is not None:
        attn = attn.view(B_ // mask.shape[0], mask.shape[0], self.num_heads, N, N) + mask.unsqueeze(1).unsqueeze(0)  # 应用mask
        attn = attn.view(-1, self.num_heads, N, N)
        attn = self.softmax(attn)  # 计算softmax
    else:
        attn = self.softmax(attn)  # 计算softmax

    attn = self.attn_drop(attn)  # 应用Dropout

    x = (attn @ v).transpose(1, 2).reshape(B_, N, C)  # 计算输出
    x = self.proj(x)  # 输出线性变换
    x = self.proj_drop(x)  # 应用Dropout
    return x

class SwinTransformer(nn.Module):
“”" Swin Transformer 主体 “”"

def __init__(self, patch_size=4, in_chans=3, embed_dim=96, depths=[2, 2, 6, 2], num_heads=[3, 6, 12, 24]):
    super().__init__()

    # 图像分块
    self.patch_embed = PatchEmbed(patch_size=patch_size, in_chans=in_chans, embed_dim=embed_dim)

    # 构建层
    self.layers = nn.ModuleList()
    for i_layer in range(len(depths)):
        layer = BasicLayer(
            dim=int(embed_dim * 2 ** i_layer),
            depth=depths[i_layer],
            num_heads=num_heads[i_layer],
            window_size=7,  # 窗口大小
            mlp_ratio=4.0,  # MLP比率
            norm_layer=nn.LayerNorm,  # 归一化层
            downsample=PatchMerging if (i_layer < len(depths) - 1) else None)  # 是否下采样
        self.layers.append(layer)

def forward(self, x):
    """ 前向传播 """
    x = self.patch_embed(x)  # 进行图像分块
    outs = []
    for layer in self.layers:
        x_out, _, _, x, _, _ = layer(x, x.size(2), x.size(3))  # 逐层前向传播
        outs.append(x_out)  # 保存输出
    return outs  # 返回所有层的输出

代码说明
Mlp类:实现了一个简单的多层感知机,包含两个线性层和一个激活函数。
WindowAttention类:实现了窗口自注意力机制,支持相对位置偏置的计算。
SwinTransformer类:实现了Swin Transformer的主体结构,包含图像分块和多层的Transformer块。每一层都可以选择是否进行下采样。
以上是Swin Transformer的核心部分及其注释,提供了模型的基本结构和前向传播的实现。

这个程序文件实现了Swin Transformer模型,主要用于计算机视觉任务。Swin Transformer是一种层次化的视觉Transformer,采用了窗口注意力机制,可以有效处理大规模图像数据。以下是对代码的详细讲解。

首先,导入了必要的库,包括PyTorch及其相关模块,以及一些辅助函数和类。接着定义了一个名为Mlp的类,它是一个多层感知机(MLP),包含两个线性层和一个激活函数(默认为GELU),并在每个线性层后面添加了Dropout以防止过拟合。

接下来,定义了window_partition和window_reverse函数,这两个函数用于将输入特征分割成窗口以及将窗口合并回原始特征图。window_partition将输入张量按照指定的窗口大小进行切分,而window_reverse则将切分后的窗口重新组合成原始的特征图。

然后,定义了WindowAttention类,它实现了基于窗口的多头自注意力机制(W-MSA)。该类支持相对位置偏置,并且可以处理移位窗口(SW-MSA)。在forward方法中,输入特征被分解为查询、键和值,计算注意力权重,并应用相对位置偏置。

接着是SwinTransformerBlock类,它是Swin Transformer的基本构建块。每个块包含一个窗口注意力层和一个MLP层,并使用残差连接。它还支持窗口的移位操作,以增强模型的表达能力。

PatchMerging类用于将特征图的补丁合并,减少特征图的分辨率。这个类在下采样时使用。

BasicLayer类代表Swin Transformer的一个基本层,包含多个Swin Transformer块,并在最后应用下采样(如果需要)。它还计算了用于SW-MSA的注意力掩码。

PatchEmbed类负责将输入图像分割成补丁并进行嵌入。它使用卷积层将图像的每个补丁映射到一个高维空间。

SwinTransformer类是整个模型的主体,负责构建整个Swin Transformer的结构。它初始化了补丁嵌入层、绝对位置嵌入、多个基本层,并在前向传播中依次通过这些层。根据指定的输出索引,模型会返回特定层的输出。

最后,定义了update_weight函数,用于更新模型的权重,并定义了SwinTransformer_Tiny函数,用于创建一个小型的Swin Transformer模型实例,并加载预训练权重(如果提供)。

整体来看,这个文件实现了Swin Transformer的核心组件,提供了灵活的构建方式和可扩展性,适用于各种计算机视觉任务。

源码文件

在这里插入图片描述

源码获取

欢迎大家点赞、收藏、关注、评论啦 、查看👇🏻获取联系方式👇🏻

更多推荐