Первичная версия: стилометрия реплик, 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

686
lab1/RUNBOOK.md Normal file
View File

@@ -0,0 +1,686 @@
# Инструкция по запуску экспериментов
Документ предназначен для коллеги, который будет запускать серию
экспериментов на своей машине. Все команды выполняются из каталога `lab1`.
Результаты нужно упаковать и передать обратно одним архивом.
Если вы впервые открыли проект, начните с разделов 1–4. Если окружение уже
готово, переходите к разделу 5.
---
## 1. Что это за проект
Исследуется, можно ли стилометрически различить 34 персонажа Stardew Valley
по отдельным репликам, если все реплики написаны одним автором. Это
многоклассовая классификация речевых масок персонажей, а не авторская
атрибуция.
Основные характеристики:
- 2005 реплик;
- 34 класса;
- около 38 тыс. словоупотреблений;
- средняя длина реплики около 19 слов;
- классы неравномерны: от 15 до 139 реплик;
- основная метрика — macro-F1;
- схема валидации — повторная стратифицированная перекрёстная проверка.
Полный протокол: `EXPERIMENTS.md`.
Описание признаков: `FEATURES.md`.
Описание метрик и классификаторов: `METRICS_AND_CLASSIFIERS.md`.
Структура презентации: `PRESENTATION_OUTLINE.md`.
---
## 2. Требования к окружению
- Python 3.11 или новее (проверено на 3.14).
- Linux, macOS или Windows с WSL.
- Свободное место: около 1 ГБ с учётом виртуального окружения и ресурсов
NLTK.
- Интернет — только для установки зависимостей и скачивания ресурсов NLTK.
После этого эксперименты выполняются локально.
Проверьте версию:
```bash
python --version
```
Если версия ниже 3.11, обновите Python: в коде используется стандартный
модуль `tomllib`.
---
## 3. Первичная установка
### 3.1. Перейти в каталог лабораторной
```bash
cd nlp/lab1
```
Все дальнейшие команды выполняются из этого каталога.
### 3.2. Создать виртуальное окружение
Можно создать отдельное окружение внутри `lab1`:
```bash
python -m venv .venv
source .venv/bin/activate
```
На Windows:
```powershell
python -m venv .venv
.venv\Scripts\activate
```
Если в корне проекта уже есть окружение `.venv`, можно использовать его:
```bash
source ../.venv/bin/activate
```
### 3.3. Установить зависимости
```bash
python -m pip install --upgrade pip
python -m pip install -r requirements.txt
```
Проверьте, что установились основные библиотеки:
```bash
python -c "import numpy, pandas, scipy, sklearn, nltk, matplotlib; print('ok')"
```
### 3.4. Скачать ресурсы NLTK
```bash
python scripts/download_nltk_data.py
```
Ресурсы сохраняются в `lab1/nltk_data`:
- `punkt`, `punkt_tab` — токенизация и разбиение на предложения;
- `averaged_perceptron_tagger`, `averaged_perceptron_tagger_eng` —
частеречная разметка;
- `stopwords` — стоп-слова для content-LSA.
Без этих ресурсов POS-признаки отключатся, и результаты будут неполными.
### 3.5. Проверить окружение
```bash
python scripts/check_environment.py
```
Скрипт печатает версии библиотек, SHA-256 корпуса, число реплик и классов,
проверяет токенизатор и POS-теггер. В конце должно быть:
```text
Окружение готово.
```
Если POS-теггер недоступен, повторите шаг 3.4.
---
## 4. Проверка перед полной серией
### 4.1. Тесты
```bash
python -m pytest -q
```
Ожидается:
```text
33 passed
```
Если тесты не проходят, не запускайте полную серию: сначала пришлите текст
ошибки.
### 4.2. Быстрый smoke-test
```bash
python -m stylometry.cli audit
python -m stylometry.cli features
python -m stylometry.cli run --preset length --model logreg --folds 3 --repeats 1
```
Проверьте, что появились:
- `data/processed/audit.md`;
- `data/processed/lines.csv`;
- `features/stylometric.csv`;
- `features/feature_dictionary.csv`;
- каталог `results/length_logreg_<timestamp>/` с `summary.md`.
Если всё создалось, окружение готово.
---
## 5. Полная серия экспериментов
### 5.1. Основной запуск
Из каталога `lab1`:
```bash
bash scripts/run_all_experiments.sh
```
Если используется окружение в корне проекта:
```bash
PYTHON=../.venv/bin/python bash scripts/run_all_experiments.sh
```
Скрипт последовательно запускает все стадии, пишет общий лог в
`results/logs/run_all_<timestamp>.log` и останавливается при первой ошибке.
### 5.2. Быстрый режим
Для проверки, что всё работает, но без финальных чисел:
```bash
QUICK=1 bash scripts/run_all_experiments.sh
```
В быстром режиме:
- все повторы равны 1;
- вложенная кросс-валидация пропускается;
- полная серия занимает меньше времени.
Результаты быстрого режима **нельзя** использовать в отчёте как финальные.
### 5.3. Настройка повторов
По умолчанию:
| Переменная | Значение | Что регулирует |
|---|---:|---|
| `REPEATS_MAIN` | 5 | основные модели |
| `REPEATS_FAST` | 3 | baseline, benchmark, delta |
| `REPEATS_LSA` | 3 | отдельные варианты LSA |
| `REPEATS_ABLATE` | 3 | поочерёдное исключение групп |
| `REPEATS_DELTA` | 2 | дельта Барроуза |
| `SKIP_NESTED` | 0 | 1 — пропустить вложенную CV |
Пример:
```bash
REPEATS_MAIN=5 REPEATS_FAST=3 SKIP_NESTED=1 \
bash scripts/run_all_experiments.sh
```
### 5.4. Долгий запуск через tmux или nohup
Полная серия может занимать несколько часов. Если подключение к машине
может прерваться, используйте `tmux`:
```bash
tmux new -s nlp
PYTHON=../.venv/bin/python bash scripts/run_all_experiments.sh
# Ctrl+B, затем D — выйти из tmux, процесс продолжит работу
```
Вернуться:
```bash
tmux attach -t nlp
```
Или `nohup`:
```bash
nohup env PYTHON=../.venv/bin/python \
bash scripts/run_all_experiments.sh >/dev/null 2>&1 &
```
Следить за логом:
```bash
tail -f results/logs/run_all_*.log
```
### 5.5. Ориентировочное время
Время зависит от процессора, числа потоков и версии библиотек. Ориентиры для
обычного ноутбука:
| Стадия | Время |
|---|---:|
| Проверка окружения и тесты | 1–2 мин |
| Аудит и ручные признаки | 1–2 мин |
| Нижние границы | 5–10 мин |
| Классическая стилометрия | 3–6 мин |
| Ручные признаки + n-граммы | 4–8 мин |
| Основной объединённый набор | 5–10 мин |
| Варианты LSA | 10–20 мин |
| Перебор числа компонент LSA | 5–10 мин |
| Сравнение классификаторов | 15–30 мин |
| Поочерёдное исключение групп | 20–40 мин |
| Дельта Барроуза | 3–5 мин |
| Верификация и кластеризация | 3–5 мин |
| Вложенная кросс-валидация | 15–40 мин |
| Анализ длины и подгрупп | 1–2 мин |
Итого: ориентировочно **1,5–3 часа**, в зависимости от машины. Если
включены все повторы и вложенная CV, может быть дольше.
---
## 6. Что делает каждая стадия
### 6.1. Аудит корпуса (E0)
```bash
python -m stylometry.cli audit
```
Создаёт:
- `data/processed/audit.md` — отчёт по корпусу;
- `data/processed/lines.csv` — нормализованные реплики;
- `data/processed/figures/class_distribution.png`.
Проверьте: 2005 реплик, 34 персонажа, нет точных дублей, распределение
классов совпадает с `stats_per_character.xlsx`.
### 6.2. Ручные признаки
```bash
python -m stylometry.cli features
```
Создаёт:
- `features/stylometric.csv` — матрица 2005 × 454;
- `features/feature_dictionary.csv` — описание признаков.
### 6.3. Нижние границы (E1)
Запускаются:
- `length` + Dummy — частотная базовая модель;
- `length` + логистическая регрессия;
- `char` + MultinomialNB;
- `pos` + MultinomialNB;
- `fw` + логистическая регрессия.
Смысл: понять, сколько сигнала даёт только длина, только символьные
n-граммы, только POS и только функциональные слова.
### 6.4. Классическая стилометрия (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.
### 6.5. Ручные признаки + n-граммы (E4)
```bash
python -m stylometry.cli run --preset stylometry_pos --model logreg --repeats 5
```
Показывает вклад символьных и частеречных n-грамм.
### 6.6. Основной объединённый набор (E8)
```bash
python -m stylometry.cli run --preset stylometry_pos_lsa --model logreg --repeats 5
```
Основной результат: ручные признаки + char/POS n-граммы + LSA по словам и
POS.
### 6.7. Варианты LSA (E5, E7)
Запускаются отдельно:
```bash
python -m stylometry.cli run --preset lsa_word --model logreg --repeats 3
python -m stylometry.cli run --preset lsa_word2 --model logreg --repeats 3
python -m stylometry.cli run --preset lsa_content --model logreg --repeats 3
python -m stylometry.cli run --preset lsa_fw --model logreg --repeats 3
python -m stylometry.cli run --preset lsa_char --model logreg --repeats 3
python -m stylometry.cli run --preset lsa_pos --model logreg --repeats 3
```
Сравнение `lsa_word`/`lsa_content` с `lsa_pos`/`lsa_fw`/`lsa_char` — это
основной эксперимент «стиль или тема».
### 6.8. Перебор числа компонент LSA (E5)
```bash
python -m stylometry.cli lsa-sweep \
--variants word,content,fw,char,pos \
--components 50,100,200 \
--model logreg --repeats 1
```
Результат: `results/lsa_sweep_<timestamp>/lsa_sweep.csv` с macro-F1 для
каждой пары «вариант — число компонент».
### 6.9. Сравнение классификаторов (E6)
```bash
python -m stylometry.cli benchmark \
--preset stylometry_pos_lsa \
--models logreg,linear_svc,random_forest,multinomial_nb,knn \
--repeats 3
```
Результат: `results/benchmark_<preset>_<timestamp>/summary.csv` и отдельные
каталоги по моделям.
### 6.10. Поочерёдное исключение групп признаков (E3)
```bash
python -m stylometry.cli ablate \
--preset stylometry_pos_lsa --model logreg --repeats 3
```
Результат: `results/ablation_<preset>_<timestamp>/ablation.csv` с колонкой
`delta_macro_f1`. Отрицательное значение означает, что удаление группы
ухудшило качество.
### 6.11. Дельта Барроуза (E11)
```bash
python -m stylometry.cli delta \
--features 100,200,500 --metrics manhattan,cosine --repeats 2
```
Классическая стилометрическая базовая модель; результат —
`results/delta_<timestamp>/delta_sweep.csv`.
### 6.12. Верификация «свой/чужой» (E12)
```bash
python -m stylometry.cli verify --preset stylometry_pos_lsa
```
Результат: `results/verification_<preset>_<timestamp>/summary.json` с
ROC-AUC, EER, TAR@FAR=1% и top-1.
### 6.13. Кластеризация (E13)
```bash
python -m stylometry.cli cluster --preset stylometry_pos_lsa
```
Результат: `results/clustering_<preset>_<timestamp>/clustering_metrics.csv`
и `character_centroids.csv`.
### 6.14. Вложенная кросс-валидация (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` и метрики внешних блоков.
### 6.15. Анализ длины и подгрупп (E9, E14, E15)
После основного запуска:
```bash
python -m stylometry.cli length-report \
--run-dir results/<каталог_основного_запуска>
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
python -m stylometry.cli compare \
--run-a results/<каталог_основного_запуска> \
--run-b results/<каталог_stylometry_pos> \
--out results/<каталог_основного_запуска>/comparison_main_vs_pos.json
```
`compare` корректно работает, если запуски сделаны с одинаковыми `--folds` и
`--seed`.
---
## 7. Если серия прервалась
Скрипт останавливается при первой ошибке. Уже созданные каталоги результатов
не удаляются. Порядок действий:
1. Посмотрите конец лога:
```bash
tail -n 100 results/logs/run_all_*.log
```
2. Исправьте причину (см. раздел 10).
3. Запустите пропущенные команды вручную из раздела 6. Можно продолжить с
нужной стадии, не повторяя всю серию.
4. Новые результаты появятся в отдельных каталогах с новыми метками времени.
Не удаляйте старые каталоги: по ним можно сравнить, что изменилось.
---
## 8. Упаковка результатов
После завершения серии:
```bash
bash scripts/collect_results.sh
```
Скрипт создаёт:
```text
exports/
├── nlp_lab1_results_<timestamp>.tar.gz
├── nlp_lab1_results_<timestamp>.tar.gz.sha256
└── nlp_lab1_results_<timestamp>_manifest.txt
```
В архив входят:
- `results/` — все запуски, метрики, предсказания, матрицы ошибок, графики,
логи;
- `features/` — матрицы признаков и LSA;
- `data/processed/` — очищенные реплики и аудит;
- `data/metadata/` — метаданные персонажей;
- манифест с версиями библиотек, SHA-256 корпуса и списком файлов.
Передайте коллеге **архив** и **файл `.sha256`**. Если архив большой,
можно передать его через файлообменник, но контрольную сумму нужно сохранить.
Проверка архива на стороне получателя:
```bash
sha256sum -c nlp_lab1_results_<timestamp>.tar.gz.sha256
tar -xzf nlp_lab1_results_<timestamp>.tar.gz
```
---
## 9. Что проверить перед отправкой
- [ ] `python scripts/check_environment.py` завершился без ошибок.
- [ ] `python -m pytest -q` — все тесты пройдены.
- [ ] В логе нет строк `ERROR`, `Traceback`, `FAILED`.
- [ ] Основной запуск `stylometry_pos_lsa` содержит `summary.md`,
`pooled_metrics.csv`, `per_class_report.csv`, `confusion_matrix.csv`.
- [ ] Есть запуски `stylometry`, `stylometry_pos`, `stylometry_pos_lsa`.
- [ ] Есть `results/benchmark_*`, `results/ablation_*`, `results/lsa_sweep_*`.
- [ ] Есть `results/verification_*` и `results/clustering_*`.
- [ ] Есть `results/logs/run_all_*.log`.
- [ ] `collect_results.sh` создал архив и `.sha256`.
---
## 10. Типичные проблемы
### 10.1. `ModuleNotFoundError`
Не установлены зависимости или выбран другой интерпретатор.
```bash
python -m pip install -r requirements.txt
python scripts/check_environment.py
```
Если используете PyCharm, проверьте, что выбран интерпретатор
`nlp/.venv/bin/python` или `lab1/.venv/bin/python`.
### 10.2. Ошибка NLTK: `Resource punkt_tab not found`
```bash
python scripts/download_nltk_data.py
```
Если интернет недоступен, скопируйте каталог `nltk_data` с другой машины в
`lab1/nltk_data`.
### 10.3. `UnicodeDecodeError` при чтении корпуса
Корпус хранится в cp1251. Код сам пробует cp1251, UTF-8 и koi8-r. Если вы
пересохранили CSV в другой кодировке, верните cp1251 или используйте
`dialogues.xlsx`.
### 10.4. `ValueError: empty vocabulary`
Обычно означает, что в обучающем блоке не осталось признаков. Проверьте:
- не пустой ли корпус;
- не отключены ли все блоки признаков;
- не выбран ли `multinomial_nb` с одним LSA без char/POS.
Для стандартных пресетов этой ошибки быть не должно.
### 10.5. Не хватает памяти
Уменьшите размерности:
```bash
python -m stylometry.cli run --preset stylometry_pos --model logreg --repeats 3
```
Или закройте другие приложения. Основной объединённый набор с LSA требует
больше памяти, чем `stylometry_pos`.
### 10.6. Серия идёт слишком долго
- Используйте `QUICK=1` для проверки.
- Уменьшите `REPEATS_FAST`, `REPEATS_ABLATE`, `REPEATS_LSA`.
- Пропустите вложенную CV: `SKIP_NESTED=1`.
- Для финального отчёта всё равно нужны основные повторы; не заменяйте их
быстрым режимом.
### 10.7. `tomllib` не найден
Нужен Python 3.11+. Обновите интерпретатор или создайте окружение на 3.11+.
---
## 11. Полная матрица экспериментов
| ID | Команда | Что проверяет |
|---|---|---|
| E0 | `audit` | Корпус, классы, длины, дубли |
| E1 | `run --preset length/char/pos/fw` | Нижние границы |
| E2 | `run --preset stylometry` | Ручная стилометрия |
| E3 | `ablate --preset stylometry_pos_lsa` | Вклад групп признаков |
| E4 | `run --preset stylometry_pos` | Вклад char/POS n-грамм |
| E5 | `lsa-sweep` и `run --preset lsa_*` | Варианты и число компонент LSA |
| E6 | `benchmark --preset stylometry_pos_lsa` | Сравнение классификаторов |
| E7 | `lsa-sweep` | Стиль или тема |
| E8 | `run --preset stylometry_pos_lsa` | Объединение блоков |
| E9 | `length-report` | Влияние длины |
| E10 | `benchmark` + per-class отчёт | Дисбаланс классов |
| E11 | `delta` | Дельта Барроуза |
| E12 | `verify` | Верификация «свой/чужой» |
| E13 | `cluster` | Кластеризация персонажей |
| E14 | `subgroup-report` | Пол, возраст, вид |
| E15 | `compare` | Мак-Немар и бутстрэп-разница |
| E16 | `run --nested` | Вложенная кросс-валидация |
| E17 | не реализовано | Агрегация в псевдодокументы по 30 предложений |
Подробные команды и интерпретация — в `EXPERIMENTS.md`.
---
## 12. Что именно отправлять обратно
Минимальный комплект:
1. `exports/nlp_lab1_results_<timestamp>.tar.gz`;
2. `exports/nlp_lab1_results_<timestamp>.tar.gz.sha256`;
3. краткий ответ: на какой машине запускали, сколько времени заняла серия,
какие стадии завершились, какие ошибки были.
Если серия не завершилась, отправьте:
- лог `results/logs/run_all_*.log`;
- архив с уже готовыми результатами;
- текст ошибки и команду, на которой она возникла.
---
## 13. Что нельзя менять без согласования
Чтобы результаты оставались сопоставимыми:
- не меняйте корпус и разметку классов;
- не удаляйте реплики и не переименовывайте персонажей;
- не меняйте `--folds`, `--seed` и схему стратификации в основных запусках;
- не меняйте определения метрик;
- не настраивайте гиперпараметры на тестовых блоках;
- не выбирайте признаки по тестовой выборке;
- не заменяйте macro-F1 на долю правильных ответов как основную метрику.
Если хотите добавить новый эксперимент, создайте отдельный сценарий и
сохраните его в `experiments/`, не изменяя основные модули.
---
## 14. Краткая шпаргалка
```bash
cd nlp/lab1
source ../.venv/bin/activate
python scripts/check_environment.py
python -m pytest -q
# быстрая проверка
QUICK=1 bash scripts/run_all_experiments.sh
# полная серия
bash scripts/run_all_experiments.sh
# упаковка результатов
bash scripts/collect_results.sh
# посмотреть лог
tail -f results/logs/run_all_*.log
```
Если что-то пошло не так, сначала пришлите лог и текст ошибки, а не только
вывод последней команды.