☰
红外图像鸟类检测:YOLO数据集与训练调参全指南
2026/9/28 17:13:46 网站建设 项目流程

简介:这是面向yolo系列算法目标检测任务的红外图像鸟类数据集,共含2679张带标签图像,适合需要训练、验证与测试鸟类检测模型的开发者。压缩包总计2000个文件,大小约49.14MB,以xml标注文件为主,同时提供yolo格式txt与voc格式xml两种标签,分别保存在独立文件夹中,文件名后缀还包含部分类别名称,便于快速识别与筛选。标签内容采用归一化的class、中心点坐标与宽高表示,数值范围0~1,可直接被yolov5、yolov7、yolov8、yolov9、yolov10、yolo11等主流框架读取;数据已按训练/验证/测试划分,并附带data.yaml配置文件,省去自行整理目录与编写配置的环节。目前已有172人学习下载,资源结构清晰,拿到后即可开始训练或评估模型,减少数据预处理时间,尤其适合想快速上手红外场景鸟情检测的入门与进阶用户。

1. 红外图像鸟类数据集:YOLO 系列目标检测的第一个分水岭

用YOLO算法做红外图像鸟类检测的人,第一道坎往往不是网络结构,而是数据集。可见光鸟图模型在红外相机上会表现得很怪异——鸟在热成像里是灰白色小块,没有羽毛颜色和纹理,直接拿预训练权重推理,漏检多到让人怀疑人生。这份2679张带标签的红外鸟类数据集,已经完成训练/验证/测试划分,内置data.yaml,同时提供yolo txt与voc xml两种标签格式,yolov5到yolo11都能直接开训。适合做机场驱鸟、生态观测、变电站巡检,以及对比yolo系列算法精度的开发者。换数据比换网络更先见效,这是目标检测领域最实用的规则。

2. 数据集结构与标签格式:从 VOC 到 YOLO 的两种标注体系

2.1 YOLO txt 与 VOC xml 在表示同一个框时的差异

先说清楚一份“带标签”的红外鸟类数据集,手里到底有什么。图像文件和标签文件名一一对应,图片是红外灰度图,标签分成两个文件夹:一个存yolo格式txt,一个存voc格式xml。yolo格式的每一行是:

0 0.4721 0.5134 0.2890 0.3871

从左到右分别是类别索引、框中心x比例、框中心y比例、框宽比例、框高比例。这些比例的值都基于图像宽高做了归一化,范围0到1。比如x_center=0.4721,表示目标中心在图像从左往右47.21%的位置;width=0.2890,表示框宽占整张图像宽度的28.90%。这种设计让YOLO在缩放输入图时不用改标签。

xml格式则完全不同,它记录的是绝对像素坐标。打开任意一个xml,常见结构是:

<object> <name>bird</name> <bndbox> <xmin>120</xmin> <ymin>80</ymin> <xmax>180</xmax> <ymax>130</ymax> </bndbox> </object>

xmin/ymin是框左上角,xmax/ymax是右下角,都是像素值。两种格式保存的信息其实是同一个框,只是坐标系不同。yolo训练器认txt,coco格式、标注平台和很多扒图脚本认xml;这份资源两边都给,省了转换时间,但代价是训练前必须先确认两边数据是否同步。

我一般拿到手第一件事不是立刻训练,而是做一次坐标互转验证,下面这段脚本能把xml转成yolo txt格式:

import xml.etree.ElementTree as ET def xml_to_yolo(xml_path, img_w, img_h): tree = ET.parse(xml_path) root = tree.getroot() boxes = [] for obj in root.iter('object'): cls_name = obj.find('name').text bbox = obj.find('bndbox') xmin = float(bbox.find('xmin').text) ymin = float(bbox.find('ymin').text) xmax = float(bbox.find('xmax').text) ymax = float(bbox.find('ymax').text) x_center = (xmin + xmax) / 2.0 / img_w y_center = (ymin + ymax) / 2.0 / img_h width = (xmax - xmin) / img_w height = (ymax - ymin) / img_h boxes.append((cls_name, x_center, y_center, width, height)) return boxes

