# How the `yolo` Command Works ## Overview 本文描述当前仓库里 `yolo ...` CLI 的真实入口流程,重点解释: - console script 是如何注册的 - `entrypoint()` 如何解析参数 - task / mode / model 如何决定最终执行哪个类和哪个方法 主要代码位置: - `pyproject.toml` - `ultralytics/cfg/__init__.py` - `ultralytics/engine/model.py` - `ultralytics/models/yolo/model.py` ## 1. Console Script 注册 `pyproject.toml` 里注册了两个命令: ```toml [project.scripts] yolo = "ultralytics.cfg:entrypoint" ultralytics = "ultralytics.cfg:entrypoint" ``` 因此终端里的 `yolo ...` 最终会调用: ```python ultralytics.cfg.entrypoint() ``` ## 2. `entrypoint()` 收集原始参数 `entrypoint(debug: str = "")` 的第一步是: ```python args = (debug.split(" ") if debug else ARGV)[1:] ``` 也就是去掉命令名本身,保留后面的参数,例如: ```text yolo detect train model=yolo26n.pt data=coco8.yaml epochs=100 ``` 会被看成: ```python ["detect", "train", "model=yolo26n.pt", "data=coco8.yaml", "epochs=100"] ``` 如果用户没有传任何参数,CLI 会直接打印帮助信息并返回。 ## 3. 特殊命令优先处理 当前代码里的特殊命令包括: - `help` - `checks` - `version` - `settings` - `cfg` - `hub` - `login` - `logout` - `copy-cfg` - `solutions` 这些命令在参数循环里一旦命中,就会立刻执行并 `return`,不会再走模型实例化。 ## 4. 常规参数解析 `entrypoint()` 逐个处理参数,核心规则是: 1. `merge_equals_args(args)` 先把 `arg = value` 这种带空格写法并回去 2. `--foo` 会被警告后改写成 `foo` 3. 若参数里有 `=`,走 `parse_key_value_pair()` 4. 若参数等于某个 task,写入 `overrides["task"]` 5. 若参数等于某个 mode,写入 `overrides["mode"]` 6. 若参数是布尔型默认配置键,则自动设为 `True` 例如: ```text yolo detect train model=yolo26n.pt data=coco8.yaml epochs=100 ``` 会被解析成近似: ```python overrides = { "task": "detect", "mode": "train", "model": "yolo26n.pt", "data": "coco8.yaml", "epochs": 100, } ``` ## 5. mode 校验 解析完成后会先确定 `mode`: - 若没传,则回退到 `DEFAULT_CFG.mode`,否则默认 `predict` - 若传了非法值,直接报错 当前合法 `mode` 包括: - `train` - `val` - `predict` - `export` - `track` - `benchmark` ## 6. task 校验 然后处理 `task`: - 若传了 task,就检查是否合法 - 若没传但给了模型,后面会从模型侧再推断 - 若传了 task 但没传 model,会根据 `TASK2MODEL` 自动补默认模型 当前合法 `task` 包括: - `detect` - `segment` - `classify` - `pose` - `obb` ## 7. 模型实例化分派 接下来 `entrypoint()` 会取出 `model`,根据文件名 stem 做分派: ```python stem = Path(model).stem.lower() ``` 当前分支逻辑是: - stem 含 `rtdetr` → `RTDETR(model)` - stem 含 `fastsam` → `FastSAM(model)` - stem 含 `sam_` / `sam2_` / `sam2.1_` → `SAM(model)` - 否则 → `YOLO(model, task=task)` 所以这里不是简单的“文件名里含 `sam` 就走 SAM”,而是更窄的前缀判断。 ## 8. task 与 model 冲突时的处理 模型实例化后,CLI 还会做一次 task 对齐: - 如果用户显式传的 `task` 和模型自身 `model.task` 冲突 - 则忽略用户传入的 task - 以模型解析出来的 task 为准 这是为了避免类似“拿分类模型跑 detect task”这种冲突配置继续往后传播。 ## 9. mode 相关默认参数补全 在真正执行前,CLI 还会按 mode 补一些默认参数: - `predict/track` 且没传 `source`:自动补默认图像 - `train/val` 且没传 `data` 且不是 `resume`:自动补默认数据集 - `export` 且没传 `format`:自动补 `torchscript` ## 10. 真正执行 最后一步就是: ```python getattr(model, mode)(**overrides) ``` 例如 `mode="train"` 时,等价于: ```python model.train(**overrides) ``` 如果当前是普通 YOLO 检测模型,这会继续进入: 1. `YOLO.train()` 2. 实例化对应 trainer 3. `trainer.train()` ## Example 命令: ```bash yolo detect train model=yolo26n.pt data=coco8.yaml epochs=100 lr0=0.01 ``` 主流程可以概括成: 1. `yolo` 命令调用 `ultralytics.cfg.entrypoint()` 2. 解析得到 `task=detect, mode=train, model=yolo26n.pt, ...` 3. 根据 `model` 实例化 `YOLO("yolo26n.pt", task="detect")` 4. 执行 `model.train(data="coco8.yaml", epochs=100, lr0=0.01, ...)` 5. `YOLO.train()` 再去构建对应 trainer 并开始训练 ## 一句话总结 `yolo ...` CLI 的本质是: - 先在 `entrypoint()` 里把命令行翻译成 `overrides` - 再根据 `model` 选择合适的 model class - 最后通过 `getattr(model, mode)(**overrides)` 分发到 `train/val/predict/export/...` 它本身不关心 Ground3D、ROI 或 virtual-camera 这些任务细节;那些差异是在具体 trainer / dataset / loss 里展开的。