ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

夜间无人机目标检测实战:VOC/COCO/YOLO标签转换与YOLO11训练

夜间无人机目标检测实战:VOC/COCO/YOLO标签转换与YOLO11训练 简介这是一份面向无人机夜间场景的车辆检测数据集共1000张真实高质量夜间图片涵盖城市道路行驶、路边停车、停车场、小区内及车辆严重遮挡等多样场景统一标注为“car”类别可直接用于YOLO等目标检测算法的训练与评测。资源包为1个PDF文件大小4.13MB采用labelimg精细标注提供VOC、COCO、YOLO三种格式标签兼顾不同训练框架的导入需求。内附YOLO11一键训练脚本支持GPU、CPU以及Mac M芯片多平台运行并附带博主训练日志供参考能降低环境配置与模型调参门槛适合无人机巡检、智慧交通等项目的算法开发与数据集补充。当前已有372人学习浏览适合需要快速构建夜间车辆检测能力的开发者、研究人员及无人机应用相关从业者。1. 夜间无人机视角的目标检测真正卡人的不是黑暗而是小目标与标签链路一个做夜间无人机车辆检测的人拿到一套「1000 张图 VOC/COCO/YOLO 三种格式标签 YOLO11 一键训练脚本」的数据集包第一反应通常是先跑起来看看效果。但以我跑过多个无人机视角检测项目的经验来看夜间场景真正让人翻车的不是模型看不懂黑暗而是两个更隐蔽的问题一是俯视视角下车辆目标小到只有几十个像素二是三种标签格式之间的转换链路一断训练集里全是错框模型再强也白搭。这套方案的意义在于它把数据集和训练脚本绑在了一起让你从拿到数据到跑出第一版权重路径足够短同时它也把夜间无人机检测最典型的坑——小目标漏检、暗处标注丢失、跨平台环境不一致——全部暴露在你能控制的范围里。适合正在做智慧交通巡检、安防巡逻、无人机夜间探查的算法工程师也适合拿数据集练手的目标检测入门者。2. 三种标签格式的底层逻辑VOC / COCO / YOLO 到底在表达什么2.1 从一张夜间俯视图看三种标注格式各自的长相同一张图、同一个车辆框在三种格式里分别是三种完全不同的写法。VOC 是 XML 文件每个目标一个object节点坐标是左上角和右下角的绝对像素值COCO 是单个 JSON 文件所有图的标注集中在一起框是[x, y, width, height]同样基于绝对像素YOLO 是每个图对应一个 txt内容是归一化到 0~1 的中心点坐标和宽高。VOC 格式000001.xml annotation filename000001.jpg/filename size width1280/width height720/height /size object namevehicle/name bndbox xmin600/xmin ymin180/ymin xmax680/xmax ymax240/ymax /bndbox /object /annotation COCO 格式annotations.json 内的一段 { images: [{id: 1, file_name: 000001.jpg, width: 1280, height: 720}], annotations: [{ id: 1, image_id: 1, category_id: 1, bbox: [600, 180, 80, 60], area: 4800, iscrowd: 0 }], categories: [{id: 1, name: vehicle}] } YOLO 格式000001.txt 0 0.500000 0.291667 0.062500 0.083333注意看三个格式的差异VOC 的xmax680其实是xmin width的结果COCO 的bbox第三项是宽度 80、第四项是高度 60而 YOLO 里四个数字全部除以了图宽 1280 和图高 720。很多人在格式转换时出错就是没搞清楚「VOC 给的是两个对角点YOLO 要的是中心点加宽高」这一步换算错了训练出来的框全偏。2.2 三种格式共存的数据集目录该怎么组织既然这个标题给的是三种格式标签那目录结构就要设计成互不干扰、又能随时互相引用的状态。我一般的组织方式是图片单独放一份三种标签各占一个目录这样无论你用哪个框架训练都不用复制图片。night_vehicle_dataset/ ├── images/ │ ├── train/ │ │ ├── 000001.jpg │ │ └── ... │ └── val/ │ ├── 000101.jpg │ └── ... ├── labels_voc/ │ ├── train/ │ │ ├── 000001.xml │ │ └── ... │ └── val/ │ └── ... ├── labels_coco/ │ ├── annotations_train.json │ └── annotations_val.json ├── labels_yolo/ │ ├── train/ │ │ ├── 000001.txt │ │ └── ... │ └── val/ │ └── ... └── data.yaml图片和 YOLO 标签按train/val分好是为了直接喂给 YOLO11VOC 和 COCO 保持同样的划分是为了你用 Detectron2、MMDetection 或自定义脚本时不需要重新切分。这里有个容易忽略的点三种格式的train/val划分必须完全一致否则同一张图在 YOLO 里进了训练集、在 COCO 里进了验证集评估结果就没有任何参考价值。拿到数据集第一件事应该是把三个目录下的文件名做一次集合比对确认划分没串。2.3 拿到数据集第一步核验标签一致性1000 张图说多不多但人工去数每个框显然不现实。我拿到这类多格式数据集的第一动作永远是写一个核对脚本统计每张图的标注数量然后交叉比对三种格式是否对齐。只要统计数字对不上后面所有训练都是白跑。import glob import json import xml.etree.ElementTree as ET from pathlib import Path def count_voc(xml_files): counts {} for xml_file in xml_files: tree ET.parse(xml_file) root tree.getroot() # 统计该 XML 里所有 object 节点数量 counts[Path(xml_file).stem] len(root.findall(object)) return counts def count_coco(json_file): with open(json_file, r, encodingutf-8) as f: data json.load(f) # 每个 image_id 对应的标注数 counts {} for ann in data[annotations]: img_id ann[image_id] counts[img_id] counts.get(img_id, 0) 1 return counts def count_yolo(txt_files): counts {} for txt_file in txt_files: with open(txt_file, r, encodingutf-8) as f: # 每个非空行就是一个框 lines [line for line in f.read().strip().splitlines() if line.strip()] counts[Path(txt_file).stem] len(lines) return counts voc_counts count_voc(glob.glob(labels_voc/train/*.xml)) yolo_counts count_yolo(glob.glob(labels_yolo/train/*.txt)) # 逐图比对 mismatch [] for stem in voc_counts: if voc_counts.get(stem) ! yolo_counts.get(stem): mismatch.append(stem) print(不一致的图片数:, len(mismatch)) print(mismatch[:10])这段脚本的逻辑很简单三个格式各自统计每个图片名对应的框数量然后做差集比对。stem是去掉后缀的文件名作为三套标签的唯一关联键。稳定的数据集mismatch应该为空如果出现不一致最常见的两种情况是 COCO JSON 里漏写了某张图的标注、或者 YOLO txt 里存在空文件。这里要特别提醒空 txt 在 YOLO 训练里是合法的表示该图没有目标但 VOC 和 COCO 里可能根本没有对应条目——这时需要你决定是保留空标注还是删掉该图我的建议是删掉因为夜间无人机场景里一张图完全没有车辆本来就很不正常多半是切分时混入了多余图片。3. 格式转换不是跑个脚本那么简单坐标系与过滤规则的四个关键处理3.1 VOC→YOLO中心点归一化转换的正确写法该数据集标题说三种格式都有但在真实工程里你一定会遇到「手里只有 VOC、但我要用 YOLO11 训练」的处境所以转换脚本必须能自己写。import xml.etree.ElementTree as ET def voc_to_yolo(xml_path, out_path, class_map): tree ET.parse(xml_path) 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: continue 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) # 边界裁剪坐标超出图像范围时拉回来 xmin max(0.0, xmin) ymin max(0.0, ymin) xmax min(img_w, xmax) ymax min(img_h, ymax) # 过滤掉小于 3 像素的退化框这类框在夜间小目标里很常见 if (xmax - xmin) 3 or (ymax - ymin) 3: continue # 绝对坐标 - 归一化中心点 归一化宽高 x_center (xmin xmax) / 2.0 / img_w y_center (ymin ymax) / 2.0 / img_h box_w (xmax - xmin) / img_w box_h (ymax - ymin) / img_h lines.append(f{class_map[name]} {x_center:.6f} {y_center:.6f} {box_w:.6f} {box_h:.6f}) with open(out_path, w, encodingutf-8) as f: f.write(\n.join(lines)) # 单类数据集映射表多类时按实际类别补齐 class_map {vehicle: 0} voc_to_yolo( labels_voc/train/000001.xml, labels_yolo/train/000001.txt, class_map )这段代码要重点理解两个地方一是x_center的算法用(xmin xmax) / 2再除以图宽而不是用xmin width/2两者数学上等价但前者对异常框更鲁棒——如果原标注里xmax xmin用中心点加法会直接得到负数而你至少还能看出来二是class_map的索引顺序决定了模型输出的类别编号一旦中途改了映射已转换的 txt 必须全部重转没有后悔药。3.2 COCO→VOCJSON 里那些容易漏掉的字段反过来从 COCO 转 VOC 更繁琐因为 COCO 把所有信息压在一个 JSON 里你需要先建 image_id 到文件名的映射再建 category_id 到类名的映射最后逐条写 XML。最容易漏掉的是iscrowd字段和segmentation字段COCO 里iscrowd1的框表示密集人群或车辆群这类目标在检测训练里通常应该丢弃否则 YOLO 的 xywh 格式遇到重叠严重的群体框会很难收敛而segmentation多边形面积和bbox面积不一致的情况也常见转 VOC 时只取bbox即可。import json import xml.etree.ElementTree as ET from xml.dom import minidom def coco_to_voc(coco_path, output_dir): with open(coco_path, r, encodingutf-8) as f: data json.load(f) # 建立 image_id - 图像信息的映射 image_map {img[id]: img for img in data[images]} # 建立 category_id - 类名的映射 category_map {cat[id]: cat[name] for cat in data[categories]} # 按 image_id 分组标注 anns_by_image {} for ann in data[annotations]: if ann.get(iscrowd, 0) 1: continue # 跳过群体标注 anns_by_image.setdefault(ann[image_id], []).append(ann) for img_id, anns in anns_by_image.items(): img image_map[img_id] img_w, img_h img[width], img[height] annotation ET.Element(annotation) filename ET.SubElement(annotation, filename) filename.text img[file_name] size ET.SubElement(annotation, size) width ET.SubElement(size, width) width.text str(img_w) height ET.SubElement(size, height) height.text str(img_h) for ann in anns: obj ET.SubElement(annotation, object) name ET.SubElement(obj, name) name.text category_map[ann[category_id]] bndbox ET.SubElement(obj, bndbox) x, y, w, h ann[bbox] xmin ET.SubElement(bndbox, xmin) xmin.text str(int(x)) ymin ET.SubElement(bndbox, ymin) ymin.text str(int(y)) xmax ET.SubElement(bndbox, xmax) xmax.text str(int(x w)) ymax ET.SubElement(bndbox, ymax) ymax.text str(int(y h)) xml_str ET.tostring(annotation, encodingunicode) pretty minidom.parseString(xml_str).toprettyxml(indent ) out_file f{output_dir}/{img[file_name].split(.)[0]}.xml with open(out_file, w, encodingutf-8) as f: f.write(pretty)转换中ann[bbox]里的宽高如果出现负数说明原始 COCO 标注本身不干净这种框要么丢弃、要么在写xmax x max(w, 0)时自然修正。我会在循环里加一个if w 0 or h 0: continue避免生成出xmin xmax的非法 VOC 框。3.3 小目标过滤阈值无人机夜间场景的特殊取舍地面道路目标检测里一个 50×50 像素的车辆框已经很值得关注但在无人机俯视场景下1000 张图里大量车辆只有 10×10 甚至 5×5 像素。转换脚本里的「小于 3 像素过滤」是通用底线实际项目中你应该根据图像分辨率和检测任务重新定阈值。以 1280×720 的夜间无人机图为例我一般把过滤阈值设在 4~5 像素小于这个尺寸的框即使人眼放大看也无法确认是车还是噪点标注本身就不可靠硬留下来只会给模型传递错误信息。但要注意别把阈值设太高——当你把阈值从 5 提到 10可能过滤掉接近 30% 的标注而这些小目标恰恰是夜间无人机检测最核心的难点。过滤不是丢数据是保数据质量这一刀切在哪直接决定模型对小目标的召回上限。3.4 转换后用召回率反查验证标签链路没断格式转换最大的风险不是写错代码而是写错的代码跑完一遍后没人发现。我习惯在转换后做一个「伪训练」验证用现成的 YOLO11 预训练权重不训练、只跑一次验证集的推理统计预测框里有标注的位置是否给出了响应。# 用 COCO 预训练权重直接验证标签可读性不训练模型 yolo detect val \ modelyolo11n.pt \ datanight_vehicle.yaml \ splitval \ imgsz640 \ conf0.05 \ save_jsonTrue关注两个输出一是val阶段能否正常读入所有 YOLO 标签任何Corrupt JPEG或Label shape报错都会在这里暴露二是即使完全不训练预训练权重也会对车辆类目标给出一定响应如果mAP0.5显示为 0 且预测框数量为 0那基本可以断定标签文件路径或类别索引有误而不是模型不行。这一招能把标签链路的验证压缩到五分钟之内比直接闷头训练再回来查快得多。4. 用 YOLO11 在 GPU / CPU / Mac 上把训练跑起来一键脚本的写法4.1 训练脚本骨架先探测平台再选 device标题里说的「支持 GPU(GPUs)/CPU/Mac 三平台」本质是在写脚本时做一次设备探测然后让yolo train的device参数跟着平台走。注意不要写死device0因为在 Mac 上它根本不存在在只有核显的机器上它会直接报错。#!/usr/bin/env bash set -e # 检测当前平台可用的推理设备 DEVICE$(python - PY import torch if torch.cuda.is_available(): # GPU 可用取第一块卡 print(0) elif hasattr(torch.backends, mps) and torch.backends.mps.is_available(): # Apple Silicon 的 Metal 加速 print(mps) else: # 兜底用 CPU print(cpu) PY ) echo 当前训练设备: $DEVICE # 根据设备取合理的 batch 与 workers case $DEVICE in 0) BATCH16 WORKERS8 ;; mps) BATCH8 WORKERS4 ;; cpu) BATCH4 WORKERS0 ;; esac # 开始训练epochs 按需修改 yolo detect train \ modelyolo11n.pt \ datanight_vehicle.yaml \ imgsz640 \ epochs100 \ batch$BATCH \ device$DEVICE \ workers$WORKERS \ project./runs \ namenight_vehicle这里几个要点torch.backends.mps需要用hasattr包一层因为旧版 PyTorch 根本没有这个属性直接调用会在 Windows 上报 AttributeErrorworkers0在 Windows 上有时是必需的多进程数据加载在 Windows 的 spawn 模式下容易卡死新手最容易在这卡半小时batch 按设备分档是最朴素也最安全的做法GPU 上 16、CPU 上 4不至于一上来就爆显存。4.2 data.yaml 怎么写单类夜间车辆最简单也最不容易错YOLO11 的数据配置走 ultralytics 的 YAML 格式。这个数据集如果标签里只有 vehicle 一个类别YAML 可以写到最简如果实际数据里还有 truck、bus就把所有类名列全数量必须和标签 txt 里的索引号对齐。# night_vehicle.yaml # 数据集根目录用相对路径时以当前工作目录为基准 path: ./night_vehicle_dataset # 训练集与验证集图片目录YOLO 会自动找同名 txt train: images/train val: images/val # 类别数量与名称映射 nc: 1 names: 0: vehiclepath字段是整个配置的锚点YOLO 会把train和val都相对它拼接。最容易踩的是path写成绝对路径换机器后必须改我通常改成相对路径并规定「训练脚本从数据集上一级目录运行」。nc是类别总数names的索引从 0 开始如果 label txt 里出现了索引 1 而names只写了0: vehicle读取时不会报错但训练会丢失那一类目标造成静默的类别漏检。4.3 必调参数与显存预算batch、imgsz 和 cache 的三方博弈YOLO11 训练脚本跑起来之后最能感受到「参数玄学」的就是 batch、imgsz 和 cache 这三者的拉扯。夜间无人机图像通常是高分辨率大图但输入网络的尺寸受显存限制。参数作用GPU 8GB 建议值CPU/Mac 建议值调参方向imgsz输入图像缩放边长640640改 960/1280 提小目标精度代价是显存和速度batch每批图数164~8爆显存就先降一半不要直接降 imgszcache是否预加载图片到内存TrueFalseGPU 显存够就开省磁盘 IOworkers数据加载进程数80~4Windows 上往小调卡死就往 0 调夜间车辆目标普遍偏小imgsz640时一个 10 像素的车在输入图上只有 8 像素左右模型很难学。我的经验是 GPU 显存允许的情况下优先把imgsz提到 960这比盲目加 epochs 对小目标的 mAP 提升更明显。CPU 和 Mac 上如果太慢优先减 epochs 而不是减 imgsz因为「模糊的大图」比「清晰的小图」更容易训练出稳定特征。4.4 Windows 上 GPU 不可用的最常见原因torch 与驱动版本匹配标题里写「GPU(GPUs)/CPU/Mac 三平台」实际操作中 Windows 用户报「GPU 不可用」的比例远远高于 Mac 和 CPU。常见场景是笔记本双显卡——一个 Intel UHD Graphics 核显加一个 NVIDIA RTX 4060 Laptop GPUtorch.cuda.is_available()返回 False。根因几乎都是同一个PyTorch 的 CUDA 版本和驱动支持的 CUDA 版本没对上或装到了 CPU 版 PyTorch。NVIDIA 驱动本身装好了还不够PyTorch 是通过自己的 CUDA runtime 去调显卡的需要按官方命令安装匹配的 CUDA 版本# 先确认当前 PyTorch 是不是 CPU 版 python -c import torch; print(torch.__version__) # 如果输出是 cpu 版本按需重装 CUDA 版例如 cu121 对应 CUDA 12.1 pip install torch torchvision --index-url https://download.pytorch.org/whl/cu121装完后再跑一次上面脚本里的探测逻辑torch.cuda.is_available()变为 True 才说明 GPU 链路通了。这里有个很坑的细节Windows 下用pip install ultralytics时它会顺带把 torch 装成 CPU 版导致你后续yolo train只能走 CPU所以顺序应该是先装 torchGPU 版再装 ultralytics利用--no-deps跳过 torch 的重复安装。双显卡笔记本上如果探测到 CUDA 但仍跑在核显上去 NVIDIA 控制面板里把 Python 设为「使用高性能 NVIDIA 处理器」这步操作解决的是驱动层面的问题和 PyTorch 无关。5. 夜间无人机车辆检测的五大翻车现场与避坑清单5.1 标注在暗处“漏了”模型怎么训都学不全现象训练完的模型在验证集上 mAP 不低但拿一段全新的夜间无人机视频去测漏检率特别高尤其车停在树荫下、楼宇阴影里时几乎全漏。原因夜间原始图像整体很暗人眼在标注时看不清楚远处的小车标注软件里没做亮度增强导致大量框根本没标出来。模型学到的不是「检测车」而是「检测亮处的车」一到暗处就失灵。解决标注阶段把图像做一次 CLAHE 自适应直方图均衡或 gamma 增强再标或者用红外/热成像版本对照着标。已经标注完的数据集检查每一张图上目标是否都落在较暗区域若暗处明显有车却无框把该图加入「重点复核清单」。1000 张图不算大人工逐张过一遍暗区比事后反复调模型划算得多。5.2 VOC 转 YOLO 出现负数与超界坐标训练 Loss 异常抬升现象转换脚本跑完训练时 loss 在初期就出现剧烈波动打开某个 txt 一看里面有-0.0023或1.0234这种越界值。原因原始 VOC 标注的 bndbox 超出了图像边界——标注员手抖拖框、或者脚本读错宽高导致分母错误。YOLO 的归一化中心点如果为负损失函数里会出现无法收敛的定位惩罚。解决转换时统一做裁剪和过滤也就是前面 3.1 代码里xmin max(0.0, xmin)和if (xmax - xmin) 3: continue这几行缺了任何一步都会把脏数据带进训练。另加一步统计转换完后用grep -E (-|[0-9]\.[0-9] )扫描所有 txt找出含负号或大于 1 的行单独抽出来人工决定是修正还是删除。5.3 Mac 上一开 MPS 就崩或显存一路飙到爆现象在 Apple Silicon 的 Mac 上脚本探测到 mps 后开始训练几秒后进程被杀或者训练到一半内存占用飙到几十 GB。原因MPS 后端在部分 PyTorch 版本上对某些 op 支持不全会触发 fallback 或直接崩溃另一个常见原因是 batch 设太大MPS 使用统一内存模型参数、激活值、图像数据全挤在一起比独立显存更容易撞上限。解决先把 batch 压到 4 或 2imgsz降到 640确认能跑完一个 epoch 后再逐步加如果torch.backends.mps.is_available()返回 True 但一训练就崩先升级 PyTorch 到最新稳定版很多 MPS 算子问题在新版里已经修掉了。再不行就devicecpu兜底——Mac 上的 CPU 训练 1000 张图虽然慢但能跑完。5.4 1000 张图训练明显过拟合val loss 后期反弹现象训练到 60 个 epoch 左右train loss 还在降val loss 开始往上走验证集 mAP 不再提升甚至下降典型的过拟合曲线。原因1000 张图的规模对 YOLO11 来说太小夜间场景又高度相似——同一批无人机航迹拍出来的图背景重复度高模型记图而不是学特征。解决三个手段并行。一是把epochs缩到 50~80配合早停patience15让它在过拟合前停住二是打开数据增强hsv_h0.02 hsv_s0.5 hsv_v0.4这类夜间友好的扰动能明显提升泛化注意夜间图别把亮度增强调太大否则模型学的是「白天化」的特征三是如果数据集本身有 1000 张合理切分验证集尽量按航迹/区域切避免同一段航迹的画面同时出现在训练和验证里否则指标虚高。5.5 训练曲线漂亮但夜间测试集 mAP 惨淡模型像是“近视眼”现象训练和验证的 mAP 都到了 0.7 以上把模型放到另一个夜间场景里中近距离的车能检出远处小目标几乎全军覆没。原因验证集和训练集来自同一个数据分布而模型对「小目标」的响应能力不够。无人机夜间场景是典型的 anchor 稀疏问题——原始输入里 5×5 像素的车辆经过骨干网络下采样后特征图上的响应可能只有 1 个像素目标检测头根本聚不到框。解决要么提高imgsz到 960 或 1280让小目标在输入图上占更多像素要么在模型结构上加一个小目标检测头YOLO11 的 P2 层或换用高分辨率特征融合的变体这个在 ultralytics 里通过自定义 yaml 实现要么用切图推理把大图切成 640×640 的小块分别检测再合并结果这一招在无人机视角下几乎是立竿见影的代价是推理时间翻倍。6. 从 1000 张图到一个能用的夜间检测模型增强、评估与部署前验证6.1 夜间数据增强三板斧亮度扰动、噪声模拟、Cutout夜间检测和白天检测的增强策略完全不同白天的随机翻转和色彩抖动在夜间图上作用有限。我常用的三板斧是亮度扰动把图像亮度拉到原始值的 0.6~1.4 倍随变化模拟夜间灯光过曝和欠曝的不同路段高斯与椒盐噪声注入模拟低照度传感器在 ISO 拉高后的噪点这个对夜间小目标检测的鲁棒性提升非常直接Cutout 随机遮挡模拟路灯杆和树木遮挡半辆车的情况。这三项增强直接对应夜间部署的真实现状同一场景不同时间亮度不同、暗处噪点明显、遮挡严重。6.2 小图切块推理不加训练就能提升远端召回如果你不想为小目标重新训练一版模型最快的提升路径是切块推理。把 1280×720 的输入切成带重叠的 640×640 小块让车辆在块内的相对尺寸变大模型更容易检出检测完后再把坐标映射回原图重叠区域用 NMS 合并。这个思路本质上是「用推理算力换训练改造成本」适合模型已经在现有数据集上训练好、临时要提升某段航线小目标召回的场景。注意切块大小要和imgsz一致重叠率设在 20% 左右就够了太高会让 NMS 合并时框数爆炸。6.3 最后一遍验证用未见过的夜间片段做“体检”模型训练完不要只看验证集指标收工。我的习惯是专门留出一段与数据集拍摄时间、地点都不相同的夜间视频对着它做一次逐帧推理统计三类失败样本暗区漏检、小目标漏检、与路灯杆重叠导致的误检。按这三类分别记录漏检帧数如果暗区漏检占比最高回去补增强如果小目标漏检占比最高优先试切图推理或提高imgsz如果误检全是路灯考虑在数据里补充路灯负样本。这步「体检」做得越细模型能不能真正落地就越有数。这套数据集加训练脚本的完整链路从核验标签到跑通训练我自己踩过的最深一个坑就是「数据到手不看标签直接开训」一个多小时训练完才发现类别索引错位浪费了时间也算白烧了显卡。现在无论拿到什么数据集第一件事永远是先核验证标签数量、转换脚本跑一遍、用预训练权重做一次五分钟的链路验证再决定要不要烧训练资源。希望帮到你让这 1000 张夜间图和 YOLO11 真正跑出你能拿出去用的模型。本文还有配套的精品资源点击获取
返回列表