图片

向AI转型的程序员都关注公众号 机器学习AI算法工程

无人机航拍影像中,那些只有十几个像素的小目标,传统检测器一不小心就漏掉了。

用更大的模型?参数太多,部署到边缘设备跑不动。

用更深的网络?梯度炸了,训练不稳定。

有没有一种方法,能用更少的参数,达到更好的效果

2026年4月,一篇叫FSDETR的论文给出了答案:

频域空域结合的方法,仅用14.7M参数,就在小目标数据集上跑出了13.9% APS的惊人成绩。

这是什么概念?相当于用一辆自行车的体积,干翻了一辆重卡。

01 先说说为什么小目标这么难

在说FSDETR之前,我们先聊聊小目标检测为什么这么痛苦。

你可能遇到过这种情况:

  • 一张航拍图里,车辆只有几十个像素

  • 一张大合影里,人脸小得看不清

  • 监控画面里,远处的行人模模糊糊

为什么会这样?问题出在特征提取特征融合上。

问题一:下采样丢失细节

CNN网络为了减少计算量,会不断下采样。16倍下采样后,一个32x32的小目标,就只剩下2x2像素了。

信息全丢了,怎么检测?

问题二:特征融合不对

很多方法把不同层级的特征强行融合,但高层特征语义虽然强,空间位置信息早就模糊了。

这就是"语义错位"——你知道这是个物体,但不知道它在哪儿。

问题三:背景干扰

小目标在整张图里占比极小,模型很容易被背景带跑。

想象一下在沙子里找金子——小目标就是那颗金子。

FSDETR 论文:

https://arxiv.org/pdf/2604.14884

代码开源地址:

https://github.com/YT3DVision/FSDETR

02 FSDETR 核心思想:频空融合

FSDETR的思路很巧妙:

与其在空间维度修修补补,不如换个维度思考——用频域来处理!

这就好像你听音乐,既要看五线谱(空域),也要听频率(频域),两个结合起来才能完整理解音乐。

FSDETR提出了三个核心模块,一个一个说:

2.1 SHAB:既看局部又看全局

Spatial Hierarchical Attention Block,分层空间注意力。

简单理解就是:

  • 细粒度分支

    :看小目标的边缘、纹理

  • 粗粒度分支

    :看小目标在整张图中的位置

  • 融合

    :把两种信息拼起来

就像你看一张航拍图,既要放大看细节(这里是车轮),也要缩小看全局(车在路的左边)。

2.2 DA-AIFI:动态采样,专挑重点

Deformable Attention based Intra-scale Feature Interaction,可变形注意力。

传统注意力是对所有像素都同等对待,DA-AIFI只关注重点区域

想象你看书的时候,不会每个字都看,而是扫一眼重点,然后仔细看关键段落。

这就是DA-AIFI做的事——自动找到小目标最可能出现的位置,然后重点关注。

2.3 FSFPN:频率+空间双剑合璧

Frequency-Spatial Feature Pyramid Network,频空特征金字塔。

这是FSDETR的核心创新:

  • 高频部分

    :提取边缘、纹理——小目标最缺的就是这些细节

  • 低频部分

    :提取轮廓、结构——告诉模型"这是个什么"

然后通过频空块(CFSB)把两者融合,既保留了细节,又有了语义信息。

⚡ 打个比方

就像我们看一张模糊的老照片:

  • 频域处理 = 看清轮廓和结构(低频)

  • 空域处理 = 看清纹理和边缘(高频)

  • 两者结合 = 既知道"这是个人",又看清了"他的衣服是什么颜色"

03 效果到底有多好?

直接上数据:

数据集

指标

FSDETR结果

对比SOTA

VisDrone 2019

APS(小目标)

13.9%

提升显著

TinyPerson

AP50

48.95%

SOTA

参数量

14.7M

极轻量

可能你对这些数字没概念,我换个说法:

  • 比它强的,参数比它多好几倍

  • 跟它参数差不多,效果没它好

  • 小目标检测AP提升了一大截

💡 简单理解:14.7M参数什么概念?一个普通的手机APP安装包大小。用这么小的模型,达到这种效果,相当于用自行车的油耗,跑出了跑车的速度

04 实战:用FSDETR训练自己的模型

下面进入硬核环节——手把手教你用FSDETR训练自己的检测模型。

4.1 环境准备

# 安装基础依赖
pip install torch torchvision

# 安装FSDETR
git clone https://github.com/YT3DVision/FSDETR.git
cd FSDETR
pip install -r requirements.txt

