Compare commits

1 Commits

3 changed files with 394 additions and 0 deletions

View File

@@ -53,5 +53,9 @@ nlp/
методическое описание метрик и классификаторов. методическое описание метрик и классификаторов.
- [`lab1/FEATURES.md`](lab1/FEATURES.md) — признаки и формулы. - [`lab1/FEATURES.md`](lab1/FEATURES.md) — признаки и формулы.
- [`lab1/TUNING.md`](lab1/TUNING.md) — настройка параметров и перебор. - [`lab1/TUNING.md`](lab1/TUNING.md) — настройка параметров и перебор.
- [`lab1/RESULTS_ANALYSIS.md`](lab1/RESULTS_ANALYSIS.md) — анализ результатов
follow-up серии и выводы.
- [`lab1/FOLLOWUP_EXPERIMENTS.md`](lab1/FOLLOWUP_EXPERIMENTS.md) — протокол
follow-up экспериментов.
- [`lab1/PRESENTATION_OUTLINE.md`](lab1/PRESENTATION_OUTLINE.md) — структура - [`lab1/PRESENTATION_OUTLINE.md`](lab1/PRESENTATION_OUTLINE.md) — структура
презентации. презентации.

View File

@@ -164,6 +164,11 @@ python -m stylometry.cli run --config configs/default.toml \
выбора; дополняет машинно-читаемый `features/feature_dictionary.csv`. выбора; дополняет машинно-читаемый `features/feature_dictionary.csv`.
- **`TUNING.md`** — где менять параметры классификаторов, признаки, LSA и веса - **`TUNING.md`** — где менять параметры классификаторов, признаки, LSA и веса
блоков; рекомендуемый порядок улучшения результатов. блоков; рекомендуемый порядок улучшения результатов.
- **`RESULTS_ANALYSIS.md`** — независимый анализ follow-up результатов:
ранжирование моделей, статистические сравнения, ошибки, ограничения и
выводы.
- **`FOLLOWUP_EXPERIMENTS.md`** — протокол и результаты follow-up серии,
подготовленные коллегой.
- **`METRICS_AND_CLASSIFIERS.md`** — методическое описание метрик и - **`METRICS_AND_CLASSIFIERS.md`** — методическое описание метрик и
классификаторов: что показывает каждая метрика, как она считается, как классификаторов: что показывает каждая метрика, как она считается, как
интерпретировать значения, чем отличаются модели и как объяснять это на интерпретировать значения, чем отличаются модели и как объяснять это на

385
lab1/RESULTS_ANALYSIS.md Normal file
View File