重点在于,x_center用的不是(xmin+xmax)/2之后再除以宽,而是先算出像素中心,再归一到0到1;顺序反了就会得到完全一样的错误结果。width和height同样要先做像素差再归一。类别索引没有写在这里,需要后续根据data.yaml里的names顺序,把cls_name映射成整数。

反过来,要把yolo txt转回xml用于可视化调试,公式是:

xmin = int((x_center - width / 2) * img_w) ymin = int((y_center - height / 2) * img_h) xmax = int((x_center + width / 2) * img_w) ymax = int((y_center + height / 2) * img_h)

这个转换在检查“图里框得准不准”时特别有用。我习惯在训练前随机挑出20张图,用txt的坐标画框,再把xml的坐标也画上去,两边重叠度高才说明标签没出问题。

2.2 用脚本核对图像、txt、xml 是否一一对应

标签错位是目标检测数据集的常见坑。图片有1000张,txt有999个,训练时模型不会直接报错,只是少学一张图的信息;更麻烦的是txt内容和图片不对应,模型学到的就是错误监督信息。解压这份数据集后,推荐先跑一遍同名检查:

from pathlib import Path img_dir = Path('images/train') txt_dir = Path('labels/train') xml_dir = Path('annotations/train') imgs = {p.stem: p for p in img_dir.glob('*.*') if p.suffix.lower() in ['.jpg', '.png']} txts = {p.stem: p for p in txt_dir.glob('*.txt')} xmls = {p.stem: p for p in xml_dir.glob('*.xml')} for name in sorted(imgs): if name not in txts: print(f'[{name}] 缺 txt') if name not in xmls: print(f'[{name}] 缺 xml') print(f'图像 {len(imgs)} 张,txt {len(txts)} 个,xml {len(xmls)} 个')

这里的glob是按后缀找文件,jpg、png都算图像。set去重后以stem为key,如果一张图同时存在jpg和png,后者会覆盖前者,所以数据集里不要混两种后缀。脚本把缺失情况打印出来,训练前解决掉。

除了缺文件,还要看空标签。txt文件大小是0,说明这张红外图像里没有标注任何目标。对训练来说,背景图不是坏事,它能让模型降低误检;但验证时mAP统计会忽略无目标的图,这不是bug,是正常现象。我见过有人把空标签当成“漏标”直接删掉,结果训练集丢了很多背景图,误检立刻上升。所以空标签文件先留着。

xml里除了bndbox,也可能有其他节点,比如truncated、difficult、pose,这是voc格式历史遗留。有些版本转换脚本只解析object里的bndbox,遇到没有bndbox的xml会抛异常;本数据集的xml按标准voc生成,所以直接按上面脚本读没问题。

2.3 类别索引为什么必须和 data.yaml 对得上

yolo txt中的数字class是类别索引,从0开始,不是类别名。如果data.yaml里写的names是['bird', 'drone'],那么txt中0代表bird,1代表drone。这份红外鸟类数据集,文件名末尾带了一部分类别标识,具体含义要以解压后的data.yaml为准,不要凭文件名猜类别顺序。

我建议先统计一遍训练集txt里出现过的类别索引:

from pathlib import Path txt_dir = Path('labels/train') class_set = set() for p in txt_dir.glob('*.txt'): if p.stat().st_size == 0: continue for line in p.read_text().strip().splitlines(): class_id = int(line.split()[0]) class_set.add(class_id) print('训练集中出现的类别索引:', sorted(class_set))

如果你的data.yaml里nc=1,但class_set里出现了1,那意味着某个txt写着类别索引1,而names里根本没有该项,训练器大概率在计算loss时报错,或者把它忽略掉。索引之间不要留空洞,比如用了0和2但不用1,不少模型会默认按连续索引做类别映射,空洞会产生歧义。检查index是训练前的必修课。

