# 脚本

包含在 github 根文件夹中的训练、验证、推理和检查点清理脚本。目前这些脚本未包含在 pip 发布包中。

训练和验证脚本源自 [PyTorch Imagenet Examples](https://github.com/pytorch/examples) 的早期版本。随着时间推移，我添加了大量功能，其中包括基于 [NVIDIA's APEX Examples](https://github.com/NVIDIA/apex/tree/master/examples) 的 CUDA 特定性能优化。

## 训练脚本

训练参数的种类很多，并非所有选项的组合（甚至某些选项本身）都经过了充分测试。对于训练数据集文件夹，请指定包含 `train` 和 `validation` 文件夹的根目录。

对于单节点运行，请使用 `torchrun`，每个 GPU 分配一个进程。例如，在将 [MobileNetV4 配置](hparams#mobilenetv4-conv-medium) 保存为 `mobilenetv4.yaml` 之后：

```bash
torchrun --standalone --nproc_per_node=2 train.py \
  --config mobilenetv4.yaml --data-dir /data/imagenet --experiment mobilenetv4-cm
```

`./distributed_train.sh 2 ...` 是 `torchrun` 的便捷封装。对于单 GPU，请使用 `python train.py ...`，并按照 [HParams](hparams#adapting-a-recorded-config) 中的说明调整批大小和学习率。

    `--amp` 使用 PyTorch 原生混合精度，默认采用 float16。在支持的硬件上，请使用 `--amp-dtype bfloat16`。以下示例使用原生 PyTorch AMP 和 DDP，无需 Apex。

### 配置文件

任何训练参数都可以通过 `--config` 在 YAML 文件中指定，键名为带下划线的参数名（`--weight-decay` 变为 `weight_decay`）。命令行参数会覆盖文件中的设置。每次运行都会将完整的生效参数集写入其输出文件夹中的 `args.yaml`，该文件本身也是一个有效的 `--config`，因此可以使用 `python train.py --config output/train/my-run/args.yaml --experiment my-run-2` 来复现某次运行。该文件还会记录 `output`、`experiment` 和 `resume`，因此如果不提供新的实验名称，命令会写入原始文件夹；而使用 `--resume auto` 则会继续原始运行，而不是重复它。配置键会针对脚本的参数进行验证：未知键会发出警告，而无效选项或数值参数的非数值值则会报错。

### 恢复和里程碑检查点

每次运行都会写入 `last.pth.tar`（始终为最新 epoch，包含模型、优化器、EMA 和 AMP scaler 状态）、`model_best.pth.tar`，以及由 `--checkpoint-hist` 限制数量的 `checkpoint-N.pth.tar` 文件排名历史。

* `--resume PATH` 会恢复该状态，并从检查点的 epoch 继续执行训练计划。这是从现有检查点启动新实验文件夹的常用方式。
* `--resume auto` 支持原地恢复：当设置了 `--output` 和 `--experiment` 时，如果该实验文件夹中存在 `last.pth.tar`，则从中继续训练，否则启动一次全新的运行。中断后可以使用相同的命令重新运行。检查点历史、最佳指标跟踪以及 `summary.csv` 都会保持一致地延续，因此 `--checkpoint-hist` 的淘汰机制和 `model_best` 在重启后依然正确。
* `--stop-epoch N` 会在完成 `N` 个 epoch 后停止，但不会更改由 `--epochs` 设置的计划。最后一个 epoch 始终会被评估并保存检查点，因此可以分段进行训练。
* `--save-epochs N [N ...]` 会在排名历史之外保留不可变的 `milestone-N.pth.tar` 快照（N 为已完成的 epoch 数）。在文件系统允许的情况下，它们通过硬链接从 `last.pth.tar` 创建，因此不占用额外空间，并且会在这些 epoch 强制进行一次评估。

这些功能共同使得手动运行 warmup-stable-decay (WSD) 实验变得轻而易举：只需训练一次稳定的主干，然后即可基于其里程碑，利用完整的优化器状态分支出独立的冷却阶段。

```bash
# stable trunk through 30 completed epochs, keeping milestones at 18, 24 and 30. The 1 epoch decay is never reached.
python train.py --config configs/vit.yaml --sched wsd --sched-on-updates --epochs 31 --decay-epochs 1 \
    --stop-epoch 30 --save-epochs 18 24 30 --output output/wsd --experiment stable --resume auto

# a 6 epoch 1-sqrt cooldown branched from epoch 24, i.e. a 30 epoch run that shares the first 24 epochs with the trunk
python train.py --config configs/vit.yaml --sched wsd --sched-on-updates --epochs 30 --decay-epochs 6 --lr-decay-fn 1-sqrt \
    --resume output/wsd/stable/milestone-24.pth.tar --output output/wsd --experiment decay-p24-d6
```

衰减阶段占据调度计划的最后 `--decay-epochs` 个 epoch，因此从已完成的 `P` 个 epoch 开始进行 `D` 个 epoch 的冷却，对应参数为 `--epochs P+D --decay-epochs D`（当使用 `--warmup-prefix` 时，需从 `--epochs` 中减去 `--warmup-epochs`）。下方的 `sweep.py` 脚本可自动完成此操作。

## 超参数扫描

`sweep.py`（PyTorch 2.5 或更高版本）针对任意 `train.py` 参数规划并执行扫描，可选地支持 WSD 分支。它为每个任务写入一个完整的 `train.py --config` 文件，外加一个包含任务依赖图的 `plan.json`，然后在本地 GPU 池上运行就绪的任务。在 worker 运行期间，该计划保持不变。每个任务都是一次普通的 `train.py` 调用，拥有独立的实验文件夹，且每个生成的 `run.sh` 都可以手动运行或交给其他调度器处理。

快速扫描无需配置文件。搜索轴来自`--sweep`，基础配置来自`--`之后的尾部`train.py`参数：

```bash
python sweep.py run --name lr-wd --sweep lr=1e-4,3e-4,1e-3 --sweep weight_decay=0.05,0.1 --gpus 0,1,2,3 \
    -- --model vit_small_patch16_224 --data-dir /data/imagenet --sched cosine --epochs 100 --amp
```

`--gpus` 列出要运行的 GPU，当 `CUDA_VISIBLE_DEVICES` 被设置时（例如在 Slurm 分配中），它作为 `CUDA_VISIBLE_DEVICES` 的索引。

WSD 扫描在多个冷却阶段之间共享每个超参数组合的一个稳定主干。对于以下每个学习率，主干将运行至 30 个 epoch，并在 18、24 和 30 处设置里程碑，从每个里程碑分支出一个 6 个 epoch 的冷却阶段，总共 4 个主干和 12 个冷却阶段，而非 12 次完整运行。一旦其里程碑存在，冷却阶段便会立即开始，同时主干继续运行。需要多于一块 GPU 的任务使用 `torchrun` 启动：

```bash
python sweep.py run --name vit-wsd --sweep lr=1e-3,5e-4,2e-4,1e-4 \
    --wsd-points 18 24 30 --wsd-decay-epochs 6 --wsd-decay-fn 1-sqrt --gpus-per-job 2 --gpus 0,1 \
    -- --model vit_small_patch16_224 --data-dir /data/imagenet --warmup-epochs 3 --warmup-prefix --amp
```

规划器拥有以下标志，因此无法在命令行中指定或通过扫描获取。

在每次扫描中：

* `--output`
* `--experiment`
* `--resume`
* `--stop-epoch`
* `--save-epochs`

此外，在 WSD 扫描中：

* `--sched`（也可以指定为 `wsd`，规划器无论如何都会将其设置为该值）
* `--epochs`
* `--decay-epochs`
* `--lr-decay-fn`

YAML 规范支持带分布的随机搜索、成对（zipped）轴、跨种子的重复以及基础配置文件，有关模式请参见 `sweep.py` 顶部的文档字符串。`python sweep.py plan spec.yaml` 会写入配置和 `graph.dot`，而不会运行任何内容。

扫描结果保存在 `output/sweeps/<name>/` 中，相对于 spec 文件（对于命令行扫描，则相对于工作目录），每个试验对应一个文件夹，文件夹名称基于扫描值（`trial-000-lr0.001`），每个文件夹包含该任务的实验文件夹。每个保留的检查点都是包含优化器状态的完整副本，因此扫描任务除了 `last.pth.tar` 和 `model_best.pth.tar` 外，还会保留一个带排名的检查点（`--checkpoint-hist 1`），并且 WSD 扫描会在每次更新时推进调度（`--sched-on-updates`）。在基础配置中设置任一选项都会覆盖默认行为。在运行期间或运行结束后：

```bash
python sweep.py status output/sweeps/vit-wsd    # job states and progress
python sweep.py results output/sweeps/vit-wsd   # ranked endpoint metrics, written to results.csv
```

结果在相同预算下比较各次试验。WSD 扫描中，每行对应一次试验，每列对应一个冷却期（`p24-d6` 表示从 24 个 epoch 开始进行 6 个 epoch 的冷却），展示的是多次重复的平均最终指标，并标记了每列中的最佳值。普通扫描则根据每个 epoch 预算内的最终指标进行排名。`results.csv` 包含所有任务（包括主干段），并记录了每个任务的最佳 epoch。在没有验证集的情况下，排名将回退到使用训练损失，并且可以使用 `validate.py` 对端点检查点进行评估。

在已中断的 sweep 上再次运行 `sweep.py run` 即可恢复：已完成的作业会被跳过，而部分完成的作业将从其自身的 `last.pth.tar` 继续。多台机器上共享 sweep 文件夹的 worker 可以各自运行它。它们通过 `state/locks` 下的 `flock` 锁进行协调，每个作业的每个训练进程也会持有这些锁，因此，只要某个作业中仍有进程未退出，该作业既不会被重新启动，也不会被重新规划。由多台机器共享的 sweep 需要一个在所有机器上都能正常工作的 `flock` 文件系统，例如以 `-o flock` 选项挂载的 NFSv4 或 Lustre。仅支持本地锁定的挂载无法在机器间进行协调，因此请从每个客户端检查锁定情况。停止 worker（Ctrl-C，或来自 `scancel` 的 SIGTERM）会停止其作业，而手动运行某个作业的 `run.sh` 则会绕过协调机制。

`state/locks` 下的锁文件是永久性的：切勿通过删除或替换它们来清除繁忙的 sweep。请改为停止或等待其 worker 和训练进程结束。`running (gone)` 状态表示训练锁已释放，但尝试尚未完成协调；请运行一个 worker 以解决该问题并恢复未完成的工作。`status` 和 `results` 不会修复作业状态。

Worker 在启动时读取一次计划。若要更改 sweep，请停止其所有 worker 和训练进程，使用 `--force` 重新制定计划，然后再次启动它们。在任何作业开始之前，计划中的任何内容都可以更改。之后，其训练作业只能被扩展：在现有作业之后添加更多试验、冷却期或 WSD 点，其中每个 trunk 都从其停止的位置继续。对现有训练作业的更改需要一个新的 sweep。生成的路径在计划写入时即被固定，因此请在新位置重新制定计划，而不是移动 sweep 文件夹。

如果规划过程被中断，工作进程会检测到来自不完整规划写入的配置或启动器，并拒绝启动。请使用 `--force` 重新运行原始的规划命令以完成或修复规划，然后重新启动工作进程。每次成功的规划写入都会在 `plan.json` 中发布一个对应的生成版本，包括配置和启动器；如果规划未发生变化且完整，则不会写入任何内容。

例如，要将上述 CLI WSD 扫描扩展到 36 个稳定 epoch，请停止其 worker 和 job，然后重复原始的规划参数，并在末尾追加后续的点：

```bash
python sweep.py plan --force --name vit-wsd --sweep lr=1e-3,5e-4,2e-4,1e-4 \
    --wsd-points 18 24 30 36 --wsd-decay-epochs 6 --wsd-decay-fn 1-sqrt --gpus-per-job 2 \
    -- --model vit_small_patch16_224 --data-dir /data/imagenet --warmup-epochs 3 --warmup-prefix --amp
python sweep.py run output/sweeps/vit-wsd --gpus 0,1
```

对于 YAML 扫描，请在原始规范中追加该点，并在重启前运行 `python sweep.py plan spec.yaml --force`。请显式保留 `wsd.decay_epochs` 且保持不变（本例中为 `[6]`）：其默认值为最终点的 20%，因此延长扫描范围可能会改变现有的冷却期。请保留现有的试验 ID；重新排序或扩展网格轴可能会导致试验重新编号。如果使用 `lr_noise`，主干的调度取决于其扫描范围，因此为已启动的扫描添加后续点需要创建新的扫描。

训练设置应包含在计划中：使用规范中的 `train` 块，或在创建 CLI 扫描时 `--` 之后的参数。不存在运行时训练覆盖。报告设置（例如排名指标）和输出设置（`checkpoint_hist`、`log_wandb`、`wandb_project`、`wandb_tags`）可以在离线状态下通过 `--force` 进行修改，而无需重置任务状态或更改训练标识。输出设置适用于未来的尝试；已完成的任务不会被重新运行。

`sweep.py` 需要 Unix `flock`：已在 Linux 上测试，预期在 macOS 上可用但未经测试。不支持原生 Windows；请使用 WSL2。如果网络分区持续时间超过 NFS 租约，锁将失效：失去锁的断开进程可能继续训练，因此另一台机器可以启动一个重叠的尝试。

## 验证 / 推理脚本

验证和推理脚本的用法相似。一个在验证集上输出指标，另一个在 csv 中输出 topk 类别 id。请指定包含验证图像的文件夹，而不是像训练脚本那样指定基础文件夹。

使用模型的预训练权重（如果存在）进行验证：

```bash
python validate.py --data-dir /imagenet/validation/ --model seresnext26_32x4d --pretrained
```

要从检查点运行推理：

```bash
python inference.py --data-dir /imagenet/validation/ --model mobilenetv3_large_100 --checkpoint ./output/train/model_best.pth.tar
```

## 训练示例

### ImageNet 训练配方

这些命令对应于[选定的 HParams 配方](hparams#selected-recipes)，您可以在其中找到可复制的 YAML 配置、源代码 gist 以及已发布的检查点结果。它们假设 ImageFolder 数据位于 `/data/imagenet/train/` 和 `/data/imagenet/validation/` 下。`--batch-size` 是**每个 GPU** 的批大小。MobileNetV4 和 ConvNeXt 示例中明确指定的学习率适用于所声明的全局批大小；如果 GPU 数量发生变化，它们不会自动缩放。

#### MobileNetV4 Conv Medium，ImageNet-1k

来自 [mnv4_cm_r256_e500_gpu2.yaml](https://gist.github.com/rwightman/f6705cb65c03daeebca8aa129b1b94ad#file-mnv4_cm_r256_e500_gpu2-yaml)：2 个 GPU，全局批大小 1024，256px 训练，以及 20 个预热 + 500 个余弦退火 epoch。对应的权重为 [mobilenetv4_conv_medium.e500_r256_in1k](https://huggingface.co/timm/mobilenetv4_conv_medium.e500_r256_in1k)。

```bash
torchrun --standalone --nproc_per_node=2 train.py --data-dir /data/imagenet \
  --model mobilenetv4_conv_medium.e500_r256_in1k --img-size 256 --crop-pct 0.95 --batch-size 512 \
  --opt adamw --lr 0.002 --weight-decay 0.1 --clip-grad 5.0 \
  --sched cosine --sched-on-updates --epochs 500 --warmup-epochs 20 --warmup-prefix \
  --warmup-lr 0 --min-lr 0 --cooldown-epochs 0 \
  --aa rand-m8-inc1-mstd1.0 --mixup 0.2 --mixup-prob 0.8 --smoothing 0.1 \
  --reprob 0.25 --remode pixel --drop 0.2 --drop-path 0.1 \
  --model-ema --model-ema-decay 0.9998 --model-ema-warmup --dist-bn reduce \
  --amp --amp-dtype bfloat16 --torchcompile inductor --experiment mobilenetv4-cm
```

如果 GPU 不支持 bfloat16，请使用 `--amp-dtype float16`；`--torchcompile inductor` 是可选的。

#### 带寄存器（registers）的 ViT Medium，ImageNet-1k

来自 [train-vit_medium_reg4-4gpu-in1k.yaml](https://gist.github.com/rwightman/fb37c339efd2334177ff99a8083ebbc4#file-train-vit_medium_reg4-4gpu-in1k-yaml)：4 个 GPU，全局批大小 1024，共 600 个 epoch，包括预热和 10 个冷却 epoch。对应的权重为 [vit_medium_patch16_reg4_gap_256.sbb_in1k](https://huggingface.co/timm/vit_medium_patch16_reg4_gap_256.sbb_in1k)。

```bash
torchrun --standalone --nproc_per_node=4 train.py --data-dir /data/imagenet \
  --model vit_medium_patch16_reg4_gap_256.sbb_in1k --model-kwargs fix_init=True --head-init-scale 0 \
  --img-size 256 --crop-pct 0.95 --batch-size 256 \
  --opt adamw --opt-eps 1e-8 --lr-base 0.0008 --lr-base-size 512 --lr-base-scale sqrt \
  --weight-decay 0.07 --clip-grad 5.0 --sched cosine --sched-on-updates \
  --epochs 600 --warmup-epochs 20 --warmup-lr 5e-7 --min-lr 5e-7 --cooldown-epochs 10 \
  --aa rand-m7-inc1-mstd1.0-n3 --aug-repeats 3 --color-jitter 0 \
  --mixup 0.8 --cutmix 1.0 --smoothing 0.1 --reprob 0.2 --remode pixel --drop-path 0.2 \
  --seed 0 --amp --amp-dtype float16 --torchcompile inductor --experiment vit-medium-sbb
```

本次运行使用 AdamW，未启用 EMA。其峰值学习率约为 0.001131，出现在全局批次 1024。

#### ConvNeXt Tiny，从 ImageNet-12k 到 ImageNet-1k 的微调

来自 [hparams_84_00-convnext_tiny.in12k-args.yaml](https://gist.github.com/rwightman/ee0b02c1e99a0761264d1d1319e95e5b#file-hparams_84_00-convnext_tiny-in12k-args-yaml)：8 个 GPU，全局批大小 4096，共 75 个 epoch，包括预热和 10 个冷却 epoch。加载 **ImageNet-12k** 权重，并设置 `--num-classes 1000` 以替换分类器进行微调。生成的检查点系列为 [convnext_tiny.in12k_ft_in1k](https://huggingface.co/timm/convnext_tiny.in12k_ft_in1k)。

```bash
torchrun --standalone --nproc_per_node=8 train.py --data-dir /data/imagenet \
  --model convnext_tiny.in12k --pretrained --num-classes 1000 \
  --img-size 224 --crop-pct 1.0 --batch-size 512 --grad-checkpointing \
  --opt adamw --lr 1e-4 --weight-decay 0.05 --layer-decay 0.75 --clip-grad 3.0 \
  --sched cosine --epochs 75 --warmup-epochs 10 --warmup-lr 1e-6 --min-lr 5e-7 \
  --cooldown-epochs 10 --lr-noise 0.1 1.0 \
  --aa rand-m8-inc1-mstd101 --mixup 0.8 --cutmix 1.0 --smoothing 0.1 \
  --reprob 0.3 --remode pixel --drop-path 0.1 --model-ema --model-ema-decay 0.9998 \
  --amp --amp-dtype float16 --experiment convnext-tiny-ft
```

### 多标签分类

对于具有多个独立标签的图像，请使用 `--task multilabel`。

* **`--num-classes`** 用于设置词汇表大小。
* **`--target-format indices`**（默认）将目标列读取为正数、从零开始的类别 ID 列表。在批处理之前，标签会被转换为浮点数多热目标，空列表表示没有正标签的图像。
* **`--target-format multihot`** 读取长度为 `--num-classes` 的稠密多热向量（取值为 `[0, 1]` 区间内的 bool、int 或 float 值）。该向量将按原样使用，因此软标签值会直接用于 BCE 训练，并在评估指标中按 0.5 进行二值化。多热目标的类别映射必须以源类别索引作为键。

如果不使用该标志，所有列表都会被读取为类别索引，因为整数多热向量与索引列表无法区分。

[植物病理学数据集](https://huggingface.co/datasets/timm/plant-pathology-2021)提供了这种格式：一个 `image` 列和一个包含 `ClassLabel` 值序列的 `labels` 列。它的六个类别是 `complex`、`frog_eye_leaf_spot`、`healthy`、`powdery_mildew`、`rust` 和 `scab`。例如，`[5, 1, 0]` 会变成 `[1, 1, 0, 0, 0, 1]`。

```bash
python train.py --dataset hfds/timm/plant-pathology-2021 \
  --task multilabel --target-key labels --num-classes 6 \
  --model resnet50 --pretrained --batch-size 32 --epochs 30 \
  --opt adamw --lr 1e-4 --weight-decay 0.01 --amp

python validate.py --dataset hfds/timm/plant-pathology-2021 \
  --task multilabel --target-key labels --num-classes 6 \
  --model resnet50 --checkpoint ./output/train/<experiment>/model_best.pth.tar
```

`hfids/timm/plant-pathology-2021` 支持以相同的目标格式进行流式处理。读取器支持：

* **Hugging Face `hfds` 和 `hfids`** 支持多标签目标。
* **WebDataset (`wds`)** 支持在每个样本的 `.json` 伴随文件中存储的类别 ID 列表，该列表位于由 `--target-key` 指定的字段下。如果不使用 `--target-key`，则 `.cls` 文件提供单个类别索引。
* **文件夹和 tar 读取器**返回单个类别索引。

存储每个标签一个二进制字体的数据集（例如 [Wake Vision](https://www.tensorflow.org/datasets/catalog/wake_vision)）使用以下设置：

* **`--target-key person,depiction,body_part`** 用于选择以逗号分隔的字段列表。每个字段按列出的顺序成为一个类别，大于零的值计为正样本。此功能适用于 `hfds`、`hfids` 和 `tfds` 读取器，以及当字段位于 `.json` 伴随文件中时的 WebDataset。
* **`--num-classes`** 必须等于字段数量。
* 由于多字段键会定义其自身的格式，因此 **`--target-format`** 保持默认值不变。
* **嵌套字段路径**使用 `/`，例如针对 TFDS `bee_dataset` 的 `--input-key input --target-key output/pollen_output,output/wasps_output`。单个目标键也接受相同的路径。包含 `/` 的字面字段名（例如 TFDS 的 `image/filename`）优先于路径。

Standard 和 NaFlex 加载器支持多标签目标，包括 mixup 和 cutmix；NaFlex 训练目前需要 map 风格的数据集，例如 `hfds`。

该任务使用带 logits 的二元交叉熵进行训练。多标签训练的平滑默认值为零；设置 `--smoothing s` 会将每个二元目标调整为 `target * (1 - s) + 0.5 * s`，当 Mixup/CutMix 激活时由它们应用，否则由损失函数应用。现有的 `--bce-sum` 和 `--bce-target-thresh` 选项同样适用。此任务不支持蒸馏和 JSD 损失。

`--loss` 用于选择其他损失函数（`bce` 是多标签的默认值）：

* `asl`：[Asymmetric Loss](https://arxiv.org/abs/2009.14119)。它与 BCE 具有相同的平滑和 Mixup/CutMix 行为，并且以 float32 计算，因此与 `--amp` 配合使用是安全的。

  * **`--asl-gamma-neg`** 设置负样本目标的聚焦参数（默认值为 4）。
  * **`--asl-gamma-pos`** 设置正样本目标的聚焦参数（默认值为 1）。
  * **`--asl-clip`** 设置负样本的概率间隔（默认值为 0.05）。
  * **`--asl-reduction`** 的默认值为 `batchmean`，即对类别求和，并对批次求平均。`sum` 会恢复原始的 ASL 归约方式，该方式同时对批次求和，因此有效学习率会随批次大小缩放。

* `twoway`：[双向多标签损失](https://staff.aist.go.jp/takumi.kobayashi/publication/2023/CVPR2023.pdf)，对比每个样本各类别以及每个类别各样本之间的正负 logit。其 logit 为排序分数，因此 mAP 是有意义的指标；在 sigmoid 阈值下的 F1 未经过校准。

  * **`--twoway-tp`** 设置正对数几率温度（默认值为 4）。
  * **`--twoway-tn`** 设置负对数温度（默认值为 1）。

* `zlpr`：[ZLPR](https://arxiv.org/abs/2208.02955)，一种无需超参数的多标签扩展，基于 softmax 交叉熵，内置的 logit 阈值为 0。
* `db`：用于长尾多标签数据的[Distribution-Balanced Loss](https://arxiv.org/abs/2007.09654)。采用针对每个样本的再平衡权重，以考虑标签共现情况，对 logits 进行负容忍正则化，并包含 focal 项。它支持 Mixup/CutMix 和 smoothing，默认值来自参考 DB 配置：

  * **`--db-neg-scale`** 设置负 logit 的缩放系数（默认值为 2）。
  * **`--db-init-bias`** 设置类别先验 logit 偏移因子（默认值为 0.05）。
  * **`--db-focal-gamma`** 设置 focal focusing（默认值为 2）。
  * **`--class-stats`** 以类别统计文件（见下文）的形式提供所需的训练标签数量。

`twoway` 和 `zlpr` 需要二分类目标，因此不支持 Mixup/CutMix 或 `--smoothing`。

对于偏斜的标签分布，`bce` 和 `asl` 支持两个加权标志：

* **`--loss-pos-weight`** 用于对每个类别的正样本项进行加权，以平衡类别内正样本与负样本的比例。这也会使预测结果偏向正样本（在固定阈值下召回率更高，概率校准程度更低）。
* **`--loss-class-weight`** 用于对每个类别的整体损失进行加权，以平衡各类别之间的关系并保持校准。

每个标志都接受显式权重（单个值、每个类别一个值的逗号分隔列表，或一个文件）或一个方法名称，该方法根据通过`--class-stats`提供的训练标签计数来推导权重：

* `--loss-pos-weight neg_pos`：每个类别的负样本/正样本数量。
* `--loss-class-weight inv_freq`：逆类别频率，归一化至均值为 1。
* `--loss-class-weight effective_num`：根据样本有效数量计算的[class-balanced](https://arxiv.org/abs/1901.05555)权重，并归一化至均值为 1。

派生权重选项：

* **`--loss-weight-power`** 用于设置 `neg_pos` 和 `inv_freq` 的指数（默认值为 1），设为 0.5 可实现更柔和的平方根重新加权。
* **`--loss-weight-beta`** 为 `effective_num` 权重设置 beta（默认值为 0.999）。
* **`--loss-weight-max`** 用于限制派生的权重。

两个权重标志可以使用相同的统计数据。`class_weights.py` 会统计某个数据划分中的标签，其数据集参数与 `train.py` 相同，并写入类别统计文件（`--method` 可预览某个方法的权重）：

```bash
python class_weights.py --dataset hfds/timm/plant-pathology-2021 --split train \
  --task multilabel --target-key labels --num-classes 6 --output stats.json

python train.py --dataset hfds/timm/plant-pathology-2021 \
  --task multilabel --target-key labels --num-classes 6 \
  --class-stats stats.json --loss-pos-weight neg_pos --loss-weight-power 0.5 ...
```

已弃用的标志：

| 旧标志 | 替换 |
|---|---|
| `--bce-loss` | `--loss bce` |
| `--jsd-loss` | `--loss jsd` |
| `--bce-pos-weight` | `--loss-pos-weight` |

验证报告包含 BCE 损失、`map`（平均精度均值）、`micro_f1`、`macro_f1` 和 `sample_f1`。指标以百分比表示，并使用完整的验证集进行计算，包括从分布式 rank 收集的预测结果。有序分布式采样器的填充部分会被排除。精确 mAP 会在评估完成前将预测结果保留在 CPU 上，因此其内存占用随示例数和类别数增加而增长。没有正样本的类别其 AP 贡献为零；分母为零的 F1 值也为零。

`--eval-metric map` 是检查点选择和调度的默认设置。F1 使用 0.5 的 sigmoid 概率阈值，可通过 `--multilabel-threshold` 进行配置；mAP 与此阈值无关。模型输出仍为 logits，因此在使用训练好的模型时，请应用 sigmoid 以获得独立的类别概率。

### 历史 ImageNet 训练方案

这些早期运行结果仍然是 RMSProp、SGD 以及 AugMix/JSD 训练的有用参考。硬件和报告的指标描述的是原始运行。对于较新的 AdamW 配方，请从上述示例或 [gist 集合](hparams#config-file-gists) 开始。

#### 使用 RandAugment 的 EfficientNet-B2 - 80.4 top-1，95.1 top-5

原始运行使用了双 Titan RTX 显卡和 NVIDIA Apex；当前的 `--amp` 使用原生 PyTorch AMP：

```bash
./distributed_train.sh 2 --data-dir /imagenet/ --model efficientnet_b2 -b 128 --sched step --epochs 450 --decay-epochs 2.4 --decay-rate .97 --opt rmsproptf --opt-eps .001 -j 8 --warmup-lr 1e-6 --weight-decay 1e-5 --drop 0.3 --drop-path 0.2 --model-ema --model-ema-decay 0.9999 --aa rand-m9-mstd0.5 --remode pixel --reprob 0.2 --amp --lr .016
```

#### MixNet-XL 配合 RandAugment - 80.5 top-1，94.9 top-5

原始运行使用了双 Titan RTX 显卡和 NVIDIA Apex；当前的 `--amp` 使用原生 PyTorch AMP：

```bash
./distributed_train.sh 2 --data-dir /imagenet/ --model mixnet_xl -b 128 --sched step --epochs 450 --decay-epochs 2.4 --decay-rate .969 --opt rmsproptf --opt-eps .001 -j 8 --warmup-lr 1e-6 --weight-decay 1e-5 --drop 0.3 --drop-path 0.2 --model-ema --model-ema-decay 0.9999 --aa rand-m9-mstd0.5 --remode pixel --reprob 0.3 --amp --lr .016 --dist-bn reduce
```

#### SE-ResNeXt-26-D 和 SE-ResNeXt-26-T

这些超参数（或类似参数）在多种 ResNet 架构上表现良好，通常建议随着模型规模的增大而增加训练轮数（epoch）……例如，ResNe(X)t50 大约需要 180-200 轮，更大的模型则需要 220 轮以上。如果使用更好的 GPU 或启用了 AMP，请按比例增加批处理大小（batch size）和学习率（LR）。这些参数是针对 2 张 1080Ti 显卡设置的：

```bash
./distributed_train.sh 2 --data-dir /imagenet/ --model seresnext26t_32x4d --lr 0.1 --warmup-epochs 5 --epochs 160 --weight-decay 1e-4 --sched cosine --reprob 0.4 --remode pixel -b 112
```
#### 使用 RandAugment 的 EfficientNet-B3 - 81.5 top-1，95.7 top-5

该模型的训练始于与上文 EfficientNet-B2 w/ RA 相同的命令行。在训练了将近三周后，进程崩溃了。由于结果并不理想，我多次恢复训练并调整了几个参数（提高 RE 概率、降低 rand-aug、增加 ema-decay）。但效果依然不佳。最终，我对所有重启过程中表现最好的检查点进行了平均。在默认的分辨率/裁剪下，结果平平无奇，但奇怪的是，在使用 1.0 的全图像测试裁剪时，性能却好得多。

#### 使用 RandAugment 的 EfficientNet-B0 - 77.7 top-1，95.3 top-5

[Michael Klachko](https://github.com/michaelklachko) 使用针对更大 batch size 适配的 B2 命令行，并采用推荐的 B0 dropout rate 0.2，取得了这些结果。

```bash
./distributed_train.sh 2 --data-dir /imagenet/ --model efficientnet_b0 -b 384 --sched step --epochs 450 --decay-epochs 2.4 --decay-rate .97 --opt rmsproptf --opt-eps .001 -j 8 --warmup-lr 1e-6 --weight-decay 1e-5 --drop 0.2 --drop-path 0.2 --model-ema --model-ema-decay 0.9999 --aa rand-m9-mstd0.5 --remode pixel --reprob 0.2 --amp --lr .048
```
#### 使用 JSD 损失和 RandAugment（clean + 2x RA augs）的 ResNet50 - 79.04 top-1，94.39 top-5

在两张较旧的 1080Ti 显卡上训练，这花费了相当长的时间。与我第一次效果较好的 AugMix 训练（78.99）相比，ImageNet 验证结果仅略有提升，且不具备统计显著性。然而，这些权重在 ImageNetV2、ImageNet-Sketch 等测试中表现出更强的鲁棒性。与最初的 AugMix 运行不同，我启用了 SplitBatchNorm，在干净数据分支上禁用了随机擦除，并提高了两条增强路径上的随机擦除概率。

```bash
./distributed_train.sh 2 --data-dir /imagenet -b 64 --model resnet50 --sched cosine --epochs 200 --lr 0.05 --amp --remode pixel --reprob 0.6 --aug-splits 3 --aa rand-m9-mstd0.5-inc1 --resplit --split-bn --loss jsd --dist-bn reduce
```
#### 使用 RandAugment 的 EfficientNet-ES (EdgeTPU-Small) - 78.066 top-1，93.926 top-5

由 [Andrew Lavin](https://github.com/andravin) 使用 8 张 V100 显卡训练。未使用模型 EMA，最终检查点是训练过程中 8 个最佳检查点的平均值。

```bash
./distributed_train.sh 8 --data-dir /imagenet --model efficientnet_es -b 128 --sched step --epochs 450 --decay-epochs 2.4 --decay-rate .97 --opt rmsproptf --opt-eps .001 -j 8 --warmup-lr 1e-6 --weight-decay 1e-5 --drop 0.2 --drop-path 0.2  --aa rand-m9-mstd0.5 --remode pixel --reprob 0.2 --amp --lr .064
```
#### MobileNetV3-Large-100 - 75.766 top-1，92.542 top-5

```bash
./distributed_train.sh 2 --data-dir /imagenet/ --model mobilenetv3_large_100 -b 512 --sched step --epochs 600 --decay-epochs 2.4 --decay-rate .973 --opt rmsproptf --opt-eps .001 -j 7 --warmup-lr 1e-6 --weight-decay 1e-5 --drop 0.2 --drop-path 0.2 --model-ema --model-ema-decay 0.9999 --aa rand-m9-mstd0.5 --remode pixel --reprob 0.2 --amp --lr .064 --lr-noise 0.42 0.9
```

#### ResNeXt-50 32x4d w/ RandAugment - 79.762 top-1, 94.60 top-5
这些参数同样适用于 SE-ResNeXt-50 和 SK-ResNeXt-50，并且很可能也适用于 101 层版本。我在训练 SK-ResNeXt-50 32x4d 时使用了这些参数，该训练使用 2 个 GPU，并根据有效批量大小采用了略高的学习率（lr=0.18，每个 GPU 的 b=192）。下面的命令行是为 8 GPU 训练调优的。

```bash
./distributed_train.sh 8 --data-dir /imagenet --model resnext50_32x4d --lr 0.6 --warmup-epochs 5 --epochs 240 --weight-decay 1e-4 --sched cosine --reprob 0.4 --recount 3 --remode pixel --aa rand-m7-mstd0.5-inc1 -b 192 -j 6 --amp --dist-bn reduce
```

