diff --git a/README.md b/README.md index 8efbe63..ae0d041 100644 --- a/README.md +++ b/README.md @@ -53,5 +53,9 @@ nlp/ методическое описание метрик и классификаторов. - [`lab1/FEATURES.md`](lab1/FEATURES.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) — структура презентации. diff --git a/lab1/README.md b/lab1/README.md index 7fe8031..94f038a 100644 --- a/lab1/README.md +++ b/lab1/README.md @@ -164,6 +164,11 @@ python -m stylometry.cli run --config configs/default.toml \ выбора; дополняет машинно-читаемый `features/feature_dictionary.csv`. - **`TUNING.md`** — где менять параметры классификаторов, признаки, LSA и веса блоков; рекомендуемый порядок улучшения результатов. +- **`RESULTS_ANALYSIS.md`** — независимый анализ follow-up результатов: + ранжирование моделей, статистические сравнения, ошибки, ограничения и + выводы. +- **`FOLLOWUP_EXPERIMENTS.md`** — протокол и результаты follow-up серии, + подготовленные коллегой. - **`METRICS_AND_CLASSIFIERS.md`** — методическое описание метрик и классификаторов: что показывает каждая метрика, как она считается, как интерпретировать значения, чем отличаются модели и как объяснять это на diff --git a/lab1/RESULTS_ANALYSIS.md b/lab1/RESULTS_ANALYSIS.md new file mode 100644 index 0000000..e05866f --- /dev/null +++ b/lab1/RESULTS_ANALYSIS.md @@ -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/