3. 用 data.yaml 把数据集接到 YOLOv8/v11:配置与训练命令

3.1 data.yaml 里必须写对的四个字段

为什么训练前要先看data.yaml?因为YOLO系列从v5到v11,官方给的统一入口都是config文件。数据集的训练、验证、测试路径全在里面,类别数和类别名也在里面,写错任何一个都不会有直观报错,只会给你一个训练完什么都不输出的模型。最常见的data.yaml结构如下:

train: dataset/images/train val: dataset/images/val test: dataset/images/test nc: 1 names: ['bird']

train、val、test分别指向图像文件夹,注意这里写的是图像目录,不是标签目录。YOLO会自己去相同前缀下的labels文件夹找同名txt标签,也就是说如果你把图像放在dataset/images/train,txt要放在dataset/labels/train。这份资源里yolo格式txt单独存了一个文件夹,使用时要把它的目录结构理顺成上面的约定,或者在yaml里直接用绝对路径指定。

nc是类别数量,names是类别名列表,两者必须严格一致。names的长度等于nc,索引位置对应标签中的类别数字。很多人在只改nc、忘了改names,或者names里包含中文的情况下训练,训练都能开始,但日志里的类别名全是乱码,不影响精度也不影响落地,排查问题时会让人分心。尽量全用英文。

还有一点容易被忽略:yaml里的路径在训练时会被当作基准目录。常见做法是data.yaml放在数据集根目录,train写成相对路径images/train,train、val、test三个字段指向图像目录。不要在路径里写中文目录名,ultralytics在Linux下对中文路径支持尚可,换到Windows就容易翻车。

3.2 从 YOLOv5 到 yolo11 的训练命令差异

这份红外鸟类数据集官方说适用yolov5、v7、v8、v9、v10、yolo11,但不同版本训练命令差别不小。如果你在跑yolov5,仓库clone下来后通常是:

python train.py --data data.yaml --weights yolov5s.pt --img 640 --epochs 100 --batch 16 --device 0

yolov5的train.py接收--img而不是--imgsz,--weights后面跟预训练权重。yolov7是train.py --data data.yaml --weights yolov7-tiny.pt --img 640 --batch 16 --epochs 100 --device 0,基本一致。到了yolov8之后的版本,统一收敛成了命令行工具:

yolo train data=data.yaml model=yolov8n.pt epochs=100 imgsz=640 batch=16 device=0

yolo11也沿用这个入口,只是model换成了yolo11n.pt。第一次用新版不要直接照抄旧命令,先看本仓库README,差一个参数名就会走默认值。

参数层面,epochs我建议先设100,红外鸟类不是复杂识别任务,50轮基本能看出趋势,100轮能稳定。batch越小,BN层统计越不稳定,如果只有一块消费级显卡,batch设为8到16比较合适。imgsz默认640,但对红外场景里的远距离小鸟,鸟在整张图中可能只有十几个像素,640输入图会增大下采样损失。

如果数据集里目标整体偏小,我习惯把imgsz提高到1280,同时把batch降到4到8,显存不够就开AMP半精度。不要盲目把imgsz拉高,目标极小但图很大的时候,1280能缓解漏检,但训练速度会掉一半。

3.3 训练前先用 val 命令试跑一版

拿到数据集立刻挂千轮训练是大忌。我会先跑一个最小的val,确认数据流是通的:

yolo val data=data.yaml model=yolov8n.pt

如果你用的是yolov5仓库,则执行:

python val.py --data data.yaml --weights yolov5s.pt

这时模型用coco预训练权重,在红外鸟类验证集上mAP可能很低,可能只有0.05到0.2,这完全正常。重要的是没有报错、没有提示labels empty、没有出现路径not found。一旦val跑通,说明YOLO找到了图像和txt标签,data.yaml配置是对的,再开始正式训练。

