单目3D初始代码
This commit is contained in:
213
docs/yolo_command_flow.md
Executable file
213
docs/yolo_command_flow.md
Executable file
@@ -0,0 +1,213 @@
|
||||
# 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 里展开的。
|
||||
Reference in New Issue
Block a user