@@ -0,0 +1,385 @@
# Анализ результатов follow-up серии
Дата анализа: 26.10.2026.
Источник результатов:
- ветка `origin/opencode_test`, коммит `ac7c6f0`;
- основной архив `lab1/exports/nlp_lab1_results_20261026_054117.tar.gz`;
- сводки `results/followup/summary.csv`, `summary.md`, `comparisons.csv`,
`comparisons.md`;
- документ коллеги `FOLLOWUP_EXPERIMENTS.md`.
Контрольные суммы архивов совпали. В основном архиве — 68 запусков; ключевые
конфигурации выполнены с 5 повторами 5-блочной стратифицированной
перекрёстной проверки, seed 42. Дополнительно есть nested-запуски, контроли
перестановкой меток, анализ почти-дублей, длины реплик и подгрупп.
Настоящий документ — независимая сводка: что можно утверждать, что нельзя,
и какие эксперименты нужны дальше.
---
## 1. Краткий ответ
1. **Персонажей можно различать, но сигнал умеренный.** Лучшая конфигурация
даёт macro-F1 около **0,258** при случайном уровне **0,029**; top-3 —
около **0,42** при случайном **0,088**. Это в 7–9 раз выше случайного
уровня, но не «почти безошибочная» классификация.
2. **Основной сигнал — лексико-тематический, а не чисто стилистический.**
Ручные стилометрические признаки дают 0,105; добавление символьных и
частеречных n-грамм поднимает до 0,182; `content`-LSA — до 0,248–0,258.
Функциональные слова и POS-LSA сами по себе слабы (0,06–0,07).
3. **Лучшие кандидаты — `char_max10000_rf` и `model_et_default`.** Они
статистически неразличимы: +0,0031, 3/5 повторов, p = 0,57,
доверительный интервал накрывает ноль. `groups_lean2_content300` тоже
близок (0,251), но опирается на `content`-LSA, то есть скорее на тему.
4. **`final_rf` — не лучшая модель.** Дефолтный random forest даёт 0,222,
тогда как ExtraTrees — 0,255, а RF с `char_max_features=10000` — 0,258.
В отчёте `final_rf` нужно переименовать в `baseline_rf` или заменить.
5. **Ошибки — это коллапс в «hub»-классы, а не демографическая путаница.**
Abigail получает 21–23% всех предсказаний при истинной доле 6,9%;
precision 0,087, recall 0,266. Редкие классы (Kent, Wizard, Penny,
Mister Qi) почти не находятся.
6. **Строгий вывод о стиле пока невозможен.** `content`-LSA и широкие
символьные n-граммы могут кодировать содержание и тему. Нужен
тематический контроль и внешняя валидация.
---
## 2. Ранжирование моделей
`Δ` — разница с `ref_logreg` (0,1785). Для nested-запусков указан один
внешний цикл из 5 блоков, поэтому std по повторам равен нулю.
| Label | Модель | Повторов | Macro-F1 | Δ | Accuracy | Top-3 |
|---|---|---:|---:|---:|---:|---:|
| `char_max10000_rf` | random forest | 5 | **0,2583 ± 0,0066** | +0,0798 | 0,2391 | 0,4197 |
| `char_max10000_rf_nested` | random forest, nested | 1×5 | **0,2579** | +0,0794 | 0,2394 | 0,4075 |
| `groups_lean2_content300_nested` | logreg, nested | 1×5 | **0,2572** | +0,0788 | 0,2369 | 0,4065 |
| `model_et_default` | extra trees | 5 | **0,2551 ± 0,0073** | +0,0767 | 0,2387 | 0,4170 |
| `et_dNone_l2` | extra trees | 3 | 0,2538 ± 0,0089 | +0,0753 | 0,2416 | 0,4308 |
| `groups_lean2_content300` | logreg | 5 | 0,2510 ± 0,0040 | +0,0725 | 0,2379 | 0,4111 |
| `model_et_nested` | extra trees, nested | 1×5 | 0,2486 | +0,0701 | 0,2344 | 0,4259 |
| `stylometry_pos_content300` | logreg | 5 | 0,2485 ± 0,0042 | +0,0700 | 0,2337 | 0,4175 |
| `rf_grid_best_5` | random forest | 5 | 0,2294 ± 0,0082 | +0,0509 | 0,2253 | 0,4004 |
| `model_rf_default` | random forest | 5 | 0,2213 ± 0,0065 | +0,0428 | 0,2108 | 0,3893 |
| `final_rf` | random forest, 10 повторов | 10 | 0,2216 ± 0,0062 | +0,0431 | 0,2094 | 0,3865 |
| `model_svc_default` | linear SVC | 5 | 0,1880 ± 0,0067 | +0,0096 | 0,1840 | 0,3386 |
| `ref_no_lsa` | logreg, `stylometry_pos` | 5 | 0,1815 ± 0,0054 | +0,0031 | 0,1708 | 0,3395 |
| `ref_logreg` | logreg, `stylometry_pos_lsa` | 5 | 0,1785 ± 0,0060 | 0 | 0,1679 | 0,3322 |
| `stylometry` | logreg, только ручные признаки | 5 | 0,1051 ± 0,0038 | −0,0733 | 0,1012 | 0,2338 |
| `lsa_content_k400` | logreg, только content-LSA | 5 | 0,2114 ± 0,0029 | +0,0329 | 0,2059 | 0,3767 |
| `lsa_pos_k300` | logreg, только POS-LSA | 5 | 0,0667 ± 0,0040 | −0,1118 | 0,0663 | 0,1556 |
| `control_length_logreg` | logreg, только длина | 5 | 0,0354 ± 0,0032 | −0,1430 | 0,0395 | 0,1345 |
| `control_length_dummy` | dummy | 1 | 0,0038 | −0,1747 | 0,0693 | 0,1022 |
Случайный уровень: macro-F1 ≈ 0,029, accuracy ≈ 0,029, top-3 ≈ 0,088.
Контроль перестановкой меток дал 0,0270–0,0282, то есть ровно случайный
уровень. Признаков утечки через метки не видно.
---
## 3. Статистическая надёжность
Главные парные сравнения:
| Сравнение | Δ macro-F1 | Повторов в плюс | McNemar p | Bootstrap CI |
|---|---:|---:|---:|---:|
| `char_max10000_rf` vs `ref_logreg` | +0,0798 | 5/5 | < 0,0001 | [+0,053, +0,099] |
| `model_et_default` vs `ref_logreg` | +0,0767 | 5/5 | < 0,0001 | [+0,054, +0,099] |
| `groups_lean2_content300` vs `ref_logreg` | +0,0725 | 5/5 | < 0,0001 | [+0,053, +0,092] |
| `stylometry_pos_content300` vs `ref_no_lsa` | +0,0669 | 5/5 | < 0,0001 | [+0,050, +0,083] |
| `char_max10000_rf` vs `model_rf_default` | +0,0369 | 5/5 | 0,0032 | [+0,020, +0,053] |
| `model_et_default` vs `model_rf_default` | +0,0338 | 5/5 | 0,0113 | [+0,018, +0,050] |
| `char_max10000_rf` vs `model_et_default` | +0,0031 | 3/5 | 0,5732 | [−0,014, +0,021] |
| `char_max10000_rf` vs `groups_lean2_content300` | +0,0073 | 5/5 | 0,6593 | [−0,017, +0,030] |
Выводы:
- преимущество деревьев над логистической регрессией устойчиво и велико;
- ExtraTrees устойчиво лучше дефолтного random forest;
- `char_max10000_rf`, ExtraTrees и `groups_lean2_content300` между собой
статистически неразличимы;
- малые различия (+0,003…+0,009) не переживают множественную поправку и
не должны трактоваться как выигрыш.
Для 46 парных сравнений нужна поправка Холма—Бонферрони. Эффекты
+0,04…+0,08 с p < 0,001 её переживут; пограничные +0,003…+0,009 — нет.
---
## 4. Что именно даёт качество
| Компонент | Macro-F1 | Интерпретация |
|---|---:|---|
| Только длина | 0,035 | Слабая нижняя граница |
| Только ручные стилометрические признаки | 0,105 | Стиль/структура без лексических n-грамм |
| Ручные + char/POS n-граммы | 0,1815 | Основной прирост +0,077 |
| То же + word/pos-LSA(k=100) | 0,1785 | LSA по умолчанию не помогает |
| То же + content-LSA(k=300) | 0,2485 | Сильный прирост, но это тема/лексика |
| Только content-LSA(k=400) | 0,2114 | Контент сам по себе сильнее ручной стилометрии |
| Только POS-LSA(k=300) | 0,0667 | Синтаксис в таком виде слаб |
| Только функциональные слова | 0,0598 | Стиль без лексики слаб |
| RF + char_max_features=10000 | 0,2583 | Лучший результат |
Это главный содержательный вывод: **модель различает персонажей в первую
очередь по лексико-тематическому профилю и символьным n-граммам**, а не по
«чистому» синтаксису или функциональным словам. Формулировка «модель выучила
идиолект» слишком сильная. Корректнее: «модель различает персонажей по
устойчивым лексическим и тематическим маркерам, которые автор закрепил за
речевыми масками».
Дополнительное подтверждение: lean-набор без `function_words`, `pos`,
`vocabulary` и `readability` даёт 0,1808, то есть **не хуже** полного
`stylometry_pos_lsa` (0,1785). Удаление «стилевых» групп не ухудшает
результат, потому что основной сигнал несут char- n-граммы и лексика.
---
## 5. LSA: тема или стиль
Зависимость от числа компонент для `content`-LSA монотонная:
| k | Macro-F1 |
|---:|---:|
| 100 | 0,1334 |
| 200 | 0,1796 |
| 300 | 0,1984 |
| 400 | 0,2128 |
Для сравнения: `word` при k=400 — 0,1781, `char` при k=400 — 0,1787,
`pos` — около 0,071, `fw` — около 0,074.
Интерпретация:
- `content`-LSA строится по содержательным словам и их биграммам; это
кодирует **о чём говорит персонаж**, а не только **как**;
- сильный рост при k=300–400 означает, что тематическая вариативность
богата и хорошо разделяет персонажей;
- `word+pos` LSA при k=100 была нейтральна, но это не значит, что «LSA
вообще не работает»: важен вариант и число компонент;
- POS-LSA при добавлении к полному набору **ухудшает** результат
(`stylometry_pos_pos300` = 0,1685 против 0,1815), то есть синтаксический
LSA в текущем виде шумит.
Пока нет тематического контроля, прирост `content`-LSA нельзя записывать в
стилометрию. Нужны: сбалансированные по темам подвыборки, удаление
тематических слов или adversarial-контроль темы.
---
## 6. Ошибки модели
Для лучшей модели `char_max10000_rf` (таблицы `per_class_report.csv` и
`confusion_matrix.csv` относятся к repeat 0; доли предсказаний проверены по
всем пяти повторам):
- Abigail получает **21–23%** всех предсказаний при истинной доле **6,9%**;
precision 0,087, recall 0,266, F1 0,131.
- Alex получает 11–12% при истинной доле 6,0%.
- Худшие F1: Kent 0,000, Wizard 0,048, Penny 0,050, Mister Qi 0,069,
Leah 0,083, Jodi 0,098, Linus 0,105.
- Лучшие F1: Evelyn 0,645, Willy 0,578, Pam 0,534, Pierre 0,518,
Leo 0,440, Vincent 0,393, Krobus 0,392.
- Крупнейшие потоки ошибок: Sebastian → Abigail (40), Haley → Abigail (31),
Alex → Abigail (28), Penny → Abigail (27), Sam → Abigail (25).
- Это **hub-коллапс**: редкие и средние классы стягиваются в несколько
популярных, а не путаются по возрасту или полу.
`groups_lean2_content300` ведёт себя иначе: Abigail получает только 3,6%
предсказаний, а Wizard F1 = 0,449. Ошибки моделей комплементарны, поэтому
ансамбль может быть полезен.
### 6.1. Длина реплики
Для `char_max10000_rf`, repeat 0:
| Длина | Macro-F1 | Доверительный интервал |
|---|---:|---:|
| 0–5 слов | 0,088 | [0,033; 0,122] |
| 6–10 | 0,218 | [0,153; 0,258] |
| 11–15 | 0,222 | [0,170; 0,258] |
| 16–20 | 0,239 | [0,190; 0,271] |
| 21–30 | **0,285** | [0,238; 0,315] |
| 31+ | 0,240 | [0,180; 0,273] |
Зависимость немонотонная: после 30 слов качество падает. Гипотеза «чем
длиннее, тем лучше» не подтверждается; длинные реплики накапливают
тематические и синтаксические различия.
### 6.2. Подгруппы
Для `char_max10000_rf`, repeat 0:
- возраст: elder 0,406, child 0,378, adult 0,232, teen 0,196;
- пол: male 0,266, unknown 0,244, female 0,218;
- вид: monster 0,295, human 0,246, unknown 0,056.
Это F1 **внутри группы персонажей**, а не качество предсказания группы.
Различия описательные; они не доказывают, что модель обязана путать
персонажей внутри группы.
### 6.3. Верификация и кластеризация
- Верификация: ROC-AUC 0,630–0,633, EER 0,412–0,415, TAR@FAR=1% 0,035–0,038,
top-1 0,133–0,141. Выше случайного, но практически слабо.
- Кластеризация: ARI = 0 для k = 3/5/8, NMI 0,14–0,53. Устойчивых кластеров
персонажей в этом пространстве нет.
---
## 7. Ансамбль: предварительный результат
По out-of-fold предсказаниям (одинаковые повторы и seed) я проверил простое
голосование:
| Комбинация | Macro-F1 |
|---|---:|
| `char_max10000_rf` + `model_et_default` | 0,2583 |
| + `groups_lean2_content300` | **0,2728 ± 0,0024** |
| + `stylometry_pos_content300` | 0,2751 ± 0,0036 |
Это **не** валидная финальная модель: голосование построено постфактум по
тем же OOF-предсказаниям, которые использовались для выбора конфигураций.
Но результат показывает, что ошибки моделей комплементарны и что
`VotingClassifier`/stacking — перспективное направление. Проверять его нужно
в отдельном nested-эксперименте, а не по этим числам.
---
## 8. Методологические ограничения
1. **Выбор признаков на тех же данных.** `char_max_features=10000`,
`content`-LSA(k=300/400) и lean-наборы выбирались по результатам на
тестовых фолдах. Nested CV в текущем виде настраивает только `C`,
`max_depth` и `min_samples_leaf`; выбор признаков остаётся снаружи.
Поэтому 0,258 может быть оптимистичной оценкой. Нужен внешний holdout или
nested-выбор признаков.
2. **Множественные сравнения.** 46 пар; мелкие эффекты не переживают
поправку Холма—Бонферрони.
3. **Один корпус.** Все 2005 реплик — из одной игры; внешней валидации нет.
Разбиение по репликам не учитывает сцены и диалоги.
4. **Один автор.** Это классификация речевых масок, а не авторская
атрибуция.
5. **Короткие тексты и дисбаланс.** 15–139 реплик на класс; редкие классы
имеют широкие интервалы, а hub-классы забирают предсказания.
6. **Тематический конфликт.** `content`-LSA и char- n-граммы могут кодировать
тему, сезоны, подарки, работу и имена. Без контроля темы нельзя утверждать,
что модель выучила стиль.
7. **McNemar и macro-F1 не всегда согласованы.** Например, отключение
`class_weight` у logreg снижает macro-F1 на 0,029, но McNemar даёт
p = 0,65, потому что общая accuracy почти не меняется. Для выводов о
macro-F1 нужны повторные разницы и bootstrap, а не только McNemar.
8. **`final_rf` — не финальная модель.** Это дефолтный RF с 0,222; он
проигрывает ET и char-RF.
---
## 9. Выводы
1. **Гипотеза о различимости персонажей подтверждается частично.**
Лучшая модель: macro-F1 ≈ 0,258, top-3 ≈ 0,42, accuracy ≈ 0,239 при
34 классах. Это заметно выше случайного уровня, но далеко от надёжной
практической классификации.
2. **Основной вклад дают лексика, символьные n-граммы и content-LSA.**
Ручные стилометрические признаки и POS/функциональные слова сами по себе
слабы. Поэтому результат нельзя интерпретировать как «чистый идиолект».
3. **Лучшие модели — ExtraTrees и RF с расширенным char-словарём.**
Они статистически неразличимы. ET проще и не требует выбора
`char_max_features`; char-RF даёт максимум, но его конфигурация выбрана
на тех же данных.
4. **`content`-LSA — сильный, но тематический сигнал.** Он улучшает
результат, однако требует тематического контроля; POS-LSA в текущем виде
вредит.
5. **Главная проблема — не seed, а hub-коллапс и редкие классы.**
Abigail, Alex и ещё несколько классов забирают чужие предсказания.
Калибровка prior/порогов и работа с редкими классами перспективнее
дальнейшего перебора глубины деревьев.
6. **Ансамбль моделей перспективен, но пока не проверен корректно.**
Предварительное голосование даёт 0,273; нужен отдельный nested-эксперимент.
7. **Для строгого вывода о стиле нужны внешняя валидация и тематический
контроль.** Без них корректная формулировка: «персонажи различимы по
лексико-тематическому профилю и символьным n-граммам».
---
## 10. Что делать дальше
### Приоритет 0 — честная финальная оценка
1. Выбрать одну финальную модель: `model_et_default` (проще) или
`char_max10000_rf` (максимум).
2. Провести nested CV, в котором внутри внешнего цикла выбираются не только
гиперпараметры, но и `char_max_features`, `min_df`, `ngram_range` и
`content`-LSA(k).
3. Либо зафиксировать конфигурацию до анализа и проверить на отдельном
holdout/новом корпусе.
4. Заменить `final_rf` на реального лидера или переименовать его в
`baseline_rf`.
### Приоритет 1 — тематический контроль
- сбалансировать реплики по темам/сезонам;
- удалить тематические слова и проверить падение `content`-LSA;
- обучить классификатор темы и проверить, не объясняет ли она результат;
- сравнить `content`-LSA с `pos`/`fw` на тематически однородных подвыборках.
### Приоритет 2 — ошибки и калибровка
- prior correction / temperature scaling для hub-классов;
- пороги на OOF, оптимизирующие macro-F1, а не только accuracy;
- анализ `Abigail`, `Alex`, `Kent`, `Wizard`, `Penny`;
- отдельный отчёт по редким классам.
### Приоритет 3 — ансамбли и признаки
- `VotingClassifier`/stacking по OOF-вероятностям;
- `content`-LSA k=400/500 с lean-набором;
- char `max_features` 10000–15000, `min_df` 2–3;
- block weights;
- внешняя валидация на другом корпусе или разбиении по сценам.
---
## 11. Артефакты
```text
lab1/exports/nlp_lab1_results_20261026_054117.tar.gz
lab1/FOLLOWUP_EXPERIMENTS.md
lab1/scripts/analyze_followup.py
lab1/scripts/run_followup_experiments.sh
```
После распаковки архива:
```text
results/followup/summary.csv
results/followup/summary.md
results/followup/comparisons.csv
results/followup/comparisons.md
results/followup/<label>/
```
Команда для повторного анализа:
```bash
python scripts/analyze_followup.py \
--followup-dir results/followup \
--pairs char_max10000_rf:ref_logreg \
char_max10000_rf:model_et_default \
groups_lean2_content300:ref_logreg
```
---
## 12. Итоговая формулировка для отчёта
> Персонажи Stardew Valley статистически различимы по отдельным репликам:
> лучшая модель достигает macro-F1 ≈ 0,26 и top-3 ≈ 0,42 при 34 классах
> (случайный уровень 0,029 и 0,088 соответственно). Однако основной вклад
> дают лексико-тематические признаки и символьные n-граммы, а не
> функциональные слова и синтаксис. Поэтому результат корректно
> интерпретировать как различимость речевых масок по лексико-тематическому
> профилю; для утверждения о «чистом стиле» необходимы тематический контроль
> и внешняя валидация.