
简介基于YOLOv8n的小目标检测算法项目面向计算机视觉方向的开发者、科研人员及高年级学生旨在解决小目标检测中目标尺寸小、纹理特征弱、易被算法忽略、与背景区分度低等难题。项目将完整源码与流程教程结合系统讲解从数据集构建、标注格式理解、YAML模型配置到模型训练、推理验证与性能评估的完整链路源码涵盖数据预处理、模型配置、训练推理与评估脚本教程则提供环境安装、数据组织到结果输出的分步指引轻量化设计也使其适合嵌入式设备与移动平台部署。压缩包约336.37MB共2000个文件以1991个txt标注与数据文件为主另有8个yaml配置文件定义模型参数、1个md说明文档导读结构。已有80人学习浏览可作为算法改进实验、课程设计或行业小目标检测预研的实战参考。1. 小目标检测为什么YOLOv8n项目源码值得下一份无人机视角下的行人、遥感图像里的车辆、工厂监控里远端的违规操作——这些场景里目标往往只有十几个像素用常规YOLO模型训练mAP50可能连0.5都跑不到而同一份数据换成YOLOv8n微调结果能明显拉开差距。这份基于YOLOv8n的小目标检测项目源码正好解决的就是这个痛点它不是一个只给了权重文件的黑匣子而是带完整流程教程的工程包从数据标注格式、anchor设计、训练参数到推理脚本全覆盖。适合的人群很明确跑过YOLO但被小目标mAP卡住的从业者以及拿公开红外小目标检测数据集练手、想快速看到效果的学生。你不需要先精通目标检测原理跟着流程走就能复现但想真正调好它仍然需要理解后面这几章讲的细节。2. 为什么选YOLOv8n小目标场景下的模型选型逻辑2.1 轻量背板不是短板反而决定了下限很多人在小目标检测上一上来就选YOLOv8x或者更大规模的模型理由是“大模型特征表达能力强”。但真实工程里小目标检测的瓶颈往往不在模型容量而在数据质量、锚点匹配和特征图分辨率。YOLOv8n作为ultralytics官方最小的标准模型参数量大约3.2M输入640分辨率下计算量约8.7 GFLOPs它的优势是训练迭代快、显存占用低可以在合理时间内反复试验数据增强和anchor策略。对小目标来讲大模型容易过拟合到背景纹理小模型反而因为正则化效应更聚焦目标本身。选YOLOv8n还有一个实际好处项目包配套的流程教程、配置文件和部署脚本都是按这个模型调好的。换成其他版本虽然ultralytics代码框架通用但anchor比例、训练轮数和增强参数需要重新调。常见做法是我会先用n版跑通全流程确实验证数据没问题之后再决定要不要换大模型。2.2 模型结构里藏着的关键参数YOLOv8n的head部分采用了解耦检测头Decoupled Head分类和回归分支分离。在一个具体的YOLOv8n小目标检测项目源码里你会看到配置文件中有几个值得关注的参数imgsz训练输入尺寸小目标场景建议从640起步不要盲目增大后面细说anchorYOLOv8默认是anchor-free但它内部仍然有anchor point的概念如果数据里目标平均尺寸只有8-20像素直接跑默认配置会出现大量低置信度漏检fliplr/mosaic数据增强开关小目标数据集上mosaic对模型提升明显但烤到最后阶段建议关掉# ultralytics默认配置节选实际以项目包内的yaml为准 train: ./datasets/VisDrone/images/train val: ./datasets/VisDrone/images/val nc: 10 # 类别列表按你的数据修改 names: [pedestrian, people, bicycle, car, van, truck, tricycle, awning-tricycle, bus, motor] # 训练参数 imgsz: 640 epochs: 100 batch: 16 mosaic: 1.0这里有一个项目包内值得注意的细节它把训练脚本单独封装成了train.py而不是直接用yolo train命令行。原因在于小目标检测通常需要自定义数据加载逻辑——比如对原图做滑窗裁剪再训练命令行模式做不了这种预处理。脚本里通常会有个--crop_size参数用来控制裁剪窗口大小这部分后面在SAHI思路里会再展开。2.3 数据标注格式不是所有“Pascal VOC”都能直接喂小目标检测项目源码里数据格式往往标注为VOC或COCO但实际打开文件你会发现标注框小于8像素的小目标在不同工具里的处理和统计口径可能不一致。项目包里如果标注文件是XML需要注意width和height是指原图尺寸还是当前缩放尺寸xmin、ymin、xmax、ymax是否为绝对坐标。更隐蔽的一个问题是有些标注工具会漏标远端极小目标人眼看不到就不框这类“脏标签”会在训练时给模型错误的负样本信号。资金相关的规范性问题先放一边这里真正影响训练结果的是类别平衡。小目标场景常见类别——以红外小目标检测数据集为例——目标可能只有几个像素一两百个标注样本就能让模型记住的是目标形态而非类别语义。我一般会先用下面这段脚本统计标注框尺寸分布确定数据集里小目标占比是否值得单独处理import xml.etree.ElementTree as ET import os import numpy as np def analyze_bbox_size(xml_dir): sizes [] for xml_file in os.listdir(xml_dir): if not xml_file.endswith(.xml): continue tree ET.parse(os.path.join(xml_dir, xml_file)) root tree.getroot() for obj in root.iter(object): box obj.find(bndbox) xmin float(box.find(xmin).text) ymin float(box.find(ymin).text) xmax float(box.find(xmax).text) ymax float(box.find(ymax).text) w xmax - xmin h ymax - ymin sizes.append((w, h)) sizes np.array(sizes) for threshold in [8, 16, 32]: count np.sum((sizes[:, 0] threshold) | (sizes[:, 1] threshold)) print(f小于{threshold}像素的目标占比: {count / len(sizes):.2%}) # 用法示例 analyze_bbox_size(./annotations)这段脚本遍历所有XML标注文件逐个读取目标框的坐标并换算成宽高最后统计不同尺寸阈值下的目标占比。如果你的数据里有超过一半目标边长小于32像素那这就是标准的小目标数据集后面章节里的训练参数和数据增强方案就该按小目标场景来调而不是直接用默认值。2.4 验证指标少看mAP50如果你的训练结果里mAP50已经到0.9以上但mAP50-95只有不到0.3问题大概率出在小目标的定位精度上。mAP50对小目标宽容——宽高误差一半也能判正例mAP50-95则要求框更精确。项目包里如果贴了训练曲线图你打开看这两条线的差距是最直观的判断方式。# 训练结束后建议在验证集上额外跑一次metrics yolo detect val modelruns/train/exp/weights/best.pt dataconfig.yaml imgsz640跑验证时对比metrics/mAP50(B)和metrics/mAP50-95(B)。如果差值在0.25以上后续优化方向应该优先考虑回归损失的权重或数据增强策略而不是盲目加深网络。3. 环境安装与项目结构从拿到压缩包到跑通第一次训练3.1 环境搭建的版本坑这份项目源码基于ultralytics 8.x版本开发依赖项包括PyTorch、opencv-python和numpy。你如果直接用pip install ultralytics装最新版大概率会碰到一个问题最新版代码对某些配置项的命名做了调整导致项目包里的yaml文件报错。# 项目包内通常会带requirements.txt优先用它 cd YOLOv8n-Small-Detection pip install -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simplerequirements.txt里会锁版本注意看torch那行。如果写着torch1.8.0那基本适配CPU也能跑推理如果要训大图我习惯装CUDA 11.8配套的torch版本。Windows用户要注意opencv版本4.8之后的版本对某些视频解码格式不兼容但只跑图片训练影响不大。3.2 项目目录结构与数据准备实际你解压后看到的结构大致是project/ ├── datasets/ # 数据集目录 │ ├── images/train/ # 训练图片 │ ├── images/val/ # 验证图片 │ └── labels/train/ # YOLO格式标签 ├── runs/ # 训练输出目录 ├── train.py # 训练脚本入口 ├── detect.py # 推理脚本入口 ├── config.yaml # 数据配置 └── requirements.txt这几个文件各司其职train.py里设定了训练超参数detect.py里有置信度阈值和NMS参数config.yaml指向数据集路径。如果你自己准备了红外小目标检测数据集或其他来源的数据只需要把图片和标签按同样目录结构放进去然后改config.yaml里对应的路径即可。注意一个经常出错的细节train和val的图片不能有重叠否则训练结果虚高后面推理到新场景马上打回原形。3.3 第一次训练跑起来# 默认配置启动训练 python train.py --data config.yaml --weights yolov8n.pt --epochs 100 --batch 16 --imgsz 640这段命令的含义是载入yolov8n.pt预训练权重在config.yaml指定的数据上微调100轮批大小16输入图像缩放为640x640。--weights参数有两种选择——填yolov8n.pt是迁移学习适合你自己的数据集填yolov8n.yaml是随机初始化从头训练适合数据量极大且分布极特殊的情况。跑起来后观察loss曲线正常情况box_loss和cls_loss在前10轮有明显下降如果loss曲线震荡剧烈多半是学习率过大或数据增强过猛。3.4 显存不够怎么办小目标检测经常用大图输入以保留细节但显存有限。常见做法是降低batch size而不是降imgsz。batch size降到8学习率相应从0.01降到0.005左右。另一个偷懒方案是开启梯度累积ultralytics里的accumulate参数不需要手动调它会根据batch大小自动计算。# 显存8G左右的推荐参数 python train.py --data config.yaml --weights yolov8n.pt --epochs 100 --batch 8 --imgsz 640项目包里如果自带train.sh脚本可以直接改里面的参数后执行。我曾经用一张GTX 1660 Super 6G显存跑通这个配置单轮时间2分钟左右100轮不到4小时对日常实验来说完全够用。3.5 预训练权重怎么选YOLOv8n官方预训练权重在COCO上训练COCO里包含大量中小目标但COCO中的小目标定义是32x32像素以下与红外单帧图像里几个像素的目标仍有差异。所以预训练权重在小目标数据集上衰减更快建议加载后先冻结backbone训10轮左右再解冻全层训练。# 冻结backbone训练ultralytics中通过参数控制 python train.py --data config.yaml --weights yolov8n.pt --epochs 10 --freeze 10--freeze 10表示冻结前10层YOLOv8n的backbone大约占前10层。每批数据通过backbone时梯度不回传只更新head部分这样模型先学会在已有特征图上调整框位置防止预训练特征被小目标数据里的噪声破坏。4. 数据增强与训练调参小目标场景的核心参数组合4.1 mosaic与copy-paste的取舍YOLOv8项目中mosaic增强默认开启它将4张图拼成1张训练对小目标检测有利有弊。好处是增加了目标在图像中的分布多样性坏处是当输入分辨率是640时小目标拼接后可能进一步缩小模型更难学到其细节特征。# 训练参数调整示例 mosaic: 0.5 mixup: 0.0 copy_paste: 0.3 scale: 0.5 # 降低随机缩放范围防止小目标被缩得更小scale参数控制随机缩放上下界默认值0.9表示图像可在0.5到1.5倍之间缩放。对小目标场景我一般把scale缩到0.5上限降到1.0这样目标最小不会缩到原先的一半以下。如果你用的是红外小目标检测数据集目标本身就是几个像素的红外亮点这组参数尤其重要。4.2 锚点设计是否需要调YOLOv8是anchor-free架构但内部依然会依据数据集的框尺寸分布生成锚点。小目标数据集如果直接用默认COCO锚点矩形框宽高比可能严重偏离实际目标导致回归头需要更长训练时间才能收敛。项目源码里如果提供了auto_anchor工具可以直接在训练前运行一次from ultralytics import YOLO # 先分析数据集锚点分布 model YOLO(yolov8n.pt) model.auto_anchor(./datasets/data.yaml)这条命令会扫描训练集标注重新计算适合当前数据的锚点尺寸并把结果输出到终端。我看到有朋友在航拍数据集上运行后锚点从原来的[10,13,16,30,33,23]等改成了[4,5,8,10,15,20]这样更贴近小目标的组合mAP提升较为明显。4.3 学习率与warmup小目标数据集相比于常规数据集噪声更大梯度更不稳定。学习率直接沿用默认0.01很容易在前几个epoch就跑飞。项目包里常见的做法是把初始学习率降低到0.005warmup轮数增加到5轮。lr0: 0.005 lrf: 0.01 warmup_epochs: 5lr0是初始学习率lrf是最终学习率相对于初始值的比例warmup_epochs是预热轮数。预热期内学习率从很低的数值逐渐升到设定值这样模型在初期不会因为大梯度震荡而丢失预训练特征。我在实际对比中发现小目标数据上warmup从默认的3轮加到5轮后前20轮的loss收敛曲线平滑很多最终mAP50提高2个百分点左右。4.4 训练轮数的最终判断小目标模型训练轮数不是越多越好本文项目包的默认值是100轮。在VisDrone这类数据上100轮通常够用你可以通过观察val loss曲线判断是否提前停止。项目包里如果有early_stopping配置建议开启patience设为30轮。如果验证集mAP50在50轮时已经到最高点后面50轮基本就是在过拟合最终保留best.pt而非last.pt。# 训练结束后对比两个权重确认是否发生过拟合 python detect.py --weights runs/train/exp/weights/best.pt --source ./datasets/images/val python detect.py --weights runs/train/exp/weights/last.pt --source ./datasets/images/val用相同图片和参数分别跑一遍推理如果last.pt的结果明显比best.pt差说明后50轮确实过拟合了。这种情况下重新训练时可以设--epochs 60省时间。5. 实践中的典型问题与排查5.1 目标太小导致漏检率高现象验证集上mAP50尚可但实际视频或新图像里远距离小目标几乎全部漏检置信度普遍低于0.3原因模型学习和推理时的输入尺寸导致小目标下采样后特征丢失。YOLOv8n最大下采样倍率是32倍640输入下最后一层特征图只有20x20分辨率而一个8x8像素目标在特征图上只剩一个点左右的响应解决常用做法是在推理时提升imgsz到960或1280代价是显存占用增加和推理延迟上升但小目标检测效果提升最直接。另一个方案是引入SAHISlicing Aided Hyper Inference把原图切成多块小图分别推理再合并下面第6章会展开讲5.2 训练时loss急剧上升然后变成NAN现象训练到第10轮左右box_loss突然飙升随后变为NAN整个训练进程崩掉原因数据里有损坏的标注文件——比如标注框坐标超出图片范围、宽度或高度为0、类别标签超出手段范围。这些脏数据在mosaic增强时被放大梯度产生异常值解决训练前跑一遍数据校验脚本过滤掉非法标注。项目包里如果没有现成的可以写一个简单的检查import os def validate_labels(label_dir, img_dir): img_files set(os.listdir(img_dir)) bad_count 0 for label_file in os.listdir(label_dir): base_name label_file.replace(.txt, .jpg) if base_name not in img_files: print(f无对应图片: {label_file}) bad_count 1 continue with open(os.path.join(label_dir, label_file), r) as f: for line in f: parts line.strip().split() if len(parts) ! 5: print(f字段数异常: {label_file}) bad_count 1 break _, x, y, w, h parts if float(w) 0 or float(h) 0: print(f框尺寸非正: {label_file}) bad_count 1 break print(f发现{bad_count}个问题文件) validate_labels(./datasets/labels/train, ./datasets/images/train)这段脚本检查标签文件与图片文件的对应关系、字段数量、框宽高是否为正。跑完一遍再把train.py重新拉起来loss曲线就会恢复正常。注意YOLO格式标签的中心坐标和宽高都是归一化到[0,1]的如果标注工具导出的是像素坐标需要先换算。5.3 混淆矩阵中某个类别重复出现现象训练结束后打开的混淆矩阵里某一列或某一行是空的或者某两个类别的预测框互相重叠严重原因类别名称中包含空格或-等符号导致数据加载时类别解析异常。项目包如果自带的类别名是从中文标注工具直接导出的拼音或英文个别带空格会在YOLO格式转换时错位解决所有类别名统一改成小写英文单词不出现空格。比如原标注里有car和bus难以合并成一个类别时命名为vehicle并涉及数据重标注的至少确保data.yaml里names顺序和标签里的类别ID一一对应5.4 推理时检测框偏移半个身位现象推理结果框明明框住了目标但位置不够准确尤其在红外小目标数据集上偏移明显原因训练数据里的标注框不精确部分框偏向目标边界一侧另一个原因是回归分支的IoU损失对小目标尺寸变化过于敏感同一像素偏差在8x8目标上造成50%的IoU损失在100x100目标上只造成2%损失解决重新核对数据标注的精度。如果你用的是公开数据集红外小目标检测数据集换一个有更精细标注的版本。如果一定要在现有数据上训练可以把回归损失权重往上调方法是在train.py里修改box_loss_gain参数默认是7.5调到10后回归头会更激进地修正框位置代价是分类精度可能轻微下降6. 进阶技巧用SAHI切图推理和小目标蒸馏6.1 SAHI切图推理的引入SAHISlicing Aided Hyper Inference专门为大图中密集小目标设计核心是把大图切成有重叠的切片分别用模型推理再把所有切片结果合并回原图坐标系最后统一做NMS去重。这套流程和YOLOv8n结合是工程上提升小目标召回率最省事的手段之一。# 安装SAHI pip install sahi # 命令行推理示例 sahi predict \ --model_type yolov8 \ --model_path runs/train/exp/weights/best.pt \ --source ./datasets/images/test \ --slice_width 512 \ --slice_height 512 \ --overlap_height_ratio 0.2 \ --overlap_width_ratio 0.2命令里的slice_width和slice_height是切片尺寸overlap_height_ratio和overlap_width_ratio是重叠比例。切片越小小目标在切片中占的比例越大被漏检的概率越低但总推理次数也越多。常见做法是切片尺寸取512重叠0.2然后再根据结果调整。如果原图是1920x1080的红外大图这个配置大约产生12个切片每张图推理12次速度会慢一些但比起把整图压缩进640输入还是要好很多。6.2 切图参数怎么调切片参数没有万能设定取决于你的目标尺寸和原图分辨率。一个实用的对照方法是先取3-5张验证图分别用切片大小640、512、384跑一遍推理对比召回率。这里有个技巧判断切片是否合适——打印出合并后每个目标的置信度如果大量目标集中在0.3-0.5之间说明切片还是偏大目标在切片里依然过小应该减小切片尺寸如果大部分目标置信度在0.7以上且漏检很少说明切片尺寸合适。重叠比例影响的是目标恰好被切在切片边缘时的情况重叠过小会出现目标框被切掉一半导致检测不到重叠过大则浪费时间重复检测。6.3 知识蒸馏作为最后的提升手段切图推理只是推理端手段训练端如果想进一步提升YOLOv8n在小目标上的精度可以尝试用更大的模型做教师模型蒸馏到学生模型YOLOv8n上。这种做法不需要改YOLOv8n的代码框架只需要在训练时额外加载teacher模型对同一批数据同时推理然后把teacher的输出作为额外监督信号。# 蒸馏逻辑示意不是完整代码 import torch from ultralytics import YOLO teacher YOLO(yolov8x.pt) student YOLO(yolov8n.pt) def distillation_loss(student_out, teacher_out, alpha0.5): # 特征图对齐后计算蒸馏损失 return alpha * torch.nn.functional.mse_loss(student_out, teacher_out.detach())这段代码的意思是学生模型前向传播得到输出教师模型在同一输入上前向传播得到软标签两者算MSE损失和原本的检测损失按权重合并。alpha控制蒸馏占的比重一般在0.3到0.7之间调。你不需要深入实现整个框架项目工程实践里通常是在原有loss计算处加一个蒸馏loss项把梯度回传到学生模型。蒸馏多花的时间主要在前向推理教师模型上但学生模型推理速度不变部署后收益明显。6.4 验证蒸馏效果的锚点指标每次改完蒸馏权重或切图参数都要回到同一个验证集上跑固定评价脚本而不是靠肉眼感受。我自己的习惯是保存每次实验的验证结果到独立目录文件名带上配置说明比如sahi_512_overlap02_conf03.json这样回溯时容易看出改动影响。如果你跑的是红外小目标检测数据集额外关注mAP50-95能否从0.16级别提升到0.2以上这是两个数量级认知差异的分界线。从那以后我每次拿到新数据集都会先跑一版最简配置当baseline再考虑优化而不是一上来就调anchor或切图参数——没有baseline的对比任何调参都是玄学。希望帮到你。本文还有配套的精品资源点击获取