YOLOv9标签映射修改:自定义类别名称方法
你训练完自己的YOLOv9模型,推理时框里却只显示数字0、1、2……而不是“person”“car”“dog”这些看得懂的类别名?或者你用官方预训练权重做检测,结果输出全是“0”“1”“2”,完全不知道对应什么物体?别急——这不是模型坏了,而是类别名称映射没改对。本文就带你从零开始,手把手在YOLOv9官方镜像中完成标签映射的完整修改,让检测结果真正“说人话”。
整个过程不碰模型结构、不重写训练逻辑,只改3个关键位置,5分钟内搞定。无论你是刚跑通第一个demo的新手,还是正在调试自定义数据集的开发者,都能照着操作一步到位。
1. 为什么YOLOv9默认只显示数字?
YOLOv9(包括v5/v8)内部用整数索引表示类别,比如0代表第1个类别,1代表第2个类别……这个映射关系由一个叫names的列表控制。但很多用户没意识到:这个列表既存在于训练配置文件中,也硬编码在推理脚本里,还可能被权重文件自带的names覆盖。三者不一致,就会出现“训练时叫cat,推理时显示0”的混乱。
更关键的是,YOLOv9官方代码(尤其是detect_dual.py这类双分支检测脚本)默认读取权重文件中保存的names,而官方发布的yolov9-s.pt里存的是COCO数据集的80类名称。如果你的数据集只有3类,或者类别顺序不同,直接加载就会错位。
所以,修改不是“选一个地方改”,而是同步校准三个环节:数据配置、推理脚本、权重兼容性。
2. 修改前准备:确认当前环境与路径
我们使用的镜像是基于YOLOv9官方代码构建的开箱即用环境,所有路径和依赖已预置好。请先确保你已完成以下两步:
- 启动镜像后,执行
conda activate yolov9激活专用环境 - 进入代码根目录:
cd /root/yolov9
验证当前状态:运行以下命令查看官方权重中保存的类别名
python -c "import torch; w = torch.load('./yolov9-s.pt', map_location='cpu'); print('Names in weight:', w['model'].names if 'names' in w['model'].__dict__ else 'Not found')"你会看到类似
['person', 'bicycle', 'car', ...]的80个COCO类别——这就是当前推理时默认显示的内容来源。
3. 三步完成自定义类别名称映射
3.1 第一步:修改data.yaml中的names字段(训练与评估依据)
YOLOv9通过data.yaml文件定义数据集结构。即使你只是做推理,这个文件也是类别映射的“权威源头”。
打开/root/yolov9/data.yaml(如果不存在可复制一份示例):
cp data/coco.yaml data/mydataset.yaml nano data/mydataset.yaml找到names:字段,将其改为你的实际类别。例如,你要检测“apple”“banana”“orange”三类水果:
train: ../mydata/images/train val: ../mydata/images/val test: ../mydata/images/test nc: 3 # number of classes names: ['apple', 'banana', 'orange'] # ← 唯一需要修改的行!必须是Python列表格式关键提醒:
nc(number of classes)必须与names列表长度严格一致- 类别名用英文单引号包裹,逗号后加空格(符合YAML语法)
- 不要写成
names: apple, banana, orange或names: [apple, banana, orange]——会报错
改完保存,这一步确保了:
✔ 训练时模型知道每个数字对应哪个名字
✔ 评估指标(mAP)能正确按名统计
✔ 后续推理脚本能从中读取标准映射
3.2 第二步:修改detect_dual.py中的names赋值(推理显示核心)
YOLOv9的detect_dual.py是主推理脚本,它默认优先从权重文件读names。但我们要强制它使用你定义的data.yaml里的names,避免被预训练权重“带偏”。
打开/root/yolov9/detect_dual.py,定位到约第170行附近的if __name__ == '__main__':下方,找到模型加载后的处理段。在model = attempt_load(weights, map_location=device)之后,插入以下代码:
# --- 新增:强制从data.yaml加载names,覆盖权重中保存的names --- if opt.data: # 如果指定了data.yaml路径 import yaml with open(opt.data) as f: data = yaml.safe_load(f) if 'names' in data and isinstance(data['names'], list): model.names = data['names'] print(f" 已从 {opt.data} 加载自定义类别名: {model.names}") else: print(" data.yaml 中未找到有效的 names 字段,继续使用权重内置names") # ---同时,确保你在运行命令时显式传入--data参数,指向你修改好的yaml:
python detect_dual.py \ --source './data/images/horses.jpg' \ --img 640 \ --device 0 \ --weights './yolov9-s.pt' \ --data './data/mydataset.yaml' \ # ← 关键!告诉脚本去哪读names --name yolov9_s_custom_detect效果验证:
运行后你会看到控制台打印已从 ./data/mydataset.yaml 加载自定义类别名: ['apple', 'banana', 'orange'],且生成的检测图中框标签将显示为文字而非数字。
3.3 第三步:保存自定义权重(一劳永逸,避免每次传参)
如果你已经用自定义数据集重新训练过模型,新权重文件会自动保存names。但若只是用官方权重做迁移推理,可以手动“注入”names:
python -c " import torch w = torch.load('./yolov9-s.pt', map_location='cpu') # 从data.yaml读取names import yaml with open('./data/mydataset.yaml') as f: data = yaml.safe_load(f) w['model'].names = data['names'] torch.save(w, './yolov9-s-custom.pt') print(' 自定义权重已保存:yolov9-s-custom.pt') "之后,你就可以直接用新权重,无需再传--data:
python detect_dual.py --source './data/images/test.jpg' --weights './yolov9-s-custom.pt'优势:
- 推理命令更简洁
- 权重文件自带语义,分享给同事时不会因缺少data.yaml而显示错误标签
- 兼容所有YOLOv9相关脚本(train_dual.py、val.py等)
4. 常见问题与避坑指南
4.1 问题:改了data.yaml,但检测结果还是显示0/1/2?
→ 检查是否在detect_dual.py中漏加了--data参数,或新增代码未生效。
→ 运行时看控制台是否有已从...加载自定义类别名提示。没有则说明代码未执行到。
4.2 问题:names改了,但绘图时中文乱码或显示方块?
YOLOv9默认使用DejaVuSans字体,不支持中文。如需中文标签:
- 下载中文字体(如
simhei.ttf)放入/root/yolov9/目录 - 在
detect_dual.py中搜索cv2.FONT_HERSHEY_SIMPLEX,替换为:from PIL import ImageFont, ImageDraw, Image font = ImageFont.truetype('simhei.ttf', 32) # 调整字号 - 修改绘图逻辑(需重写
plot_one_box函数),此处不展开——强烈建议英文命名,避免字体、编码、宽高计算等连锁问题。
4.3 问题:训练时类别名正确,但验证结果(val.py)里还是数字?
val.py同样依赖data.yaml,确保运行时指定--data:
python val.py --data ./data/mydataset.yaml --weights ./runs/train/yolov9-s/weights/best.pt4.4 问题:修改后报错AttributeError: 'NoneType' object has no attribute 'names'
→ 检查model = attempt_load(...)是否成功。常见原因:
- GPU显存不足导致加载失败(加
--device cpu测试) - 权重路径错误(用
ls ./yolov9-s.pt确认存在) - PyTorch版本不匹配(本镜像为1.10.0,勿升级)
5. 进阶技巧:动态切换类别名(多任务场景)
假设你有一个通用模型,需在“水果检测”和“工业零件检测”两个任务间切换。不必反复修改文件,可用以下方式动态传入:
# 定义环境变量,在命令中直接注入 NAMES="['bolt','nut','washer']" python detect_dual.py \ --source './images/' \ --weights './yolov9-s.pt' \ --name industrial_detect然后在detect_dual.py中,于模型加载后添加:
import os if os.getenv('NAMES'): try: import ast model.names = ast.literal_eval(os.getenv('NAMES')) print(f"🔧 动态加载NAMES环境变量: {model.names}") except: print("❌ NAMES环境变量格式错误,请用Python列表格式")这样,同一份代码+权重,通过环境变量即可秒切任务,适合部署到Docker或CI/CD流程。
6. 总结:标签映射修改的本质与要点
YOLOv9的类别名不是“写死”的,而是一套三层映射系统:
- 数据层(data.yaml):定义“真实世界类别” → “数字ID”的原始映射
- 模型层(weights):存储训练时确定的映射快照,用于快速加载
- 应用层(detect_dual.py等):决定运行时采用哪套映射,是最终显示效果的开关
你只需抓住三个动作:
- 在
data.yaml中写对names和nc - 在推理脚本中强制从yaml读取,覆盖权重默认值
- 用
--data参数或保存新权重,让修改落地
改完你会发现:检测框旁不再是冰冷的数字,而是清晰的“apple”“banana”——这才是AI该有的样子。下一步,你可以基于此进一步优化:给不同类别配不同颜色框、添加置信度阈值过滤、导出带标签的JSON结果……而这一切,都建立在“名字对得上”这个最基础却最关键的环节之上。
获取更多AI镜像
想探索更多AI镜像和应用场景?访问 CSDN星图镜像广场,提供丰富的预置镜像,覆盖大模型推理、图像生成、视频生成、模型微调等多个领域,支持一键部署。