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

20 KiB
Raw Blame History

Серия настройки (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. Вся серия скриптом

cd lab1
bash scripts/run_tuning_experiments.sh

Быстрый прогон:

REPEATS=1 bash scripts/run_tuning_experiments.sh

Другой интерпретатор:

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. Каждый файл — одна точка перебора признаков. Модель и пресет задаются в команде, не в имени файла.

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. Сравнение с базой

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 совпадает с полной серией; его можно не гонять, если база уже есть.

Как запускать.

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 близки, вывод про регуляризацию устойчив.

Как запускать.

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 и т.д.).

Как запускать.

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

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

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

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

stylo = 1.0
char = 0.5
pos = 0.7
lsa_word = 0.3
lsa_pos = 0.5
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

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 такая модель легко запоминает обучающие блоки. Сначала ограничивают глубину и увеличивают лист, а не число деревьев.

Как запускать.

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. Один контролируемый запуск (не полный перебор) проверяет, есть ли смысл включать модель в сравнение.

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; это независимая нижняя граница, не конкурент основному пресету.

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 лучших конфигураций:

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. Настройка модели эти ограничения не снимает.