ARTICLE DETAIL

资讯详情

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

YOLOv5训练报错“No labels found”的根源分析与彻底解决方案

YOLOv5训练报错“No labels found”的根源分析与彻底解决方案 1. 问题概述当YOLOv5告诉你“没找到标签”如果你正在兴致勃勃地准备用YOLOv5训练自己的目标检测模型却在运行train.py脚本后在终端看到一行刺眼的红色警告WARNING: No labels found in /path/to/your/dataset/train.cache紧接着训练过程戛然而止那么恭喜你你遇到了YOLOv5新手上路最常见的“拦路虎”之一。这个错误信息直白得有点伤人“没找到标签”。它意味着YOLOv5的数据加载器在遍历你指定的训练集目录时无法找到任何有效的标注文件通常是.txt格式。没有标签模型就不知道图片里有什么、在哪里自然无法学习。这个问题看似简单但根源可能藏在你数据准备的任何一个环节。别慌这绝不是你一个人的战斗几乎所有从零开始准备自定义数据集的人都会踩这个坑。今天我们就来彻底拆解它从根上理解数据是如何被YOLOv5“看见”的并提供两个经过实战检验、一劳永逸的解决方案。2. 核心原理YOLOv5如何寻找并读取你的数据在直接动手修复之前我们有必要花几分钟理解YOLOv5期望的数据世界是什么样子的。这能帮你从“照猫画虎”的配置升级到“心中有数”的调试。2.1 数据目录结构的“标准答案”YOLOv5遵循一种非常清晰的数据集组织结构。假设你的项目叫my_project其标准结构应该如下my_project/ ├── datasets/ │ └── my_custom_dataset/ │ ├── images/ │ │ ├── train/ │ │ │ ├── image1.jpg │ │ │ ├── image2.jpg │ │ │ └── ... │ │ └── val/ │ │ ├── image100.jpg │ │ ├── image101.jpg │ │ └── ... │ └── labels/ │ ├── train/ │ │ ├── image1.txt │ │ ├── image2.txt │ │ └── ... │ └── val/ │ ├── image100.txt │ ├── image101.txt │ └── ... ├── yolov5/ (这是克隆的YOLOv5官方代码仓库) └── train.py (你的训练脚本)关键点解析images/ 和 labels/ 目录同级且命名严格对应这是铁律。images/train/里的每一张图片如cat_001.jpg都必须在labels/train/目录下有一个同名的.txt标注文件cat_001.txt。标注文件(.txt)的格式每个.txt文件可能包含多行每一行代表图片中的一个物体。格式为class_id x_center y_center width height。class_id: 类别索引从0开始整数。对应你data.yaml里names:列表的顺序。x_center, y_center, width, height: 边界框的中心点坐标和宽高必须是归一化后的值即除以图片宽度和高度后的浮点数范围在0到1之间。例如一个在512x512图片中中心位于(256, 256)宽高为(100, 50)的框其标注应为0 0.5 0.5 0.1953 0.0977计算256/5120.5 100/512≈0.1953。2.2 配置文件(data.yaml)的桥梁作用data.yaml文件是连接你的数据和YOLOv5代码的“地图”。一个典型的data.yaml如下# 数据集根目录路径相对路径或绝对路径 path: ../datasets/my_custom_dataset # 训练集和验证集的图片目录相对于path的路径 train: images/train val: images/val # 类别数量 nc: 2 # 类别名称列表顺序与标注文件中的class_id对应 names: [cat, dog]当你在命令行执行python train.py --data data.yaml时YOLOv5会做以下事情读取data.yaml找到path。根据path和train的值拼接出训练图片的绝对路径例如/full/path/to/datasets/my_custom_dataset/images/train。自动推导标签路径这是核心YOLOv5的代码在utils/dataloaders.py中有一个约定它会将图片路径中的/images/替换为/labels/然后去寻找同名的.txt文件。所以对于图片/.../images/train/cat_001.jpg它会自动去/.../labels/train/cat_001.txt找标签。注意很多新手错误地将train:直接写成图片的完整路径列表文件如train.txt这是早期某些版本或其他框架的用法。对于标准的YOLOv5train:和val:字段应该填写目录路径而非文件路径。2.3 “no labels found”错误的触发逻辑错误发生在数据加载的初始化阶段。YOLOv5会遍历train目录下的所有图片文件支持.jpg,.png,.bmp等格式然后为每一张图片按照上述规则去构建其对应的标签文件路径并尝试打开它。如果一张图片的标签文件不存在或者存在但为空0字节这张图片就会被加入一个“无效样本”列表。当遍历完所有图片后如果所有图片都被判定为无效即“无效样本”列表长度等于图片总数程序就会抛出WARNING: No labels found并终止。如果只有部分图片缺失标签你会看到类似WARNING: 10 images out of 100 have no labels的警告但训练仍会继续不过效果会打折扣。3. 方法一系统性检查与修复数据准备流程推荐首选这是最根本、最彻底的解决方法。你需要像一个侦探一样从头到尾检查你的数据流水线。3.1 第一步验证目录结构与文件对应关系打开终端进入你的数据集目录执行一些简单的命令来检查。检查图片和标签目录是否严格对应# 假设你在数据集根目录下 ls images/train/ | head -5 # 查看训练图片的前5个文件名 ls labels/train/ | head -5 # 查看训练标签的前5个文件名直观对比两个列表的文件名不含扩展名是否完全一致。一个快速检查的Python脚本更可靠import os image_dir ‘images/train’ label_dir ‘labels/train’ image_files {os.path.splitext(f)[0] for f in os.listdir(image_dir) if f.endswith((.jpg, .png, .jpeg))} label_files {os.path.splitext(f)[0] for f in os.listdir(label_dir) if f.endswith(‘.txt’)} # 找有图片没标签的 no_label image_files - label_files # 找有标签没图片的通常问题不大但值得检查 no_image label_files - image_files print(f“有图片但无标签的文件: {no_label}”) print(f“有标签但无图片的文件: {no_image}”)检查标签文件内容是否有效 找到标签文件用文本编辑器打开一个检查。确保文件不为空。每行有5个由空格分隔的数字。第一个数字类别ID是整数且小于data.yaml中定义的类别数nc。后面四个数字坐标是介于0到1之间的浮点数。最常见的问题就是这里很多人直接用标注工具导出的像素坐标没有做归一化。3.2 第二步深度审查data.yaml配置文件90%的“no labels found”错误根源都在data.yaml。检查路径path:这个路径是相对于你运行train.py脚本的位置而言的。如果你在yolov5/目录下运行而数据集在上一级的datasets/里那么path: ../datasets/my_custom_dataset是正确的。一个保险的做法是使用绝对路径如path: /home/user/projects/my_project/datasets/my_custom_dataset。train:和val:确认它们是从path出发的相对路径。例如如果path指向数据集根目录那么train: images/train就是正确的。绝对不要在这里写train.txt除非你明确知道自己在做什么使用了自定义的数据集加载方式。验证YOLOv5的路径解析 你可以写一个简单的脚本来模拟YOLOv5的行为看它最终找到的路径是什么import yaml from pathlib import Path # 加载你的yaml with open(‘data.yaml’, ‘r’) as f: data yaml.safe_load(f) base_path Path(data[‘path’]) train_image_dir base_path / data[‘train’] # 模拟YOLOv5的路径替换逻辑 # 它会把 ‘images’ 替换成 ‘labels’ train_label_dir Path(str(train_image_dir).replace(‘/images/’, ‘/labels/’)) print(f“代码会去这个目录找图片: {train_image_dir}”) print(f“代码会去这个目录找标签: {train_label_dir}”) print(f“标签目录是否存在: {train_label_dir.exists()}”) if train_label_dir.exists(): print(f“标签目录下的文件示例: {list(train_label_dir.glob(‘*.txt’))[:3]}”)3.3 第三步检查图像文件格式与损坏情况有时问题出在图片本身。隐藏的文件扩展名在Windows系统下文件可能显示为image.jpg但实际全名是image.jpg.jpg。YOLOv5是按后缀名过滤的这会导致它找不到image.jpg。在终端或文件管理器设置中显示完整的文件扩展名并修正。损坏的图片文件极少数情况下图片文件可能损坏导致无法读取。YOLOv5在尝试读取图片失败时可能会跳过但有时也会引发混乱。可以用PIL或OpenCV写个脚本批量尝试打开图片剔除损坏的。from PIL import Image import os image_dir ‘images/train’ for img_name in os.listdir(image_dir): try: with Image.open(os.path.join(image_dir, img_name)) as img: img.verify() # 验证文件完整性 except Exception as e: print(f“损坏文件: {img_name}, 错误: {e}”)完成以上三步系统性检查几乎可以定位并解决所有因数据准备不当导致的“no labels found”错误。修复后记得删除之前运行训练时生成的缓存文件通常是train.cache和val.cache因为YOLOv5会缓存数据集信息旧的错误缓存会导致它无法感知到你的修复。4. 方法二修改源码以绕过或自定义路径逻辑高级方案如果你的数据集结构因为某些历史原因或特殊需求确实无法调整为YOLOv5的标准格式例如图片和标签文件散落在不同层级的目录或者你有自己独特的命名规则那么修改源码是更灵活的选择。注意修改源码意味着你维护了一个自定义分支未来更新官方代码时需要留意合并冲突。4.1 定位关键代码文件YOLOv5中负责数据加载和标签查找的核心代码位于utils/dataloaders.py文件中不同版本可能略有差异但核心逻辑不变。我们需要关注的是LoadImagesAndLabels类或类似的类的初始化部分特别是构建图片和标签路径对应关系的地方。4.2 方案A自定义标签路径生成规则这是最常用的修改方式。在dataloaders.py中找到构建label_path或suffix替换的代码段。通常它看起来像这样# 原始代码片段示例具体行号可能不同 def img2label_paths(img_paths): # Define label paths as a function of image paths sa, sb os.sep ‘images’ os.sep, os.sep ‘labels’ os.sep # /images/, /labels/ substrings return [sb.join(x.rsplit(sa, 1)).rsplit(‘.’, 1)[0] ‘.txt’ for x in img_paths]这个img2label_paths函数就是执行将/images/替换为/labels/魔术的地方。如果你的数据结构是/data/images/对应/data/annotations/你可以修改这个函数def img2label_paths(img_paths): # 自定义规则将 /data/images/ 替换为 /data/annotations/ sa, sb os.sep ‘data’ os.sep ‘images’ os.sep, os.sep ‘data’ os.sep ‘annotations’ os.sep return [sb.join(x.rsplit(sa, 1)).rsplit(‘.’, 1)[0] ‘.txt’ for x in img_paths]4.3 方案B直接指定标签文件列表适用于极度非标准结构如果替换规则也无法描述你的数据结构最暴力的方法是在LoadImagesAndLabels类的__init__函数中直接覆盖掉它自动生成的标签路径列表。首先你需要自己写一个函数根据你的图片路径列表img_files生成对应的标签路径列表label_files。这个函数逻辑完全由你控制。然后在dataloaders.py中找到初始化self.label_files的地方通常在__init__函数中在调用img2label_paths之后用你自己的列表替换它。操作示例需仔细对照你的代码版本# 假设在 LoadImagesAndLabels.__init__ 函数中找到如下代码 self.img_files img_files self.label_files img2label_paths(self.img_files) # 这是原始自动生成的 # 在这行之后插入你的自定义逻辑 custom_label_files [] for img_path in self.img_files: # 你的自定义映射逻辑例如从某个字典查找或根据特定规则拼接 label_path your_custom_mapping_function(img_path) custom_label_files.append(label_path) # 用自定义列表覆盖自动生成的列表 self.label_files custom_label_files警告修改源码是最后的手段。务必在修改前备份原文件并在一个小的子数据集上充分测试确保你的修改没有破坏其他功能如验证集加载、缓存机制等。每次更新YOLOv5官方仓库时都要小心处理这个文件的合并。5. 实战排查清单与常见陷阱实录即使理解了原理和方法实际调试中还是会遇到各种“坑”。下面是我在多次帮人排查和自身实践中总结的清单和陷阱。5.1 “no labels found” 终极排查清单按照以下顺序检查99%的问题都能被发现第一反应检查缓存。立刻删除数据集目录下的所有.cache文件如train.cache,val.cache以及可能存在的labels.cache。然后重新运行训练。YOLOv5缓存了上次扫描的结果旧缓存包含错误信息。路径三连问Q1: 我的data.yaml里的path是绝对路径吗如果不是它相对于我运行命令的目录正确吗Q2: 我的train:字段指向的是一个目录吗这个目录下真的有图片吗Q3: 根据pathtrain拼接出来的完整图片路径在操作系统里能访问吗用ls命令或文件管理器确认。文件对应三连问Q1:labels/train/目录存在吗Q2: 对于images/train/里的任意一张图片xxx.jpglabels/train/xxx.txt存在吗Q3:.txt文件打开后内容格式正确吗5个数字坐标已归一化环境与权限在Docker或远程服务器上运行时确保数据集目录被正确挂载或映射到了容器/服务器内部。检查运行Python程序的用户是否有权限读取图片和标签文件。版本与代码你使用的YOLOv5版本是什么不同小版本间dataloaders.py可能有细微差别。尝试使用官方最新的master分支或一个稳定的发布版本。你是否无意中修改过utils/dataloaders.py或相关文件用官方原版替换试试。5.2 我踩过的坑与奇葩案例坑1空格与特殊字符。图片文件名或路径中包含空格、括号()、引号‘’等字符。虽然现代系统支持但在脚本处理字符串时极易出错。一律使用下划线_或连字符-杜绝空格。坑2软链接Symbolic Link的陷阱。为了方便我在服务器上用ln -s创建了指向实际数据集的软链接。结果data.yaml中的路径指向了软链接而YOLOv5解析出的真实路径可能在某些环节出现偏差。对于关键数据集建议直接使用物理路径或确保软链接在所有环节都被正确解析。坑3标注文件编码。某些标注工具在Windows下生成的.txt文件可能是带有BOM字节顺序标记的UTF-8编码。这可能导致文件开头有不可见字符使得Python读取第一行时出错。用notepad等编辑器将文件转为UTF-8无BOM格式。坑4图片列表文件.txt的残留。从其他框架如Darknet转换数据集时可能会留下train.txt这样的文件里面列出了所有图片的绝对路径。如果你在data.yaml的train:字段不小心写成了这个.txt文件的路径YOLOv5会把它当作目录去遍历自然找不到图片。牢记YOLOv5标准用法中train:字段填的是目录路径。坑5类别ID越界。你的data.yaml里定义nc: 2即0和1两个类别。但某个标注文件里写了一行2 0.5 0.5 0.2 0.2。在数据加载初期这可能不会直接报“no labels found”但会导致后续训练出现诡异问题。务必检查所有标签的class_id。6. 从问题延伸构建健壮的数据准备流水线解决一次“no labels found”是治标建立一个健壮的数据准备流程才是治本。分享一下我的标准流程原始数据收集与清洗将所有图片集中到一个临时文件夹。使用脚本批量检查并修复损坏图片、统一重命名如%06d.jpg、统一格式如转JPEG。划分训练集/验证集使用脚本如sklearn.model_selection.train_test_split按比例如8:2随机划分生成两个文件列表train_list.txt和val_list.txt。务必确保随机种子固定使划分可复现。创建标准目录结构根据train_list.txt和val_list.txt将图片文件分别拷贝到images/train/和images/val/。对标注文件进行同样操作放入labels/train/和labels/val/。这个拷贝过程本身就是一个强制性的“图片-标签”对应性检查。编写data.yaml使用绝对路径。在文件中添加一行注释写明数据集创建日期和版本。验证脚本编写一个独立的Python验证脚本在新数据集上运行。这个脚本应该读取data.yaml检查所有路径是否存在。遍历所有图片确保对应的标签文件存在且非空。随机抽样打开一些标签文件解析内容检查格式和数值范围类别ID是否nc坐标是否在0-1。尝试用OpenCV或PIL读取图片获取其宽高并用标签中的归一化坐标反算出像素坐标在图上画出框可以保存为预览图人工抽查验证标注是否正确。首次训练与监控使用很小的参数如--epochs 1 --batch-size 2跑一个极短的训练周期。观察终端输出确保没有警告并且损失函数开始正常下降。在runs/train/exp目录下查看生成的标签预览图train_batchX.jpg确认数据被正确加载和增强。遵循这样的流程虽然前期多花一点时间但能将数据问题在训练开始前就彻底消灭避免在训练了十几个小时后才发现因为标签问题导致模型什么都没学到那才是最大的时间浪费。数据处理是机器学习项目中最脏最累但也是最关键的一环这里多花一分功夫后面就少受十分折腾。
返回列表