目标检测tricks

news/2025/2/26 8:02:21
A. Stochastic Weight Averaging (SWA)
1. 基本思想

SWA 的核心思想是通过对训练过程中不同时间点的模型参数进行加权平均,从而获得一个更好的模型。具体来说,SWA 在训练过程的后期阶段对多个不同的模型快照(snapshots)进行平均,而不是只使用最终的模型参数。

2. 为什么有效?

在深度学习训练中,尤其是在使用随机梯度下降(SGD)及其变体时,模型参数会在局部最优解附近波动。这些波动通常反映了损失函数的不同局部极小值或鞍点。通过平均这些波动中的参数,可以平滑这些波动,并找到一个更稳定的解决方案,从而提高模型的泛化能力。

SWA 的优点

提高泛化能力:通过平均多个模型的参数,可以减少单个模型可能存在的过拟合问题,从而提高模型的泛化能力。
增加稳定性:由于 SWA 平滑了参数的波动,使得最终模型更加稳定,减少了对初始条件和随机性的敏感性。
简单易用:相比于其他复杂的正则化方法(如 Dropout、DropConnect 等),SWA 实现起来非常简单,只需要在训练的后期阶段进行简单的参数平均即可。

SWA 的局限性

内存需求:如果需要存储多个模型快照,则可能会增加内存需求。
计算成本:在 SWA 阶段,虽然学习率较低,但仍然需要进行额外的前向和后向传播计算。
适用范围:SWA 主要适用于那些在训练后期参数波动较大的模型。对于一些已经非常稳定的模型,SWA 可能不会带来显著的改进。

SWA 的工作流程

1. 标准训练阶段
首先,模型按照标准的优化算法(如 SGD 或 Adam)进行训练。在这个阶段,模型参数会逐渐收敛到某个局部最优解。

for epoch in range(total_epochs):
    for batch_i, (inputs, targets) in enumerate(train_loader):
        optimizer.zero_grad()
        outputs = model(inputs)
        loss = criterion(outputs, targets)
        loss.backward()
        optimizer.step()

2. SWA 启动阶段
当训练进入某一特定阶段(通常是训练的最后 25%),开始应用 SWA。此时,模型参数会被周期性地保存下来,并用于计算平均值。

swa_model = AveragedModel(model)
swa_start = int(0.75 * total_epochs)

for epoch in range(total_epochs):
    if epoch >= swa_start:
        swa_model.update_parameters(model)

3. SWA 调度器
为了更好地控制学习率,在 SWA 阶段通常使用一个固定的学习率调度器(如 SWALR)。这个调度器确保在 SWA 阶段学习率保持在一个较低且固定的值。

swa_scheduler = SWALR(optimizer, swa_lr=0.05)

4. BN 层更新
在 SWA 结束后,批归一化(Batch Normalization, BN)层的统计量需要更新。这是因为 BN 层依赖于训练数据的均值和方差统计量,而在 SWA 过程中这些统计量没有被更新。因此,需要通过重新遍历训练数据集来更新这些统计量。

torch.optim.swa_utils.update_bn(train_loader, swa_model, device='cuda')

运用在YoloV8中的代码

import torch
from torch.optim.swa_utils import AveragedModel, SWALR
from ultralytics import YOLO
from torch.utils.data import DataLoader
from torchvision import transforms
from yolov8_dataset import YOLOv8Dataset  # 假设你有一个自定义的数据集类

# 数据集和数据加载器
transform = transforms.Compose([transforms.ToTensor()])
train_dataset = YOLOv8Dataset(root='path/to/dataset', transform=transform)
train_loader = DataLoader(train_dataset, batch_size=16, shuffle=True)

# 加载预训练的 YOLOv8 模型
model = YOLO('yolov8n.yaml')  # 根据需要选择合适的模型配置

# 初始化优化器和学习率调度器
optimizer = torch.optim.SGD(model.parameters(), lr=0.01, momentum=0.9)
lr_scheduler = torch.optim.lr_scheduler.StepLR(optimizer, step_size=30, gamma=0.1)

# 初始化 SWA 模型和调度器
swa_model = AveragedModel(model.model)
swa_start = int(0.75 * 100)  # 在训练的最后 25% 开始 SWA
swa_scheduler = SWALR(optimizer, swa_lr=0.05)

total_epochs = 100

