# Скрипты

Скрипт для обучения, валидации, инференса и очистки контрольных точек, включённый в корневую папку репозитория на GitHub. В настоящее время скрипты не включены в пакет для установки через pip.

Скрипты обучения и валидации развивались из ранних версий [PyTorch Imagenet Examples](https://github.com/pytorch/examples). Со временем я добавил значительный функционал, включая оптимизации производительности, специфичные для CUDA, на основе [NVIDIA's APEX Examples](https://github.com/NVIDIA/apex/tree/master/examples).

## Скрипт обучения

Список аргументов обучения обширен, и не все комбинации опций (или даже сами опции) были полностью протестированы. Для папки с обучающим набором данных укажите папку-базу, содержащую папки `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 не требуется.

### Файлы конфигурации

Любой аргумент обучения можно указать в YAML-файле через `--config`, используя в качестве ключей имена аргументов с подчёркиваниями (`--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` (всегда последний эпоху, с состоянием модели, оптимизатора, EMA и масштабирующего коэффициента AMP), `model_best.pth.tar` и ранжированную историю файлов `checkpoint-N.pth.tar`, ограниченную параметром `--checkpoint-hist`.

* `--resume PATH` восстанавливает это состояние и продолжает расписание с эпохи контрольной точки. Это стандартный способ создать новую папку эксперимента на основе существующей контрольной точки.
* `--resume auto` возобновляет выполнение на месте: при заданных `--output` и `--experiment` оно продолжает с `last.pth.tar` в папке этого эксперимента, если такой файл существует, иначе начинает новый запуск. Эту же команду можно повторить после прерывания. История контрольных точек, отслеживание лучшей метрики и `summary.csv` сохраняются согласованно, поэтому удаление по `--checkpoint-hist` и `model_best` остаются корректными после перезапуска.
* `--stop-epoch N` останавливает процесс после `N` завершённых эпох, не изменяя расписание, заданное параметром `--epochs`. Последняя эпоха всегда оценивается и сохраняется в контрольную точку, что позволяет обучать модель частями.
* `--save-epochs N [N ...]` сохраняет неизменяемые снимки `milestone-N.pth.tar` (N — количество завершённых эпох) вне ранжированной истории. Если файловая система позволяет, они создаются как жёсткие ссылки на `last.pth.tar`, поэтому не занимают дополнительное место, а в указанных эпохах принудительно выполняется оценка.

Вместе это позволяет легко вручную проводить эксперименты с расписанием warmup-stable-decay (WSD): обучите стабильную основную часть один раз, а затем от её контрольных точек запустите независимые cooldown-фазы с полным состоянием оптимизатора.

```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` расписания, поэтому охлаждение в течение `D` эпох после `P` завершённых эпох задаётся как `--epochs P+D --decay-epochs D` (вычтите `--warmup-epochs` из `--epochs`, если используется `--warmup-prefix`). Скрипт `sweep.py` ниже автоматизирует этот процесс.

## Подбор гиперпараметров

`sweep.py` (PyTorch 2.5 или новее) планирует и запускает серии экспериментов по любым аргументам `train.py`, при необходимости с ветвлением WSD. Для каждой задачи он создаёт один полный файл `train.py --config` и файл `plan.json` с графом зависимостей задач, после чего запускает готовые задачи на пуле локальных GPU. План остаётся неизменным, пока работают воркеры. Каждая задача представляет собой обычный вызов `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).

Свип WSD использует один стабильный ствол для каждой комбинации гиперпараметров во многих фазах cooldown. Для каждой из указанных ниже значений learning rate ствол выполняется до 30 эпох с контрольными точками (milestones) на 18, 24 и 30 эпохах, а от каждой контрольной точки ответвляется фаза cooldown длительностью 6 эпох. В итоге получается 4 ствола и 12 фаз cooldown вместо 12 полных прогонов. Фазы cooldown запускаются сразу после появления соответствующей контрольной точки, пока ствол продолжает работу. Задачи, требующие более одного 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 добавляет случайный поиск с распределениями, связанные (zip-ованные) оси, повторения по разным сидам и базовый файл конфигурации; схему описывает docstring в начале `sweep.py`. Команда `python sweep.py plan spec.yaml` записывает конфигурации и `graph.dot`, не запуская ничего.

