# Настройка моделей и признаков Документ отвечает на практический вопрос: **где и какие параметры менять, чтобы пытаться улучшить macro-F1**. Он не заменяет `EXPERIMENTS.md`: там описан протокол, здесь — точки настройки и рекомендуемый порядок перебора. Главное правило: настраивать параметры можно только по обучающим блокам перекрёстной проверки. Нельзя выбирать признаки, число компонент LSA или гиперпараметры по тестовой выборке: это даёт оптимистическую оценку и не воспроизводится. --- ## 1. Где что находится | Что менять | Файл | Где именно | |---|---|---| | Параметры классификаторов | `stylometry/pipelines.py` | `make_classifier` | | Сетки для вложенной CV | `stylometry/pipelines.py` | `PARAM_GRIDS` | | Состав блоков признаков | `stylometry/pipelines.py` | `FeatureConfig`, `preset_config` | | Веса блоков | `stylometry/pipelines.py` | `FeatureConfig.block_weights` | | Параметры TF-IDF/LSA | `stylometry/lsa.py` | `_DEFAULT_CONFIGS`, `LSAVectorizer` | | Ручные признаки | `stylometry/features/` | модули групп | | Списки функциональных слов | `stylometry/lexicons.py` | тематические множества | | Параметры дельты | `stylometry/delta.py` | `DeltaClassifier`, `MFWVectorizer` | | Схема CV | `stylometry/cv.py` | `run_repeated_cv`, `run_nested_cv` | | Конфигурация запуска | `configs/default.toml` | `[cv]`, `[experiment]`, `[model_params]`, `[features]` | | CLI | `stylometry/cli.py` | `cmd_run`, `cmd_benchmark`, `cmd_ablate`, `cmd_lsa_sweep` | --- ## 2. Параметры классификаторов ### 2.1. Через конфигурацию В `configs/default.toml` есть секция `[model_params]`: ```toml [model_params] C = 0.3 class_weight = "balanced" ``` Запуск: ```bash python -m stylometry.cli run \ --config configs/default.toml \ --preset stylometry_pos_lsa \ --model logreg \ --repeats 3 ``` Параметры из `[model_params]` передаются в `make_classifier`. Для каждой модели используются только её собственные параметры; лишние игнорируются. ### 2.2. Через командную строку Удобно для быстрых проверок: ```bash python -m stylometry.cli run \ --preset stylometry_pos_lsa \ --model logreg \ --repeats 1 \ --model-params '{"C": 0.3, "class_weight": "balanced"}' ``` JSON позволяет передать `null`, например отключить веса классов: ```bash --model-params '{"class_weight": null}' ``` ### 2.3. Что менять у конкретных моделей #### Логистическая регрессия (`logreg`) | Параметр | Смысл | Разумный диапазон | |---|---|---| | `C` | Обратная сила L2-регуляризации | 0,01; 0,03; 0,1; 0,3; 1; 3; 10; 30 | | `class_weight` | Веса классов | `"balanced"` или `null` | | `solver` | Алгоритм оптимизации | `lbfgs`, `liblinear`, `saga` | | `max_iter` | Максимум итераций | 1000–10000 | Что ожидать: - меньше `C` — сильнее регуляризация, модель проще, меньше переобучения; - больше `C` — модель лучше подстраивается под обучающие данные, но может переобучиться; - `class_weight="balanced"` почти всегда полезен при дисбалансе 15–139; - `liblinear` иногда лучше на разреженных признаках, но медленнее при многих классах. #### Линейный метод опорных векторов (`linear_svc`) | Параметр | Смысл | Разумный диапазон | |---|---|---| | `C` | Штраф за нарушения зазора | 0,01; 0,03; 0,1; 0,3; 1; 3; 10 | | `loss` | Функция потерь | `squared_hinge`, `hinge` | | `class_weight` | Веса классов | `"balanced"` или `null` | | `tol` | Допуск сходимости | 1e-3–1e-5 | | `max_iter` | Максимум итераций | 10000–50000 | Помните: `linear_svc` не выдаёт вероятности. Для top-k используются значения решающей функции; log loss для него не считается. #### Случайный лес (`random_forest`) | Параметр | Смысл | Разумный диапазон | |---|---|---| | `n_estimators` | Число деревьев | 300–1000 | | `max_depth` | Глубина дерева | `null`, 10, 20, 30, 50 | | `min_samples_leaf` | Минимум объектов в листе | 1, 2, 3, 5 | | `min_samples_split` | Минимум объектов для разбиения | 2, 5, 10 | | `max_features` | Число признаков на разбиение | `"sqrt"`, `"log2"`, 0.1, 0.3 | | `class_weight` | Веса классов | `"balanced_subsample"`, `"balanced"`, `null` | На 2005 объектах и 34 классах лес легко переобучается. Сначала уменьшайте `max_depth` и увеличивайте `min_samples_leaf`, а не число деревьев. #### Сверхслучайные деревья (`extra_trees`) Параметры те же, что у случайного леса. `max_features` и `min_samples_leaf` здесь особенно важны: случайные пороги уменьшают дисперсию, но при слишком большой глубине модель всё равно переобучается. #### Наивный байесовский классификатор (`multinomial_nb`) | Параметр | Смысл | Разумный диапазон | |---|---|---| | `alpha` | Сглаживание Лапласа | 0,01; 0,1; 0,3; 1; 3; 10 | | `fit_prior` | Учитывать априорные вероятности классов | `true`, `false` | В текущем конвейере для `multinomial_nb` исключаются LSA-блоки, потому что компоненты SVD могут быть отрицательными. Используются ручные неотрицательные признаки, char- и POS- n-граммы. Весов классов у модели нет; при сильном дисбалансе она смещается к частым классам. #### k ближайших соседей (`knn`) | Параметр | Смысл | Разумный диапазон | |---|---|---| | `n_neighbors` | Число соседей | 3, 5, 7, 9, 11, 15 | | `weights` | Вес соседей | `"uniform"`, `"distance"` | | `metric` | Метрика расстояния | `"cosine"`, `"euclidean"`, `"manhattan"` | Для разреженных текстовых признаков косинусное расстояние обычно лучше евклидова. kNN чувствителен к размерности; уменьшение числа признаков или LSA может помочь. #### Дельта Барроуза (`delta`) В `stylometry/delta.py`: - `n_features` — число наиболее частотных слов; - `metric` — `manhattan`, `cosine`, `euclidean`; - `zscore` — z-нормирование частот. CLI перебирает `n_features` и `metric`: ```bash python -m stylometry.cli delta \ --features 50,100,200,500,1000 \ --metrics manhattan,cosine \ --repeats 2 ``` Чтобы менять `zscore`, передайте параметр в `DeltaClassifier` из сценария или добавьте его в CLI. --- ## 3. Сетки для вложенной кросс-валидации `PARAM_GRIDS` в `stylometry/pipelines.py` задаёт, что именно перебирает `--nested`. Текущие сетки небольшие, чтобы серия завершалась за разумное время. Их можно расширить: ```python PARAM_GRIDS = { "logreg": {"model__C": [0.03, 0.1, 0.3, 1.0, 3.0, 10.0]}, "linear_svc": {"model__C": [0.03, 0.1, 0.3, 1.0, 3.0, 10.0]}, "random_forest": { "model__max_depth": [None, 10, 20, 30], "model__min_samples_leaf": [1, 2, 3, 5], "model__max_features": ["sqrt", 0.1, 0.3], }, "multinomial_nb": {"model__alpha": [0.01, 0.1, 0.3, 1.0, 3.0]}, "knn": {"model__n_neighbors": [3, 5, 7, 9, 11, 15]}, } ``` После изменения: ```bash python -m stylometry.cli run \ --preset stylometry_pos_lsa \ --model logreg \ --nested ``` Вложенная CV обучает модель для каждой комбинации гиперпараметров во внутренних блоках и оценивает выбранную модель на внешних. Это дороже, но честнее, чем настраивать параметры на тех же данных, где измеряется качество. Не расширяйте сетку одновременно по всем моделям: сначала найдите удачный диапазон на быстрых запусках, затем проверьте его через `--nested`. --- ## 4. Признаки: что и где менять ### 4.1. Состав групп `FeatureConfig` в `stylometry/pipelines.py`: ```python groups: tuple[str, ...] = ( "length", "lexical", "vocabulary", "characters", "punctuation", "function_words", "pronouns", "pos", "syntax", "readability", "dialogue", ) ``` В `configs/default.toml` можно задать список групп: ```toml [features] groups = ["length", "lexical", "characters", "punctuation", "function_words", "pronouns", "pos", "syntax", "dialogue"] ``` Это исключит `vocabulary` и `readability` — самые нестабильные группы на коротких репликах. Сравните результат с полным набором через `ablate`. ### 4.2. Символьные n-граммы В `[features]`: ```toml char_ngram_range = [2, 6] char_max_features = 8000 min_df = 2 ``` Что пробовать: - `[2, 5]` — текущий вариант; - `[2, 4]` — меньше шума, но меньше морфологии; - `[2, 6]` — больше деталей, выше риск переобучения; - `char_max_features`: 3000, 5000, 8000, 12000; - `min_df`: 1, 2, 3, 5. ### 4.3. POS n-граммы ```toml pos_ngram_range = [1, 4] pos_max_features = 800 min_df = 2 ``` POS-признаки обычно устойчивее к теме, чем словарные. Если увеличивать `pos_max_features`, следите за переобучением: редкие POS-шаблоны могут быть шумом. ### 4.4. LSA В `configs/default.toml`: ```toml lsa_components = 150 ``` В `stylometry/lsa.py` в `_DEFAULT_CONFIGS` задаются параметры каждого варианта: `ngram_range`, `min_df`, `max_df`, `max_features`, `token_pattern`. Например, для `word` можно увеличить `max_features` с 5000 до 10000, а для `char` — с 5000 до 8000. Что пробовать: - `lsa_components`: 50, 100, 150, 200, 300; - `lsa_variants`: `word`, `word2`, `content`, `fw`, `char`, `pos`; - `min_df`: 2, 3, 5; - `max_features`: 3000–12000 в зависимости от варианта. Команда для перебора: ```bash python -m stylometry.cli lsa-sweep \ --variants word,content,fw,char,pos \ --components 50,100,150,200,300 \ --model logreg --repeats 1 ``` После выбора лучшего `k` проверьте его с 5 повторами через `run`. ### 4.5. Веса блоков После L2-нормировки каждый блок имеет единичную норму, поэтому блоки конкурируют на равных. Если один блок шумит, его вес можно уменьшить: ```toml [features.block_weights] stylo = 1.0 char = 0.5 pos = 0.7 lsa_word = 0.3 lsa_pos = 0.3 ``` Правило: сумма весов не важна, важны относительные значения. Начните с единиц, затем уменьшайте шумные блоки. Веса — такой же гиперпараметр, поэтому выбирать их нужно по внутренней CV, а не по тесту. ### 4.6. Добавление своих признаков 1. Создайте модуль в `stylometry/features/`, например `my_features.py`. 2. Реализуйте `extract(row: pd.Series) -> dict[str, float]`. 3. Зарегистрируйте группу в `FEATURE_GROUPS` и префикс в `FEATURE_GROUP_PREFIXES` в `stylometry/features/stylometry.py`. 4. Добавьте группу в `FeatureConfig.groups` или в пресет. 5. Проверьте, что значения конечны и не зависят от меток. 6. Запустите `features` и `ablate`. Не добавляйте признаки, которые используют целевую переменную или статистики тестовой выборки. --- ## 5. Рекомендуемый порядок улучшения 1. **Зафиксируйте базовый уровень:** `stylometry`, `stylometry_pos`, `stylometry_pos_lsa`, 5 повторов. Без этого нельзя понять, помогает ли изменение. 2. **Уберите шумные группы.** Поочерёдно исключайте `vocabulary`, `readability`, `length`; смотрите `delta_macro_f1` и доверительные интервалы. 3. **Настройте char/POS n-граммы.** Диапазоны, `max_features`, `min_df`. 4. **Настройте LSA.** Варианты и число компонент; отдельно проверьте «стиль или тема». 5. **Настройте веса блоков.** 6. **Настройте классификатор.** Начните с `logreg.C`, затем `linear_svc.C`, затем деревья, NB и kNN. 7. **Проверьте устойчивость.** Лучшие 2–3 конфигурации — через `--nested`, 5 повторов, разные seed. 8. **Сравните статистически.** `compare` (Мак-Немар, бутстрэп-разница), `subgroup-report`, `length-report`, `per_class_report`. 9. **Только после этого** делайте выводы. Не пытайтесь улучшить всё одновременно: при 2005 объектах и 34 классах разница в 1–2 процентных пункта часто находится в пределах шума. --- ## 6. Что обычно даёт прирост, а что — нет ### Обычно помогает - исключение нестабильных групп (`vocabulary`, `readability`); - char- и POS- n-граммы; - `class_weight="balanced"` для линейных моделей; - подбор `C` для логистической регрессии и SVM; - уменьшение `max_features` или `min_df` при переобучении; - веса блоков: уменьшение шумного LSA или char-блока; - больше повторов и вложенная CV для честной оценки. ### Может помочь, но требует проверки - увеличение `char_max_features` и диапазона n-грамм; - увеличение числа компонент LSA; - `max_features=0.1/0.3` у деревьев; - `min_samples_leaf` > 1 у деревьев; - kNN с косинусным расстоянием и `weights="distance"`; - ансамбли (голосование logreg + SVM) — в текущем коде не реализованы, но могут дать небольшой прирост. ### Обычно не помогает или вредит - увеличение сложности модели без контроля переобучения; - настройка гиперпараметров на тестовой выборке; - удаление пунктуации и стоп-слов в стилометрии; - лемматизация по умолчанию; - SMOTE и синтез текста; - подбор признаков по тестовым метрикам; - сравнение моделей только по доле правильных ответов. --- ## 7. Как фиксировать результаты Для каждого варианта сохраняйте: - `run_metadata.json` (конфигурация, seed, версии); - `pooled_metrics.csv` (macro-F1, balanced accuracy, top-3); - `bootstrap_ci.json`; - `per_class_report.csv`; - `confusion_matrix.csv`; - `run_id` и команду запуска. Не сравнивайте запуски с разными `--folds` или `--seed` через `compare`. Для корректного парного сравнения используйте одинаковые схему CV и seed. --- ## 8. Быстрые команды для перебора ```bash # 1. Логистическая регрессия: C 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 --model-params "{\"C\": $c}" done # 2. Линейный SVM: C 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 --model-params "{\"C\": $c}" done # 3. LSA: число компонент python -m stylometry.cli lsa-sweep \ --variants word,content,fw,char,pos \ --components 50,100,150,200,300 \ --model logreg --repeats 1 # 4. Деревья: глубина и лист for depth in 10 20 30; do for leaf in 1 2 3; do python -m stylometry.cli run \ --preset stylometry_pos_lsa --model random_forest \ --repeats 3 \ --model-params "{\"max_depth\": $depth, \"min_samples_leaf\": $leaf}" done done ``` Такие циклы создают много каталогов результатов. После серии удобно собрать сводку по `pooled_metrics.csv` и `run_metadata.json`; для этого можно использовать `pandas` или добавить отдельный сценарий. --- ## 9. Ограничения - 2005 реплик и 34 класса — небольшая выборка; многие различия статистически незначимы. - Короткие тексты ограничивают пользу словарного богатства и читаемости. - Один автор: задача измеряет различимость речевых масок, а не авторский стиль. - LSA может отражать тему; сравнение вариантов обязательно. - Ручная фильтрация корпуса не воспроизводима; это ограничение нельзя исправить настройкой модели. - Любая настройка по тестовой выборке делает результат невалидным.