红外微小目标检测实战:YOLOv5权重、数据集训练与PyQt部署全流程 简介面向红外场景下的低空微小目标检测任务该压缩包提供训练好的YOLOv5模型权重及4000余张红外数据集覆盖无人机、直升机、飞机、飞鸟四类目标适合无人机反制、低空安防、遥感图像分析等方向的开发者和课程项目实践者。数据集已按YOLO格式配置标签划分好训练集、验证集与测试集并附带data.yaml文件可直接在YOLOv5、YOLOv7、YOLOv8算法中训练或微调。资源共2000个文件其中1994个XML标注文件、3个Python脚本和3份PDF教程包体约587MB。配套PyQt界面支持对图片、视频和摄像头画面进行检测同时提供环境配置与PyQt使用说明便于快速复现结果。目前已有548人学习下载适合希望结合模型训练与图形界面演示完成课程设计或科研验证的读者。1. 红外微小目标检测为什么一套YOLOv5权重加4000张红外图能撑起实战项目红外场景下的无人机、直升机、飞机、飞鸟识别是个看起来冷门但实际很要命的方向。可见光下大目标检测已经卷到刷榜可一旦换到红外波段目标没了颜色纹理只剩一团热辐射轮廓常规模型直接水土不服。更棘手的是这几类目标在远距离下常常只有十几二十个像素一套通用COCO权重丢上去漏检率能高到让人怀疑人生。这里说的“yolov5红外微小无人机-直升机-飞机-飞鸟目标检测模型权重4000数据集使用教程pyqt界面”其实是一个把数据、权重、训练方法和可视化界面打包好的完整落地链路目标就是解决“红外小目标检测从零起步成本过高”的痛点。适合谁做毕设和竞赛的学生、要做红外光电吊舱或边海防原型验证的工程师以及想快速评估YOLOv5在红外场景上限的技术负责人。最反直觉的一点是这个任务对模型结构的要求极低真正决定成败的是数据分布、标注规范和anchor配置。2. 模型权重与4000数据集红外图像里四类目标怎么定义、怎么标、够不够用2.1 红外图与可见光图的本质差异单通道、低纹理、高动态范围红外图像和普通可见光照片最根本的区别在信息维度。可见光图像有R、G、B三个通道携带颜色、纹理、阴影等丰富线索而红外图像通常是8位单通道灰度图每个像素值对应的是物体表面的热辐射强度。无人机在红外图里是一块亮斑或暗斑直升机的旋翼会拖出一道模糊的热迹飞鸟则是一个小到几乎看不清的高亮点。这种“低纹理”特性意味着靠颜色和纹理做识别的常规数据增强策略比如HSV色域扰动、随机色调变换在红外数据集上几乎完全失效。另一个被忽视的坑是高动态范围。红外探测器的输出往往在某个局部区域内对比度极高地面热源可能比天空背景亮几十倍。如果把整张红外图直接缩放到0到255的范围远距离的微小飞鸟很容易被压缩进背景灰阶里人眼看着都费劲模型更学不到。拿到4000张数据集之后第一件事不是训练而是统计每张图的灰度分布。常见做法是分区域做直方图均衡化拉高目标与背景的局部对比度再在训练时把这步写进数据加载管线而不是预处理完就丢弃原始信息。在标注层面单通道红外图像没有“颜色”这个属性可用标注员只能靠形状和热特征去区分四类目标。这带来一个直接后果标注的主观偏差会被放大。同一个人在精神状态不同的时段标出的框大小可能差出两三个像素不同标注员对“飞机”和“无人机”的判定边界也可能不一致。如果数据集是多人协作标注的训练前最好抽检一遍边界框的一致性否则模型会学到标注者的手抖规律而不是目标的真实特征。2.2 4000张图怎么分配训练与验证划分、标注格式转换、单通道转三通道4000张红外图像做四类目标检测数据量处于“够用但不宽裕”的区间。常见做法是按811划分训练、验证和测试集也就是约3200张训练、400张验证、400张测试。如果原始数据存在大量连续帧比如同一段视频抽帧得到的序列划分时必须按视频片段分组不能随机抽样否则同一个目标的相邻帧会同时出现在训练集和验证集里导致验证指标虚高这个坑排在所有数据问题里的第一位。标注格式方面基于YOLOv5做项目最终要把标注统一成YOLO的txt格式。每行五个数值类别id、归一化中心x、归一化中心y、归一化宽、归一化高。如果数据集原始给的是VOC XML格式写个转换脚本一劳永逸。下面这段Python代码可以处理标准的VOC格式标注import xml.etree.ElementTree as ET import os import random def voc_to_yolo(xml_file, output_txt, class_map): tree ET.parse(xml_file) root tree.getroot() size root.find(size) img_w int(size.find(width).text) img_h int(size.find(height).text) lines [] for obj in root.iter(object): name obj.find(name).text if name not in class_map: print(f跳过未定义类别: {name} 在 {xml_file}) continue class_id class_map[name] bbox obj.find(bndbox) xmin int(bbox.find(xmin).text) ymin int(bbox.find(ymin).text) xmax int(bbox.find(xmax).text) ymax int(bbox.find(ymax).text) # 边界保护坐标超出图像范围就截断 xmin max(0, min(xmin, img_w - 1)) xmax max(0, min(xmax, img_w - 1)) ymin max(0, min(ymin, img_h - 1)) ymax max(0, min(ymax, img_h - 1)) # 过滤掉面积过小的标注通常是标注误差 if (xmax - xmin) 5 or (ymax - ymin) 5: continue 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 lines.append(f{class_id} {x_center:.6f} {y_center:.6f} {width:.6f} {height:.6f}) with open(output_txt, w) as f: f.write(\n.join(lines)) # 用法一次性转换一个目录下所有xml文件 # class_map {drone: 0, helicopter: 1, airplane: 2, bird: 3} # 转换完成后对照原图抽查10张确认归一化坐标没有偏移这段脚本里的关键点有两个。第一是边界保护如果标注框超出图像尺寸就截断到合法范围内防止训练时出现负坐标导致loss变成NaN第二是过滤过小框红外小目标标注里经常出现一两个像素的噪声框这类框没有学习价值还容易把loss带偏。转换完成后要抽查图片和txt的叠加效果推荐用OpenCV写一个可视化脚本检查标注是否贴合目标轮廓。红外图是单通道但YOLOv5的输入层接收的是三通道图。常见做法是在数据加载环节将单通道灰度图复制成三通道np.repeat(gray_img[:, :, np.newaxis], 3, axis2)。这么做不会增加信息量但能复用YOLOv5的标准模型结构。如果后续要做模型量化和加速部署也可以直接改模型输入层为单通道省去复制操作的额外耗时但训练阶段用三通道复制更省事不必改模型代码。2.3 要不要用预训练权重4000张数据集的收敛路径4000张红外图说多不多说少不少。关键问题是从零初始化训练还是用预训练权重做迁移学习做过红外检测的都知道纯随机初始化在这么小的数据集上很难收敛到可用状态因为目标太小、特征太弱模型学不到稳定的梯度信号。常见做法是加载YOLOv5的COCO预训练权重比如yolov5s.pt把最后一层分类头的输出维度从80改成4然后在红外数据集上微调。这里有个反直觉的经验红外图和自然光图差异虽大但COCO预训练权重仍然有效。原因是浅层特征提取器学到的是边缘、角点、热斑这类通用视觉特征这些特征在红外图上同样成立需要重新学的主要是深层语义特征和分类头。用公开的yolov5s.pt做起点4000张数据配合适当的数据增强200个epoch内能看到明显收敛。如果完全不加载预训练权重同等效果可能需要两倍以上的数据或训练时长。数据集标签的命名也值得统一。四类目标建议固定为drone、helicopter、airplane、bird避免出现中文名或不同缩写混用。类别id映射一旦确定训练和PyQt界面里都要沿用同一套映射否则推理结果和真实目标对不上排查起来非常痛苦。3. 用YOLOv5训练红外微小目标的超参数调整从默认配置到可用的三步3.1 最小可复现训练命令与数据配置文件训练的第一步是准备好数据目录和配置文件。YOLOv5要求数据集目录结构为images和labels两个子目录各自按train和val拆分。一个标准的infrared.yaml配置文件内容如下# infrared.yaml train: /path/to/infrared-dataset/images/train/ val: /path/to/infrared-dataset/images/val/ test: /path/to/infrared-dataset/images/test/ nc: 4 names: [drone, helicopter, airplane, bird]配置文件的路径建议写绝对路径避免在服务器上切换工作目录时找不到数据。然后执行训练命令python train.py \ --img 640 \ --batch 16 \ --epochs 200 \ --data infrared.yaml \ --weights yolov5s.pt \ --patience 50 \ --project /path/to/runs \ --name infrared-exp01 \ --exist-ok这里有几个参数是按红外微小目标场景特别调过的。--img 640意味着输入图像会被缩放或填充到640x640。对红外小目标来说输入分辨率直接决定了目标在特征图上的像素占用。640是显存占用和检测精度的平衡点如果你的显卡显存大于16GB可以尝试--img 800很多微小目标在800分辨率下能从两三个像素变成六七个像素漏检率会有肉眼可见的改善。--patience 50是早停参数连续50个epoch验证集mAP没有提升就停止训练。对小数据集来说这是必要的保护防止后期过拟合白烧时间。--weights yolov5s.pt加载COCO预训练权重。训练过程中要留意终端输出的loss曲线和mAP进度。通常前20个epoch就能看到box_loss和obj_loss的快速下降如果这里迟迟不动优先排查数据标注和配置路径而不是盲目调超参数。3.2 三个必调超参数imgsz、anchors、mosaic训练YOLOv5红外微小目标默认超参数有一半是给COCO这种大目标数据集调的直接套用会很别扭。三个必调的参数按优先级排序第一个是imgsz。前面提到640起步、800更优但具体选哪个要看你的目标像素规模。如果四类目标在大多数样本里的短边不超过15像素800分辨率远好于640如果目标已经在30像素以上640就够用了。为了兼容推理速度和检测精度我通常训练用800、推理用640模型对尺度的鲁棒性会更强。这个trick在工程里很实用。第二个是anchors。YOLOv5默认anchor是在COCO数据集上通过k-means聚类出来的等差数列式的宽高设定对大目标友好但直接用在红外微小目标上偏大。在训练脚本里加一行--noautoanchor然后在开始训练前对训练集标注重新聚类就能得到适配微小目标的anchor设定。python train.py \ --img 800 \ --batch 16 \ --epochs 200 \ --data infrared.yaml \ --weights yolov5s.pt \ --noautoanchor \ --patience 50加了这个参数之后YOLOv5会在训练启动前自动对训练集标签运行k-means聚类重新计算9组anchor。你会在启动日志里看到类似“Analyzing anchors... anchors/target X.X”的输出这个比值大于4.0就说明anchor偏大需要重聚类。红外微小目标的数据集聚类结果往往前几组anchor非常小最小anchor可能只有两三像素宽这是正常的说明聚类确实匹配了数据分布。第三个是mosaic数据增强。YOLOv5默认在训练前4个epoch启用mosaic这个增强把四张图拼接在一起喂进模型对小目标检测尤其有利因为它让模型在缩小的视野里学习识别小目标。红外场景可以保持mosaic1.0的开头设定但要注意如果混入的四张图对比度差异极大一张是全黑夜空、一张是烈日下的地面模型可能学到错误的亮度关联。遇到这种情况把mosaic在最后20个epoch关掉只用普通增强做精调收敛更稳。YOLOv5的mosaic开关在hyp.scratch-low.yaml中的mosaic: 1.0字段训练过半后再找机会调整即可。3.3 类别不平衡与负样本为什么不能只盯mAP四类目标在4000张红外图里的出现频率几乎不可能均衡。飞鸟可能占了60%的样本直升机可能只有5%。如果直接按原始分布训练模型会严重偏向高频类别把所有小目标都预测成飞鸟。这种模型的总mAP不会太难看但按类别细分看直升机的AP可能只有0.2。解决办法常用的有两个。第一个是给低频类别加权重YOLOv5支持在训练参数里传--class-weights根据类别出现频率的倒数生成权重向量。第二个是在数据加载层面做过采样让每个epoch里低频类别的样本出现次数至少达到一个下限。这两个方法可以叠加使用但注意不要给低频类别过高权重否则会在高频类别上产生大量误检。表格里是一个典型的类别分布和权重配置示例类别样本数占比建议Loss权重drone120030%1.0helicopter40010%2.5airplane60015%1.5bird180045%0.7训练过程中要按类别看AP曲线而不是只盯整体mAP。YOLOv5的验证输出会打印每个类别的AP如果发现某个类别AP明显低于其他类别说明该类别特征没被模型学到要回到数据和增强层面去找原因。3.4 训练过程监控loss曲线和PR曲线到底看什么红外微小目标训练的loss曲线和常规目标检测有些不同。值得重点监控的是val/obj_loss和val/cls_loss这两个验证loss如果先降后升说明开始过拟合。对4000张这种规模的数据集过拟合大约出现在150到200个epoch之间早停回调会帮你判断什么时候该收手。另一个必看的图表是PR曲线。YOLOv5在验证阶段会输出每个类别的精确率-召回率曲线。红外场景里误检通常来自地面热源、树叶、路灯等背景物体表现为精确率低漏检则表现为召回率低。如果你的PR曲线右下角缺了一大块说明存在大量漏检优先提高输入分辨率或增加高频类别权重如果左上角塌陷说明误检太多要检查负样本或降低置信度阈值。4. 用PyQt做红外目标检测界面模型加载、推理线程与结果绘制的完整实现4.1 PyQt界面的功能拆分三种输入模式与界面布局PyQt界面的核心价值有两个一是让没有命令行经验的人能直接操作模型二是让推理过程和结果显示可视化。一个标准的红外目标检测界面至少要支持三种输入模式单张红外图片、一段红外视频文件、实时红外摄像头画面。同时要提供模型选择、置信度阈值调节和检测结果列表。界面布局按自上而下的顺序划分顶部是菜单栏包含模型加载和文件打开入口中间左侧显示原始红外图像或视频帧右侧实时显示检测结果底部是置信度滑条和一个结果统计栏统计每种目标的数量。实操中还有个小细节不要直接在主界面线程里跑推理必须用独立线程否则视频一播放界面就直接假死这是PyQt开发最常见的翻车点。4.2 用QThread做推理线程避免界面卡死的可复用代码下面这段代码是PyQt推理线程的核心骨架可直接复制到你的工程里按实际路径调整模型加载方式就能跑通import sys import cv2 import torch import numpy as np from PyQt5.QtCore import QThread, pyqtSignal from PyQt5.QtWidgets import QApplication, QMainWindow, QLabel, QSlider, QPushButton, QVBoxLayout, QWidget class DetectThread(QThread): # 信号帧数据、检测结果、目标数量统计 frame_signal pyqtSignal(np.ndarray) result_signal pyqtSignal(list) count_signal pyqtSignal(dict) def __init__(self): super().__init__() self.model None self.running False self.input_source None # 0表示摄像头字符串表示视频文件路径 self.conf_thres 0.25 self.is_grayscale_input True # 红外图是单通道需要特殊处理 def load_model(self, weights_path): # 加载YOLOv5模型这里以torch.hub方式加载为例 self.model torch.hub.load(ultralytics/yolov5, custom, pathweights_path, force_reloadTrue) self.model.conf self.conf_thres self.model.classes None # None表示检测所有类别 # 关键红外图输入需要灰度转三通道 self.model.eval() def set_input_source(self, source): self.input_source source def set_conf_thres(self, conf): self.conf_thres conf if self.model: self.model.conf conf def preprocess_frame(self, frame): # 红外摄像头输出可能是单通道灰度图 if len(frame.shape) 2: # 复制成三通道保持和训练时数据格式一致 frame_bgr cv2.cvtColor(frame, cv2.COLOR_GRAY2BGR) else: frame_bgr frame return frame_bgr def run(self): self.running True cap cv2.VideoCapture(self.input_source) if not cap.isOpened(): print(无法打开视频源) return while self.running: ret, frame cap.read() if not ret: break frame_bgr self.preprocess_frame(frame) results self.model(frame_bgr) # 提取检测框、类别、置信度 detections [] for *xyxy, conf, cls in results.xyxy[0].tolist(): x1, y1, x2, y2 map(int, xyxy) class_name self.model.names[int(cls)] detections.append([class_name, conf, x1, y1, x2, y2]) # 统计每类目标数量 count_dict {} for name, conf, _, _, _, _ in detections: count_dict[name] count_dict.get(name, 0) 1 # 把检测框画到图上 for name, conf, x1, y1, x2, y2 in detections: cv2.rectangle(frame_bgr, (x1, y1), (x2, y2), (0, 255, 0), 2) label f{name} {conf:.2f} cv2.putText(frame_bgr, label, (x1, y1 - 8), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (255, 0, 0), 2) self.frame_signal.emit(frame_bgr) self.result_signal.emit(detections) self.count_signal.emit(count_dict) cap.release() def stop(self): self.running False这段代码的逻辑并不复杂但几个地方的工程决策需要说明。第一红外单通道输入通过cv2.cvtColor(frame, cv2.COLOR_GRAY2BGR)扩展成三通道这一步必须在推理之前完成否则模型输入维度不匹配直接报错第二所有耗时操作包括视频读取、预处理和推理都放在run()方法里通过信号把结果回传给主界面UI线程只负责接收信号并刷新画面第三detections列表里包含坐标、置信度和类别名主界面拿到这个列表后可以同时做三件事画框、更新统计数字、把结果追加到日志表格。主界面侧连接信号的代码也很关键class MainWindow(QMainWindow): def __init__(self): super().__init__() self.thread DetectThread() self.thread.frame_signal.connect(self.update_frame) self.thread.count_signal.connect(self.update_count) def update_frame(self, frame_bgr): # 把OpenCV的BGR帧转成Qt能显示的RGB图像 rgb cv2.cvtColor(frame_bgr, cv2.COLOR_BGR2RGB) h, w, ch rgb.shape bytes_per_line ch * w from PyQt5.QtGui import QImage, QPixmap qimg QImage(rgb.data, w, h, bytes_per_line, QImage.Format_RGB888) self.video_label.setPixmap(QPixmap.fromImage(qimg)) def update_count(self, count_dict): self.count_label.setText(fdrone: {count_dict.get(drone, 0)} fhelicopter: {count_dict.get(helicopter, 0)} fairplane: {count_dict.get(airplane, 0)} fbird: {count_dict.get(bird, 0)})这里要把注意力放在信号槽机制上。frame_signal是一个np.ndarray类型的信号它可以把一帧图像数据直接跨线程传给主界面。PyQt的信号槽是线程安全的数据到达主界面的update_frame后只在主线程里执行图像格式转换和setPixmap不需要任何额外加锁处理。4.3 视频流实时推理线程启停与资源释放界面上放置“开始检测”和“停止检测”两个按钮点击事件绑定到线程的启动和停止。视频推理的目标是算力受限环境下也能流畅显示所以单帧处理速度要控制在30毫秒以内。如果推理耗时过高第一优先级优化是缩小输入尺寸到416x416代价是微小目标召回率下降第二优先级是用TensorRT导出的engine模型替换PyTorch模型这个在最后一章会具体展开。线程停止时要注意显存和内存释放。stop()方法里设置self.running False之后主线程调用self.thread.wait()等待子线程退出再执行cv2.destroyAllWindows()关闭窗口。如果不做这一步下一次启动线程时摄像头可能无法打开这是PyQtOpenCV项目里反复出现的坑。4.4 置信度滑条不用重新推理即可调整显示效果置信度滑条改变的是结果筛选阈值不需要触发重新推理。正确做法是让滑条信号连接一个槽函数更新线程对象的conf属性并调用self.model.conf new_conf这样后续帧会采用新阈值已绘制的结果也可以通过一个“刷新”按钮重新绘制。真正想要已显示帧立即响应滑条变化就得在界面侧缓存最后一帧的原始检测结果列表滑条变化时按新阈值重新过滤并再次绘制到画布上。def on_slider_change(self, value): conf value / 100.0 self.thread.set_conf_thres(conf) print(f置信度阈值已更新为: {conf:.2f})注意滑条的值范围设为25到95对应0.25到0.95的置信度阈值。红外场景建议默认0.25这个值能让模型在用户观察时暴露足够多的候选框用户根据实际误检情况手动提高阈值。5. 避坑红外微小目标检测项目里的六个翻车点5.1 训练loss变成NaN数据里藏着黑匣子现象训练几个epoch后box_loss或obj_loss输出为nan终端打印的loss值变成nan训练进程继续但模型权重彻底损坏。原因最常见的是标注框出现负坐标或坐标超过图像宽高还有一个容易被忽略的原因是归一化后width或height为0。如果你从网上找的标注脚本存在除零bug或者某张图在VOC转YOLO脚本里没做边界保护loss计算时就会除以0梯度瞬间变成NaN。解决在数据转换脚本里强制加边界剪辑前面VOC转YOLO脚本里的max(0, min(xmax, img_w - 1))就是干这个的。训练前再写一个数据检查脚本扫描所有txt文件筛选出宽度或高度小于等于0的行直接把对应样本清理掉。如果训练已开始先用这个小脚本修完数据再重启训练。5.2 红外小目标漏检严重不是模型不行是输入分辨率不够现象训练结束时mAP看着还行0.85左右但推理视频时距离稍远的目标全部漏检只有近距离的目标能检测到。原因目标在原始图像里可能只有8x8像素但YOLOv5在640分辨率输入下最大特征图的步长是8目标在特征图上只剩下1个像素。一个像素的响应想要被分类神经网络识别出来难度极大。解决把推理输入分辨率提升到800或960同时在训练时就用800分辨率让模型更多地在高分辨率特征图下学习微小目标的特征。还有一个补充手段使用SAHI切片推理库把大图切成若干小块分别推理再合并结果思路是把每个小目标放大后再检测。5.3 PyQt界面点击开始就卡死推理线程阻塞了Qt主线程现象点击开始按钮后界面立刻变成“无响应”状态鼠标转圈几秒后系统提示强制关闭。原因代码里直接在按钮绑定的槽函数里写了视频循环推理比如while True这样的结构把Qt主线程整个占住了。Qt的事件循环被阻塞界面无法刷新看起来就是卡死状态。解决所有推理操作必须放进QThread子线程。第4章给出的DetectThread类已经是完整可用的线程模板直接套用。一个细节是启动线程后不要再在主线程里调用self.thread.run()必须调用self.thread.start()start()会在内部自动调用run()并在线程上下文里执行。5.4 灰度图输入直接报错维度不匹配的硬编码问题现象视频源是红外摄像头帧格式为单通道灰度。运行界面时直接打印Expected 3 input channels, got 1这样的错误。原因YOLOv5模型输入层要求三通道灰度图直接送进去自然是灾难。解决在preprocess_frame里做单通道转三通道复制。这个时机选择很关键必须在缩放和归一化之前转换这样才能保证后续的预处理步骤letterbox、归一化对三通道图统一执行。如果先缩放再转换坐标对齐会出混乱。5.5 类别不平衡导致直升机几乎不检只盯总mAP是最大的错觉现象训练结果打印的总mAP有0.87但按类别细看bird的AP是0.96helicopter的AP只有0.15。程序里完全检测不到直升机目标。原因训练数据里直升机样本太少只占5%左右模型把有限的容量都用在了学习bird和drone的特征上对helicopter几乎没有有效学习。解决按第3.3节的方式设置类别loss权重或做简单过采样把低频类别的图片复制进训练集。最好是针对数据分布单独处理识别类AP偏低的情况在训练后阶段单独调参。5.6 验证集mAP高但实测不可用的“玄学”漂移现象测试集mAP达到0.9但部署现场后误检满天飞甚至把地面反射的太阳光斑当作无人机。原因数据集划分时随机跳帧导致同一目标或同一个场景背景同时出现在训练集和验证集验证结果虚高恰如盲人摸象。真正到了野外场景背景特征完全陌生模型泛化能力暴露出来。解决第一按视频片段划分数据同一段的帧全部进同一集合第二训练完成后在额外标注的一两百张全新场景图上做一次“野外验证”专门检查背景误检率。这一步在红外任务里几乎是必须的因为红外背景的温度分布五花八门模型很容易学到“凡是亮的就是目标”这类简陋决策。6. 进阶从mAP 0.85到稳定实战——验证技巧与轻量化部署方向训练阶段跑完拿到权重工作其实才完成一半。真正有价值的验证方式是把测试集按目标的像素尺寸分桶统计。比如把目标分成小于16像素、16到32像素、大于32像素三组分别计算每组AP。你会发现小于16像素这一组AP往往惨不忍睹这个数据才是你对这套系统上限的真实判断。如果这个桶的AP低于0.5建议直接提高输入分辨率到960或者调整anchor聚类的最小尺寸不要指望更高的置信度阈值能拯救小目标。拿到模型权重后往两个方向考虑下一步一是用TensorRT导出engine文件加速推理。对YOLOv5 v7.0以后的版本官方提供export.py脚本可以导出FP16精度的engine格式。在NVIDIA Jetson平台或PC上的RTX显卡上推理速度能提升2到3倍。红外场景下FP16精度损失通常可以接受不需要降到INT8。如果目标是树莓派或RK3568这类边缘设备需要走ONNX到RKNN的转换链路重点检查anchor和输出层的维度映射这一步踩坑率很高但没有传得那么玄乎。说到底这套YOLOv5红外检测方案的价值不在模型本身而在于把数据和工程链路打通了。我自己的习惯是每次换场景都做一次小样本实验拍一段几十秒的红外视频手动标注几十帧用训练好的权重做fine-tune。目标检测模型不一定要从零训只要掌握好从权重到界面这条路后面换数据集就是重复流程的事。希望帮到你。本文还有配套的精品资源点击获取