Свип хранится в `output/sweeps/<name>/` (относительно файла спецификации или рабочей директории для свипа из командной строки), с одной папкой на каждый эксперимент, названной по значениям, которые варьировались (`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 содержит одну строку на эксперимент и одну колонку на cooldown (`p24-d6` — это cooldown в 6 эпох из 24 эпох) с итоговым метрикой, усреднённой по повторам, где лучший результат в каждой колонке отмечен. Обычная сводка ранжируется по итоговой метрике в пределах каждого бюджета эпох. Файл `results.csv` содержит все задачи, включая сегменты trunk, с лучшей эпохой для каждой. При отсутствии валидационной выборки ранжирование выполняется по потере обучения, а контрольные точки на конечных этапах можно оценить с помощью `validate.py`.

Повторный запуск `sweep.py run` для прерванного sweep возобновляет его: завершённые задачи пропускаются, а задача с частично выполненным прогоном продолжается с её собственного `last.pth.tar`. Рабочие процессы на нескольких машинах, разделяющих папку sweep, могут запускать его каждый. Они координируются через блокировки `flock` в `state/locks`, которые также удерживает каждый процесс обучения задачи, поэтому задача, рабочий процесс которой завершился, не запускается заново и не перепланируется, пока её последний процесс не завершится. Для sweep, разделяемого несколькими машинами, требуется файловая система, в которой `flock` работает между всеми ними, например NFSv4 или Lustre, смонтированные с параметром `-o flock`. Точки монтирования с локальной блокировкой не координируют работу между машинами, поэтому проверьте блокировку с каждого клиента. Остановка рабочего процесса (Ctrl-C или SIGTERM от `scancel`) останавливает его задачи, а ручной запуск `run.sh` задачи обходит механизмы координации.

Файлы блокировок в `state/locks` являются постоянными: никогда не удаляйте и не заменяйте их для очистки занятого процесса. Вместо этого остановите или дождитесь завершения его рабочих процессов и процессов обучения. Статус `running (gone)` означает, что блокировка обучения свободна, но попытка не была согласована; запустите рабочий процесс, чтобы завершить её и возобновить незавершённую работу. `status` и `results` не восстанавливают состояние задания.

Рабочий процесс читает план один раз при запуске. Чтобы изменить sweep, остановите всех его рабочих процессов и процессы обучения, затем снова составьте план с флагом `--force` и запустите их заново. До начала выполнения любой задачи можно изменять любые параметры плана. После этого существующие задачи обучения можно только расширять: добавлять новые trials, cooldowns или точки WSD после уже существующих, при этом каждый trunk продолжает с того места, где остановился. Изменения существующих задач обучения требуют нового sweep. Сгенерированные пути фиксируются в момент записи плана, поэтому при необходимости составьте план в новом месте, а не перемещайте папку sweep.

Если планирование прерывается, рабочие процессы обнаруживают конфигурации или лаунчеры из неполной записи плана и отказываются запускаться. Повторно выполните исходную команду планирования с `--force`, чтобы завершить или восстановить план, а затем перезапустите рабочие процессы. Каждая успешная запись плана публикует соответствующее поколение в `plan.json`, а также конфигурации и лаунчеры; неизменённый, полный план ничего не записывает.

Например, чтобы расширить описанный выше CLI-прогон WSD до 36 стабильных эпох, остановите его воркеры и задачи, затем повторите исходные аргументы планирования, добавив к ним более позднюю точку:

```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% от последней точки, поэтому расширение горизонта может изменить существующие периоды охлаждения. Сохраняйте идентификаторы существующих испытаний; изменение порядка или расширение осей сетки может привести к перенумерации испытаний. При использовании `lr_noise` расписание основной части зависит от горизонта, поэтому добавление более поздней точки в уже запущенный свип требует создания нового свипа.

Настройки обучения указываются в плане: используйте блок `train` из спецификации или аргументы после `--` при создании CLI-свипа. Переопределение настроек обучения во время выполнения не поддерживается. Настройки отчётности, такие как метрика ранжирования, и настройки вывода (`checkpoint_hist`, `log_wandb`, `wandb_project`, `wandb_tags`) можно изменить офлайн с помощью `--force` без сброса состояния задания или изменения идентификатора обучения. Настройки вывода применяются к будущим попыткам; завершённые задания не перезапускаются.

`sweep.py` требует Unix `flock`: протестировано на Linux, ожидается работа, но не проверено на macOS. Нативная Windows не поддерживается; используйте WSL2. Блокировки не переживают сетевое отключение, длящееся дольше, чем срок аренды NFS: отключённый процесс может продолжать обучение после потери блокировок, из-за чего на другом компьютере может начаться пересекающаяся попытка.

## Скрипты валидации / вывода

Скрипты валидации и инференса используются аналогично. Один выводит метрики на валидационном наборе, а другой — topk идентификаторы классов в csv. Укажите папку, содержащую валидационные изображения, а не базовую папку, как в скрипте обучения.

Для проверки с использованием предобученных весов модели (если они существуют):

```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 эпох с косинусной раскладкой. Соответствующие веса — [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
```

Используйте `--amp-dtype float16`, если ваша GPU не поддерживает bfloat16; `--torchcompile inductor` — необязательный параметр.

#### 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 эпох, включая warmup и 10 эпох cooldown. Соответствующие веса — [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. Пиковое значение LR составляет приблизительно 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 эпох, включая warmup и 10 эпох cooldown. Загрузите веса **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`** (по умолчанию) считывает целевой столбец как список положительных, нумеруемых с нуля идентификаторов классов. Метки преобразуются в float-многоцелевые (multi-hot) значения перед формированием батча, а пустые списки обозначают изображения без положительных меток.
* **`--target-format multihot`** читает плотный многогорячий вектор длины `--num-classes` (значения bool, int или float в диапазоне `[0, 1]`). Вектор используется как есть, поэтому мягкие значения напрямую обучаются с BCE и бинаризируются при 0.5 для метрик оценки. Карты классов для многогорячих целей должны индексироваться исходным индексом класса.

Без этого флага каждый список интерпретируется как индексы классов, поскольку целочисленный мульти-горячий вектор неотличим от списка индексов.

Набор данных [plant pathology dataset](https://huggingface.co/datasets/timm/plant-pathology-2021) предоставляет данные в следующем формате: столбец `image` и столбец `labels`, содержащий последовательность значений `ClassLabel`. Его шесть классов — `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` каждого примера в поле, указанном параметром `--target-key`. Если параметр `--target-key` не задан, файл `.cls` содержит один индекс класса.
* **Folder and tar readers** возвращают одиночные индексы классов.

Наборы данных, хранящие одно двоичное поле на каждую метку, например [Wake Vision](https://www.tensorflow.org/datasets/catalog/wake_vision), используют следующие настройки:

* **`--target-key person,depiction,body_part`** выбирает список полей, разделённых запятыми. Каждое поле становится отдельным классом в указанном порядке, а значения, большие нуля, считаются положительными. Это работает для читателей `hfds`, `hfids` и `tfds`, а также для WebDataset, если поля находятся в файле-спутнике `.json`.
* **`--num-classes`** должно быть равно количеству полей.
* **`--target-format`** остаётся по умолчанию, поскольку ключи с несколькими полями определяют собственный формат.
* **Вложенные пути полей** используют `/`, например `--input-key input --target-key output/pollen_output,output/wasps_output` для набора данных TFDS `bee_dataset`. Одиночные целевые ключи принимают те же пути. Буквальное имя поля, содержащее `/` (например, `image/filename` в TFDS), имеет приоритет над путём.

Стандартные и NaFlex-загрузчики поддерживают многометочные цели, включая mixup и cutmix; обучение с NaFlex в настоящее время требует набора данных в стиле map, например `hfds`.

Задача обучается с использованием бинарной кросс-энтропии с логитами. Сглаживание по умолчанию равно нулю для обучения с несколькими метками; установка `--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). Поведение сглаживания и Mixup/CutMix такое же, как у BCE, а вычисление выполняется в float32, поэтому безопасно использовать с `--amp`.

  * **`--asl-gamma-neg`** задаёт фокусировку для отрицательных целей (по умолчанию 4).
  * **`--asl-gamma-pos`** задаёт фокусировку для положительных целей (по умолчанию 1).
  * **`--asl-clip`** задаёт порог вероятности для отрицательных примеров (по умолчанию 0.05).
  * **`--asl-reduction`** по умолчанию имеет значение `batchmean`, что означает суммирование потерь по классам и усреднение по батчу. Значение `sum` восстанавливает исходное приведение ASL, при котором потери также суммируются по батчу, из-за чего эффективная скорость обучения масштабируется с размером батча.

* `twoway`: [Two-way Multi-Label Loss](https://staff.aist.go.jp/takumi.kobayashi/publication/2023/CVPR2023.pdf), контрастирует положительные и отрицательные логиты между классами каждого образца и образцами каждого класса. Его логиты являются ранжировочными оценками, поэтому mAP является осмысленной метрикой; F1 при сигмоидном пороге не откалиброван.

  * **`--twoway-tp`** задаёт положительную температуру логитов (по умолчанию 4).
  * **`--twoway-tn`** задаёт отрицательную температуру логитов (по умолчанию 1).

* `zlpr`: [ZLPR](https://arxiv.org/abs/2208.02955), многометочное расширение softmax cross-entropy без гиперпараметров, с встроенным порогом логитов, равным 0.
* `db`: [Distribution-Balanced Loss](https://arxiv.org/abs/2007.09654) для данных с длинным хвостом и несколькими метками. BCE с весами перераспределения для каждого примера, которые учитывают совместное появление меток, регуляризацию логитов с допуском отрицательных значений и фокальный член. Поддерживает Mixup/CutMix и сглаживание, значения по умолчанию взяты из конфигурации DB из справочного источника:

  * **`--db-neg-scale`** задаёт масштаб отрицательных логитов (по умолчанию 2).
  * **`--db-init-bias`** задаёт коэффициент сдвига логитов для априорного класса (по умолчанию 0.05).
  * **`--db-focal-gamma`** задаёт фокусировку focal (по умолчанию 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`** задает beta для весов `effective_num` (по умолчанию 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 loss, `map` (средняя средняя точность), `micro_f1`, `macro_f1` и `sample_f1`. Метрики выражаются в процентах и рассчитываются на полном наборе валидации, включая предсказания, собранные со всех распределённых рангов. Заполнение упорядоченного распределённого сэмплера исключается. Точный mAP удерживает предсказания на CPU до завершения оценки, поэтому потребление памяти масштабируется в зависимости от количества примеров и классов. Классы без положительных примеров дают нулевой AP; значения F1 с нулевым знаменателем равны нулю.

`--eval-metric map` используется по умолчанию для выбора контрольных точек и планирования. F1 применяет порог сигмоидной вероятности 0.5, который можно настроить с помощью `--multilabel-threshold`; mAP не зависит от этого порога. Выходные данные модели остаются логитами, поэтому при использовании обученной модели применяйте сигмоиду для получения независимых вероятностей классов.

### Исторические рецепты ImageNet

Эти более ранние прогоны остаются полезными для справки при обучении с использованием RMSProp, SGD и AugMix/JSD. Характеристики оборудования и приведённые метрики описывают исходные прогоны. Для новых рецептов с AdamW начните с примеров выше или с [сборников в gist](hparams#config-file-gists).

#### EfficientNet-B2 с RandAugment - 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. Как правило, по мере увеличения размера модели рекомендуется увеличивать количество эпох: примерно 180–200 для ResNe(X)t50 и 220+ для более крупных моделей. Для более мощных GPU или при включённом AMP пропорционально увеличивайте размер батча и learning rate. Эти параметры были рассчитаны для 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
```
#### EfficientNet-B3 с RandAugment - 81.5 top-1, 95.7 top-5

Обучение этой модели началось с той же командной строки, что и EfficientNet-B2 w/ RA выше. После почти трёх недель обучения процесс упал. Результаты не выглядели впечатляюще, поэтому я несколько раз возобновлял обучение, внося небольшие изменения в параметры (увеличение вероятности RE, уменьшение rand-aug, увеличение ema-decay). Ничего не выглядело хорошо. В итоге я усреднил лучшие контрольные точки из всех перезапусков. Результат посредственный при стандартном разрешении/кропе, но странно хорошо работает с полным тестовым кропом изображения 1.0.

#### EfficientNet-B0 с RandAugment - 77.7 top-1, 95.3 top-5

[Michael Klachko](https://github.com/michaelklachko) получил эти результаты с помощью командной строки для 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
```
#### ResNet50 с потерей JSD и RandAugment (чистые данные + 2x RA augs) - 79.04 top-1, 94.39 top-5

Обучение на двух старых картах 1080Ti заняло довольно много времени. Результат валидации на ImageNet лишь незначительно, не статистически значимо, лучше, чем у моего первого удачного обучения AugMix (78.99). Однако эти веса более устойчивы на тестах с 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
```
#### EfficientNet-ES (EdgeTPU-Small) с RandAugment - 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 с RandAugment - 79.762 top-1, 94.60 top-5
Эти параметры также хорошо подойдут для SE-ResNeXt-50 и SK-ResNeXt-50, а также, вероятно, для версии 101. Я использовал их для SK-ResNeXt-50 32x4d, который обучал на 2 GPU с немного более высокой LR на эффективный размер батча (lr=0.18, b=192 на GPU). Командная строка ниже настроена для обучения на 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
```

