20 KiB
Настройка моделей и признаков
Документ отвечает на практический вопрос: где и какие параметры менять,
чтобы пытаться улучшить 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]:
[model_params]
C = 0.3
class_weight = "balanced"
Запуск:
python -m stylometry.cli run \
--config configs/default.toml \
--preset stylometry_pos_lsa \
--model logreg \
--repeats 3
Параметры из [model_params] передаются в make_classifier. Для каждой
модели используются только её собственные параметры; лишние игнорируются.
2.2. Через командную строку
Удобно для быстрых проверок:
python -m stylometry.cli run \
--preset stylometry_pos_lsa \
--model logreg \
--repeats 1 \
--model-params '{"C": 0.3, "class_weight": "balanced"}'
JSON позволяет передать null, например отключить веса классов:
--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:
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. Текущие сетки небольшие, чтобы серия завершалась за разумное время.
Их можно расширить:
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]},
}
После изменения:
python -m stylometry.cli run \
--preset stylometry_pos_lsa \
--model logreg \
--nested
Вложенная CV обучает модель для каждой комбинации гиперпараметров во внутренних блоках и оценивает выбранную модель на внешних. Это дороже, но честнее, чем настраивать параметры на тех же данных, где измеряется качество.
Не расширяйте сетку одновременно по всем моделям: сначала найдите удачный
диапазон на быстрых запусках, затем проверьте его через --nested.
4. Признаки: что и где менять
4.1. Состав групп
FeatureConfig в stylometry/pipelines.py:
groups: tuple[str, ...] = (
"length", "lexical", "vocabulary", "characters", "punctuation",
"function_words", "pronouns", "pos", "syntax", "readability", "dialogue",
)
В configs/default.toml можно задать список групп:
[features]
groups = ["length", "lexical", "characters", "punctuation",
"function_words", "pronouns", "pos", "syntax", "dialogue"]
Это исключит vocabulary и readability — самые нестабильные группы на
коротких репликах. Сравните результат с полным набором через ablate.
4.2. Символьные n-граммы
В [features]:
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-граммы
pos_ngram_range = [1, 4]
pos_max_features = 800
min_df = 2
POS-признаки обычно устойчивее к теме, чем словарные. Если увеличивать
pos_max_features, следите за переобучением: редкие POS-шаблоны могут быть
шумом.
4.4. LSA
В configs/default.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 в зависимости от варианта.
Команда для перебора:
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-нормировки каждый блок имеет единичную норму, поэтому блоки конкурируют на равных. Если один блок шумит, его вес можно уменьшить:
[features.block_weights]
stylo = 1.0
char = 0.5
pos = 0.7
lsa_word = 0.3
lsa_pos = 0.3
Правило: сумма весов не важна, важны относительные значения. Начните с единиц, затем уменьшайте шумные блоки. Веса — такой же гиперпараметр, поэтому выбирать их нужно по внутренней CV, а не по тесту.
4.6. Добавление своих признаков
- Создайте модуль в
stylometry/features/, напримерmy_features.py. - Реализуйте
extract(row: pd.Series) -> dict[str, float]. - Зарегистрируйте группу в
FEATURE_GROUPSи префикс вFEATURE_GROUP_PREFIXESвstylometry/features/stylometry.py. - Добавьте группу в
FeatureConfig.groupsили в пресет. - Проверьте, что значения конечны и не зависят от меток.
- Запустите
featuresиablate.
Не добавляйте признаки, которые используют целевую переменную или статистики тестовой выборки.
5. Рекомендуемый порядок улучшения
- Зафиксируйте базовый уровень:
stylometry,stylometry_pos,stylometry_pos_lsa, 5 повторов. Без этого нельзя понять, помогает ли изменение. - Уберите шумные группы. Поочерёдно исключайте
vocabulary,readability,length; смотритеdelta_macro_f1и доверительные интервалы. - Настройте char/POS n-граммы. Диапазоны,
max_features,min_df. - Настройте LSA. Варианты и число компонент; отдельно проверьте «стиль или тема».
- Настройте веса блоков.
- Настройте классификатор. Начните с
logreg.C, затемlinear_svc.C, затем деревья, NB и kNN. - Проверьте устойчивость. Лучшие 2–3 конфигурации — через
--nested, 5 повторов, разные seed. - Сравните статистически.
compare(Мак-Немар, бутстрэп-разница),subgroup-report,length-report,per_class_report. - Только после этого делайте выводы.
Не пытайтесь улучшить всё одновременно: при 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. Быстрые команды для перебора
# 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 может отражать тему; сравнение вариантов обязательно.
- Ручная фильтрация корпуса не воспроизводима; это ограничение нельзя исправить настройкой модели.
- Любая настройка по тестовой выборке делает результат невалидным.