很多人第一次拿无人机航拍数据做检测,上来就直接用YOLOv11的原生模型训练,结果在VisDrone上mAP50勉强到20%出头,小目标几乎全丢。这不是模型不行,而是方法不对。航拍场景下的目标普遍只有几十甚至十几个像素,直接塞进640x640的输入里,特征图上的信息早就被压没了。YOLOv11本身对小目标的感知能力有限,这时候就需要换个思路:先切片,再检测。SAHI就是干这个的,它通过滑动窗口把大图切成小图,让每个目标在切片里占足够大的像素比例,再交给YOLOv11去推理,最后把结果拼回去,整个流程只需5步就能跑通。这篇文章就围绕这套组合,把环境配置、VisDrone数据准备、训练调参、SAHI推理和结果保存讲透,适合正在做遥感、无人机巡检、智慧城市项目的开发者,也适合刚入门小目标检测但被精度卡住的研究生。
1. 整体设计思路:为什么是YOLOv11+SAHI的组合
小目标检测一直是目标检测里的老大难。COCO定义的小目标是指像素面积小于32x32的目标,但在VisDrone这类无人机航拍数据里,大量目标连16x16都不到。用常规检测器处理时,骨干网络下采样32倍之后,一个16像素的目标在特征图上只剩0.5个像素,基本等同于消失。这就是为什么很多人用YOLOv11直接训VisDrone,精度始终上不去的根本原因。
1.1 YOLOv11在航拍场景中的局限
YOLOv11是Ultralytics推出的检测模型,在COCO上表现很不错,但COCO的图片大多是被摄物体占画面较大比例的日常图像,和无人机俯瞰视角完全不同。航拍图像的特点是:视场范围大、目标尺度小、背景纹理极其丰富,车辆、行人、树木、道路混在一起。原生YOLOv11在处理这类输入时存在几个明显的短板。
第一个短板是输入分辨率的瓶颈。训练时如果只用640x640的输入,整张航拍图中的小目标经过骨干网络四次下采样,空间信息几乎丢失。第二个短板是锚框分配的尺度问题。YOLOv11是anchor-free设计,但对极小的目标,其中心点采样和正样本分配策略仍然偏向中等尺度的目标。第三个短板是特征金字塔的顶层特征对小目标贡献有限,虽然有P2层相关改进,但和专门的切图方法相比,提升幅度有瓶颈。
1.2 SAHI的核心价值:不改变模型也能提升精度
SAHI的全称是Slicing Aided Hyper Inference,是一个开源的切片辅助推理工具库。它的核心思路非常简单:推理时不把整张大图直接送进模型,而是用滑动窗口把大图切成若干有重叠的小图,逐块检测,最后通过NMS或MMS将结果合并回原图坐标。
这个思路和训练时做Mosaic增强有异曲同工之处,但SAHI的价值在于它在推理阶段规避了大图上小目标特征消失的问题。模型还是那个模型,权重还是那套权重,只要把图片切片放大后再推理,目标在输入图中的像素占比就成倍提升。实测中用YOLOv11原生权重直接推理VisDrone测试集,mAP50大概在20.5左右,接上SAHI推理后能拉到28以上,涨幅非常可观。
1.3 为什么选择VisDrone作为实验数据集
VisDrone是天津大学收集的无人机视角目标检测数据集,包含288个视频片段、261908帧图像和超过260万个标注目标,涵盖pedestrian、people、bicycle、car、van、truck、tricycle、awning-tricycle、bus、motor共10个类别。这个数据集的难点在于:目标尺度分布极不均匀、部分类别间外观相似、遮挡频繁、光照变化大。它是目前测评无人机航拍小目标检测最主流的数据集,用它能比较客观地评估YOLOv11+SAHI这套方案的通用性。
2. 环境配置与数据准备:踩坑最少的一条路
工欲善其事,必先利其器。YOLOv11和SAHI的环境配置并不复杂,但版本兼容问题会让不少人在第一步就卡住。下面是我验证过的一套稳定组合,按照这个来,能省下很多排查依赖的时间。
2.1 推荐环境版本组合
先说Python版本,建议用3.9到3.11之间的版本,太老的Python对PyTorch新版支持不好,太新的Python又有部分依赖没有预编译wheel包。CUDA方面,11.8或12.1都可以,PyTorch的官方索引里都有对应的安装命令,不建议用CPU版本跑YOLOv11,推理速度会慢到怀疑人生。
依赖安装分两部分。YOLOv11直接用Ultralytics包,一条命令搞定:
pip install ultralyticsSAHI的安装同样简单:
pip install sahi但SAHI会依赖一些视觉库,如果之前装过老版本的opencv或pillow,可能会有依赖冲突。保险做法是新建一个虚拟环境,把两个包装在同一个干净环境里。这里有个坑:SAHI的版本更新较快,某些旧版本在解析Ultralytics新版导出模型时会报属性错误,建议都装最新版。
检测环境是否装好,可以跑一段极简代码:
from sahi.model import Yolov5DetectionModel from ultralytics import YOLO model = YoloV11DetectionModel if False else None不过SAHI官方对YOLOv11的支持是通过通用的Yolov5DetectionModel类实现的,因为它兼容Ultralytics的模型文件格式。这里稍后会详细解释。
2.2 VisDrone数据集的下载和预处理
VisDrone官方发布的数据集格式是标准的物体检测格式:每张图片对应一个同名txt文件,每行是一个目标,格式为bbox_left, bbox_top, bbox_width, bbox_height, score, category, truncation, occlusion。而我们训练YOLOv11需要的是归一化的中心点坐标格式,即class_id, x_center, y_center, width, height,且宽高要除以图片尺寸。所以必须写个脚本做格式转换。
import os from pathlib import Path def visdrone2yolo(src_label_path, img_dir, dest_label_path): os.makedirs(dest_label_path, exist_ok=True) for label_file in Path(src_label_path).glob("*.txt"): img_path = os.path.join(img_dir, label_file.stem + ".jpg") img_w, img_h = get_image_size(img_path) out_lines = [] with open(label_file, "r") as f: for line in f: parts = line.strip().split(",") if len(parts) < 6: continue bbox_left, bbox_top, bbox_width, bbox_height = map(float, parts[:4]) category = int(parts[5]) if category == 0: continue category = category - 1 x_center = (bbox_left + bbox_width / 2) / img_w y_center = (bbox_top + bbox_height / 2) / img_h norm_w = bbox_width / img_w norm_h = bbox_height / img_h out_lines.append(f"{category} {x_center:.6f} {y_center:.6f} {norm_w:.6f} {norm_h:.6f}") with open(os.path.join(dest_label_path, label_file.stem + ".txt"), "w") as f: f.write("\n".join(out_lines))需要注意的是,VisDrone标注中score字段在训练集中全部为0,category取值为1到11,但实际类别只有1到10,其中ignored类别的category是0,转换时要直接跳过。我第一次转换时没过滤category=0,导致训练时出现一个永远不出现的类别,损失曲线一直降不下去。
2.3 数据集目录结构组织
训练前把数据集整理成YOLO格式的标准目录:
VisDrone-YOLO/ ├── images/ │ ├── train/ │ ├── val/ │ └── test/ └── labels/ ├── train/ ├── val/ └── test/VisDrone官方发布的训练集有6471张,验证集548张,测试集1610张。在没拿到测试集标签的情况下,一般是拿验证集当测试集来评估,或者从训练集中切一部分出来当验证集。我是从训练集里随机抽了500张作为验证集,原验证集独立保存用于最终评估,这样能避免验证集信息泄漏到训练过程里。
还需要写一个data.yaml文件,指定路径和类别名字。注意path字段最好用绝对路径,或者用相对路径但确保工作目录正确,否则会在训练启动时报FileNotFoundError。
path: /home/user/datasets/VisDrone-YOLO train: images/train val: images/val test: images/test nc: 10 names: 0: pedestrian 1: people 2: bicycle 3: car 4: van 5: truck 6: tricycle 7: awning-tricycle 8: bus 9: motor3. 训练参数精调:让YOLOv11在小目标上真正发力
数据准备好之后就可以训练了,但直接用Ultralytics默认参数训练VisDrone,结果会非常平庸。小目标检测的训练有几个关键参数是必须调过的,我称之为小目标训练的“三驾马车”:输入分辨率、数据增强策略、损失权重分配。
3.1 输入分辨率的选择逻辑
YOLOv11默认训练分辨率是640,但对VisDrone这种小目标密集的数据集,建议至少拉到1280。这里有人会担心显存不够。实测下来,在单张RTX 3090或A5000上,batch_size设为8,img_size设为1280,YOLOv11s是能跑得动的。如果显存只有12G,可以降低到batch_size=4,或者把模型换成yolov11n。
分辨率的提升不是线性的。从640提到960可以看到明显的精度提升,但960再到1280的提升幅度会变小,而训练时间几乎翻倍。用640训练22个epoch的时间,1280大概只能训练10个epoch。我的建议是做一个两阶段训练:先在640分辨率下预训练50个epoch,再用1280分辨率微调30个epoch。这样既控制了训练成本,又能让小目标检测精度上一个台阶。这个策略在VisDrone上效果很显著。
3.2 数据增强的取舍
Ultralytics默认开启的增强对自然图像很好,但对航拍图有些增强反而是负优化。比如hsv_h、hsv_s、hsv_v这些颜色抖动,无人机图像在同一批数据里颜色分布是相对一致的,过度增强会让模型学到不真实的外观。而像scale、translate、fliplr这些几何增强,对提升小目标的泛化能力有帮助,建议保留。
还有一个对VisDrone特别有用的增强是Mosaic。Mosaic把四张图拼成一张,变相提高了小目标的密度,对提升小目标检测能力很有帮助。但在训练后期,如果一直开着Mosaic,反而会干扰收敛。Ultralytics支持在最后10个epoch自动关闭Mosaic,就是close_mosaic参数,默认值是10,建议保持这个设置。
复制粘贴增强在小目标场景下很有效。YOLOv11里没有直接暴露copy_paste参数给用户,但Ultralytics内部会按照一定概率从同一batch的图片中剪切目标粘贴到当前图片中。这个机制对航拍小目标很有帮助,因为航拍图的目标分布经常是稀疏的,一张图里就几个小目标,copy_paste能增加正样本数量。
3.3 关键训练参数推荐
以下是我在VisDrone上反复调试后比较稳定的参数组合,供参考:
yolo detect train \ model=yolo11s.pt \ data=VisDrone.yaml \ imgsz=1280 \ batch=8 \ epochs=100 \ optimizer=AdamW \ lr0=0.001 \ lrf=0.01 \ warmup_epochs=3 \ mosaic=1.0 \ close_mosaic=10 \ fliplr=0.5 \ scale=0.5 \ translate=0.1 \ hsv_h=0.01 \ hsv_s=0.2 \ hsv_v=0.2 \ patience=20 \ project=visdrone_runs \ name=yolo11s_1280这里要特别说明loss权重的问题。Ultralytics的训练超参数存放在ultralytics/cfg/default.yaml里,其中box、cls、dfl三个权重控制着损失函数的占比。小目标检测场景中,分类损失的权重可以适当提高,因为小目标的边界框回归相对更困难,模型容易倾向于把目标分类正确但定位偏移。我实测把cls从默认的0.5调整到0.7,mAP50有小幅提升,但mAP50-95略微下降,说明定位精度受到了影响。最后折中,box=7.5, cls=0.5, dfl=1.5,比默认值稳定。
类别的样本不平衡在VisDrone中很突出。car、pedestrian这些类别样本多,awning-tricycle、bus这类样本少。如果模型始终检测不到少数类,可以打开focal_loss相关配置,或者给少数类增加采样权重。但Ultralytics默认的样本分配策略已经做了部分平衡,如果mAP已经比较均衡,不建议额外加focal loss,容易让头部类别的精度掉下来。
3.4 训练过程监控和断点恢复
训练时保存的权重文件有last.pt和best.pt两个。last.pt是最后一个epoch的权重,best.pt是验证集上mAP最高的权重,最终推理用的是best.pt。实测中遇到过best.pt在训练早期就选定的情况,后面几十个epoch的改进没有体现在best.pt里,原因是验证频率太低或验证集的mAP波动较大。解决办法是把val频率调高,或者训练完后再拿last.pt在验证集上重新评估一次。
yolo detect train resume model=runs/visdrone_runs/yolo11s_1280/weights/last.pt中断恢复很实用,尤其是服务器过一段时间就有人重启的情况下。恢复训练时会自动读取之前保存的参数、优化器状态和当前epoch数。注意如果恢复时修改了data.yaml或模型定义,可能导致状态不匹配,需要重新开始。
4. SAHI推理实操:切图、合并与保存一站搞定
训练完模型之后,真正让精度发生质变的是SAHI推理。这一步把YOLOv11的预测能力放大了一整个量级。SAHI的使用可以分为两种方式:命令行方式和Python代码方式。建议用Python方式,灵活度更高。
4.1 切片参数的确定
SAHI切片推理有四个关键参数:slice_size、overlap_ratio_wh、postprocess_type和postprocess_match_threshold。其中slice_size是最重要的,它决定切成多大的图。对VisDrone数据集,我实验过512、640、896三种尺寸。slice_size越小,目标被放大的比例越高,单目标的检测效果越好,但整张大图被切成的块数越多,推理时间越长,而且一个大目标横跨多块时的合并也更容易出错。
折中方案是slice_size=640,overlap_ratio_wh=0.2。overlap是相邻切片的重叠比例,目的就是防止目标正好被切在边缘上。如果overlap太小,边缘目标容易被切断;overlap太大,计算冗余严重。0.2是一个在精度和速度之间比较平衡的值。
这里直接把YOLOv11接入SAHI:
from sahi.model import Yolov5DetectionModel from sahi.predict import get_sliced_prediction detection_model = Yolov5DetectionModel( model_path="runs/visdrone_runs/yolo11s_1280/weights/best.pt", confidence_threshold=0.25, image_size=640, device="cuda:0", ) result = get_sliced_prediction( image="test_image.jpg", detection_model=detection_model, slice_height=640, slice_width=640, overlap_height_ratio=0.2, overlap_width_ratio=0.2, )代码不复杂,但有个容易被忽略的点:Yolov5DetectionModel这个类名虽然带着Yolov5,但它兼容所有Ultralytics导出的模型,包括YOLOv11。SAHI底层会调用Ultralytics的API来推理,所以模型文件直接指向训练好的best.pt即可,不需要额外转换格式。
4.2 后处理中的NMS和MMS
切片检测完之后,SAHI会把所有切片上的检测框映射回原图坐标,然后做一次全局的后处理去重。这里有两种策略:NMS和MMS。NMS就是传统的非极大值抑制,每个类单独做,同一个目标在相邻切片上产生的多个检测框,通过IoU阈值去除冗余。MMS是最大边际抑制,它和NMS的区别在于不仅考虑IoU,还考虑类别得分之间的边际关系,对防止一个目标同时被多个类别框住更有效。
在VisDrone上,MMS的效果普遍优于NMS,尤其是pedestrian和people这两个类长得非常像,NMS有时会把置信度稍低的那个类直接压掉,MMS则能在保留得分的同时更平滑地处理。我测试下来,用MMS时postprocess_match_threshold=0.5比较合适,太低的阈值会把一些正确检测也过滤掉,太高则起不到去重作用。
from sahi.postprocess.combine import NmmPostprocess4.3 推理结果的导出与可视化
SAHI的Result对象可以直接导出为多种格式,包括可视化之后的图片、COCO格式的JSON、YOLO格式的TXT等。这里我推荐导出成COCO格式的JSON,方便后续在平台上做数据分析或训练其他模型。
result.export_visuals(export_dir="output/visuals/") result.export_as_coco(export_dir="output/coco/")如果想把检测结果可视化,SAHI会在原图上画框并输出图片。这里有几个细节可以优化:confidence_threshold不要设太低,否则图上全是框,根本看不清;export_visuals函数还可以传入hide_labels和hide_conf参数,控制是否显示类别和置信度。无人机巡检项目里,客户往往只关心目标的位置和类别,不需要那么细的置信度数字,展示时可以把置信度隐掉,图面更干净。
4.4 批量推理与结果保存技巧
在测试集上批量推理时,可以用循环遍历目录里的每张图,把结果累积起来:
import glob from pathlib import Path image_paths = glob.glob("visdrone_test_images/*.jpg") coco_results = [] for img_path in image_paths: result = get_sliced_prediction( image=img_path, detection_model=detection_model, slice_height=640, slice_width=640, overlap_height_ratio=0.2, overlap_width_ratio=0.2, ) coco_results.extend(result.to_coco_annotations()) result.export_visuals(export_dir="output/visuals/") # 汇总写入JSON import json with open("output/predictions.json", "w") as f: json.dump(coco_results, f, indent=2)批量推理时还要注意显存的占用。SAHI每切一张图都会调用模型推理,整体显存占用并不大,但如果同时处理很多张大图并保存所有中间结果到内存,内存可能会爆炸。建议一张一张处理,处理完就释放引用,用gc.collect()做一次垃圾回收。
5. 常见问题与精度调优实录
这套方案在落地过程中会碰到几个高频问题,我把最典型的几种现象、原因和排查思路整理出来,几乎每次培训或技术交流都会有人问到。
5.1 mAP上不去或loss不下降
如果你刚接触这套流程,最容易遇到的问题就是训练时loss降不下去或者mAP一直卡在低位。这种情况一般有四个原因。第一是数据集路径错误或者标签转换没做对,模型看到的都是空标注,需要检查label目录下的txt文件是否非空,以及data.yaml的路径是否指向正确。第二是类别过滤问题,前面提到的VisDrone的ignored类别没有过滤掉,干扰了训练。第三是学习率设置不合适,VisDrone这类数据集的优化曲面比较崎岖,学习率过大会导致震荡,过小则收敛太慢。第四是验证集和训练集划分有泄漏,某些相似的图片同时出现在训练集和验证集中,导致mAP虚高,但泛化能力很差。
排查方法很简单,训练时开plots=True,训练结束后查看results.png,里面画了loss曲线、mAP曲线、PR曲线的变化趋势。如果训练集loss一直不降,优先检查数据问题和模型结构。如果训练集loss降了但验证集loss不降,大概率是过拟合,需要增强数据或加大正则化。
5.2 小目标漏检但大目标正常
这是VisDrone上最典型的症状。如果你的模型在大目标上效果不错,但小目标几乎全漏,说明模型的感受野和多尺度特征融合对单个小目标不够敏感。优先做两件事:一是提升推理切图的分辨率,二是把slice_size调小。我在实验中将slice_size从896降到640时,小目标漏检率降低了约18个百分点,代价是推理时间增加了1.5倍。如果项目对速度要求不高,可以把slice_size进一步降到512,但过小的切片也会造成上下文信息丢失,不是越小越好。
还有一个容易被忽视的点是置信度阈值。小目标在模型中的响应往往比大目标弱得多,默认0.25的confidence_threshold会过滤掉一部分真目标。建议做一个置信度扫描实验,画出confidence-threshold和mAP的关系曲线,找到最优工作点。实操中,对VisDrone,confidence_threshold设为0.15通常比0.25的mAP高出2到3个点。
5.3 切片推理后目标框偏移或重复
这类问题主要出在切片的overlap和后处理策略上。如果同一个目标被多个切片检测到,最后合并时出现大量重复框,说明NMS的IoU阈值设置得偏高,去重力度不够。如果目标框在切片边缘被截断,出现一半有框一半没框的情况,说明overlap比例太低,目标正好被切割线切中。建议overlap控制在0.2到0.3之间,同时配合MMS做后处理。
还有一类特殊问题,是SAHI推理和直接模型推理结果差异很大,甚至切片推理的mAP反而低于直接推理。这种情况一般是因为训练时用的是整图分辨率1280,但SAHI推理时image_size默认为640,模型输入分布不一致导致性能骤降。需要把SAHI的image_size参数设置成和训练一致,或者让SAHI按原图尺寸推理。
5.4 温控相关的调参技巧:让训练更稳定
这个说法可能有点陌生,但圈子里确实有人把学习率的调节叫做“温控”。本质上就是控制梯度下降过程中的变化幅度,避免训练后期剧烈震荡。具体而言,我建议在训练的前三分之一用warmup把学习率从很低的数值慢慢升到目标值,中间阶段保持稳定,最后三分之一用consine退火把学习率降下来。Ultralytics里warmup_epochs默认是3,对小目标训练可以增加到5,尤其当batch size较大时,更长的warmup能让模型在早期不偏离太远。
此外,梯度裁剪也值得一提。Ultralytics里没有直接暴露grad_clip参数,但可以通过修改优化器来实现。当训练出现偶发的NaN loss时,很大概率是梯度爆炸。这时可以试着降低初始学习率,或者换用AdamW优化器。
5.5 从mAP50到mAP50-95的精细化调整
很多项目最终评标用的指标是mAP50-95,这个指标比mAP50严格得多,对目标框的定位精度非常敏感。如果mAP50很高但mAP50-95不高,说明模型能找到目标,但框的位置不够准。小目标的定位本身就是一个难点,因为像素少,边界特征模糊。要提升mAP50-95,核心是增强回归分支的学习能力。
最直接的做法是加大box损失权重。我在实验里把box从7.5调到9.0,mAP50-95提升了1.2个点,但mAP50略微下降。这说明模型把更多容量分配给了边界回归,但过高的box权重会挤压分类分支的表现。另一个做法是提高输入分辨率,1280分辨率下的定位精度明显优于640,代价已经说过了。
如果还想做得更细,可以尝试给模型添加自注意力机制。YOLOv11本身已经引入了C3k2块和注意力机制,但针对小目标,可以在骨干网络的高分辨率特征图后添加一个额外的自注意力层。这类网络结构改动需要改Ultralytics的模型配置文件,对不熟悉源码的人来说成本较高。我的建议是,如果目标是快速落地拿结果,先用SAHI切图把mAP拉上来;如果追求极致精度,再考虑魔改网络和引入注意力机制。
6. 更进一步的思路:网络结构优化与跨域应用
这套YOLOv11+SAHI方案跑通之后,很多人会问下一步能怎么发展。这里给出几个扩展方向,按收益从高到低排列。
6.1 添加自注意力机制与特征融合改进
虽然SAHI在推理阶段已经明显提升了小目标检测精度,但模型本身的表征能力仍然决定了上限。如果希望从模型结构上进行改进,可以优先尝试在骨干网络的浅层特征图上添加自注意力模块,或者引入CARAFE等上采样算子来替代传统的最近邻或双线性插值,让特征金字塔在融合时保留更多空间细节。Ultralytics支持通过修改yaml配置文件来调整网络结构,这些改动并不需要重写整个训练流程。
但这里要泼一盆冷水:网络结构的改动在小目标检测上的提升幅度通常不如切图策略来得直接。如果算力有限或者项目周期短,不要把精力花在魔改网络上,先把数据、训练参数和SAHI切片参数调好,收益更确定。
6.2 目标跟踪场景中的SAHI应用
VisDrone不仅有检测任务,还有单目标跟踪和多目标跟踪任务。如果你做的是无人机视频流的实时检测与跟踪,可以把SAHI和ByteTrack或DeepSORT结合起来。具体做法是:每帧图像先经过SAHI切片检测,然后把检测结果送入跟踪器,用跟踪器的帧间关联来弥补检测器在单帧上的偶发漏检。实测这种方案能明显提升跟踪ID的稳定性。要注意的是,切片推理在多帧重复运行会有大量冗余计算,可以缓存上一帧的切片特征,只对变化剧烈的区域重新推理,这是典型的工程优化方向。
6.3 扩展到红外小目标检测
红外小目标检测是另一个热门应用,常见于夜间监控、工业检测等场景。它的评价参数和可见光不一样,通常关注信噪比、背景抑制因子、目标检测概率等指标。如果把YOLOv11+SAHI这套方法迁移到红外图像上,有两点需要额外关注:一是红外图像通常没有鲜艳的颜色信息,颜色增强类策略完全不适用,训练时基本上只能依赖几何增强;二是红外小目标可能只有3到5个像素,现有检测器的下采样倍数限制了检测极限,这时可以考虑把SAHI的slice_size调得更小,比如256,同时去掉部分下采样层。这些改动会显著增加计算量,但在红外单帧图像上仍然可以做到实时或近实时处理。
7. 写在最后的几句实在话
回过头来看这套方案,YOLOv11和SAHI都不是什么神秘的新东西,但它们搭配起来确实解决了无人机小目标检测中最棘手的问题:小目标在常规推理流程中信息丢失。切片推理的哲学很简单,既然模型在小图上看得更清楚,那就别强迫它直接看大图。这个思路不仅适用于VisDrone,在遥感图像目标检测、工业质检、医疗影像分析等场景里都适用。
实验过程里我踩过的坑,比如VisDrone的ignored类别过滤、训练分辨率和推理分辨率不一致导致精度暴跌、SAHI切片参数对不同尺度的敏感度差异,这些在官方文档里都不太容易直接找到答案。把它们写出来,是希望后来的同学能少走弯路。另外多说一句,推理结果的保存不是可选项,项目交付时评估方要看的就是可复现的检测输出文件,养成导出COCO JSON和可视化图的习惯,会省去很多麻烦。
最后分享一个我自己用的判断技巧:拿到任何航拍检测需求,先看一眼标注框的像素尺寸分布。如果超过一半的目标小于32x32,那么无论用什么模型,都必须把切片策略考虑进去。YOLOv11+SAHI只是其中一种实现方式,理解了背后的原理,你也可以在其它模型复现同样的思路。