Files
NLP/lab1/TUNING_EXPERIMENTS.md
2026-10-25 19:08:11 +03:00

408 lines
20 KiB
Markdown
Raw 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.

# Серия настройки (T1–T11)
Документ описывает **дополнительные** эксперименты поверх полной серии
`scripts/run_all_experiments.sh`. Они не заменяют протокол E0–E16 из
`EXPERIMENTS.md` и не повторяют его запуски с теми же параметрами.
Полная серия измеряет качество на **значениях по умолчанию**. Эта серия
проверяет, меняется ли macro-F1, если изменить регуляризацию, состав
признаков, n-граммы, веса блоков, число компонент LSA или гиперпараметры
деревьев.
Справочник «где в коде лежит параметр» — `TUNING.md`. Здесь — что запускать,
зачем и как интерпретировать.
---
## 1. Зачем эта серия
Исследовательский вопрос тот же: различимы ли 34 персонажа по одной реплике.
Базовый ответ даёт `stylometry_pos_lsa` + `logreg` из полной серии.
Дополнительно нужно понять:
- не слишком ли слабая или сильная регуляризация при 2005 объектах и 34 классах;
- не вредят ли нестабильные группы (`vocabulary`, `readability`) и признаки длины;
- не переобучаются ли широкие символьные n-граммы;
- не давит ли один блок признаков на остальные после L2-нормировки;
- не лучше ли другое число компонент LSA, чем `k=100`;
- не лучше ли более мелкие деревья, чем лес без ограничения глубины;
- даёт ли дельта Барроуза что-то на краях диапазона MFW (50 и 1000 слов).
Главная метрика по-прежнему **macro-F1** с бутстрэп-интервалом. Разница в
1–2 процентных пункта часто шум: сравнивайте запуски через `compare`
(Мак-Немар и бутстрэп-разница), а не «на глаз» по одному числу.
---
## 2. Что нельзя путать с полной серией
| Полная серия (`run_all_experiments.sh`) | Эта серия |
|---|---|
| Базовые пресеты, абляции leave-one-out, LSA 50/100/200, benchmark моделей, nested CV | Перебор **внутри** лучшего пресета |
| Logreg/SVM с `C=1`, `class_weight="balanced"` | Другие `C`, отключение весов классов |
| Char `[2,5]` / 5000, POS `[1,3]` / 500, `min_df=3` | Узкие и широкие n-граммы |
| Равные веса блоков | Заданные веса |
| Дельта 100/200/500 MFW | Дельта 50 и 1000 MFW |
| Extra trees нет | Один контролируемый запуск extra trees |
Не запускайте эту серию вместо E0–E16. Сначала нужна база: каталог
`results/stylometry_pos_lsa_logreg_<timestamp>/` с 5 повторами (или хотя бы 3).
---
## 3. Как запускать
Все команды — из каталога `lab1`. Схема CV та же, что в протоколе:
5 блоков, seed `42`. Для черновика достаточно 1 повтора, для отчёта — 3
(лучшие 1–2 конфигурации потом перепроверьте с 5).
### 3.1. Вся серия скриптом
```bash
cd lab1
bash scripts/run_tuning_experiments.sh
```
Быстрый прогон:
```bash
REPEATS=1 bash scripts/run_tuning_experiments.sh
```
Другой интерпретатор:
```bash
PYTHON=../.venv/bin/python bash scripts/run_tuning_experiments.sh
```
Лог: `results/logs/tune_<timestamp>.log`.
Каждый `run` создаёт **новый** каталог `results/<preset>_<model>_<timestamp>/`.
У двух запусков с одним пресетом имена отличаются только меткой времени;
какой это был вариант, смотрите в `run_metadata.json` (`model_params`,
`feature_config`).
### 3.2. Один эксперимент вручную
Конфиги лежат в `configs/tune_*.toml`. Каждый файл — **одна** точка перебора
признаков. Модель и пресет задаются в команде, не в имени файла.
```bash
python -m stylometry.cli run \
--config configs/tune_no_vocab_readability.toml \
--preset stylometry_pos_lsa \
--model logreg \
--repeats 3 \
--seed 42
```
Параметры классификатора toml не требуют — их передаёт `--model-params`.
### 3.3. Сравнение с базой
```bash
python -m stylometry.cli compare \
--run-a results/stylometry_pos_lsa_logreg_<база> \
--run-b results/stylometry_pos_lsa_logreg_<тюнинг>
```
Оба запуска должны иметь одинаковые `--folds` и `--seed`. Иначе парное
сравнение некорректно.
---
## 4. Конфиги признаков
Файлы не «включают серию целиком»: каждый подменяет только указанные поля
`FeatureConfig`. Остальное берётся из пресета `stylometry_pos_lsa`.
| Файл | Что меняет |
|---|---|
| `configs/tune_no_vocab_readability.toml` | группы без `vocabulary` и `readability` |
| `configs/tune_no_vocab_readability_length.toml` | ещё без `length` |
| `configs/tune_char_narrow.toml` | char `[2,4]`, 3000 признаков, POS 500, `min_df=5` |
| `configs/tune_char_wide.toml` | char `[2,6]`, 8000, POS `[1,4]` / 800, `min_df=2` |
| `configs/tune_block_weights.toml` | веса `stylo/char/pos/lsa_word/lsa_pos` |
| `configs/tune_lsa_k150.toml` | `lsa_components = 150` |
| `configs/tune_lsa_k300.toml` | `lsa_components = 300` |
---
## 5. Эксперименты
Базовый пресет везде `stylometry_pos_lsa`, если не сказано иное.
Ориентир для сравнения — pooled macro-F1 базового logreg из полной серии.
### T1. Логистическая регрессия: `C`
**Зачем.** `C` — обратная сила L2-регуляризации. На широком признаковом
пространстве (ручные признаки + n-граммы + LSA) модель с `C=1` может
переобучаться или, наоборот, быть слишком простой. Нужно найти диапазон,
а не одно «магическое» число.
**Что меняется.** `C ∈ {0.03, 0.1, 0.3, 1, 3, 10}`. Остальное как в базе
(`class_weight="balanced"`). Значение `C=1` совпадает с полной серией;
его можно не гонять, если база уже есть.
**Как запускать.**
```bash
for c in 0.03 0.1 0.3 1 3 10; do
python -m stylometry.cli run --preset stylometry_pos_lsa --model logreg \
--repeats 3 --seed 42 --model-params "{\"C\": $c}"
done
```
**Как читать.** Меньше `C` — проще модель. Если macro-F1 растёт при уменьшении
`C`, база переобучалась. Если растёт только при `C ≥ 3`, регуляризации в базе
было слишком много. Смотрите также log loss: он показывает калибровку, не
только точность меток.
### T2. Линейный SVM: `C`
**Зачем.** Та же логика, другой критерий (зазор вместо логистической потери).
SVM в полной серии тоже шёл с `C=1`. Если лучший `C` у SVM и logreg близки,
вывод про регуляризацию устойчив.
**Как запускать.**
```bash
for c in 0.03 0.1 0.3 1 3 10; do
python -m stylometry.cli run --preset stylometry_pos_lsa --model linear_svc \
--repeats 3 --seed 42 --model-params "{\"C\": $c}"
done
```
**Ограничение.** У `linear_svc` нет вероятностей: log loss не считается,
top-k строится по решающей функции.
### T3. Logreg без балансировки классов
**Зачем.** В корпусе 15–139 реплик на персонажа. `class_weight="balanced"`
почти наверняка нужен, но это надо показать: иначе macro-F1 может казаться
низким «из-за модели», хотя виноват сдвиг к частым классам (Abigail и т.д.).
**Как запускать.**
```bash
python -m stylometry.cli run --preset stylometry_pos_lsa --model logreg \
--repeats 3 --seed 42 --model-params '{"class_weight": null}'
```
**Как читать.** Если accuracy почти не падает, а macro-F1 и balanced accuracy
падают, модель без весов игнорирует редких персонажей. Это аргумент в пользу
macro-F1 как основной метрики.
### T4. Без `vocabulary` и `readability`
**Зачем.** На репликах ~19 слов TTR, индексы Honore/Yule и формулы читаемости
нестабильны (`FEATURES.md`, гипотеза H2). Полная серия делает leave-one-out
(`ablate`): удаляет **по одной** группе. Здесь обе шумные группы снимаются
сразу — ближе к тому, как собирали бы «очищенный» основной набор.
**Конфиг.** `configs/tune_no_vocab_readability.toml`
```bash
python -m stylometry.cli run --config configs/tune_no_vocab_readability.toml \
--preset stylometry_pos_lsa --model logreg --repeats 3 --seed 42
```
**Как читать.** Рост или отсутствие падения macro-F1 значит, что эти группы
не несут устойчивого сигнала (или мешают). Сильное падение — в протоколе их
нельзя вычёркивать без оговорки.
### T5. Ещё без `length`
**Зачем.** Гипотеза H4: длинные реплики классифицируются лучше. Если качество
не падает после удаления length-признаков, различимость персонажей не сводится
к «кто говорит длиннее». Если падает — часть результата объясняется длиной,
это нужно явно написать в отчёте.
**Конфиг.** `configs/tune_no_vocab_readability_length.toml`
```bash
python -m stylometry.cli run --config configs/tune_no_vocab_readability_length.toml \
--preset stylometry_pos_lsa --model logreg --repeats 3 --seed 42
```
Имеет смысл сопоставить с `length-report` по базовому запуску.
### T6. Символьные и POS n-граммы: узкие vs широкие
**Зачем.** В полной серии фиксированы char 2–5 / 5000 и POS 1–3 / 500.
Узкий вариант (короче n-граммы, выше `min_df`) проверяет переобучение на
редких орфографических шаблонах. Широкий (2–6, больше признаков, `min_df=2`)
проверяет, не теряем ли морфологию и пунктуационные привычки.
**Конфиги.** `tune_char_narrow.toml`, `tune_char_wide.toml`
```bash
python -m stylometry.cli run --config configs/tune_char_narrow.toml \
--preset stylometry_pos_lsa --model logreg --repeats 3 --seed 42
python -m stylometry.cli run --config configs/tune_char_wide.toml \
--preset stylometry_pos_lsa --model logreg --repeats 3 --seed 42
```
**Как читать.** Если wide лучше на обучении блоков, но pooled macro-F1 не выше
базы (или хуже на редких классах) — переобучение. Если narrow не хуже базы —
лишняя размерность не нужна.
### T7. Веса блоков
**Зачем.** После L2-нормировки каждый блок имеет единичную норму и конкурирует
наравне, даже если один из них шумный. В базе веса равны. Здесь ослабляются
char и особенно `lsa_word` (тематический риск, гипотеза H3), POS и стилометрия
остаются сильнее.
**Конфиг.** `configs/tune_block_weights.toml`
```text
stylo = 1.0
char = 0.5
pos = 0.7
lsa_word = 0.3
lsa_pos = 0.5
```
```bash
python -m stylometry.cli run --config configs/tune_block_weights.toml \
--preset stylometry_pos_lsa --model logreg --repeats 3 --seed 42
```
**Как читать.** Веса — такой же гиперпараметр, как `C`. Один удачный набор не
доказывает оптимум. Если macro-F1 вырос и `lsa_word` при этом ослаблен, это
дополнительный довод, что тема мешала стилю.
### T8. Число компонент LSA: 150 и 300
**Зачем.** В полной серии `lsa-sweep` смотрит `k ∈ {50, 100, 200}` по
**отдельным** вариантам LSA. В основном пресете всегда `k=100` сразу для
`word` и `pos`. Здесь проверяется, помогает ли больше компонент **в
объединённом** наборе, плюс недостающие точки 150 и 300 в переборе вариантов.
**Конфиги.** `tune_lsa_k150.toml`, `tune_lsa_k300.toml`
```bash
python -m stylometry.cli run --config configs/tune_lsa_k150.toml \
--preset stylometry_pos_lsa --model logreg --repeats 3 --seed 42
python -m stylometry.cli run --config configs/tune_lsa_k300.toml \
--preset stylometry_pos_lsa --model logreg --repeats 3 --seed 42
python -m stylometry.cli lsa-sweep \
--variants word,content,fw,char,pos \
--components 150,300 \
--model logreg --repeats 1 --seed 42
```
**Как читать.** Рост `k` без роста macro-F1 — лишние компоненты ловят шум.
Сравнивайте `word`/`content` с `pos`/`fw`: если выигрыш только у word-LSA,
это снова тема, а не стиль.
### T9. Случайный лес: глубина и лист
**Зачем.** В `benchmark` лес идёт с дефолтами (`n_estimators=400`,
`max_depth=None`, `min_samples_leaf=1`). На 2005×34 такая модель легко
запоминает обучающие блоки. Сначала ограничивают глубину и увеличивают лист,
а не число деревьев.
**Как запускать.**
```bash
for depth in 10 20 30; do
for leaf in 2 3 5; do
python -m stylometry.cli run --preset stylometry_pos_lsa --model random_forest \
--repeats 3 --seed 42 \
--model-params "{\"max_depth\": $depth, \"min_samples_leaf\": $leaf}"
done
done
python -m stylometry.cli run --preset stylometry_pos_lsa --model random_forest \
--repeats 3 --seed 42 \
--model-params '{"max_depth": 20, "min_samples_leaf": 3, "max_features": 0.1}'
```
**Как читать.** Если мелкие деревья не хуже (или лучше) дефолтного леса из
benchmark, сложность в базе была лишней. Сравнивайте с logreg, а не только
друг с другом: лес может выигрывать accuracy и проигрывать macro-F1.
### T10. Сверхслучайные деревья
**Зачем.** Extra trees не входили в `benchmark` полной серии. Случайные пороги
обычно снижают дисперсию относительно RF. Один контролируемый запуск
(не полный перебор) проверяет, есть ли смысл включать модель в сравнение.
```bash
python -m stylometry.cli run --preset stylometry_pos_lsa --model extra_trees \
--repeats 3 --seed 42 \
--model-params '{"max_depth": 20, "min_samples_leaf": 3}'
```
Если результат заметно хуже logreg, модель достаточно упомянуть как проверку,
не раздувая сетку.
### T11. Дельта Барроуза: 50 и 1000 MFW
**Зачем.** Классическая стилометрия на самых частотных словах. Полная серия
уже смотрит 100/200/500. Края диапазона: 50 слов — очень компактный профиль,
1000 — больше лексики и больше темы. На коротких репликах дельта обычно
слабее ML; это независимая нижняя граница, не конкурент основному пресету.
```bash
python -m stylometry.cli delta \
--features 50,1000 --metrics manhattan,cosine --repeats 2 --seed 42
```
`zscore` в CLI не переключается (в коде по умолчанию включён), поэтому его
здесь нет.
---
## 6. Рекомендуемый порядок, если запускаете вручную
Не гоните все циклы сразу, если времени мало. Порядок совпадает с `TUNING.md`:
1. T4, T5 — состав групп (дешево и часто полезно).
2. T6 — n-граммы.
3. T8 — `k` LSA в объединённом наборе.
4. T7 — веса, если после T4–T6 остался шумный блок.
5. T1, затем T2 — `C`.
6. T3 — один раз, для отчёта про дисбаланс.
7. T9, T10 — только если линейные модели уже сняты.
8. T11 — короткая классическая проверка.
После отбора 1–2 лучших конфигураций:
```bash
python -m stylometry.cli run --preset stylometry_pos_lsa --model logreg \
--repeats 5 --seed 42 --nested
```
Вложенная CV с расширенной сеткой честнее, чем выбор `C` по тем же OOF,
что потом публикуете. Узкая сетка `{0.1, 1, 10}` уже есть в полной серии;
имеет смысл `--nested` только если T1 сдвинул разумный диапазон (например,
к `{0.03, 0.1, 0.3}`). Для этого нужно изменить `PARAM_GRIDS` в
`stylometry/pipelines.py` — это уже правка кода, не конфиг.
---
## 7. Как фиксировать и чего не делать
Для каждого запуска сохраняются стандартные файлы: `run_metadata.json`,
`pooled_metrics.csv`, `bootstrap_ci.json`, `per_class_report.csv`,
матрица ошибок. Перед сравнением откройте `run_metadata.json` и убедитесь,
что отличаются только задуманные поля.
Нельзя:
- выбирать победителя по тестовой выборке вне CV;
- менять `--folds` или `--seed` «чтобы стало лучше»;
- сравнивать `QUICK=1` / `REPEATS=1` с финальными 5 повторами как равные;
- объявлять улучшение без интервала и `compare`;
- подменять macro-F1 долей правильных ответов.
Ограничения корпуса те же, что в `EXPERIMENTS.md`: короткие реплики, один
автор, ручная фильтрация, дисбаланс 15–139. Настройка модели эти ограничения
не снимает.