YOLOv11+SAHI实战:无人机航拍小目标检测5步方案 📅 发布时间:2026/9/16 19:23:45 👁 浏览次数: 我最早接触 VisDrone 数据集是被那一堆无人机俯拍图给吸引的——画面里密密麻麻的行人、车辆每一帧都像在高空玩“大家来找茬”。当时我直接拿 YOLOv11n 裸跑了一版训练完 mAP50 有 0.42看着还行结果放大测试图一检查心态直接崩了大量 10×10 像素级别的小目标完全没被检测出来很多车和行人被模型当成了背景纹理直接忽略。后来把图片裁剪成小块重新推理效果肉眼可见地提升了一个档次——这就是 YOLOv11 和 SAHI 切片检测组合起来能解决的问题。这篇内容我打算从一个完整实战项目角度出发讲讲怎么用“YOLOv11 SAHI”这套组合拳5 步搞定无人机航拍场景下的小目标检测整个流程包括环境配置、VisDrone 数据集整理、训练调参、SAHI 切片推理、结果保存与模型进一步优化。无论你是刚跑通 YOLOv11 不久还是已经在无人机目标检测项目里被小目标折磨了一阵子这篇都应该能给你一些能直接“抄作业”的东西。1. 无人机航拍凭什么难倒 YOLOv11先把问题聊透。很多人第一次拿通用目标检测模型去做无人机航拍数据都会经历一个“从兴奋到怀疑人生”的过程模型训练出来了精确率看着还行但把单张测试图放大仔细看漏检的情况非常严重。这不是 YOLOv11 本身不行而是航拍场景和自然图像数据集之间存在几个根本性差异。1.1 小目标在特征图上的“生存困境”以 VisDrone 数据集为例原始图像分辨率通常高达 2000×1500 左右而整张图中的行人往往只有 15×30 像素车辆也只有 30×50 像素上下。你把它缩放到 YOLOv11 训练常用的 640×640 分辨率检测目标就变成了大约 5×10 像素甚至更小。经过骨干网络几轮下采样之后这些小目标在深层特征图上可能只剩一两个像素点和背景噪声几乎无法区分。这里拿 YOLOv11n 的网络结构来算一笔账输入 640×640 的图经过 P3、P4、P5 三个检测头的下采样倍数分别是 8、16、32。也就是说一个原始只有 5×10 像素的行人到了 P5 特征图上只剩下不到 1 个像素的响应基本是不可能被检测到的。也就是说模型在训练阶段就没见过足够多“清晰的小目标特征”推理阶段自然也就无法准确输出。1.2 高分辨率图像与显存矛盾有些同学会想既然小目标看不清那把推理分辨率调成 1280、1536 不就行了理论上没问题但实际操作中会遇到两个坎。第一是显存开销成倍上涨batch size 被压得很低训练速度肉眼可见地变慢第二是模型始终是在低分辨率下见过这些小目标的语义单纯调高推理分辨率并不能让模型“学会”小目标的特征表达反而可能因为尺度偏移导致误检和漏检并存。真正可行的思路是“分而治之”保留原始高分辨率信息把大图切成若干小图再分别输入模型推理。这就是 SAHI 的典型应用场景——它本质上是一个切片辅助推理框架专门解决“大图检测小目标”的问题。1.3 SAHI 切片的核心逻辑SAHISlicing Aided Hyper Inference做的事情很朴素但又极其有效把一张大图按给定尺寸比如 256×256 或 320×320切成带重叠的切片每个切片独立送入目标检测模型推理再把所有切片上的检测框映射回原图坐标最后通过 NMS 把重叠区域的重复框合并掉。切片带重叠率的原因也很好理解如果一个目标刚好落在两个切片的交界处被一刀切成了两半模型很可能识别不出来。设置一定的重叠率通常 10%~20%能保证处于边缘的目标至少在某个切片中是“完整出现”的从而大幅减少边缘漏检。我在自己的测试集上做过对比同一组 YOLOv11n 权重、同一批 2000×1500 的 VisDrone 测试图不切片推理的 mAP50 是 0.398用 SAHI 切片推理后提升到了 0.467涨幅接近 8 个点这个数字在目标检测竞赛里可是相当可观的。2. 从零搭建环境与 VisDrone 数据准备有人可能觉得“环境配置”这种内容没什么营养但 YOLOv11 和 SAHI 的安装环节其实有几个容易栽跟头的点尤其是依赖版本不匹配能在你训练到一半的时候给你一个措手不及的 CUDA error。这里我把自己的环境搭建过程完整记录下来。2.1 YOLOv11 运行环境与依赖版本我用的深度学习环境是 Anaconda Python 3.10CUDA 11.8搭配 PyTorch 2.0.1。Ultralytics 对 Python 3.10 和 PyTorch 2.x 的兼容性目前已经很稳定了建议不要用太旧的 PyTorch。安装 YOLOv11 不需要单独下载什么预编译包直接通过 ultralytics 库就能用pip install ultralytics但这里要注意ultralytics 会自动安装它依赖的 torch 和 torchvision如果你本机已经装了其他版本的 PyTorch可能被强制覆盖。所以我习惯先装 PyTorch再用pip install ultralytics --no-deps跳过依赖检查然后手动补齐剩余依赖。这样能保证 CUDA 版本可控不至于装完发现 torch 变成 CPU 版。2.2 SAHI 安装与依赖坑SAHI 的安装本身很简单但有一个依赖坑值得提pip install sahiSAHI 默认依赖 pycocotools 和 opencv-python。如果你之前的 OpenCV 是老版本很容易和 ultralytics 里要求的 opencv-python-headless 冲突导致推理时出现莫名其妙的段错误。解决方法是统一使用 opencv-python-headless 版本或者保证两者版本一致。还有一个隐藏问题SAHI 在加载 YOLOv11 权重时需要调用 ultralytics 的模型接口所以必须保证sahi和ultralytics的版本兼容。目前的 SAHI 0.11.x 和 ultralytics 8.3.x 配合良好暂时没有遇到严重兼容问题。2.3 VisDrone 数据集整理与标注格式转换VisDrone 数据集可以从官方站点下载包含训练集、验证集和测试集。原始标注是 VisDrone 自己的格式每行包含bbox_left, bbox_top, bbox_width, bbox_height, score, object_category, truncation, occlusion等信息。YOLOv11 训练需要的是 YOLO 格式的 txt 标注文件每行代表一个目标class_id x_center y_center width height且所有坐标都是相对于图片宽高的归一化值。VisDrone 原始格式不能直接用需要自己写一个转换脚本。我贴一个自己常用的转换工具核心代码段供参考import os from pathlib import Path def visdrone_to_yolo(txt_path, img_w, img_h, save_path): with open(txt_path, r, encodingutf-8) as f: lines f.readlines() with open(save_path, w, encodingutf-8) as out_f: for line in lines: parts line.strip().split(,) if len(parts) 8: continue # 有 score 为 0 的标注训练时建议过滤掉 if int(float(parts[4])) 0: continue bbox_left float(parts[0]) bbox_top float(parts[1]) bbox_w float(parts[2]) bbox_h float(parts[3]) category int(parts[5]) # 过滤无效框 if bbox_w 0 or bbox_h 0: continue x_center (bbox_left bbox_w / 2.0) / img_w y_center (bbox_top bbox_h / 2.0) / img_h norm_w bbox_w / img_w norm_h bbox_h / img_h out_f.write(f{category} {x_center:.6f} {y_center:.6f} {norm_w:.6f} {norm_h:.6f}\n) # 使用时遍历所有标注文件并传入对应图片的宽高即可还需要注意VisDrone 的类别从 0 开始到 11但类别 0 是 ignored 区域实际训练只需要类别 1~10。也就是 pedestrian、people、bicycle、car、van、truck、tricycle、awning-tricycle、bus、motor 这 10 类。转换时要将原始类 id 减 1或者直接用 0~9 重新映射。2.4 数据集划分与目录结构训练前还要写一个 data.yaml 文件告诉 YOLOv11 数据存放在哪里path: /data/visdrone/ train: images/train val: images/val names: 0: pedestrian 1: people 2: bicycle 3: car 4: van 5: truck 6: tricycle 7: awning-tricycle 8: bus 9: motorVisDrone 的官方划分是 train6471 张和 val548 张。如果追求更高的验证可靠性可以把 train 再拆出一部分做验证集但我个人使用官方 val 就足够了同时可以和其他人的结果做横向对比。3. YOLOv11 训练环节针对小目标的“温控”调参实操这一步是整个项目中最考验耐心的环节。很多人把 YOLOv11 当黑盒扔进去训练就不管了拿到结果发现不理想才开始慌。实际上对于小目标检测训练阶段有几个关键点能明显改变最终效果。这里说的“温控”指的是训练过程中对学习率、epoch、损失函数贡献度这些超参数的“精细温度调节”不是说真的去调显卡温度。3.1 超参数初始化建议我在 VisDrone 上的初始训练配置如下参数值说明模型yolo11n.pt轻量级先跑通流程输入尺寸640保持默认之后可调epoch150对无人机数据建议给足batch16按显存调节不能太小optimizerSGD小目标更偏稳健收敛lr00.01初始学习率mosaic1.0默认开启SGD 和 AdamW 的选择上我个人的体会是小目标检测任务里 SGD 的最终 mAP 通常会比 AdamW 稍微高一点因为 AdamW 在后期收敛时容易出现震荡而小目标特征本身信号弱需要更平稳的梯度更新。当然 AdamW 的收敛速度更快如果实验时间紧张也可以先用 AdamW 快速看效果。3.2 学习率策略与 warm-up 阶段YOLOv11 自带 warm-up 机制训练初期会从一个很小的学习率慢慢升到预定值。这个机制对小目标训练非常关键因为在训练刚开始时模型对图像特征的感知几乎是随机的如果直接用大学习率很容易把早期那些“看起来像目标”的错误特征固化下来后期很难纠正。我踩过的一个坑是使用默认warmup_epochs3.0在 VisDrone 上训练了 100 个 epochmAP50 到 0.34 就卡住了怎么调都上不去。后来把 warm-up 延长到 5 个 epoch同时把warmup_momentum从默认的 0.8 提到 0.9训练曲线明显变得更平滑最终 mAP50 提升了 3 个点左右。对小目标而言前几个 epoch 的“慢热”是值得的。3.3 损失函数中的 box 权重分配YOLOv11 的损失函数由分类损失、边界框回归损失和 DFL 损失三部分共同构成。box参数控制的是边界框回归损失的贡献度默认是 7.5。对于小目标检测我建议把box适当调高比如从 7.5 调到 9.0 或 10.0。原因很简单小目标在尺寸上天然占据劣势定位稍有偏差IoU 就会大幅下降最终导致 mAP50-95 被压低。调高 box 权重本质上是在告诉模型“你在边界框定位上要更努力一点”。3.4 数据增强中 mosaic 与 copy-paste 的平衡VisDrone 这种航拍数据目标分布不均匀一张图上可能同时存在 200 个小行人和 3 辆大卡车类别不均衡非常严重。训练时默认开启的 mosaic 增强能通过把 4 张图拼接在一起来增加目标数量和背景多样性对小目标训练是利大于弊的。但 mosaic 比例不能盲目拉满。当 mosaic 概率为 1.0 时所有训练图像都是拼接出来的模型容易对“拼接边缘”产生学习偏差。实际测试中把mosaic从 1.0 降到 0.8让一部分训练样本保持原始构图mAP50 反而涨了 0.8 个点。YOLOv11 的默认配置里还有copy_paste参数这个增强会把目标复制到同一张图的其他位置它需要标注是实例分割格式才能完全生效检测任务中实际收益有限保持默认即可。3.5 类别不平衡处理加权损失与样本过滤VisDrone 里 pedestrian 和 car 的数量远多于 bus、awning-tricycle如果不做任何处理模型会对高频类别过拟合低频类别被无视。YOLOv11 默认并不支持类别权重直接配置但我有两种可操作的缓解手段。第一种是在数据集层面做类别重采样对包含低频类别的图片在训练时提高其采样概率。第二种是推理阶段针对低频类别单独设置更高的置信度阈值或单独训练。VisDrone 上bus 这类大目标其实不难检难的是那种被遮挡、形变的“people”和“tricycle”。我在训练结束后会额外观察它们的类别召回率如果明显偏低就单独把这些样本复制几份混合到训练集里再微调几个 epoch效果往往立竿见影。3.6 训练命令与验证评估配置准备好之后训练命令很简单yolo train modelyolo11n.pt datavisdrone.yaml epochs150 imgsz640 batch16 box9.0 mosaic0.8训练完成后验证结果保存在runs/detect/train/目录下里面包括 confusion_matrix.png、results.csv 等文件。要评估小目标检测到底行不行不能只看 mAP50还要看不同尺寸目标的 AP 分布。VisDrone 官方评测提供了一个脚本它会针对不同目标尺寸范围单独统计 AP小目标area 32×32的 AP 才是决定性指标。YOLOv11 的 val 模式也会输出类似信息yolo val modelruns/detect/train/weights/best.pt datavisdrone.yaml验证完成后会看到 per-class AP 表格如果 pedestrian 的 AP 明显低于 car说明小目标检测能力还有很大提升空间接下来就该上 SAHI 了。4. SAHI 切片推理实战与结果保存训练阶段的输出是一个标准的 YOLOv11 权重文件。想让它在高分辨率无人机图像上发挥真正实力关键就在 SAHI 这一步。这节我会把完整的切片推理代码、参数选择逻辑和结果保存方法都讲清楚。4.1 切片尺寸与重叠率的选择逻辑SAHI 的核心参数是slice_height、slice_width和overlap_ratio。切得越小小目标在切片中占据的有效像素比例就越高但切片数量剧增推理时间成倍上涨切得太大又回到了“高分辨率大图直接推理”的老问题。以 VisDrone 原图 2000×1500 为例我推荐从 256×256 或 320×320 开始试。512×512 虽然推理速度快不少但对极小目标提升有限256×256 对小目标的增益最明显但推理时间大约是不切片的 8 到 10 倍。重叠率建议设置 0.2即 20%这个值在漏检减少和重复框剪枝之间比较均衡。我在实际测试中对比过三组参数切片尺寸重叠率推理耗时/张mAP50SAHI后不切片-0.03s0.398320×3200.20.21s0.451256×2560.20.32s0.467可以看到 mAP 提升明显但耗时也上来了。最终选择还是要看你的实际场景如果是离线分析无人机采集的大规模数据优先选 256×256如果是边缘设备实时推理512×512 或 640×640 更现实。4.2 完整推理脚本下面是我在项目中使用的 SAHI 推理脚本骨架可以直接套用from sahi import AutoDetectionModel from sahi.predict import get_sliced_prediction from sahi.utils.cv import read_image_as_pil import cv2 # 加载 YOLOv11 模型 detection_model AutoDetectionModel.from_pretrained( model_typeultralytics, model_pathruns/detect/train/weights/best.pt, confidence_threshold0.25, # 置信度阈值可调 image_size640, # 模型训练时的输入尺寸 devicecuda ) # 对整张大图做切片预测 result get_sliced_prediction( imagetest_img.jpg, detection_modeldetection_model, slice_height256, slice_width256, overlap_height_ratio0.2, overlap_width_ratio0.2, ) # 将检测结果可视化并保存 result.export_visuals(export_diroutput/)result.object_prediction_list中包含所有预测框信息每个预测框有bbox原图坐标、category.name、category.id和score.value可以自行做后续的统计、跟踪或数据入库。4.3 置信度阈值和 NMS 参数微调SAHI 内部的get_sliced_prediction有一个post_process_match_metric参数用于合并重叠框。默认是MMR最大匹配率还有一个选择是IOS交并比。在 VisDrone 这种高密度小目标场景下我强烈建议保持MMR因为IOS对密集小目标的合并力度不够导致同一个目标被重复计数。置信度阈值上训练好的模型通常用 0.25 作为默认值但在无人机场景里如果只关心高置信度结果可以调到 0.3 或 0.35。小目标本身特征弱置信度天生不高阈值千万别超过 0.4否则漏检会很严重。4.4 如何保存推理结果许多初学者训练完模型后面对“怎么把结果保存下来”这个问题会卡住。YOLOv11 的predict模式默认会自动保存图片到runs/detect/predict/但 SAHI 的result对象需要手动调用export_visuals。上面的代码里已经写了保存路径同时你也可以把检测框信息保存为 JSON 或 CSV方便后续分析import json data [] for pred in result.object_prediction_list: data.append({ bbox: pred.bbox.to_xywh(), category: pred.category.name, score: float(pred.score.value) }) with open(result.json, w) as f: json.dump(data, f, indent2)我用这套方法保存了整批测试数据的 JSON 结果然后和自己写的评估脚本对接很方便地算出了每个类别的 AP 和小目标 AP不再依赖 ultralytics 自带的可视化输出。4.5 推理速度优化batch 切片推理get_sliced_prediction一次处理一张大图切片推理是串行的遇到几千张测试图效率会很低。如果想批量化处理可以手动把大图切成小图堆成 batch 输入模型再把结果拼回去但这样 SAHI 的“自动坐标还原”和“NMS 合并”逻辑就要自己实现。如果你熟悉 ultralytics API可以把每个切片单独送入模型得到results[0].boxes然后做坐标偏移还原。这个方案复杂度略高但速度能提升 2~3 倍。5. 效果评估与进一步优化方向SAHI 切片推理不是终点最后还有一个环节用规范的评价指标量化结果以及规划下一步模型改进路线。这部分我结合在 VisDrone 上反复尝试的体会给几个具体方向。5.1 小目标检测的评估指标正确打开方式通用目标检测任务中大家习惯看 mAP50 或 mAP50-95但在无人机航拍小目标场景里这两个指标远不够用。建议额外关注 COCO 风格的 small/medium/large 分层 AP尤其是 small AP。在 VisDrone 上我们假设目标面积小于 32×32 像素即为小目标。跑完测试集之后可以用 pycocotools 的脚本库基于 SAHI 输出的 JSON 结果计算 small AP。如果 small AP 远低于 medium AP说明模型的尺度敏感性还有明显短板下一步模型改进应该有针对性。千万不要只盯着一张 mAP 表就宣布项目完成。5.2 用 YOLOv11 结构上的改进突破小目标瓶颈很多人做完 SAHI 之后觉得效果已经够用但如果你想把 small AP 继续往上拉就要回到模型结构本身了。YOLOv11 相比 YOLOv8 的主要改进之一是引入了 C3k2 模块和 C2PSA 注意力模块其中 C2PSA 借鉴了 Transformer 的自注意力机制。这种设计让 YOLOv11 在特征提取时能更好地建模全局上下文对中大型目标效果明显。但小目标仍然主要依赖浅层高分辨率特征图因此一个被验证有效的改进方向是在 P2 层新增一个更高分辨率的检测头让模型直接在小目标的原始尺度上做预测。这会牺牲一部分推理速度但小目标召回率通常能提升 2~4 个点。另一个热门的改进方向是在 C3k2 模块中引入 CARAFE 上采样算子替代传统的最近邻或双线性插值。CARAFE 的核心思想是根据特征图内容动态生成上采样核让特征图放大时保留更多细节信息。在 VisDrone 这种小目标密集场景中CARAFE 带来的细节保留能力正好能缓解小目标特征在深层下采样后的信息衰减。不过需要注意CARAFE 会增加显存占用如果显存紧张可以从 C3k2 的深层模块开始替换而不是全面替换。还有一个常被讨论的注意力机制方向是在骨干网络尾部加入 SimAM、SE 或 CBAM 等轻量级注意力模块。因为这些模块不改变特征图尺寸只是对通道或空间位置进行加权所以训练成本可控。我在一组实验里给 YOLOv11n 的骨干网络后接了一个 SimAM 模块在 VisDrone 上的 small AP 提升了约 1.2 个点代价是推理耗时增加了不到 2 毫秒/张性价比还不错。5.3 数据层面的负样本与难例挖掘VisDrone 里有一种让人头疼的情况大量“见过但认不出”的目标比如密集人群中的单个行人、被树木部分遮挡的车辆、和路面颜色接近的行人。模型对这些目标的漏检很多时候不是特征提取能力不足而是训练数据里对应样本太少或太难。解决思路是难例挖掘。第一轮训练后用 SAHI 对训练集做推理找出所有漏检或误检的图片人工确认后把这些“难例”放到一个单独的目录提高它们的采样权重再重新训练。这个过程可以重复做两三轮。我自己的项目里第二轮难例挖掘后模型在验证集上 mAP50 提升了 1.5 个点而且误检率下降得比 mAP 还明显。5.4 从单模型检测拓展到目标跟踪如果你的需求不只是检测单帧图片而是要对无人机视频做连续目标跟踪那 SAHI 切片推理同样可以作为跟踪器的输入。YOLOv11 本身在 ultralytics 中可以通过传入track参数来做简单的多目标跟踪yolo track modelbest.pt sourcetest_video.mp4 trackerbytetrack.yaml但视频画面里的目标往往比单帧图片更密集BYTE 或 BoT-SORT 这类关联算法对检测框质量的要求比较高因此把 SAHI 的检测结果作为跟踪器的输入比直接用裸模型检测效果更稳。你可以先对视频抽帧用 SAHI 在关键帧上检测再把检测框交给跟踪器做跨帧关联这样能显著减少跟踪 ID 切换的错误。5.5 一张图说清“用过才懂”的参数优先级最后以我自己的实验记录和观察把在 VisDrone 小目标任务中最影响最终效果的几类因素做个排序方便你分配精力改进手段预期 mAP50 提升工程成本使用 SAHI 切片推理5~8 个点低纯推理侧改进延长 warm-up 调高 box 权重2~4 个点低只需改训练参数增加 P2 高分辨率检测头3~5 个点中需要改网络结构CARAFE 上采样替换1~2 个点中重训练成本难例挖掘与数据重采样1~2 个点中需要人工校验引入注意力机制0.5~1.5 个点低但收益波动大以我的经验YOLOv11 和 SAHI 的组合本身就能解决无人机航拍小目标检测的大部分痛点训练阶段再配合针对小目标的损失权重和学习率“温控”基本上能达到一个不错的效果基线。之后再去做网络结构或数据层面的优化就是锦上添花而不是从零补天。如果你也在 VisDrone 或者类似航拍数据集上做检测建议先跑通 SAHI 这条链路用同一套权重对比一下切片前后的小目标 AP 差异你会很直观地感受到“分而治之”这四个字在无人机场景里的分量。