4.2 数据准备

FSDETR支持COCO格式,这里我们用YOLO格式举例(更通用):

my_dataset/
├── data.yaml          # 配置文件
├── train/
│   ├── images/      # 训练图片
│   └── labels/      # 训练标签
└── valid/
    ├── images/      # 验证图片
    └── labels/      # 验证标签

data.yaml 配置:

path: ./my_dataset
train: train/images
val: valid/images

nc: 3  # 你的类别数
names: ['car', 'person', 'dog']  # 类别名称

标签格式(YOLO格式):

# 每行一个目标:类别ID x_center y_center width height
# 所有值都是归一化的(0-1之间)
0 0.716 0.427 0.108 0.284
1 0.342 0.536 0.075 0.156

4.3 训练代码

import torch
from fsdetr import FSDETR

# 创建模型(小体积,适合边缘部署)
model = FSDETR(
    num_classes=3,          # 你的类别数
    backbone='resnet50',    # 骨干网络
    d_model=256,           # Transformer维度
    nhead=8,                # 注意力头数
    num_encoder_layers=6,  # Encoder层数
    num_decoder_layers=6,  # Decoder层数
    dim_feedforward=2048,   # FFN维度
    dropout=0.1,
    pre_norm=True,
)

# 使用RT-DETR作为基础(已经集成好了)
# 官方提供了更简单的接口:
from fsdetr import build_model

# 方法1:使用预训练模型微调
model = build_model('fsdetr_r50')
model.load_state_dict(torch.load('pretrained.pth'))

# 训练配置
trainer = torch.optim.AdamW(model.parameters(), lr=1e-4)

# 训练循环
for epoch in range(100):
    for batch in dataloader:
        images, targets = batch

        # 前向传播
        outputs = model(images)

        # 计算损失(分类+框回归)
        loss = compute_loss(outputs, targets)

        # 反向传播
        loss.backward()
        trainer.step()
        trainer.zero_grad()

        if epoch % 10 == 0:
            print(f"Epoch {epoch}, Loss: {loss.item():.4f}")

4.4 推理代码

import torch
from fsdetr import FSDETR
from PIL import Image
import numpy as np

# 加载训练好的模型
model = FSDETR(num_classes=3)
model.load_state_dict(torch.load('best_model.pth'))
model.eval()

# 读取图片
image = Image.open('test.jpg').convert('RGB')
image_tensor = torch.from_numpy(np.array(image)).permute(2, 0, 1).float() / 255.0

# 推理
with torch.no_grad():
    outputs = model([image_tensor])

# 解析结果
boxes = outputs['boxes']      # 边界框
scores = outputs['scores']    # 置信度
labels = outputs['labels']    # 类别

# 打印结果
for box, score, label in zip(boxes, scores, labels):
    if score > 0.5:  # 置信度阈值
        print(f"检测到: {label}, 置信度: {score:.2f}, 位置: {box}")

4.5 导出ONNX部署

# 导出为ONNX(跨平台部署)
model.eval()
dummy_input = torch.randn(1, 3, 640, 640)

torch.onnx.export(
    model,
    dummy_input,
    'fsdetr.onnx',
    input_names=['input'],
    output_names=['boxes', 'scores', 'labels'],
    dynamic_axes={
        'input': {0: 'batch'},
        'boxes': {0: 'batch'},
        'scores': {0: 'batch'},
        'labels': {0: 'batch'}
    }
)
print("ONNX导出成功!")
📌 完整训练脚本
# train.py - 完整训练脚本
import torch
import torch.optim as optim
from torch.utils.data import DataLoader
from fsdetr import build_model, CocoDetection
import transforms as T

# 数据增强
def get_transform():
    return T.Compose([
        T.RandomResize([640], max_size=1333),
        T.RandomHorizontalFlip(),
        T.ToTensor(),
        T.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])
    ])

# 创建数据集
train_dataset = CocoDetection('train2017', get_transform())
val_dataset = CocoDetection('val2017', get_transform())

train_loader = DataLoader(train_dataset, batch_size=4, shuffle=True)
val_loader = DataLoader(val_dataset, batch_size=2)

# 创建模型
model = build_model('fsdetr_r50')

# 优化器
optimizer = optim.AdamW(model.parameters(), lr=1e-4, weight_decay=1e-4)

# 学习率调度
lr_scheduler = optim.lr_scheduler.StepLR(optimizer, step_size=30, gamma=0.1)