这一步值得养成习惯,它能过滤掉至少一半的翻车。我自己有一次把txt标签文件夹放在和images平行的目录,小版本之间对目录名要求不一样,yolov5要求labels文件夹必须和images同级,名字必须是labels;yolov8则容忍不一样。不同版本行为不一致,所以用val脚本验证永远比查文档快。

对了,如果你的数据集下已经有runs目录或上次训练的输出,先看一眼data.yaml里的路径,确保没有指向旧路径。路径写错最诡异的表现是训练loss下降正常,但验证时mAP全是零——因为val图片实际是空背景图,目标全在另一个文件夹。

3.4 数据集已经划分好:不要重复切分

这份资源在发布时已经划分好训练、验证、测试集,这是它比纯标签包省事的地方。但我发现很多人下载后,会因为不放心再用自己的脚本重新切一次,结果把图片和标签对应关系切乱了。尤其是YOLO依赖同名机制,一个random_split脚本没有同时移动xml/txt,就会导致验证集里图像还在、标签却只剩train的txt。

下载后先确认三个目录的图片数量,更清晰的做法是逐目录统计:

from pathlib import Path for split in ['train', 'val', 'test']: img_dir = Path('dataset/images') / split if not img_dir.exists(): print(split, '目录不存在') continue img_count = len(list(img_dir.glob('*.*'))) print(f'{split}: {img_count} 张')

总数量应该等于2679,如果三个数字加起来对不上,先检查是不是有子目录或者图片后缀未被包含。比如有的图片是.bmp,glob用*.*已经算进去了。如果count对得上,就不要动目录结构,直接开始训练。

这句话很重要:别多手。数据划分后的比例不一定非要遵循7:2:1,只要train和val来自同一分布,val数量足够算mAP就行。

4. 红外图像特性与训练参数调整:盲元、两点校正与数据增强

4.1 红外图像和可见光图像在 YOLO 眼里的差异

红外图像本质上是热辐射分布图,保存成图片后通常是单通道灰度。YOLO模型输入是RGB三通道,框架会自动把单通道复制成三份,这个过程不丢信息,但也不增加任何颜色特征。这意味着常规的hsv_h、hsv_s这些颜色增强对这种数据几乎无意义,因为三个通道本来就是同一个灰度值,色调和饱和度改变会产生伪彩色噪声,反而干扰训练。

鸟类在红外图像里的表现也不同于可见光。白天照片里的鸟有轮廓、羽毛纹理、喙和腿;热成像里的鸟只是比环境背景稍微亮一点的团块,站立不动时甚至和岩石、树枝的温度接近,对比度很低。所以迁移学习阶段,直接用imagenet或者coco预训练权重,在红外鸟图上掉点极快。

处理思路有两个:一个是把红外图先做预处理,增强对比度再喂给模型;另一个是让模型在训练阶段充分接触这种对比度不足的图像性质,用数据增强模拟。两者可以一起做,但重点在后者,因为推理阶段你不会总有机会手动调图。

常见做法是在红外图送入网络前做一次CLAHE(对比度受限自适应直方图均衡),在OpenCV里一行代码就能完成。但这份数据集本身是原始红外图,训练时如果做了CLAHE,那验证和预测时也要做同样的预处理,否则训练与推理分布不一致,模型会失灵。这是一个容易踩的坑。

4.2 盲元检测:先把坏点从训练集里排除掉

红外探测器因为制造工艺和使用损耗,难免出现盲元——永远亮或永远暗的死像素。它在图像上表现为固定白点或黑点,位置不变。对普通可见光检测影响不大,但对红外小目标检测是致命的:一只鸟在距离相机50米外可能只占几个像素,如果盲元正好压住它,模型看到的就是一个永远不变的高亮或暗点,很容易学成“这个位置有一个目标”的假规律。

