Files
NLP/lab1/TUNING.md

20 KiB
Raw Blame History

Настройка моделей и признаков

Документ отвечает на практический вопрос: где и какие параметры менять, чтобы пытаться улучшить 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. Добавление своих признаков

  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. Быстрые команды для перебора

# 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 может отражать тему; сравнение вариантов обязательно.
  • Ручная фильтрация корпуса не воспроизводима; это ограничение нельзя исправить настройкой модели.
  • Любая настройка по тестовой выборке делает результат невалидным.