for epoch in range(total_epochs):
    model.train()
    for batch_i, (imgs, targets) in enumerate(train_loader):
        optimizer.zero_grad()
        loss, outputs = model(imgs, targets)
        loss.backward()
        optimizer.step()

    if epoch >= swa_start:
        swa_model.update_parameters(model.model)
        swa_scheduler.step()
    else:
        lr_scheduler.step()

# 在训练结束后,应用 SWA 最终步骤
torch.optim.swa_utils.update_bn(train_loader, swa_model, device='cuda')

# 保存 SWA 模型
torch.save(swa_model.state_dict(), 'yolov8_swa.pth')

AveragedModel 类简化实现

import copy

class AveragedModel:
    def __init__(self, model):
        self.n_averaged = 0  # 记录已经累加的模型快照数量
        self.module = copy.deepcopy(model)  # 复制原始模型结构和参数

    def update_parameters(self, model):
        self.n_averaged += 1
        for p_swa, p_model in zip(self.module.parameters(), model.parameters()):
            device = p_swa.device
            p_model_ = p_model.detach().to(device)
            if self.n_averaged == 1:
                p_swa.detach().copy_(p_model_)
            else:
                p_swa.detach().mul_(1.0 - 1.0 / self.n_averaged).add_(p_model_, alpha=1.0 / self.n_averaged)
Stochastic Weight Averaging (SWA) 参数更新公式

在 Stochastic Weight Averaging (SWA) 中,参数更新的过程涉及到将当前模型的参数逐步累加到一个平均模型中。以下是具体的公式和解释。

第一次调用 update_parameters

当第一次调用 update_parameters 方法时,直接将 p_model 赋值给 p_swa

p swa = p model p_{\text{swa}} = p_{\text{model}} pswa=pmodel

这意味着 p_swa 直接被设置为当前模型的参数。

后续调用 update_parameters

从第二次调用开始,使用以下公式更新 p_swa

p swa = ( 1 − 1 n ) ⋅ p swa + 1 n ⋅ p model p_{\text{swa}} = \left(1 - \frac{1}{n}\right) \cdot p_{\text{swa}} + \frac{1}{n} \cdot p_{\text{model}} pswa=(1n1)pswa+n1pmodel

其中 $ n $ 是已经累加的模型快照数量。

第二次调用 update_parameters 的例子

n = 2 n = 2 n=2 时,公式变为:

p swa = 1 2 ⋅ p swa + 1 2 ⋅ p model p_{\text{swa}} = \frac{1}{2} \cdot p_{\text{swa}} + \frac{1}{2} \cdot p_{\text{model}} pswa=21pswa+21pmodel

这表示 p_swa 被更新为其当前值的一半加上 p_model 的一半。

更多调用的例子

随着训练的继续,每次调用 update_parameters 都会更新 p_swa,逐渐平滑模型参数的的波动。例如:

  • n = 5 n = 5 n=5 时:
    p swa = 4 5 ⋅ p swa + 1 5 ⋅ p model p_{\text{swa}} = \frac{4}{5} \cdot p_{\text{swa}} + \frac{1}{5} \cdot p_{\text{model}} pswa=54pswa+51pmodel

  • n = 6 n = 6 n=6 时:
    p swa = 5 6 ⋅ p swa + 1 6 ⋅ p model p_{\text{swa}} = \frac{5}{6} \cdot p_{\text{swa}} + \frac{1}{6} \cdot p_{\text{model}} pswa=65pswa+61pmodel

B. SAHI (Slice and Hyper Inference)
SAHI 的核心思想

图像切片:将大图像切分成多个小块(slices),以便更好地捕捉小目标。
超推理(Hyper Inference):对每个切片进行独立的推理,并合并结果以获得最终的检测结果。
自动切片选择:根据目标的大小和分布,自适应地选择合适的切片策略。

工作流程

以下是 SAHI 的典型工作流程:

加载图像:读取待检测的大图像。
图像切片:将图像切分成多个小块(slices),每个切片的大小和重叠度可以根据需求调整。
模型推理:对每个切片分别进行目标检测模型的推理。
结果合并:将所有切片的检测结果合并成一个完整的检测结果,并去除重复检测框。
后处理:对合并后的检测结果进行进一步处理,如非极大值抑制(NMS)等。

以Yolov5为例的插入sahi代码