我拿到这份数据集后,第一步不是修改网络,而是先统计盲元。用多张图像同一位置的灰度均值和方差就能筛出来,盲元因为灰度固定,方差接近于0,均值又偏向极亮或极暗。下面这段脚本在训练集上跑一遍:

import numpy as np from PIL import Image from pathlib import Path img_paths = sorted(Path('images/train').glob('*.png'))[:50] if not img_paths: img_paths = sorted(Path('images/train').glob('*.jpg'))[:50] imgs = [] for p in img_paths: gray = np.array(Image.open(p).convert('L'), dtype=np.float32) imgs.append(gray) stack = np.stack(imgs) mean_map = stack.mean(axis=0) var_map = stack.var(axis=0) bad_pixel = (mean_map > 245) | (mean_map < 10) dead_mask = bad_pixel & (var_map < 1.0) print('首帧尺寸:', imgs[0].shape, '盲元数量:', int(dead_mask.sum()))

注意这里取了前50张图,假设它们是在相近条件下拍的。如果数据集包含不同相机、不同时间段的图像,混在一起统计会把正常像素也误判为盲元。更严谨的做法是按图分组,或者直接用单帧的局部邻域对比检测,但这需要了解探测器型号。

盲元数量如果小于总像素的千分之一,可以直接不管。如果几千像素甚至更多,建议在训练增强里加一个盲元遮挡mask,随机把盲元区域压黑。实现不复杂,但能有效防止模型把固定坏点当特征。

4.3 两点校正:把非均匀性压到最低

红外图像除了盲元,还有非均匀性。探测器阵列每个像素对温度的响应不一致,导致即使是均匀温度的黑体,成像也会出现竖条纹或网状噪声。这个噪声在每帧都存在,但位置和灰度模式相对固定。两点校正专门对付它:采集低温黑体和高温黑体两幅图像,分别记录每个像素的暗场响应和亮场响应,然后用线性映射把每个像素拉齐。

两点校正的公式可以写成:

def two_point_correction(raw, dark, bright, out_range=(0, 255)): gain = (out_range[1] - out_range[0]) / (bright - dark + 1e-6) corrected = (raw - dark) * gain + out_range[0] return np.clip(corrected, out_range[0], out_range[1]).astype(np.uint8)

dark和bright是对应相同拍摄参数下低温、高温黑体的平均帧。具体到本数据集,如果图片上没有明显的条纹噪声,说明数据已经做过校正,可以直接用。如果肉眼能看到固定的明暗条纹,训练前先做一次两点校正再统一格式,否则模型会额外学一个“条纹过滤器”,浪费参数量。

红外图像领域经常同时出现盲元检测和两点校正,这两个处理合在一起,基本是红外目标检测与可见光检测的预处理分水岭。做得好,模型泛化能力提升明显;不做,换成另一台红外相机几乎必然失效。

4.4 训练参数怎么调:从 imgsz 到增强超参数

用yolov8训练时,我通常会在模型yaml里调整增强参数。不同版本对增强的开关方式略有差异,yolov8通过模型yaml或者命令行参数控制,yolo11沿用了类似机制。下面是适合红外灰图的一组基础配置,按实际效果再动:

hsv_h: 0.0 hsv_s: 0.0 hsv_v: 0.2 fliplr: 0.5 mosaic: 1.0 mixup: 0.0

hsv_h和hsv_s都设0,因为红外图没有真实颜色信息,改色调只会造出假彩色,模型学不到有用特征还可能过拟合到噪声。hsv_v保留0.2,模拟红外相机自动增益导致的亮度波动,这个对红外图是真实存在的。fliplr水平翻转对鸟类检测有效,鸟的位置左右对称不影响识别。mosaic保持1.0,把四张图拼在一起训练,相当于做了小目标上下文增强。mixup建议关掉,红外目标太小,混图会把目标边界糊没。

