Files
NLP/lab1/TUNING.md

456 lines
20 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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