from sahi import AutoDetectionModel
from sahi.predict import get_sliced_prediction
from sahi.utils.yolov5 import Yolov5TestConstants

# 加载预训练的 YOLOv5 模型
detection_model = AutoDetectionModel.from_pretrained(
    model_type='yolov5',
    model_path=Yolov5TestConstants.YOLOV5N_MODEL_LOCAL_PATH,  # 替换为你的模型路径
    confidence_threshold=0.3,
    device="cuda"  # 或 "cpu"
)

# 图像路径
image_path = "path/to/your/image.jpg"

# 使用 SAHI 进行小目标检测
result = get_sliced_prediction(
    image=image_path,
    detection_model=detection_model,
    # slice_height 和 slice_width:每个切片的高度和宽度。可以根据图像大小和目标的尺度进行调整
    slice_height=512,
    slice_width=512, 
    # 切片之间的重叠比例。增加重叠可以减少目标被切分的风险,但会增加计算量。
    overlap_height_ratio=0.2,
    overlap_width_ratio=0.2
)

# 打印检测结果
print(result)
C. SeNet (Squeeze-and-Excitation Networks) 通道注意力
SeNet 工作流程图解:

输入特征图

输入是一个三维张量,包含高度 H H H、宽度 W W W 和通道数 C C C

Input:  U ∈ R H × W × C \text{Input: } U \in \mathbb{R}^{H \times W \times C} Input: URH×W×C

Squeeze(挤压):全局平均池化 (Global Average Pooling, GAP)

对每个通道进行全局平均池化操作,将空间维度压缩为一个标量。输出是一个向量 z ∈ R C z \in \mathbb{R}^C zRC,表示每个通道的全局信息。

z c = 1 H × W ∑ i = 1 H ∑ j = 1 W U ( i , j , c ) z_c = \frac{1}{H \times W} \sum_{i=1}^{H} \sum_{j=1}^{W} U(i,j,c) zc=H×W1i=1Hj=1WU(i,j,c)

其中, z c z_c zc 是第 c c c 个通道的全局平均值。

Excitation(激励):两个全连接层

使用两个全连接层来生成每个通道的权重。第一个全连接层将特征维度从 C C C 降到 C r \frac{C}{r} rC,其中 r r r 是降维比率。第二个全连接层将特征维度从 C r \frac{C}{r} rC 升回到 C C C。中间使用 ReLU 激活函数和 Sigmoid 激活函数分别处理两个全连接层的输出。

z ^ = ReLU ( W 1 ⋅ z ) s = σ ( W 2 ⋅ z ^ ) \begin{aligned} \hat{z} &= \text{ReLU}(W_1 \cdot z) \\ s &= \sigma(W_2 \cdot \hat{z}) \end{aligned} z^s=ReLU(W1z)=σ(W2z^)

其中, W 1 ∈ R C r × C W_1 \in \mathbb{R}^{\frac{C}{r} \times C} W1RrC×C W 2 ∈ R C × C r W_2 \in \mathbb{R}^{C \times \frac{C}{r}} W2RC×rC 分别是第一和第二全连接层的权重矩阵, σ \sigma σ 表示Sigmoid激活函数。

Reweight(重加权):逐元素相乘

将生成的通道权重 s ∈ R C s \in \mathbb{R}^C sRC 应用到原始特征图 U U U 上,通过逐元素相乘的方式重新加权特征图。输出是一个与输入特征图形状相同的张量 U ′ ∈ R H × W × C U' \in \mathbb{R}^{H \times W \times C} URH×W×C

U ′ ( i , j , c ) = s c × U ( i , j , c ) U'(i,j,c) = s_c \times U(i,j,c) U(i,j,c)=sc×U(i,j,c)

以上就是SENet的工作流程描述,包括了公式和步骤。

Spatial Attention Mechanism 空间注意力机制

SAM工作流程

1. 输入特征图

输入是一个三维张量,包含高度 H H H、宽度 W W W 和通道数 C C C

Input:  F ∈ R H × W × C \text{Input: } F \in \mathbb{R}^{H \times W \times C} Input: FRH×W×C

2. 生成注意力图

通过卷积层和其他操作生成注意力图 A A A

(1). 第一卷积层
Z 1 = C o n v 1 × 1 ( F ) Z_1 = Conv_{1 \times 1}(F) Z1=Conv1×1(F)