imgsz前面提过,如果分辨率不够,先看图像本身大小。图像宽高如果是1280x720,imgsz=640时鸟确实可能只有几个像素。只用下采样后的特征图,小目标信息基本丢了。这也是为什么很多红外检测项目会把imgsz设成1280或1600。显存不够就先减少batch,不要用更小分辨率换速度,红外小目标检测场景里召回率比速度更敏感。

epochs方面,红外鸟类数据集的标注噪声比可见光数据略高,目标边界模糊,训练太久容易过拟合到个别样本。我一般在100轮基础上加早停,patience=15。如果模型验证loss连续15轮不降,就不再训练,避免后期震荡。

4.5 用画框脚本可视化标签:第一眼判断数据集质量

指标再好看不如眼睛看一遍。我会在训练前随机抽10到20张训练图,把txt里的坐标转成像素框画到图上,截图看框和鸟的位置对不对、框有没有盖住整个目标、多目标时是否重叠严重。下面是基于matplotlib的实现:

import matplotlib.pyplot as plt from PIL import Image from pathlib import Path img_path = Path('images/train/img_0975_720.jpg') img = Image.open(img_path).convert('L') w, h = img.size txt_path = Path('labels/train/img_0975_720.txt') fig, ax = plt.subplots() ax.imshow(img, cmap='gray') for line in txt_path.read_text().strip().splitlines(): class_id, xc, yc, bw, bh = map(float, line.split()) x1 = (xc - bw / 2) * w y1 = (yc - bh / 2) * h rect = plt.Rectangle((x1, y1), bw * w, bh * h, fill=False, edgecolor='red', linewidth=1) ax.add_patch(rect) ax.text(x1, y1 - 3, str(int(class_id)), color='red', fontsize=8) plt.show()

这里有一个在jupyter里运行的细节:plt.show()在无GUI服务器上不显示,改成plt.savefig('check.png')。另外,如果txt里出现了坐标大于1或者小于0,也就是归一化坐标越界,画框时会跑到图像外面,这类标签必须过滤。红外图很多标注框边缘本来就贴近图像边缘,一点点越界可以接受,但严重越界的多半是格式错乱。

数据质量检查比训练本身更能节省时间,一张画错的图对模型的影响远超一次盲目的学习率调整。

5. 常见问题排查:训练掉点、路径报错和标签错位

5.1 训练日志里 All labels empty 是什么情况

现象:运行yolo train或val时,日志打印出各种标签统计,但显示0 images with labels,也就是所有标签都是空的。

原因:最常见是txt标签目录没有被加载到。YOLO约定图像目录在images/train,标签目录在labels/train,如果这份数据集的txt标签放在其他文件夹名,比如txt_labels,训练器找不到。另一个原因是txt内容格式错误,比如第一行不是数字开头的类别索引,程序把每一行都当成无效标签跳过。

解决:先确认数据集解压后的目录结构。如果txt标签单独在别的文件夹,就按YOLO约定建立软链或复制过去。然后用前面的class索引统计脚本,看txt文件能否正常读出行和坐标。如果某一行不是5列数字,直接单独处理该文件。不要手工逐条改,写一个循环脚本过滤掉坏行。

5.2 换服务器后报 data.yaml 找不到图像

现象:在本地电脑训练正常,拷到服务器或换一台机器后,报类似“Invalid dataset: dataset/images/train does not exist”。

原因:data.yaml里写的是绝对路径,比如/home/user/dataset/images/train,换机器后路径前缀变了。更隐蔽的是yaml文件里用了Windows盘符路径,Linux下不认识。

解决:把data.yaml放在数据集根目录,内部路径全部改成相对路径,train: images/train。然后命令行进入数据集根目录再执行训练命令,或者在命令中用--data指向完整的yaml路径。这样换机器只要整个数据集目录一起拷贝即可,不用改配置。

5.3 验证集 mAP 不低,实际红外相机上漏了很多鸟

