20 KiB
Серия настройки (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:
- T4, T5 — состав групп (дешево и часто полезно).
- T6 — n-граммы.
- T8 —
kLSA в объединённом наборе. - T7 — веса, если после T4–T6 остался шумный блок.
- T1, затем T2 —
C. - T3 — один раз, для отчёта про дисбаланс.
- T9, T10 — только если линейные модели уже сняты.
- 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. Настройка модели эти ограничения
не снимает.