# Серия настройки (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_/` с 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_.log`. Каждый `run` создаёт **новый** каталог `results/__/`. У двух запусков с одним пресетом имена отличаются только меткой времени; какой это был вариант, смотрите в `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. Настройка модели эти ограничения не снимает.