
简介面向计算机视觉开发者与目标检测研究者的YOLO-World学习资料包聚焦开放词汇检测与视觉定位任务提供PyTorch实现、预训练权重以及预训练/微调代码便于复现“先提示后检测”的推理范式并理解大规模检测、定位与图文数据上的训练流程。压缩包共161个文件约2.35MB以110个Python源码和20个Markdown文档为主辅以少量txt、json配置与png/jpg示例图另含Dockerfile、Shell脚本和Notebook兼顾代码阅读、环境搭建与快速试跑。已有883人学习下载适合需要从源码层面掌握YOLO-World结构、开放词表推理和微调方法的中高级读者也可作为课程项目或算法原型的参考底稿。1. 换个品类就要重训一轮这份资料省下的是「改词表」的时间上周一个做仓储视觉的朋友接了个临时活客户在验收前一天要求加两类目标「破损纸箱」和「缠绕膜」。原来的模型是标准闭集检测器类别写死在分类头里加类等于重标数据、重训、重调阈值两天工期直接崩。换成 YOLO-World 这条路子改的是推理时的一份词表不是网络权重十几分钟就能出结果。这份资料给的是源码加文档覆盖模型结构、开放词表设置、自定义词表落盘、重参数化导出到 ONNX 这一整条链路不是一份只会跑 demo 的脚本。它适合两类人手上已经有 YOLO 系检测流水线、需要一个「临时加类不掉链子」的方案以及想搞清楚开放词表检测内部到底在算什么、为什么能零样本出框的人。下面是拆解顺序结构、跑通、速度、排坑、验证。2. 开放词表是怎么跑通的文本嵌入、RepVL-PAN 与两条推理路径2.1 从闭集分类头到文本-视觉对齐被替换的是哪一层先看 YOLOv8 的检测头。解耦头里分类分支最后一层是 1×1 卷积权重形状(num_classes, C, 1, 1)num_classes在训练时就被烧死了推理阶段改不了。YOLO-World 做的事很直接把这个 1×1 卷积的权重矩阵替换成文本编码器输出的类别嵌入矩阵W ∈ R^(N×D)N是当前词表里的类别数D是嵌入维度ViT-B/32 量级是 512。前向传播时特征图上每个位置的特征向量和W做点积得到的就是「这块区域像不像这个词」的相似度分数。这样一换类别就不再是网络的固有属性而是一个运行时输入。你给[person, forklift]它输出两个通道给[crack, stain, dent, oil leak]它输出四个通道。分类头的结构没变只是权重来源变了。资料里的源码在这一点上写得很清楚txt_feats这个张量是贯穿整个前向过程的核心模型对象上挂着它改词表就是改它。理解这一层很关键因为后面所有坑都从这里派生。词表存在模型对象上、不在权重文件里的说法不准确——它会跟着save()一起落盘但你如果只做了set_classes()没保存进程一退就没了。另一个派生问题是文本嵌入的质量直接决定分类质量所以用什么 prompt 模板、用中文还是英文都会实打实影响 mAP这不是玄学。2.2 RepVL-PAN 与 T-CSPLayer文本信息怎么注入多尺度特征光换最后一层卷积效果是不够的。纯点积意味着文本只在最末端参与浅层特征完全靠视觉先验遇到训练集里没有的类别特征响应很弱。YOLO-World 在 neck 上做了文章就是名字里的 RepVL-PAN可重参数化的视觉-语言路径聚合网络。它做了两件事。第一件是 T-CSPLayer。在 PAN 的每个融合阶段对应 P3、P4、P5 三个尺度原本的 CSP 层被改造文本嵌入先沿空间维度广播和图像特征在通道维拼接再走卷积融合。用代码的眼光看就是# 概念示意非官方源码帮助理解 T-CSPLayer 的张量流向 # x: (B, C, H, W) 图像特征 # txt: (B, N, D) 文本嵌入 # 广播后: (B, D, H, W) - 与 x 在通道维 cat # 融合后: (B, C, H, W) 回到原通道数继续往下走拼接带来的代价是通道数临时翻倍所以后面的卷积要把它压回去。这个设计让文本信号在多个尺度上都参与浅层的小目标也能被词表激活。文档里对这段结构的描述比较简略实际读源码时重点盯住txt_feats在哪些层被repeat或expand、拼接位置在残差之前还是之后这两点搞明白了改结构才有下手的地方。第二件是 I-Pooling Attention图像感知的文本嵌入。它拿图像特征做多尺度池化得到一组视觉 token反过来去更新文本嵌入。效果是同一个词在不同场景下的嵌入会微调比如「box」在堆叠场景和散放场景下语义侧重不同。这一路是可选的增强项推理时如果追求极致速度可以关掉但精度会掉一些具体掉多少取决于词表的抽象程度。2.3 离线词表与在线词表两条路径的取舍这是决定工程方案的分水岭很多人在这一步选错然后抱怨「速度慢得没法用」。对比项离线词表offline vocabulary在线词表online vocabulary文本编码次数每个词表只编一次每帧图像都编一次能否重参数化能融成普通 1×1 卷积不能单帧延迟与同规模 YOLOv8 基本持平额外增加文本编码开销运行中换词需要重新 set_classes 重新导出可以逐帧换 prompt导出 ONNX / TensorRT支持基本不支持典型场景产线固定几类、边缘设备部署交互式检索、词表随用户输入变离线模式的做法是先set_classes文本嵌入算出来之后把分类头的点积操作「折叠」成普通的卷积权重之后模型就退化成一个标准的、类别数为 N 的 YOLOv8。这也是「重参数化」三个字在项目名之外的第二层含义。在线模式则保留文本编码器每帧都跑一遍 CLIP 文本分支。我的建议很明确只要类别集合在部署期内是确定的一律走离线 重参数化。在线模式看着灵活但文本编码器跑一次就是几十毫秒级别视频流场景下完全吃不消。只有在做「用户输入一句话、立刻出框」这种交互式 demo 时在线模式才有存在价值。3. 把源码跑起来从权重加载到自定义词表的完整链路3.1 环境和依赖pytorch 与 ultralytics 的版本约束这套东西依赖链很短但版本对不上会以很隐蔽的方式报错。核心就两个包torch和ultralytics。YOLO-World 的权重是.pt格式加载走的是 ultralytics 的YOLOWorld类不是YOLO类别混着用。# 建议单独建环境版本以资料文档里给出的为准 python -m venv yworld source yworld/bin/activate # torch 按你的 CUDA 版本装CPU 版也能跑只是慢 pip install torch torchvision --index-url https://download.pytorch.org/whl/cu121 # ultralytics 版本要支持 YOLOWorld太老的版本没有这个类 pip install ultralytics装完之后先验证一下类能不能导入python -c from ultralytics import YOLOWorld; print(ok)报ImportError就说明版本太低升级ultralytics即可。第二个要注意的是torch和torchvision版本必须配套单独升一个会出现operator torchvision::nms does not exist这种看着像模型问题的报错实际是依赖问题。资料里如果给了 requirements 文件直接照装别自己配。提示CPU 上跑开放词表模型可以出结果但 640 输入下单帧几百毫秒到一秒做功能验证没问题做视频流验证会很痛苦。有条件先用一块入门级显卡把链路跑通。3.2 加载权重与设置自定义词表下面这段是整份资料里最核心的一段代码涵盖加载、设词、推理、结果解析。from ultralytics import YOLOWorld # 1) 加载开放词表权重s 是速度优先m/l/x 精度递增、速度递减 model YOLOWorld(yolov8s-worldv2.pt) # 2) 设置本次任务的词表必须是英文短语用逗号拆开的自然语言描述也可以 classes [ person, cardboard box, damaged cardboard box, pallet, forklift, ] model.set_classes(classes) # 3) 推理imgsz 取 32 的整数倍conf 对开放词表要比闭集模型抬一点 results model.predict( sourcedemo.jpg, imgsz640, conf0.30, iou0.50, device0, verboseFalse, ) # 4) 解析结果boxes.cls 是词表下标要映射回 classes 里的字符串 r results[0] for xyxy, cid, score in zip( r.boxes.xyxy.tolist(), r.boxes.cls.tolist(), r.boxes.conf.tolist(), ): label classes[int(cid)] x1, y1, x2, y2 [round(v, 1) for v in xyxy] print(f{label:24s} {score:.3f} ({x1},{y1})-({x2},{y2}))逐段说清楚逻辑。第一步的权重选择上s和m是最常用的两个档位s在 640 输入、单卡上能跑到实时m精度明显好一截但速度掉一半左右。别一上来就用x除非你的场景对精度极其敏感且不计成本。第二步set_classes是改词表的唯一入口。这里的classes是有序的因为后面boxes.cls出来的就是下标顺序错了标签就全错。实测下来词表里写具体短语比写抽象名词效果好「damaged cardboard box」比「damage」检出率高不少因为 CLIP 文本编码器对完整名词短语的语义更稳。中文词条能跑通但精度下降明显这是文本编码器训练语料的问题不是代码 bug。第三步的conf是个需要反复调的量。闭集模型上 0.25 往往合适开放词表因为相似度分布更平阈值给低了会冒出大量「像但不对」的框给到 0.30 到 0.35 起步更稳。iou保持 0.5 一般够用密集小目标可以压到 0.45。第四步的解析逻辑容易被忽略。r.boxes.cls返回的是浮点数张量直接拿去索引classes会报TypeError必须先int()。另外r.names在set_classes之后会变成list而不是默认的dict有些老代码里写r.names[i]的地方会静默出错改成直接索引classes更省事。3.3 批量推理与结果落盘单图跑通之后实际项目基本都是批量或视频流。批量推理的关键是把模型实例复用起来别在循环里反复构造模型。from pathlib import Path from ultralytics import YOLOWorld model YOLOWorld(yolov8s-worldv2.pt) model.set_classes([person, helmet, safety vest]) img_dir Path(./images) out_dir Path(./outputs) out_dir.mkdir(exist_okTrue) # streamTrue 时返回生成器逐张吐结果内存占用可控 for res in model.predict( sourcestr(img_dir), imgsz640, conf0.30, batch8, streamTrue, saveTrue, projectstr(out_dir), namerun, exist_okTrue, ): src Path(res.path).name counts {} for cid in res.boxes.cls.tolist(): label model.model.names[int(cid)] counts[label] counts.get(label, 0) 1 print(src, counts)batch8是显存和吞吐的平衡点8G 显存的卡上跑s模型、640 输入8 到 16 都可以试。streamTrue在大目录场景下必须开否则 ultralytics 会先把所有结果攒在内存里再返回几千张图能把内存打满。saveTrue配合project和name指定输出目录exist_okTrue避免重复运行时目录冲突报错这个小参数在批量脚本里很值得加。读结果的另一个常见需求是统计每类数量上面用model.model.names拿标签名比外部再维护一份classes更不容易错位。注意这里是model.model.names两层.model第一层是YOLOWorld包装类第二层才是内部的检测模型写一层会拿到外层对象、没有names属性。4. 固定词表与重参数化把推理速度拉回 YOLOv8 档位4.1 set_classes 之后必须 save否则下次加载白干这是整份资料里最容易踩、也最容易被忽视的一点。set_classes()只改了内存里模型对象的txt_feats和names如果你在同一个进程里接着推理一切正常一旦进程退出、重新加载原始.pt词表就回到默认的 80 类COCO 词表。表现就是「昨天还能检出叉车今天全是 person」。正确顺序是设词表、立刻保存、之后统一用保存后的权重。from ultralytics import YOLOWorld model YOLOWorld(yolov8s-worldv2.pt) classes [pallet, forklift, cardboard box, damaged cardboard box] model.set_classes(classes) # 把词表和文本嵌入一起写进新的权重文件 model.save(pallet_world_fixed.pt)保存后的文件里带着txt_feats和对应的names重新加载就能直接推理from ultralytics import YOLOWorld m YOLOWorld(pallet_world_fixed.pt) print(m.model.names) # 检查词表是不是你要的那几类 print(m.model.txt_feats.shape) # (1, N, D)N 是类别数D 是嵌入维度txt_feats的形状值得看一眼。如果N和你设的类别数对不上说明某一步被覆盖了大概率是保存之后再调了一次set_classes但没重新save。这个检查我一般写在部署脚本的启动自检里几行代码能省掉半小时的排查。4.2 导出 ONNX / TensorRT哪些设置是硬约束离线词表模式下模型已经退化成固定类别的检测器导出这条路是通的。但有几个约束必须满足否则导出的图在推理端结果对不上。from ultralytics import YOLOWorld # 一定要先固定词表再导出顺序反了导出的就是默认 80 类 model YOLOWorld(pallet_world_fixed.pt) model.export( formatonnx, opset12, imgsz640, simplifyTrue, dynamicFalse, halfFalse, )opset12是个稳妥选择太低不支持某些算子太高部分推理引擎跟不上。dynamicFalse建议先用固定尺寸验证通链路动态 shape 会引入额外的对齐工作而且部分算子对动态维支持不好。halfFalse在导出阶段先关掉FP16 放到推理引擎那边做出问题时更容易定位是哪一层的问题。TensorRT 这条路类似把format换成engine前提是本机装了 TensorRT 并且版本和 CUDA 匹配。导出后第一件事是拿同一张图分别用 PyTorch 权重和引擎跑一遍比对框的数量和坐标偏差。偏差在 1 到 2 个像素内属正常框数差很多就说明导出时类别没固定住或者后处理参数不一致。导出格式适用场景注意点ONNX跨平台、跨推理引擎先固定词表opset 选 12 左右TensorRT engine固定显卡的边缘部署引擎与显卡型号绑定不能跨卡拷OpenVINOCPU 侧加速输入尺寸固定动态 shape 支持有限TorchScript保留 PyTorch 生态文本分支未剥离时容易失败最后一行要重点说。如果模型还保留在线词表能力没走重参数化导出 TorchScript 或 ONNX 时大概率会失败或丢掉文本分支导出成功也只是一个固定了当时那次文本嵌入的静态图。所以导出的前置条件永远只有一个词表已固定、已保存、已重参数化。4.3 视频流场景的参数取舍视频流和批量图片的差别在于延迟和吞吐是两套指标参数调法完全不同。做实时预览imgsz降到 480 甚至 416conf相应抬到 0.35 补精度帧间复用模型实例别每帧set_classes。做离线批处理imgsz拉到 800 或 960batch尽量吃满显存conf可以降到 0.25 换召回。一个实测经验词表规模对速度的影响是非线性的。10 类以内离线模式下和同规模 YOLOv8 几乎没差到 100 类分类头那层 1×1 卷积的输出通道从 80 涨到 100开销开始可见到 1000 类即便重参数化之后那层卷积的 FLOPs 也不容忽视建议按实际需求裁剪词表别把整个标签体系一股脑塞进去。注意改词表之后已经导出的 ONNX / engine 全部作废必须重导。版本管理上把「权重文件 词表文件 引擎文件」绑成一个版本号别只改其中一个。5. 避坑与排查五类高频翻车现场5.1 现象换进程后类别全变回 COCO 80 类原因基本只有一个set_classes()之后没有save()或者save()之后又在同一进程里调了一次set_classes但没重新保存导致落盘的是前一份词表。还有一种更隐蔽的情况导出脚本里重新YOLOWorld(原始权重)加载忘了这次加载是原始的词表已经丢了。解决方式是把「设词表 → 保存 → 校验」写成一个固定函数部署流程里只允许通过这个函数产出权重def build_fixed_weights(base_ckpt: str, classes: list[str], out_ckpt: str): from ultralytics import YOLOWorld m YOLOWorld(base_ckpt) m.set_classes(classes) m.save(out_ckpt) # 立刻回读校验避免带着错误配置往下走 chk YOLOWorld(out_ckpt) assert list(chk.model.names) classes, f词表写入失败: {chk.model.names} return out_ckptassert那一行是关键。少了它错误会一直飘到推理结果里才暴露那时候你已经在怀疑模型精度了。5.2 现象模型什么框都不出或者框一大堆但标签全错前者通常是词表用了中文或者类别名过于抽象比如单个词「defect」。文本编码器对完整英文名词短语的响应最稳抽象词和中文都会让相似度分布整体偏低conf0.3直接把它们全过滤掉。后者则相反是词表里几个类别语义太近比如同时放「box」和「cardboard box」短词会到处抢框。解决上先把conf降到 0.05 看原始分布如果连低阈值都没框问题在文本侧换英文短语重试如果低阈值下框很多但高阈值全乱问题在词表区分度把语义相近的项合并或改写得更具体。我一般的习惯是词表里每两项之间至少有一个明确的修饰词差异比如「worker with helmet」对「worker without helmet」。5.3 现象imgsz设成任意值报错或者结果明显偏移YOLO 系列的骨干是下采样 32 倍的输入尺寸必须是 32 的整数倍否则内部 padding 的对齐方式会让特征图和原图坐标映射出偏差。表现是框整体偏移几十像素或者小目标直接漏检。解决很直接imgsz只取 416、480、512、640、800、960 这几个值。如果你确实需要非标准尺寸先在原图上做 letterbox 到最近的 32 倍数再推理最后把框坐标反算回原图。ultralytics 内部对标准尺寸做了 letterbox但自定义预处理如果和它对不上就会出现「训练时好好的推理全偏」这种血泪情况。5.4 现象微调之后精度不升反降甚至比零样本还差这个坑在于数据集配置里的names顺序和set_classes的顺序不一致。YOLO 训练时标签是按索引读的索引 0 在数据集里是「crack」在词表里是「stain」模型学到的就是错位的映射越训越偏。解决方式是让custom.yaml的names和代码里的classes从同一份配置生成别手工维护两份import yaml classes [crack, stain, dent] with open(custom.yaml, w, encodingutf-8) as f: yaml.safe_dump( {path: ./dataset, train: images/train, val: images/val, names: {i: n for i, n in enumerate(classes)}}, f, allow_unicodeTrue, )顺手把这份classes也写进权重保存流程三处同源错位问题从根上消掉。5.5 现象视频流跑几分钟后显存爆掉model.predict每帧调用一次如果每次都传saveTrue或者保留Results对象引用显存会缓慢累积。另外在循环里反复set_classes也会不断新建文本嵌入张量旧的不释放。解决是把推理循环写干净模型实例在循环外构造一次循环内只调predict不保存Results引用streamTrue只在批量图片场景开、视频流逐帧传入更可控。需要可视化的帧立刻转成 numpy 或写入视频流别让它挂在 Python 对象上等 GC。这类问题排查起来没有明确报错只能靠nvidia-smi看曲线涨了就是泄漏。6. 在自己的数据上微调一套能对上数的验证流程零样本能出框但对特定工业缺陷的精度往往不够微调是绕不开的一步。资料里给了训练入口实际跑之前有几个参数值得先说清楚。from ultralytics import YOLOWorld model YOLOWorld(yolov8s-worldv2.pt) model.set_classes([crack, stain, dent]) # 必须与 custom.yaml 的 names 同源 model.train( datacustom.yaml, epochs80, imgsz640, batch8, patience20, # 20 轮无提升就早停微调场景很有效 lr00.001, # 比从头训低一个量级避免破坏预训练的视觉特征 freeze10, # 冻结骨干前 10 层小数据集上明显更稳 cacheram, # 数据集不大时直接进内存省掉 IO 瓶颈 )lr0和freeze是微调的两个关键旋钮。学习率按从头训练的十分之一起步样本量小于两千张时再往下压freeze冻结骨干前若干层让模型只调整 neck 和检测头收敛快也更不容易过拟合。patience一定要设微调场景下验证集指标往往在第 30 到 50 轮就见顶后面纯属浪费卡时。训完别只看 loss 曲线跑一次带低阈值的val看真实分布metrics model.val(datacustom.yaml, splitval, conf0.001, iou0.6) print(mAP50:, round(metrics.box.map50, 4)) print(mAP50-95:, round(metrics.box.map, 4))conf0.001是为了让 PR 曲线从最左端开始算得到的 mAP 才准iou0.6在工业缺陷这类框偏紧的任务上比默认 0.5 更严格一点能提前暴露定位不准的问题。把微调后的 mAP50 和零样本基线放在一张表里对比才知道这轮训练到底值不值。指标零样本基线微调后判读mAP500.420.71有实质提升微调值得mAP50-950.230.44定位质量同步改善单帧延迟21ms21ms离线模式下不变误检数每图3.60.9词表歧义被训练消掉最后一句是踩出来的教训拿到一份开放词表模型的资料别急着把它当万能检测器铺到所有场景。我现在的习惯是任何一次词表变更都强制走一遍「设词表 → 保存 → 回读校验 → 用同一张图对比前后结果」四步一步不落因为翻车从来不是发生在模型结构上而是发生在「我以为保存过了」的那一刻。希望帮到你。本文还有配套的精品资源点击获取