(2). 第二卷积层及激活函数
A = σ ( C o n v 1 × 1 ( Z 1 ) ) A = \sigma(Conv_{1 \times 1}(Z_1)) A=σ(Conv1×1(Z1))
其中 σ \sigma σ 是 Sigmoid 函数:

σ ( x ) = 1 1 + e − x \sigma(x) = \frac{1}{1 + e^{-x}} σ(x)=1+ex1

3. 归一化处理

对生成的注意力图进行归一化处理,使其值在合理范围内。

  • Sigmoid 归一化

    A = σ ( Z 1 ) = 1 1 + e − Z 1 A = \sigma(Z_1) = \frac{1}{1 + e^{-Z_1}} A=σ(Z1)=1+eZ11

  • Softmax 归一化(可选):

    A = Softmax ( Z 1 ) = e Z 1 ∑ e Z 1 A = \text{Softmax}(Z_1) = \frac{e^{Z_1}}{\sum e^{Z_1}} A=Softmax(Z1)=eZ1eZ1

4. 加权融合

将生成的注意力图与原始输入特征图逐元素相乘,从而实现对重要区域的强调和不重要区域的抑制。

F ′ = A ⊙ F F' = A \odot F F=AF

其中 ⊙ \odot 表示逐元素相乘操作。


http://www.niftyadmin.cn/n/5868347.html

相关文章

监督学习——分类问题:以鸢尾花分类案例为例

监督学习——分类问题:以鸢尾花分类案例为例 一、引言 监督学习作为机器学习领域的核心分支之一,在诸多实际场景中发挥着重要作用。它基于带有标签的数据进行模型训练,旨在让模型学习输入特征与输出标签之间的映射关系,从而对新数据做出准确预测。分类问题和回归问题是监…

KylinSP3 | 防火墙和麒麟安全增强设置KySec

一、系统防火墙原理 麒麟操作系统从V10版本开始,默认使用了Firewalld防火墙,Firewalld是能提供动态管理的防火墙,支持网络/防火墙区域,用于定义网络连接或接口的信任级别。支持IPv4和IPv6防火墙设置、以太网桥接和IP集。将运行时…

【前端定位线上问题的多种方案(不依赖 Sentry)】

前端定位线上问题的多种方案(不依赖 Sentry) 🛠️ 一、构建时注入调试信息 🔧 1. 注入版本信息与 Git 提交哈希 Webpack 配置: // webpack.config.js const webpack require(webpack); const gitRevision require(…

React进阶之前端业务Hooks库(三)

前端业务Hooks库 hooks 方法localStorage和sessionStorager区别packages/hooks/src/useLocalStorageStatepackages/hooks/src/useSessionStorageStatepackages/hooks/src/createUseStorageState模块Hooks在不同场景下的应用Hooks陷阱前提例子useLatest和useMemoizedFn其他功能的…

jmeter 如何做移动端的测试 特别是兼容性测试

JMeter本身主要是一款用于性能测试和功能测试的工具,虽然它并非专门为移动端测试设计,但可以通过一些方式来对移动端应用进行测试,以下从测试准备、测试过程及注意事项等方面为你详细介绍: 一、测试准备 (一)环境搭建 JMeter安装与配置:确保JMeter已经正确安装在测试机…

【MySQL篇】MySQL操作库

目录 1,创建数据库 2,字符集和校验规则 2.1,查看系统默认字符集和校验规则 2.2,查看数据库支持的字符集 2.3,查看数据库支持的字符集校验规则而 2.4,校验规则对数据库的影响 3,操作数据库…

Docker 搭建 Gitlab 服务器 (完整详细版)

参考 Docker 搭建 Gitlab 服务器 (完整详细版)_docker gitlab-CSDN博客 Docker 安装 (完整详细版)_docker安装-CSDN博客 Docker 日常命令大全(完整详细版)_docker命令-CSDN博客 1、Gitlab镜像 # 查找Gitlab镜像 docker search gitlab # 拉取Gitlab镜像 docker pull gitlab/g…

基于django图书信息管理系统的搭建(增删改查)

✍django项目搭建教程 ☞ ----------------- 教程 本文主要讲解django如何连接数据库MySQL并且可视化展示,实现增删改查功能 目录 一. 创建django应用 二. 数据库配置 三. 查看数据库 四. 编写代码 4.1视图函数 4.2 配置URL 4.3创建模板文件 4.…