Первичная версия: стилометрия реплик, LSA и протокол экспериментов

This commit is contained in:
2026-10-24 01:17:36 +03:00
commit c390224500
52 changed files with 10099 additions and 0 deletions

747
lab1/EXPERIMENTS.md Normal file
View File

@@ -0,0 +1,747 @@
# Протокол экспериментов
Документ описывает постановку, признаки, схему валидации, метрики и порядок
запуска экспериментов. Он рассчитан на следующего исследователя, который будет
проводить финальные серии опытов и готовить отчёт.
Методические пояснения к метрикам и классификаторам вынесены в
`METRICS_AND_CLASSIFIERS.md`. Здесь — практическая часть: что запускать, в
каком порядке и как интерпретировать результаты.
---
## 1. Исследовательский вопрос
**Вопрос.** Различимы ли 34 персонажа Stardew Valley по стилометрическим
признакам отдельных реплик, если все реплики написаны одним автором?
Это задача **классификации речевых масок персонажей**, а не авторской
атрибуции. Автор один, но персонажи наделены разной лексикой, синтаксисом,
междометиями, длиной реплик и пунктуацией. Положительный результат означает,
что созданные автором речевые маски статистически различимы; он не означает,
что у персонажей разные авторы.
### Гипотезы
| Обозначение | Гипотеза | Как проверяем |
|---|---|---|
| H1 | Функциональные слова, POS-признаки и символьные n-граммы различают персонажей выше случайного уровня | E2, E3, E4 |
| H2 | На коротких репликах POS- и char-признаки устойчивее словарного богатства и читаемости | E3, E9 |
| H3 | LSA по всем словам отражает тему, а LSA по POS и функциональным словам — стиль | E7 |
| H4 | Более длинные реплики классифицируются лучше | E9 |
| H5 | Ошибки группируются вокруг персонажей со сходным возрастом, полом, ролью или «человечностью» | E14 |
Случайный уровень для 34 классов: доля правильных ответов около 2,9%,
macro-F1 около 2,9%, top-3 около 8,8%. Любые результаты нужно сравнивать с
этими значениями, а не с нулём.
---
## 2. Данные
### 2.1. Текущий корпус
| Параметр | Значение |
|---|---|
| Файл | `data/dialogues.csv` |
| Кодировка | cp1251 |
| Реплик | 2005 |
| Персонажей | 34 |
| Словоупотреблений | около 38,2 тыс. |
| Средняя длина реплики | 19,1 слова |
| Медиана | 17 слов |
| Минимум / максимум | 3 / 78 слов |
| Классы | 15–139 реплик |
| Точные дубли реплик | 0 |
Корпус уже прошёл ручную фильтрацию: удалены короткие и общие фразы. Точные
правила очистки не сохранились, поэтому корпус используется как есть. Это
ограничение нужно указать в отчёте: восстановить исходное распределение
коротких реплик нельзя.
Редкие классы: Mister Qi (15), Sandy (21), Dwarf (23), Wizard (24),
Vincent (26), Krobus (28). При 5-блочной кросс-валидации у Mister Qi в каждом
тестовом блоке около 3 реплик; метрики по нему будут иметь широкий
доверительный интервал.
### 2.2. Аудит
```bash
cd lab1
python -m stylometry.cli audit
```
Результаты:
- `data/processed/lines.csv` — нормализованные реплики;
- `data/processed/audit.md` — отчёт с распределением классов, длин и сверкой
с `stats_per_character.xlsx`;
- `data/processed/figures/class_distribution.png` — график.
---
## 3. Предобработка
Корпус не перечищается. Выполняются только обратимые и не меняющие смысл
операции:
1. Приведение Unicode: многоточие, кавычки, тире, неразрывные пробелы.
2. Замена плейсхолдеров `<PLAYER>`, `<FARM>`, `<PET>`, `<SPOUSE>`, `<TIME>`,
`<NOUN>`, `<NAME>` на слова `PLAYER`, `FARM` и т.д. Количество плейсхолдеров
сохраняется отдельным признаком.
3. Схлопывание повторных пробелов.
4. Токенизация NLTK: `word_tokenize`.
5. Разбиение на предложения: `sent_tokenize`.
6. Частеречная разметка: `pos_tag`; перевод тегов Penn Treebank в Universal
POS.
7. Для POS-последовательностей подряд идущие знаки препинания схлопываются в
один тег `PUNCT`.
Не выполняется:
- удаление стоп-слов (они являются стилометрическим сигналом);
- удаление пунктуации;
- лемматизация по умолчанию (инфлексия — тоже стиль);
- удаление коротких реплик;
- приведение регистра к нижнему для всех признаков.
### 3.1. Ресурсы NLTK
```bash
cd lab1
python scripts/download_nltk_data.py
```
Скачиваются `punkt`, `punkt_tab`, `averaged_perceptron_tagger`,
`averaged_perceptron_tagger_eng`, `stopwords` в `lab1/nltk_data`. Если ресурсы
недоступны, токенизатор переходит в резервный режим на регулярных выражениях,
а POS-признаки отключаются. Для финальных экспериментов нужен NLTK-режим:
проверьте, что в метаданных запуска `pos_available=true`.
---
## 4. Признаковое пространство
### 4.1. Ручные стилометрические признаки
Всего около 454 признаков. Они не обучаются на данных, поэтому могут быть
посчитаны заранее.
| Группа | Примеры | Статус |
|---|---|---|
| Длина | число токенов, слов, символов, предложений; средняя длина предложения; доля длинных слов | Основная, но чувствительна к длине |
| Лексика | распределение длин слов, доля слов с апострофом, заглавных, цифр | Основная |
| Словарное богатство | TTR, Guiraud, Herdan, Yule's K, Simpson's D, hapax/dis, Honoré | Предварительный |
| Символы | частоты букв a–z, доля заглавных, цифр, пунктуации, апострофов | Основная |
| Пунктуация | запятые, точки, восклицательные, вопросительные, многоточия, повторная пунктуация | Основная |
| Функциональные слова | около 150 закрытых слов и 12 категорий | Основная |
| Местоимения | 1-е, 2-е, 3-е лицо, возвратные, притяжательные, отношение I/you | Основная |
| Части речи | Universal POS, Penn Treebank, модальность, время, герундий, причастие | Основная |
| Синтаксис-прокси | число клауз, подчинительные союзы, вопрос/восклицание, пассив-прокси | Основная |
| Читаемость | Flesch, FK grade, Gunning Fog, SMOG, Coleman-Liau, ARI | Предварительный |
| Разговорные маркеры | плейсхолдеры, ALL CAPS, многоточия, междометия, смех, дискурсивные маркеры, хеджи, интенсификаторы, вежливость, отрицания, сокращения | Основная |
Признаки `vocabulary` и `readability` помечены как предварительные: на репликах
длиной 10–20 слов они нестабильны. Их влияние нужно проверять отдельно, а не
включать в основной вывод без оговорок.
### 4.2. Символьные n-граммы
TF-IDF по `char_wb`, диапазон 2–5, `min_df=3`, до 5000 признаков, L2-норма.
Хорошо отражают морфологию, пунктуацию и орфографические привычки.
### 4.3. POS n-граммы
TF-IDF по последовательностям Universal POS, диапазон 1–3, `min_df=3`, до 500
признаков, L2-норма. Это относительно устойчивый к теме синтаксический сигнал.
### 4.4. Словарь признаков
```bash
python -m stylometry.cli features
```
Создаёт:
- `features/stylometric.csv` — матрица 2005 × 454;
- `features/feature_dictionary.csv` — название, группа, описание, статус.
Словарь нужно использовать в отчёте: для каждой группы указать, какие
признаки и почему выбраны.
---
## 5. Латентно-семантический анализ
LSA реализован как:
```text
TF-IDF -> TruncatedSVD(k) -> L2-нормализация
```
Варианты:
| Вариант | Колонка | n-граммы | Что отражает |
|---|---|---|---|
| `word` | `text` | 1 | тему и стиль |
| `word2` | `text` | 1–2 | тему и устойчивые сочетания |
| `content` | `content_text` | 1–2 | содержание после удаления стоп-слов |
| `fw` | `fw_text` | 1–2 | синтаксис через функциональные слова |
| `char` | `text` | 2–5 (символы) | морфологию и пунктуацию |
| `pos` | `pos_text` | 1–4 (POS) | синтаксический стиль |
Основные параметры: `sublinear_tf=true`, `min_df=3`, `max_df=0.9`,
`max_features=2000–8000`, `n_components=50–300`. Все параметры настраиваются
только на обучающих блоках; для интерпретационных графиков можно обучить LSA
на всём корпусе, но это нельзя использовать для оценки качества.
### 5.1. Что проверять в LSA
1. **График объяснённой дисперсии и накопленная дисперсия.** Сколько
компонент объясняют основную часть дисперсии.
2. **Top-термины компонент.** Интерпретируемость осей: тема, эмоциональность,
формальность, длина.
3. **Внешняя оценка.** Macro-F1 классификатора при разных `k`.
4. **Сравнение вариантов.** Если `lsa_word` и `lsa_content` значительно лучше
`lsa_pos` и `lsa_fw`, классификация в существенной степени опирается на
тему, а не на стиль.
5. **Стабильность.** Bootstrap по репликам и углы между подпространствами
(опционально; при малом числе компонент полезно).
### 5.2. Команды
```bash
python -m stylometry.cli lsa --variant word --components 100
python -m stylometry.cli lsa --variant pos --components 50
python -m stylometry.cli lsa --variant char --components 50
python -m stylometry.cli lsa-sweep --variants word,content,fw,char,pos \
--components 50,100,200 --model logreg --repeats 1
```
Результаты: матрицы `features/lsa_<variant>_k<k>.csv`, наиболее
информативные термины компонент, объяснённая дисперсия, график объяснённой
дисперсии.
---
## 6. Классификаторы
В основном сравнении участвуют:
| Модель | Краткая характеристика | Вероятности |
|---|---|---|
| `dummy` | предсказывает самый частый класс | да |
| `multinomial_nb` | наивный байесовский классификатор для неотрицательных признаков | да |
| `logreg` | логистическая регрессия с L2 и балансировкой классов | да |
| `linear_svc` | линейный метод опорных векторов | нет, есть решающая функция |
| `random_forest` | случайный лес | да |
| `extra_trees` | сверхслучайные деревья | да |
| `knn` | k ближайших соседей с косинусным расстоянием | да |
Для MultinomialNB автоматически исключаются LSA-блоки (компоненты могут быть
отрицательными) и используется MinMax-масштабирование. Для остальных моделей
ручные признаки стандартизуются и L2-нормируются, а TF-IDF- и LSA-блоки уже
нормированы. Это нужно, чтобы блоки разной размерности не подавляли друг
друга.
Подробное описание моделей, их формул, гиперпараметров и ограничений — в
`METRICS_AND_CLASSIFIERS.md`.
### 6.1. Сетки гиперпараметров
| Модель | Параметры |
|---|---|
| `logreg` | `C ∈ {0.1, 1, 10}` |
| `linear_svc` | `C ∈ {0.1, 1, 10}` |
| `random_forest`, `extra_trees` | `max_depth ∈ {None, 30}`, `min_samples_leaf ∈ {1, 2}` |
| `multinomial_nb` | `alpha ∈ {0.1, 1, 3}` |
| `knn` | `n_neighbors ∈ {3, 5, 9}` |
Настройка выполняется внутри вложенной кросс-валидации. Для одиночного запуска
`run` без `--nested` гиперпараметры фиксированы значениями по умолчанию; это
быстрее, но для финального сравнения нужен `--nested`.
---
## 7. Схема валидации
### 7.1. Основная схема
```python
RepeatedStratifiedKFold(n_splits=5, n_repeats=5, random_state=42)
```
Почему 5 блоков:
- минимальный класс — 15 реплик;
- в каждом тестовом блоке около 3 реплик на класс;
- все 34 класса присутствуют и в обучении, и в тесте;
- 10 блоков дали бы 1–2 реплики на класс и ещё более шумные оценки.
Почему повторная:
- один прогон 5-блочной CV даёт одну оценку; повторность позволяет оценить
разброс;
- для финального отчёта используйте 5 повторов; для быстрых проверок — 1–3.
### 7.2. Вложенная кросс-валидация
```python
outer = StratifiedKFold(n_splits=5, shuffle=True, random_state=42)
inner = StratifiedKFold(n_splits=3, shuffle=True, random_state=42)
```
Внешние блоки — оценка качества; внутренние — настройка гиперпараметров.
Такой протокол уменьшает оптимистическое смещение, которое возникает, если
настраивать гиперпараметры на тех же данных, где измеряется качество.
### 7.3. Контроль утечек
- TF-IDF, SVD, масштабирование и отбор признаков обучаются только на
обучающем блоке.
- Ручные признаки и POS-теги не используют метки; их можно считать заранее.
- Точных дублей реплик в корпусе нет, поэтому одна и та же реплика не может
попасть в обучение и тест.
- При появлении почти одинаковых реплик нужно проверить `StratifiedGroupKFold`
с группами по нормализованному тексту. В текущем корпусе точных дублей нет.
- Нельзя выбирать признаки или `k` LSA по тестовым метрикам. Все решения — по
внутренней кросс-валидации.
---
## 8. Метрики
Подробные определения, формулы и интерпретация — в
`METRICS_AND_CLASSIFIERS.md`. Кратко:
| Метрика | Роль |
|---|---|
| Macro-F1 | Основная метрика: каждый из 34 классов равноправен |
| Сбалансированная доля правильных ответов | Средняя полнота по классам; удобна для редких персонажей |
| Доля правильных ответов | Нужна для сравнения с литературой |
| MCC | Устойчив к дисбалансу, учитывает все клетки матрицы ошибок |
| Cohen's kappa | Согласие с поправкой на случай |
| Top-3, top-5 | Важны при 34 классах |
| Log loss, Brier | Качество вероятностей и калибровка |
| Точность, полнота и F-мера по классам | Показывают, какие персонажи проваливаются |
| Матрица ошибок | Анализ пар перепутывания |
Основной отчётный результат — **pooled out-of-fold macro-F1** с 95%
бутстрэп-интервалом и среднее ± стандартное отклонение по повторам. Среднее
macro-F1 по отдельным блокам тоже полезно, но на класс приходится около трёх
объектов, поэтому оно шумное.
### 8.1. Статистические сравнения
- **Мак-Немар** — парное сравнение двух моделей на одних и тех же тестовых
репликах.
- **Уилкоксон** — парное сравнение результатов по блокам; описательная
проверка стабильности.
- **Фридман** — сравнение трёх и более моделей.
- **Поправка Холма—Бонферрони** — при множественных сравнениях.
- **Бутстрэп-интервалы** — основной способ показать неопределённость.
Не сравнивайте модели только по доле правильных ответов: при дисбалансе и
34 классах она может вводить в заблуждение. Пример: модель, предсказывающая
только Abigail, имеет долю правильных ответов около 7%, но macro-F1 около
0,004.
---
## 9. Матрица экспериментов
Все команды выполняются из `lab1`. Перед серией полезно один раз выполнить
`audit` и `features`.
### E0. Аудит корпуса
```bash
python -m stylometry.cli audit
```
Что проверить: число реплик и классов, распределение длин, отсутствие дублей,
совпадение с `stats_per_character.xlsx`.
### E1. Базовые уровни
```bash
python -m stylometry.cli run --preset length --model logreg --repeats 5
python -m stylometry.cli run --preset char --model multinomial_nb --repeats 3
python -m stylometry.cli run --preset pos --model multinomial_nb --repeats 3
python -m stylometry.cli run --preset fw --model logreg --repeats 3
python -m stylometry.cli run --preset length --model dummy --repeats 1
```
Смысл: понять, сколько сигнала даёт только длина, только символьные n-граммы,
только POS, только функциональные слова и случайная/частотная базовая модель.
### E2. Классическая стилометрия
```bash
python -m stylometry.cli run --preset stylometry --model logreg --repeats 5
python -m stylometry.cli run --preset stylometry --model linear_svc --repeats 5
```
Проверяет H1 на ручных признаках без n-грамм и LSA.
### E3. Поочерёдное исключение групп признаков
```bash
python -m stylometry.cli ablate --preset stylometry_pos_lsa --model logreg --repeats 3
```
Для каждой группы признаков строится вариант «без этой группы». Результат —
`results/ablation_*/ablation.csv` с колонкой `delta_macro_f1`. Отрицательное
значение означает, что удаление группы ухудшило качество; положительное — что
группа мешала (возможно, из-за шума или переобучения).
### E4. POS-признаки
Сравните:
```bash
python -m stylometry.cli run --preset stylometry --model logreg --repeats 5
python -m stylometry.cli run --preset stylometry_pos --model logreg --repeats 5
```
Разница macro-F1 показывает вклад символьных и POS n-грамм.
### E5. LSA: варианты и число компонент
```bash
python -m stylometry.cli lsa-sweep \
--variants word,content,fw,char,pos \
--components 50,100,200 \
--model logreg --repeats 1
```
Затем лучшие `k` и варианты проверить с 5 повторами через `run`:
```bash
python -m stylometry.cli run --preset lsa_word --model logreg --repeats 5
```
Для `lsa_word` и `lsa_pos` число компонент задаётся в `FeatureConfig`; в
командной строке текущая версия использует значение из `presets`
(`lsa_components=100`). Если нужно другое `k`, используйте `lsa-sweep` или
измените `FeatureConfig` в сценарии.
### E6. Сравнение классификаторов
```bash
python -m stylometry.cli benchmark --preset stylometry_pos_lsa \
--models logreg,linear_svc,random_forest,multinomial_nb,knn \
--repeats 3
```
Результат: `results/benchmark_*/summary.csv`, отдельные каталоги по моделям.
### E7. Стиль или тема
Сравните LSA-варианты:
```bash
python -m stylometry.cli lsa-sweep \
--variants word,content,fw,char,pos \
--components 50,100,200 --model logreg --repeats 1
```
Ожидаемая интерпретация:
- `word` и `content` лучше — вклад темы велик;
- `pos` и `fw` сопоставимы — стиль/синтаксис достаточно информативны;
- `char` полезен для морфологии и пунктуации.
### E8. Объединение блоков
```bash
python -m stylometry.cli run --preset stylometry_pos_lsa --model logreg --repeats 5
python -m stylometry.cli run --preset all --model logreg --repeats 3
```
Сравните с E2 и E5. Если объединение не лучше лучшего отдельного блока,
проверьте веса блоков и размерности.
### E9. Влияние длины
1. Запустите `run --preset length`, `--preset stylometry`,
`--preset stylometry_pos_lsa`.
2. Для сохранённого запуска выполните:
```bash
python -m stylometry.cli length-report --run-dir results/<каталог_запуска>
```
Команда сама соединяет предсказания с `data/processed/lines.csv`, разбивает
реплики на группы 0–5, 6–10, 11–15, 16–20, 21–30, 31+ слов и считает
macro-F1 с бутстрэп-интервалом. В каждой группе macro-F1 считается по
классам, которые в ней присутствуют; поэтому сравнение групп с разным
набором персонажей нужно интерпретировать осторожно. Результат сохраняется
в `length_bins.csv` внутри каталога запуска.
Ожидание: качество может зависеть от длины, но монотонность не гарантирована.
В предварительном прогоне объединённого набора лучшие значения оказались
в группах 21–30 и 31+ слов, а группа 0–5 слов дала широкий интервал из-за
малого числа объектов. Если длина вносит существенный вклад, сравните
результаты с `--preset length` и без length-признаков (поочерёдное исключение
групп).
### E10. Дисбаланс классов
```bash
python -m stylometry.cli benchmark --preset stylometry_pos_lsa \
--models logreg,linear_svc,random_forest --repeats 3
```
Смотрите не только macro-F1, но и F-меру по классам, сбалансированную долю
правильных ответов и support. Сравните `class_weight="balanced"` (в коде
включено для logreg, SVM, RF) с
вариантом без балансировки: для этого нужно изменить `make_classifier` или
передать параметр.
### E11. Дельта Барроуза
```bash
python -m stylometry.cli delta --features 100,200,500 --metrics manhattan,cosine --repeats 3
```
Классическая стилометрическая базовая модель. Ожидается, что на коротких репликах
он уступает ML-моделям; важен как независимая проверка.
### E12. Верификация «свой/чужой»
```bash
python -m stylometry.cli verify --preset stylometry_pos_lsa
```
Для каждой тестовой реплики считается близость к центроидам всех персонажей.
Положительный класс — центроид своего персонажа, отрицательный — чужие.
Метрики: ROC-AUC, EER, TAR@FAR=1%, top-1. Это более мягкая
постановка, чем 34-классовая классификация.
### E13. Кластеризация
```bash
python -m stylometry.cli cluster --preset stylometry_pos_lsa
```
Строятся центроиды персонажей, иерархическая кластеризация и k-means.
Метрики: ARI, NMI, силуэт. Кластеризация носит предварительный характер: она показывает, какие
персонажи близки в признаковом пространстве, но не заменяет классификацию.
### E14. Анализ ошибок
После любого запуска откройте:
- `per_class_report.csv` — precision, recall, F1, support по персонажам;
- `confusion_matrix_normalized.csv` — доли перепутывания;
- `top_confusions.csv` — самые частые пары;
- `figures/confusion_matrix.png` — тепловая карта.
Что искать:
- персонажи с малым support и низким F1;
- пары с высокой взаимной ошибкой (например, дети, нелюди, персонажи с
похожей ролью);
- влияние длины: короткие реплики чаще путаются;
- признаки, важные для лучшей модели (коэффициенты логистической регрессии,
permutation importance для деревьев).
Для анализа по метаданным (пол, возраст, вид) используйте:
```bash
python -m stylometry.cli subgroup-report \
--run-dir results/<каталог_запуска> --group gender
python -m stylometry.cli subgroup-report \
--run-dir results/<каталог_запуска> --group age_group
python -m stylometry.cli subgroup-report \
--run-dir results/<каталог_запуска> --group species
```
Команда сохраняет `subgroup_<group>_metrics.csv` и
`subgroup_<group>_confusions.csv`. Метаданные лежат в
`data/metadata/characters.csv`; это экспертные пометки, а не часть корпуса,
поэтому они не используются как признаки классификатора.
### E15. Статистическое сравнение
Для двух лучших моделей:
```bash
python -m stylometry.cli compare \
--run-a results/<каталог_запуска_A> \
--run-b results/<каталог_запуска_B>
```
Команда соединяет out-of-fold предсказания по `line_id`, проверяет
совпадение истинных классов и считает точный критерий Мак-Немара и
бутстрэп-интервал разницы macro-F1. Результат сохраняется в
`comparison_with_other.json`. Для корректного парного сравнения запускайте
модели с одинаковыми `--folds` и `--seed`.
Для трёх и более моделей используйте `friedman_test` из
`stylometry/stats.py` по метрикам блоков и поправку Холма.
### E16. Вложенная кросс-валидация
```bash
python -m stylometry.cli run --preset stylometry_pos_lsa --model logreg --nested
python -m stylometry.cli run --preset stylometry_pos_lsa --model linear_svc --nested
```
Это финальная проверка с настройкой гиперпараметров. Сохраняются
`best_params.json` и метрики внешних блоков.
### E17. Дополнительно: агрегация в псевдодокументы
Основной протокол — уровень реплик. Если понадобится формально закрыть
требование «не менее 30 предложений на текст», можно реализовать отдельный
сценарий: склеить реплики одного персонажа в псевдодокументы по 30 предложений
и повторить E2–E9. Этот сценарий **не включён** в основной код, потому что
меняет единицу анализа и уменьшает число объектов. Его результаты следует
рассматривать как robustness check, а не как замену основного эксперимента.
---
## 10. Контроль утечек и воспроизводимости
Перед финальным запуском проверьте:
- [ ] `data/processed/tokenized.pkl` соответствует текущему SHA-256 корпуса.
- [ ] В метаданных запуска `pos_available=true`; POS-признаки не отключились.
- [ ] TF-IDF, SVD и масштабирование обучаются внутри блоков (это обеспечивает
`Pipeline`; не выносите их наружу).
- [ ] `k` LSA и гиперпараметры выбраны по внутренней CV, а не по тесту.
- [ ] В корпусе нет точных дублей; при появлении почти одинаковых реплик
используется группировка.
- [ ] Для финальных чисел выполнено не менее 3–5 повторов.
- [ ] Сохранены `run_metadata.json`, `fold_metrics.csv`, `pooled_metrics.csv`,
`oof_predictions.csv`, `confusion_matrix.csv`.
- [ ] Указаны версии библиотек и seed.
---
## 11. Текущие технические прогоны
Ниже — предварительные результаты одного повтора 5-блочной CV. Они нужны
только для проверки работоспособности конвейера и **не являются финальными**:
один повтор даёт широкий интервал, а гиперпараметры не настраивались.
| Набор признаков | Модель | Macro-F1 | Доля правильных ответов | Top-3 |
|---|---|---:|---:|---:|
| `stylometry` | logreg | 0,1002 | 0,0988 | 0,2389 |
| `stylometry_pos` | logreg | 0,1780 | 0,1681 | 0,3367 |
| `stylometry_pos_lsa` | logreg | 0,1802 | 0,1706 | 0,3387 |
| `lsa_word`, k=50 | logreg | 0,0618 | 0,0628 | 0,1835 |
| `lsa_pos`, k=50 | logreg | 0,0443 | 0,0459 | 0,1202 |
| `char` | multinomial_nb | 0,0280 | 0,0973 | 0,2464 |
Наблюдения:
- символьные и POS n-граммы дают основной прирост относительно ручных
признаков;
- LSA по словам и POS сама по себе слабее, но в объединении даёт небольшой
прирост;
- доля правильных ответов у `char + multinomial_nb` около 9,7%, но
сбалансированная доля правильных ответов 4,7% и macro-F1 2,8%: модель
предсказывает частые классы. Это иллюстрация того, почему долю правильных
ответов нельзя использовать как единственную метрику.
Верификация для `stylometry` (5 блоков): ROC-AUC ≈ 0,648, EER ≈ 0,395,
top-1 ≈ 0,093. Это выше случайного уровня (ROC-AUC 0,5), но далеко от
практически надёжного различения.
Поочерёдное исключение групп для `stylometry` (один повтор): удаление группы `characters`
снижает macro-F1 примерно на 0,015, удаление `vocabulary` — примерно на
0,008; остальные изменения в пределах шума. Эти числа нужно перепроверить с
несколькими повторами.
---
## 12. Как интерпретировать результаты
1. **Сравнивайте с случайным уровнем.** Для 34 классов доля правильных
ответов 2,9%, macro-F1 2,9%, top-3 8,8%.
2. **Основной вывод — по macro-F1 и доверительному интервалу**, а не по
доле правильных ответов.
3. **Смотрите F-меру по классам.** Если редкие персонажи не различаются, это
важный вывод, а не техническая деталь.
4. **Сравнивайте стиль и тему.** Если `lsa_word`/`lsa_content` значительно
лучше `lsa_pos`/`lsa_fw`, классификатор опирается на содержание.
5. **Проверяйте длину.** Если `length` даёт заметный сигнал, часть результата
объясняется длиной реплик, а не синтаксисом.
6. **Не переинтерпретируйте небольшие различия.** При 2005 объектах и 34
классах разница в 1–2 процентных пункта может быть шумом; нужны
доверительные интервалы и парные тесты.
7. **Помните про одного автора.** Результат говорит о различимости персонажей,
а не о разных авторах.
---
## 13. Ограничения
- Короткие тексты: признаки словарного богатства и читаемости нестабильны.
- Один автор: это классификация речевых масок, не авторская атрибуция.
- Ручная фильтрация корпуса: короткие и общие фразы удалены, правила не
восстановлены.
- Дисбаланс классов: 15–139 реплик; редкие классы дают широкие интервалы.
- Нет идентификаторов сцен: нельзя учесть корреляцию реплик внутри одной
сцены.
- NLTK-теггер ошибается на разговорных конструкциях.
- LSA может отражать тему; сравнение вариантов обязательно.
- Кластеризация и t-SNE — предварительный анализ, не доказательство.
---
## 14. Чек-лист перед защитой
- [ ] В отчёте описаны корпус, ручная фильтрация и её ограничения.
- [ ] Указано, что задача — character idiolect, а не авторская атрибуция.
- [ ] Перечислены группы признаков и обоснован выбор.
- [ ] Описаны варианты LSA и способ выбора числа компонент.
- [ ] Указаны классификаторы, гиперпараметры и схема валидации.
- [ ] Основная метрика — macro-F1; приведены доверительные интервалы.
- [ ] Есть отчёт по классам и матрица ошибок.
- [ ] Есть сравнение стиля и темы (`lsa_word` vs `lsa_pos`/`lsa_fw`).
- [ ] Есть анализ длины и поочерёдное исключение групп признаков.
- [ ] Есть статистическое сравнение моделей.
- [ ] Сформулированы ограничения и направления улучшения.
- [ ] Код, отчёт и презентация воспроизводимы по `README.md`.
---
## 15. Куда сохраняются результаты
```text
results/
├── <preset>_<model>_<timestamp>/
│ ├── run_metadata.json
│ ├── fold_metrics.csv
│ ├── fold_summary.csv
│ ├── pooled_metrics.csv
│ ├── bootstrap_ci.json
│ ├── oof_predictions.csv
│ ├── per_class_report.csv
│ ├── confusion_matrix.csv
│ ├── confusion_matrix_normalized.csv
│ ├── top_confusions.csv
│ ├── summary.md
│ └── figures/
├── benchmark_<preset>_<timestamp>/
│ ├── summary.csv
│ └── <model>/
├── ablation_<preset>_<timestamp>/
│ └── ablation.csv
├── lsa_sweep_<timestamp>/
│ └── lsa_sweep.csv
├── delta_<timestamp>/
│ └── delta_sweep.csv
├── verification_<preset>_<timestamp>/
│ ├── fold_metrics.csv
│ ├── pair_scores.csv
│ └── summary.json
└── clustering_<preset>_<timestamp>/
├── clustering_metrics.csv
├── character_centroids.csv
└── figures/
```