# 训练循环
num_epochs = 50
for epoch in range(num_epochs):
    model.train()
    total_loss = 0

    for images, targets in train_loader:
        outputs = model(images, targets)
        losses = sum(loss for loss in outputs.values())

        optimizer.zero_grad()
        losses.backward()
        optimizer.step()

        total_loss += losses.item()

    avg_loss = total_loss / len(train_loader)
    print(f"Epoch {epoch+1}/{num_epochs}, Loss: {avg_loss:.4f}")

    lr_scheduler.step()

    # 保存检查点
    if (epoch + 1) % 10 == 0:
        torch.save(model.state_dict(), f'checkpoint_{epoch+1}.pth')

# 保存最终模型
torch.save(model.state_dict(), 'fsdetr_best.pth')
print("训练完成!")

05 效果对比:传统方法 vs FSDETR

为了让你更直观地感受FSDETR的威力,我们做个对比:

方面

传统YOLO系列

FSDETR

小目标检测

容易漏检

13.9% APS

参数体积

50M+

14.7M

边缘部署

困难

轻松

特征融合

简单拼接

频空融合

计算量

🎯 适合场景
  • ✅ 无人机航拍目标检测

  • ✅ 自动驾驶小目标识别

  • ✅ 工业质检微小缺陷

  • ✅ 边缘设备部署

  • ✅ 资源受限的移动端

FSDETR的核心贡献,是证明了频域和空域结合这条路走得通。

以前我们总在空间维度上纠结——怎么融合特征、怎么增强感受野。

FSDETR告诉我们:换个维度思考,可能就是破局的关键。

阅读过本文的人还看了以下文章:

最顶尖的OCR算法有哪些?

最强一键抠图19Kstar 的 Rembg 开源神器

实时语义分割ENet算法,提取书本/票据边缘

整理开源的中文大语言模型,以规模较小、可私有化部署、训练成本较低的模型为主

《大语言模型》PDF下载

动手学深度学习-(李沐)PyTorch版本

YOLOv9电动车头盔佩戴检测,详细讲解模型训练

TensorFlow 2.0深度学习案例实战

基于40万表格数据集TableBank,用MaskRCNN做表格检测

《基于深度学习的自然语言处理》中/英PDF

Deep Learning 中文版初版-周志华团队

【全套视频课】最全的目标检测算法系列讲解,通俗易懂!

《美团机器学习实践》_美团算法团队.pdf

《深度学习入门:基于Python的理论与实现》高清中文PDF+源码

《深度学习:基于Keras的Python实践》PDF和代码

特征提取与图像处理(第二版).pdf

python就业班学习视频,从入门到实战项目

2019最新《PyTorch自然语言处理》英、中文版PDF+源码

《21个项目玩转深度学习:基于TensorFlow的实践详解》完整版PDF+附书代码

《深度学习之pytorch》pdf+附书源码

PyTorch深度学习快速实战入门《pytorch-handbook》

【下载】豆瓣评分8.1,《机器学习实战:基于Scikit-Learn和TensorFlow》

《Python数据分析与挖掘实战》PDF+完整源码

汽车行业完整知识图谱项目实战视频(全23课)

李沐大神开源《动手学深度学习》,加州伯克利深度学习(2019春)教材

笔记、代码清晰易懂!李航《统计学习方法》最新资源全套!

《神经网络与深度学习》最新2018版中英PDF+源码

将机器学习模型部署为REST API

FashionAI服装属性标签图像识别Top1-5方案分享

重要开源!CNN-RNN-CTC 实现手写汉字识别

yolo3 检测出图像中的不规则汉字

同样是机器学习算法工程师,你的面试为什么过不了?

前海征信大数据算法:风险概率预测

【Keras】完整实现‘交通标志’分类、‘票据’分类两个项目,让你掌握深度学习图像分类

VGG16迁移学习,实现医学图像识别分类工程项目

特征工程(一)

特征工程(二) :文本数据的展开、过滤和分块

特征工程(三):特征缩放,从词袋到 TF-IDF

特征工程(四): 类别特征

特征工程(五): PCA 降维

特征工程(六): 非线性特征提取和模型堆叠

特征工程(七):图像特征提取和深度学习

如何利用全新的决策树集成级联结构gcForest做特征工程并打分?

Machine Learning Yearning 中文翻译稿

不断更新资源

深度学习、机器学习、数据分析、python

 搜索公众号添加: datayx  

图片


Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