现象:训练完val mAP@0.5有0.85,把best.pt接到现场红外视频上,飞过的鸟有一半没检测出来,尤其是距离远、像素小的目标。

原因:验证集和训练集来自同一数据源,模型已经见过这部分特征分布,所以验证分数偏高。现场红外相机的非均匀性、盲元位置、增益参数和训练集不一样,导致域偏移。另一个常见原因是默认推理conf阈值是0.25,对低置信度的真目标过滤太多。

解决:推理时把conf调低到0.1,配合NMS再看召回。对现场相机的盲元做一次两点校正,让输入图像灰度分布接近训练集。如果还是漏,从现场视频里抽帧,用conf=0.1跑一遍,把漏检帧加入训练集做增量训练。这个方法比单纯调网络结构更快。

5.4 标签里出现大批 x_center=0.5,y_center=0.5 的异常框

现象:解压后随机抽几个txt,发现不少行显示“0 0.5 0.5 0.1 0.1”,中心点全是0.5。

原因:某些转换工具把xml的xmin、ymin、xmax、ymax像素值直接当成归一化中心了,或者使用了错误的转换工具链。这类问题通常出现在人工标注再导出的过程,训练时目标全被拉到图像中心,损失能下降,但完全学不到位置信息。

解决:用前面给的xml转txt脚本重新生成一遍。生成后画框检查20张图,确认框落在图像对应区域。马上能发现异常就改,训练前没检查,训练后mAP会是假象。这个坑我踩过一次,从那以后每次换新数据集,先画框再训练,不省这一步。

5.5 训练 loss 下降但验证 mAP 不涨

现象:训练日志里box_loss一路下降,但每5轮val一次的mAP始终在0.1以下,甚至不涨。

原因:模型把训练集背下来了,却没有学会通用的目标模式。红外图像对比度低,目标区域和噪声区分度小,如果增强参数太弱,模型很容易过拟合。另一个可能是验证集里的目标比训练集小很多,模型学习到的尺度不对。

解决:先确认是不是同一批相机的数据。如果val和train来自不同分布,mAP不涨是正常的,不用慌。如果同源,打开增强参数,至少保留mosaic=1.0和hsv_v=0.2。然后把学习率从默认调低一半,因为红外数据标注噪声大,太高的学习率会把细微信号直接冲掉。再用早停看是否连续30轮不涨,如果还不涨,回去看标签画框是不是有大量错误。

6. 用验证集做一次红外鸟类检测验收:为什么我要单独看低置信度

训练结束确认best.pt路径后,我会先在验证集上做一次完整的预测输出,保存所有框和置信度,而不是只看yolo val给出的mAP数值。因为mAP是整体指标,红外小目标的漏检会被大量简单目标平均掉。

yolo predict model=runs/train/exp/weights/best.pt source=dataset/images/val conf=0.1 save_txt save_conf

save_txt会把每个预测写到labels文件夹对应的txt里,save_conf会在每行后面追加置信度。这时用一个小脚本统计每张图的预测框数量,和数据集原来的标签框数量做对比。如果某张验证图的GT框有2个,预测txt里只有0个,这就是漏检样本,直接把图像路径记下来,集中看它们是不是都带盲元或低对比度。

这一类目标置信度分布最值得看。红外鸟类目标往往不是靠网络最后一层的强响应识别,而是靠微弱梯度,所以大量真目标会卡在0.1到0.25之间。如果数量太多,说明模型没有学好,先回头检查盲元预处理和增强配置,而不是去调NMS。反过来,如果真目标置信度集中在0.3以上,mAP也不差,那这个模型基本可以上线。

我习惯把验证集里所有漏检帧拼在一张图上,标注出盲元位置,做成一份现场验收报告。这样既能看到问题集中在哪个相机区域,也能给后续补充数据提供依据。从那以后,我每次拿到红外目标检测数据集,都会先查一遍盲元,再调一次增强,最后才把训练挂上,这样翻车的概率小很多。希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询