Files
PracticeClassif/PROTOCOL_HPO_EdgeNeXt_NarutoSign.md
2026-06-30 15:44:41 +03:00

308 lines
40 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
type: protocol
status: draft
date: 2026-06-26
component: backbone
supervisor: "мнс Павленко Б.В."
task_type: student-research
topic: hyperparameter-optimization
model: EdgeNeXt
dataset: Naruto Sign
tools: [Optuna, timm, UMAP]
related:
- "[[B134_BB_2022_EdgeNeXt_Efficient_CNN-Transformer_Mobile_Vision]]"
- "[[PROTOCOL_text_encoder_benchmark_StripNet_GTA_UAV]]"
- "[[ПРАКТИКА_2026-07-01_задания_16_студентов]]"
- "[[HYP_SOFIA_v77_design]]"
tags:
- protocol
- methodology
- hpo
- optuna
- edgenext
- mona
- transfer-learning
- classification
- component/backbone
- task/experiment
author: claude
---
# PROTOCOL: Подбор гиперпараметров для классификации изображений (EdgeNeXt × Naruto Sign × Optuna)
> Учебно-исследовательская методичка для студента под руководством **мнс Павленко Б.В.**
> Тема: **оптимизация/подбор гиперпараметров (HPO)** для задачи классификации изображений на наборе **Naruto Sign** с компактным энкодером визуальных признаков **EdgeNeXt**. Сквозные инструменты: **Optuna** (поиск), **timm** (модель), **UMAP** (анализ признаков). Дополнительно изучаются **заморозка / частичная заморозка / MONA-адаптер** как точки на оси «ёмкость адаптации ↔ риск переобучения», подбираемые наравне с прочими гиперпараметрами.
---
## §0. TL;DR
| Что | Решение |
| :---------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| **Цель** | Освоить и применить методы HPO: на малом датасете Naruto Sign подобрать гиперпараметры дообучения EdgeNeXt так, чтобы максимизировать **macro-F1** на валидации, и обосновать выбор экспериментально |
| **Энкодер (фикс. семейство)** | **EdgeNeXt** (timm): `edgenext_xx_small.in1k` (1.3M) / `edgenext_x_small.in1k` (2.3M) / `edgenext_small.usi_in1k` (5.6M), ImageNet-pretrained — вариант сам по себе является гиперпараметром |
| **Датасет** | **Naruto Sign** — классификация ручных печатей. Verified (сиблинг-трек, см. §3): **13 классов** (12 печатей + `zero`), **~2245 изобр.** (train 2159 / test 86, **val нет**), PNG+JPG, лицензия «Unknown». Источник — [Kaggle vikranthkanumuru](https://www.kaggle.com/datasets/vikranthkanumuru/naruto-hand-sign-dataset) (ImageFolder). Числа **пересчитать после скачивания** |
| **Режимы переноса** | `full` (full fine-tuning) · `partial` (разморозить последние `n` стадий, `n=0` ⇒ linear probing) · `mona` (frozen backbone + Conv-MONA адаптеры, PEFT) |
| **Поиск** | **Optuna**, default = `TPESampler(multivariate=True)` + `MedianPruner`/`HyperbandPruner`; затем multi-objective (`NSGAIISampler`): macro-F1 ↑ vs trainable-params ↓ |
| **Главная метрика** | **macro-F1** (валидация). Доп.: balanced accuracy, top-1, MCC, confusion matrix. При дисбалансе accuracy — вводит в заблуждение |
| **Анализ данных** | Статистика датасета + **UMAP** эмбеддингов EdgeNeXt, раскраска по классам, кластеризация на промежуточном (1020-мерном) вложении, ARI/NMI/silhouette → предсказание «трудных» классов |
| **Гипотезы** | H1 (TPE+pruning ≫ random/grid при равном бюджете) · H2 (оптимум — partial freeze, не full FT и не linear probing) · H3 (MONA ≥ full FT при <5% параметров) · H4 (loss/sampler против дисбаланса ↑ macro-F1) · H5 (multi-objective находит Pareto-лучшие edge-конфигурации) · H6 (UMAP-перекрытие классов предсказывает ошибки) — см. §8 |
| **Численные эксперименты** | B0 baseline → E1 ablation режимов → E2 Optuna single-objective → E3 Optuna multi-objective → E4 UMAP-анализ → (опц.) E5 sampler/random vs TPE — см. §9 |
| **Сроки** | 14 дней (MVP ~810 дней): рабочий честный baseline > богатство экспериментов |
| **Compute** | 1×RTX 4090; EdgeNeXt малый → прогон минуты; десятки trial-ов Optuna реалистичны |
---
## §1. Контекст, цель и зачем проекту
### 1.1 Учебная цель
Студент осваивает полный цикл HPO на «чистой» автономной CV-задаче: (1) что такое кодирование визуальных признаков и почему backbone = feature encoder; (2) какие бывают методы подбора гиперпараметров и чем Bayesian/TPE лучше grid/random; (3) как устроена Optuna; (4) что подбирать в DL-классификации (lr, wd, loss, аугментации, **глубина заморозки**, PEFT); (5) как честно мерить качество при дисбалансе; (6) как анализировать признаковое пространство (UMAP) и превращать наблюдения в гипотезы; (7) как провести и описать численные эксперименты.
### 1.2 Зачем это проекту MERIDIAN/SOFIA
Задача автономна (публичные данные, не трогает проприетарный Triple-Teacher), но методически прямо питает проект:
- **EdgeNeXt** — критически релевантный для SOFIA-Tiny edge-backbone (см. конспект [[B134_BB_2022_EdgeNeXt_Efficient_CNN-Transformer_Mobile_Vision]]: 256×256 вход, Jetson-деплой, 1.35.6M params, SDTA channel-attention). Этот эксперимент — песочница для SDTA/adaptive-kernels интуиций.
- **MONA** — у проекта уже есть **Conv-MONA** в text-encoder бенчмарке ([[PROTOCOL_text_encoder_benchmark_StripNet_GTA_UAV]] §2.1): здесь студент реализует и измеряет тот же класс PEFT на чистой классификации.
- **Optuna-методология** переиспользуема для всего проекта (E0E9 recipe-tuning), а decision-rule-культура (равный бюджет, фикс. seed, p<0.05, честные caveat) — общая для практики ([[ПРАКТИКА_2026-07-01_задания_16_студентов]] §«Общие правила»).
### 1.3 Что НЕ изучаем
- ❌ Детекцию/сегментацию печатей (только классификация, ImageFolder→softmax).
- ❌ Архитектурный NAS (варьируем только пресет EdgeNeXt XXS/XS/S, не перестраиваем блоки).
- ❌ Дистилляцию от Teacher (это E2 MERIDIAN, не эта задача).
---
## §2. Карта теоретических глав (читать перед экспериментами)
Подробная теория вынесена в самостоятельные главы (папка `_research/`). Каждая — самодостаточна, с формулами, таблицами и проверенными источниками:
| Глава | Файл | О чём |
|:--|:--|:--|
| **Кодирование признаков и EdgeNeXt** | [[SECTION_edgenext]] | feature encoding, архитектура EdgeNeXt (stem/4 стадии/SDTA/adaptive kernels), варианты, **timm-практика** (`create_model`, `forward_features`, `forward_head(pre_logits=True)`, `resolve_model_data_config`) |
| **Методы HPO (таксономия)** | [[SECTION_hpo_methods]] | гиперпараметры vs параметры, grid/random ([Bergstra&Bengio 2012]), Bayesian (GP/TPE/SMAC, EI/UCB), CMA-ES/PBT, multi-fidelity (Successive Halving/Hyperband/ASHA/BOHB), фреймворки, методология, типичные ошибки |
| **Optuna (полный разбор)** | [[SECTION_optuna]] | Study/Trial/define-by-run, **samplers** (TPE/GP/CMA-ES/NSGA-II/III/QMC/**AutoSampler**), **pruners** (Median/ASHA/Hyperband/...), multi-objective+constraints, v4.5/v4.6, storage, визуализация, интеграция с PyTorch, шпаргалка «sampler+pruner» |
| **Заморозка / частичная заморозка** | [[SECTION_freezing]] | full FT vs linear probing vs partial freeze, **LP-FT** ([Kumar 2022]), механика `requires_grad`/`eval()`/FrozenBN, gradual unfreezing, discriminative LR, глубина заморозки как гиперпараметр |
| **MONA-layer (PEFT)** | [[SECTION_mona]] | adapter-PEFT, разбор Mona ([arXiv:2408.08345], CVPR 2025) по коду репозитория `Leiyi-Hu/mona`: scaled-LayerNorm + MonaOp (DW 3/5/7) + down/up-proj; «5%>100% full FT»; vs LoRA/Adapter; PyTorch-скелет |
| **UMAP (анализ признаков)** | [[SECTION_umap]] | интуиция, PCA/t-SNE/UMAP, параметры (`n_neighbors`/`min_dist`/`metric`), пайплайн EdgeNeXt→UMAP, кластеризация на промежуточном вложении, ARI/NMI/AMI/silhouette, предостережения |
| **Классификация: метрики/loss/sampling** | [[SECTION_classification]] | softmax/top-k, метрики (macro/micro/weighted-F1, balanced acc, MCC, ECE), loss (CE/LS/Focal/Class-Balanced/LDAM/KD), семплирование (WeightedRandomSampler, mixup/cutmix), связка loss×sampler |
---
## §3. Датасет Naruto Sign — протокол подготовки
Полное описание и источники — в [[SECTION_naruto_dataset]].
> [!info] Verified-факты (из параллельного студенческого трека под рук. Устенко В.Ю. — тот же датасет; зафиксированы в рабочей памяти проекта)
> Тот же Kaggle-набор `vikranthkanumuru/naruto-hand-sign-dataset` уже верифицирован параллельным треком: **13 классов** (12 печатей + `zero`/покой), **2245 изображений** (train 2159 / test 86, **val отсутствует**), 1797 PNG + 448 JPG, реальные фото рук, лицензия **«Unknown»**, числа из notebook 2020. Поэтому: `num_classes=13` по умолчанию; **val нет** → выделяется стратифицированно из train (реализовано в [`data.py`](code/src/data.py), ветка `use_predefined_split`); **не** переоткрывать эти факты заново — только пересчитать `dataset_stats.py` после скачивания (числа могли измениться). Документ трека Устенко может находиться в другой ветке репозитория.
Ключевые решения протокола:
1. **Источник по умолчанию:** Kaggle `vikranthkanumuru/naruto-hand-sign-dataset` (формат ImageFolder, заточен под классификацию). Roboflow-наборы — детекционные, использовать как запасные (crop по bbox).
2. **Сверить статистику после скачивания** скриптом [`dataset_stats.py`](code/src/dataset_stats.py): число классов (12 или 13?), всего изображений, per-class, разрешения, дисбаланс. Это **первый артефакт** студента — таблица статистики в отчёте.
3. **Стратифицированный split** (seed=42), доли 70/15/15. Реализация — [`data.py`](code/src/data.py) (`_stratified_indices`).
4. **Утечка из видео — главный риск честности.** Кадры извлечены из видео покадрово ⇒ соседние кадры почти идентичны. Случайный per-frame split **завышает** метрику. Если в именах файлов есть id видео/серии — делать **group-split** (`StratifiedGroupKFold`, см. [[SECTION_naruto_dataset]] §5). Если id нет — отметить риск в отчёте и сделать дедупликацию near-duplicate (perceptual hash). Базовый `data.py` делает per-frame split — это допустимо для учебного MVP, **но факт и риск утечки обязательно фиксируется в REPORT.md**.
5. **Размер входа** — 256×256 (дефолт EdgeNeXt), нормализация ImageNet (берётся из `timm.data.resolve_model_data_config`).
> [!danger] Horizontal flip — отключён по умолчанию
> Печать — это **конкретная конфигурация левой/правой руки**; зеркалирование меняет семантику жеста ⇒ label noise. В [`data.py`](code/src/data.py) `use_hflip=False` зафиксировано и **не подаётся в Optuna**. См. [[SECTION_naruto_dataset]] §6.
---
## §4. Задача классификации — метрики, loss, семплирование (решения)
Теория — [[SECTION_classification]]. Решения протокола:
| Аспект | Решение | Обоснование |
|:--|:--|:--|
| **Главная метрика (objective Optuna)** | **macro-F1** на валидации | равный вес классам; accuracy при дисбалансе вводит в заблуждение |
| Сопутствующие метрики | balanced accuracy, top-1, MCC, Cohen κ, per-class F1, confusion matrix | полная картина; confusion matrix — диагностика похожих печатей |
| Loss (категориальный гиперпараметр) | `{ce, ce_ls, weighted_ce, focal}` | от baseline до imbalance-aware; LS для калибровки, focal/weighted/CB для дисбаланса |
| Веса классов | effective-number ([Cui 2019], `cb_beta`) | мягче, чем грубая обратная частота |
| Семплирование | `WeightedRandomSampler` (вкл/выкл — гиперпараметр) | альтернатива loss-ребалансировке |
| ⚠️ Двойная компенсация | **не** включать `weighted_ce/focal` + `WeightedRandomSampler` одновременно с полной силой | переусиление minority → падение precision (см. [[SECTION_classification]] §5) |
| Аугментации (гиперпараметры) | RandomResizedCrop, лёгкий rotation, color jitter, RandAugment(M), mixup(α) | mixup/RandAugment — «умный oversampling»; **без hflip** |
Реализация: [`metrics.py`](code/src/metrics.py) (sklearn), [`losses.py`](code/src/losses.py) (`FocalLoss`, `effective_number_weights`, `build_criterion`), [`data.py`](code/src/data.py) (`_make_sampler`, `compute_class_weights`).
---
## §5. Модель и режимы переноса (заморозка / частичная / MONA)
Теория — [[SECTION_edgenext]], [[SECTION_freezing]], [[SECTION_mona]]. Три режима реализованы в [`model.py`](code/src/model.py) (`build_model`, `ModelConfig.regime`):
| Режим | Что обучается | Когда ожидаемо лучше | Параметризация в Optuna |
|:--|:--|:--|:--|
| `full` | весь backbone + голова (2 LR-группы) | данных достаточно / домен далёк | — |
| `partial` | последние `n_unfrozen_stages` стадий + голова; `n=0` ⇒ linear probing | малый датасет, нужна частичная адаптация | `n_unfrozen_stages ∈ [0,4]` |
| `mona` | frozen backbone + **Conv-MONA** адаптеры + голова (PEFT, <5% весов) | малый датасет, адаптация без переобучения | `mona_bottleneck`, `mona_kernels`, `mona_last_only` |
Ключевые инженерные детали (см. [[SECTION_freezing]] §2):
- Замороженные параметры **исключаются** из optimizer (`build_param_groups` берёт только `requires_grad=True`).
- Замороженные **norm-слои переводятся в `eval()`** каждый train-шаг (`freeze_consistency`) — иначе running-статистика BN «уезжает». EdgeNeXt в основном на LayerNorm (этой ловушки нет), но правило соблюдаем для гибридных модулей.
- **Discriminative LR:** backbone получает `base_lr × backbone_lr_mult` (по умолчанию 0.1), голова/адаптеры — `base_lr`.
MONA-адаптер ([`mona.py`](code/src/mona.py)) — conv-вариант (вход `[B,C,H,W]`), верный коду репозитория `Leiyi-Hu/mona`: scaled-LayerNorm (`γ·LN(x)+γx·x`) → down 1×1 → `MonaOp` (DW 3/5/7, два residual) → GELU → up 1×1 (zero-init ⇒ старт = identity). Вставляется forward-hook'ом после выбранных стадий (число каналов определяется dry-run'ом, без зависимости от `feature_info`).
---
## §6. Анализ признаков через UMAP
Теория и предостережения — [[SECTION_umap]]. Реализация — [`umap_analysis.py`](code/src/umap_analysis.py).
Пайплайн: `forward_features``forward_head(pre_logits=True)` → L2-норма → матрица `[N,D]`
(а) UMAP→**2D** (раскраска по истинному классу, `random_state=42`, `metric=cosine`) для визуализации;
(б) UMAP→**1020D** для **кластеризации** (KMeans с `k`=число классов) → silhouette, **ARI/NMI** относительно истинных меток.
Что извлечь:
- Какие печати образуют изолированные «острова» (легко различимы), какие перекрываются (Ram/Snake/Dragon — сложенные ладони).
- **Сверка с confusion matrix**: перекрытие в эмбеддинге ⇒ off-diagonal масса. Это валидирует, что ошибки идут от признаков, а не от головы.
- Выполнить анализ **дважды**: на frozen ImageNet-EdgeNeXt (до дообучения) и на лучшей дообученной модели (после) → показать, как дообучение «растягивает» классы.
> [!warning] UMAP — инструмент генерации гипотез, не доказательство. Не интерпретировать абсолютные расстояния/размеры кластеров; фиксировать seed; выводы подтверждать метриками (§5 [[SECTION_umap]]).
---
## §7. Выбор метода и настройка Optuna
Обоснование выбора метода — [[SECTION_hpo_methods]]; разбор Optuna — [[SECTION_optuna]]. Решения:
- **Single-objective (E2):** `TPESampler(multivariate=True, seed=42)` + `MedianPruner` (или `HyperbandPruner`, если бюджет на trial неясен). Прунинг по эпохам через `trial.report(macro_f1, epoch)` + `trial.should_prune()` — реализовано в [`train.py`](code/src/train.py).
- **Multi-objective (E3):** `NSGAIISampler` или `AutoSampler` (OptunaHub), `directions=["maximize","minimize"]` = macro-F1 ↑ vs trainable-params ↓ → Pareto-фронт edge-конфигураций.
- **Baseline для честного сравнения метода (E5, опц.):** `RandomSampler` при **равном** бюджете trial-ов и **том же** пространстве поиска.
- **Storage:** SQLite (`--storage`) для персистентности и дашборда; `gc_after_trial=True` (VRAM).
- **Пространство поиска** (define-by-run, [`optuna_search.py`](code/src/optuna_search.py) `suggest_configs`): `model_name`, `regime` (+условные `n_unfrozen`/MONA-параметры), `lr` (log), `weight_decay` (log), `optimizer`, `backbone_lr_mult` (log), `loss_name` (+условный `focal_gamma`), `label_smoothing`, `weighted_sampler`, `img_size`, `batch_size`, `mixup_alpha`, `rrc_scale_min`, `randaug_magnitude`, `drop_rate`, `drop_path_rate`.
- **После поиска:** `plot_optimization_history`, `plot_param_importances` (fANOVA — какие гиперпараметры реально важны), `plot_slice`, `plot_pareto_front`.
> [!important] Лог-шкала для `lr`/`weight_decay` обязательна. Их эффект мультипликативен; линейный сэмплинг почти не пробует малые значения (см. [[SECTION_hpo_methods]] §8.1).
---
## §8. Гипотезы
Формат: «Если [метод], то [результат], потому что [обоснование]». Каждую проверяем численно (§9) при **равном бюджете** и фикс. seed; вывод — со стат-проверкой (bootstrap-CI / 3 seed), а не по разнице в 0.1%.
| ID | Гипотеза | Проверка | Основание |
|:--|:--|:--|:--|
| **H1** | **Если** искать TPE+pruning, **то** при равном бюджете trial-ов он найдёт конфиг с macro-F1 не хуже (обычно лучше) random/grid, **потому что** TPE моделирует $\ell(\lambda)/g(\lambda)$ и концентрирует бюджет в перспективной зоне, а pruner отсеивает плохих рано | E2 vs E5 (random), та же сетка, тот же бюджет | [Bergstra 2011/2012], [Akiba 2019] — [[SECTION_hpo_methods]], [[SECTION_optuna]] |
| **H2** | **Если** варьировать глубину заморозки, **то** оптимум — **partial freeze** (разморозить 12 верхние стадии), а не full FT и не linear probing, **потому что** на малом датасете full FT переобучается/искажает признаки (feature distortion), а linear probing не хватает ёмкости адаптации | E1 ablation (`n_unfrozen ∈ {0,1,2,3,4}`) + важность параметра `regime`/`n_unfrozen` в E2 | [Kumar 2022 LP-FT], CS231n — [[SECTION_freezing]] |
| **H3** | **Если** применить **MONA** (frozen backbone + адаптеры, <5% параметров), **то** macro-F1 ≥ full FT при многократно меньшем числе обучаемых параметров, **потому что** multi-scale conv-фильтры дают vision-friendly адаптацию, а заморозка регуляризует на малых данных | E1 (`full` vs `partial` vs `mona`) при равном бюджете; E3 Pareto (F1 vs params) | [Yin 2024 Mona] — [[SECTION_mona]] |
| **H4** | **Если** включить imbalance-aware loss/sampler (weighted/focal/CB или WeightedRandomSampler), **то** вырастет **macro-F1 / balanced accuracy / recall редких классов** (ценой лёгкого падения overall accuracy), **потому что** редкие классы получают больший вклад в обучение | E2: важность `loss_name`/`weighted_sampler`; целевой контраст на самых редких классах | [Cui 2019], [Lin 2017] — [[SECTION_classification]] |
| **H5** | **Если** оптимизировать multi-objective (macro-F1 ↑, trainable-params ↓), **то** Pareto-фронт даст **edge-предпочтительные** конфигурации (например MONA/partial-малый вариант) при незначимой потере F1, **потому что** PEFT/малые варианты EdgeNeXt дают высокую точность при низкой ёмкости | E3 (`NSGAIISampler`/`AutoSampler`), анализ `plot_pareto_front` | [[SECTION_optuna]] §4, [[SECTION_edgenext]] §3 |
| **H6** | **Если** классы перекрываются в UMAP-эмбеддинге, **то** именно эти пары дают наибольшую off-diagonal массу в confusion matrix, **потому что** ошибки классификатора порождаются неразделимостью в признаковом пространстве | E4: сверка UMAP-перекрытий с confusion matrix лучшей модели; ARI/NMI | [McInnes 2018] — [[SECTION_umap]] |
---
## §9. План численных экспериментов и decision-rules
Все прогоны: seed=42 (финальные сравнения — 42/123/456), **равный бюджет** на сравниваемые конфигурации, меняется **только один фактор**. Эталон метрик — [`metrics.py`](code/src/metrics.py).
```mermaid
flowchart TD
D[Naruto Sign<br/>ImageFolder] --> S[dataset_stats.py<br/>STATS.md]
D --> SP[Стратиф. split seed=42<br/>data.py]
SP --> B0[B0 baseline<br/>partial n=1, ce_ls]
B0 --> E1{E1 ablation режимов<br/>full / partial 0..4 / mona}
E1 -->|лучший режим| E2[E2 Optuna single-obj<br/>TPE + MedianPruner<br/>objective = val macro-F1]
E2 -->|best_params| FT[Финал: 3 seed<br/>+ test + bootstrap-CI]
E2 --> E3[E3 Optuna multi-obj<br/>NSGA-II: F1 up vs params down<br/>Pareto-front]
E2 -. контроль .-> E5[E5 random vs TPE<br/>равный бюджет, H1]
FT --> E4[E4 UMAP до/после<br/>2D + кластеризация<br/>ARI/NMI]
E4 --> CM[Сверка с confusion matrix<br/>H6]
E1 --> HYP[Гипотезы H2/H3]
E3 --> HYP5[Гипотеза H5]
E2 --> HYP4[Важность параметров fANOVA<br/>H4]
HYP --> R[REPORT.md<br/>вердикты H1-H6 + caveat]
HYP4 --> R
HYP5 --> R
CM --> R
```
| Этап | Что делаем | Команда | Артефакт | Decision-rule |
|:--|:--|:--|:--|:--|
| **B0** baseline | Один честный прогон `partial, n=1, ce_ls`, eval на test + confusion | `python -m src.run_baseline --data-root <...> --regime partial --n-unfrozen 1` | `report.json`, `confusion.png` | Зафиксировать baseline macro-F1, который HPO обязан побить |
| **E1** ablation режимов | `full` vs `partial(n=0..4)` vs `mona` при равном recipe/бюджете | `run_baseline` × режимы (скрипт-обёртка) | таблица режим×(macro-F1, params), кривые | H2/H3: лучший режим; если `mona``full` при <5% params — H3 поддержана |
| **E2** Optuna single-obj | TPE+pruner, objective=val macro-F1, 4080 trial | `python -m src.optuna_search --data-root <...> --sampler tpe --pruner median --n-trials 60 --storage` | `*_best.json`, `*_trials.csv`, importance/history/slice png | Лучший trial должен побить B0; зафиксировать best_params |
| **E3** Optuna multi-obj | NSGA-II/AutoSampler, F1↑ vs params↓, 60100 trial | `python -m src.optuna_search --data-root <...> --multi-objective --sampler nsga --n-trials 80` | Pareto-front png + таблица недоминируемых | H5: выбрать edge-точку (max F1 при params ≤ бюджета) |
| **E4** UMAP-анализ | эмбеддинги до/после дообучения, 2D + кластеризация | `python -m src.umap_analysis --data-root <...> [--checkpoint best.pt]` | `umap_2d.png`, `cluster_report.json` | H6: сверить перекрытия с confusion matrix |
| **E5** (опц.) метод-контроль | random vs TPE при равном бюджете и пространстве | `--sampler random` vs `--sampler tpe` | кривые «best-so-far vs число trial» | H1: TPE ≥ random; вывод со стат-проверкой |
**Финальная переоценка:** топ-конфиг(и) из E2/E3 переобучить на 3 seed (42/123/456), сравнить по среднему ± std, оценить на **test** (трогается один раз) + bootstrap-CI. Только после этого делать выводы.
> Абсолютные числа могут быть скромными при малом/«грязном» датасете — оценивается **корректность методологии** (равный бюджет, фикс. seed, отсутствие утечки, честный eval, признание caveat), а не голый macro-F1.
---
## §10. Воспроизводимость, честность, типичные ловушки
- **Seed** 42 везде (`set_seed` в [`train.py`](code/src/train.py)); финал — 3 seed.
- **Test трогать один раз**, в самом конце; подбор — только на val (иначе утечка test в HPO).
- **Не сравнивать при разном бюджете** методы/конфиги; единое пространство поиска.
- **Лог-шкала** для lr/wd.
- **Заморозка BN** — `freeze_consistency` после каждого `model.train()`.
- **Двойная компенсация дисбаланса** — не включать sampler + weighted-loss одновременно на полную (см. [[SECTION_classification]] §5).
- **Утечка из видео** — главный риск Naruto Sign; зафиксировать в REPORT.md, при наличии id — group-split.
- **UMAP** — не «доказывать» кластерами; фиксировать seed, подтверждать метриками.
- **Атомарная запись** результатов (`os.replace`), `gc_after_trial` для VRAM.
- Отрицательные результаты и подтверждённые caveat фиксировать явно — это плюс.
---
## §11. Что сдаёт студент
1. **Код** (папка [`code/`](code/)) с `README.md` и `requirements.txt` — запускается на чужой машине по инструкции.
2. **`STATS.md`** — таблица статистики Naruto Sign по факту скачивания ([`dataset_stats.py`](code/src/dataset_stats.py)) + источник/версия/лицензия.
3. **`REPORT.md`** — результаты всех этапов: таблицы (B0/E1/E2/E3), кривые обучения, importance-график Optuna, Pareto-фронт, UMAP-картинки (до/после) + confusion matrix, проверка каждой гипотезы H1H6 с вердиктом (поддержана/опровергнута/неубедительно), анализ ошибок (35 примеров), честные caveat.
4. **Артефакты:** `*_trials.csv`, `*_best.json`, `umap_2d.png`, `cluster_report.json`, лучший чекпойнт + config, папка PNG.
**MVP (≈810 дней):** статистика + B0 baseline + E1 ablation 3 режимов + E2 Optuna (≥40 trial, TPE+pruner) с побитием baseline + E4 UMAP (2D + ARI) + REPORT с проверкой ≥3 гипотез.
**Stretch:** E3 multi-objective + Pareto-анализ; E5 random vs TPE со стат-проверкой; 3-seed переоценка + bootstrap-CI; MONA-абляция (bottleneck/ядра) внутри Optuna; group-split без утечки; AutoSampler.
---
## §12. Календарь (14 дней)
| Дни | Задача |
|:--|:--|
| 12 | Скачать датасет, `dataset_stats.py`, `STATS.md`; прочитать главы [[SECTION_edgenext]], [[SECTION_classification]] |
| 3 | Поднять окружение (`requirements.txt`), smoke-тест `build_model` для 3 режимов; прочитать [[SECTION_freezing]], [[SECTION_mona]] |
| 4 | **B0** baseline + confusion matrix |
| 56 | **E1** ablation режимов (full/partial 04/mona); таблица + кривые |
| 6 | Прочитать [[SECTION_hpo_methods]], [[SECTION_optuna]] |
| 79 | **E2** Optuna single-objective (TPE+pruner), importance/history/slice; побить B0 |
| 9 | **E4** UMAP (до/после), сверка с confusion matrix |
| 10 | Воспроизводимый прогон зафиксирован; черновик REPORT |
| 1112 | **E3** multi-objective + Pareto (stretch); 3-seed переоценка |
| 13 | **E5** random vs TPE (stretch); анализ ошибок |
| 14 | Финализация REPORT.md/README.md, графики, выводы, caveat |
---
## §13. Карта кода (`code/`)
| Файл | Назначение |
|:--|:--|
| [`src/data.py`](code/src/data.py) | ImageFolder, стратифицированный split, transforms (hflip OFF), WeightedRandomSampler, class weights |
| [`src/model.py`](code/src/model.py) | EdgeNeXt (timm), режимы full/partial/mona, freeze-логика, param-groups (discriminative LR), `extract_features` |
| [`src/mona.py`](code/src/mona.py) | Conv-MONA адаптер (по коду `Leiyi-Hu/mona`), вставка forward-hook'ом после стадий |
| [`src/losses.py`](code/src/losses.py) | CE/LS/weighted/Focal, effective-number weights, `build_criterion` |
| [`src/metrics.py`](code/src/metrics.py) | macro-F1, balanced acc, top-k, MCC, κ, confusion (sklearn) |
| [`src/train.py`](code/src/train.py) | train/eval цикл, mixup (timm), early-stop, **Optuna pruning** (`report`/`should_prune`), VRAM-hygiene |
| [`src/optuna_search.py`](code/src/optuna_search.py) | пространство поиска (define-by-run), single/multi-objective, sampler+pruner, экспорт best/trials |
| [`src/umap_analysis.py`](code/src/umap_analysis.py) | эмбеддинги → UMAP 2D + кластеризация → silhouette/ARI/NMI + 2D-картинка |
| [`src/run_baseline.py`](code/src/run_baseline.py) | одиночный прогон + test-метрики + confusion-heatmap (B0/E1) |
| [`src/dataset_stats.py`](code/src/dataset_stats.py) | статистика ImageFolder (число классов, per-class, дисбаланс, разрешения) |
> Конфигурация: задача управляется Optuna (define-by-run) + argparse/dataclass, поэтому здесь сознательное отступление от gin-strict (`.claude/rules/python-code.md`) — пространство гиперпараметров описывается в `suggest_configs`, а не в статическом `.gin`. Остальные конвенции (type hints, Google-docstrings, `from __future__ import annotations`, seeds, atomic writes, `inference_mode`) соблюдены.
---
## §14. Чем задание выявляет сильного студента
Слабый студент сделает MVP буквально (посчитает числа, прогонит Optuna). Сильный проявит **критическое мышление**:
- осознает и измерит **утечку из видео** (group-split vs per-frame), не примет завышенный R/F1 за чистую монету;
- проверит H2/H3 честно (равный бюджет) и свяжет вывод с теорией (LP-FT, «5%>100%»), а не просто выберет «что победило»;
- покажет **важность гиперпараметров** (fANOVA) и сузит пространство во второй итерации;
- сверит UMAP-перекрытия с confusion matrix (H6) — увяжет признаковый анализ с ошибками;
- докажет H1 со стат-проверкой (random vs TPE при равном бюджете), а не на одном seed;
- честно зафиксирует отрицательные результаты и caveat (hflip, двойная компенсация, малый/грязный датасет).
- **Сквозной маркер:** способность связать эксперимент с проектом (EdgeNeXt ↔ SOFIA-Tiny [[B134_BB_2022_EdgeNeXt_Efficient_CNN-Transformer_Mobile_Vision]]; Conv-MONA ↔ [[PROTOCOL_text_encoder_benchmark_StripNet_GTA_UAV]]) и сделать честный вывод с признанием caveat.