Первичная версия: стилометрия реплик, LSA и протокол экспериментов
This commit is contained in:
77
lab1/scripts/check_environment.py
Normal file
77
lab1/scripts/check_environment.py
Normal file
@@ -0,0 +1,77 @@
|
||||
"""Проверка окружения перед серией экспериментов."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
PROJECT_ROOT = Path(__file__).resolve().parents[1]
|
||||
sys.path.insert(0, str(PROJECT_ROOT))
|
||||
|
||||
from stylometry.io import DEFAULT_DATA_PATH, corpus_fingerprint, read_corpus, validate_corpus # noqa: E402
|
||||
from stylometry.tokenization import Tokenizer # noqa: E402
|
||||
|
||||
|
||||
def package_versions() -> dict[str, str]:
|
||||
versions: dict[str, str] = {}
|
||||
for name in ("numpy", "pandas", "scipy", "sklearn", "nltk", "matplotlib"):
|
||||
try:
|
||||
module = __import__(name)
|
||||
versions[name] = getattr(module, "__version__", "unknown")
|
||||
except Exception:
|
||||
versions[name] = "missing"
|
||||
return versions
|
||||
|
||||
|
||||
def main() -> int:
|
||||
print("=== Проверка окружения ===")
|
||||
print(f"Python: {sys.version.split()[0]} ({sys.executable})")
|
||||
print(f"Каталог проекта: {PROJECT_ROOT}")
|
||||
|
||||
versions = package_versions()
|
||||
print("Версии библиотек:")
|
||||
for name, version in versions.items():
|
||||
print(f" {name}: {version}")
|
||||
missing = [name for name, version in versions.items() if version == "missing"]
|
||||
if missing:
|
||||
print(f"ОШИБКА: не установлены библиотеки: {missing}")
|
||||
print("Выполните: pip install -r requirements.txt")
|
||||
return 1
|
||||
|
||||
if not DEFAULT_DATA_PATH.exists():
|
||||
print(f"ОШИБКА: нет файла корпуса {DEFAULT_DATA_PATH}")
|
||||
return 1
|
||||
|
||||
frame = read_corpus(DEFAULT_DATA_PATH)
|
||||
stats = validate_corpus(frame)
|
||||
print("Корпус:")
|
||||
print(f" файл: {DEFAULT_DATA_PATH}")
|
||||
print(f" SHA-256: {corpus_fingerprint(DEFAULT_DATA_PATH)}")
|
||||
print(f" реплик: {stats['n_lines']}")
|
||||
print(f" персонажей: {stats['n_characters']}")
|
||||
print(f" словоупотреблений: {stats['n_words']}")
|
||||
|
||||
try:
|
||||
tokenizer = Tokenizer(use_nltk=True, strict=True)
|
||||
except Exception as error:
|
||||
print(f"ОШИБКА NLTK: {error}")
|
||||
print("Выполните: python scripts/download_nltk_data.py")
|
||||
return 1
|
||||
analyzed = tokenizer.analyze(frame.iloc[0]["text"])
|
||||
print("NLTK:")
|
||||
print(f" токенизатор: {'NLTK' if tokenizer.use_nltk else 'резервный'}")
|
||||
print(f" POS-теггер: {'доступен' if tokenizer.pos_available else 'недоступен'}")
|
||||
print(f" пример токенов: {analyzed.tokens[:8]}")
|
||||
print(f" пример POS: {analyzed.upos[:8]}")
|
||||
if not tokenizer.pos_available:
|
||||
print("ОШИБКА: POS-теггер недоступен; эксперименты с POS будут неполными.")
|
||||
return 1
|
||||
|
||||
print("\nОкружение готово.")
|
||||
print(json.dumps({"versions": versions, "n_lines": stats["n_lines"]}, ensure_ascii=False))
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
56
lab1/scripts/collect_results.sh
Executable file
56
lab1/scripts/collect_results.sh
Executable file
@@ -0,0 +1,56 @@
|
||||
#!/usr/bin/env bash
|
||||
# Упаковка результатов для передачи коллеге.
|
||||
#
|
||||
# Запуск из каталога lab1:
|
||||
# bash scripts/collect_results.sh
|
||||
#
|
||||
# Создаёт exports/nlp_lab1_results_<timestamp>.tar.gz и файл SHA-256.
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
PROJECT_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)"
|
||||
cd "${PROJECT_ROOT}"
|
||||
|
||||
PYTHON="${PYTHON:-python}"
|
||||
RUN_ID="${RUN_ID:-$(date +%Y%m%d_%H%M%S)}"
|
||||
OUT_DIR="${OUT_DIR:-${PROJECT_ROOT}/exports}"
|
||||
ARCHIVE="${OUT_DIR}/nlp_lab1_results_${RUN_ID}.tar.gz"
|
||||
MANIFEST="${OUT_DIR}/nlp_lab1_results_${RUN_ID}_manifest.txt"
|
||||
|
||||
mkdir -p "${OUT_DIR}"
|
||||
|
||||
{
|
||||
echo "Дата: $(date)"
|
||||
echo "Каталог: ${PROJECT_ROOT}"
|
||||
echo "Интерпретатор: ${PYTHON}"
|
||||
echo
|
||||
"${PYTHON}" scripts/check_environment.py || true
|
||||
echo
|
||||
if git rev-parse --is-inside-work-tree >/dev/null 2>&1; then
|
||||
echo "Git commit: $(git rev-parse HEAD 2>/dev/null || echo 'нет коммита')"
|
||||
else
|
||||
echo "Git: репозиторий не инициализирован"
|
||||
fi
|
||||
echo
|
||||
echo "Содержимое results:"
|
||||
find results -maxdepth 2 -type f | sort | head -n 500
|
||||
} > "${MANIFEST}"
|
||||
|
||||
INCLUDE=()
|
||||
for path in results features data/processed data/metadata; do
|
||||
if [[ -e "${path}" ]]; then
|
||||
INCLUDE+=("${path}")
|
||||
fi
|
||||
done
|
||||
INCLUDE+=("${MANIFEST#"${PROJECT_ROOT}/"}")
|
||||
|
||||
tar -czf "${ARCHIVE}" "${INCLUDE[@]}"
|
||||
|
||||
sha256sum "${ARCHIVE}" > "${ARCHIVE}.sha256"
|
||||
|
||||
echo "Архив: ${ARCHIVE}"
|
||||
echo "Контрольная сумма: ${ARCHIVE}.sha256"
|
||||
echo "Манифест: ${MANIFEST}"
|
||||
echo
|
||||
echo "Передайте коллеге архив и файл .sha256."
|
||||
29
lab1/scripts/download_nltk_data.py
Normal file
29
lab1/scripts/download_nltk_data.py
Normal file
@@ -0,0 +1,29 @@
|
||||
"""Скачивает ресурсы NLTK в локальный каталог lab1/nltk_data."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
import nltk
|
||||
|
||||
PACKAGES = [
|
||||
"punkt",
|
||||
"punkt_tab",
|
||||
"averaged_perceptron_tagger",
|
||||
"averaged_perceptron_tagger_eng",
|
||||
"stopwords",
|
||||
]
|
||||
|
||||
|
||||
def main() -> int:
|
||||
target = Path(__file__).resolve().parents[1] / "nltk_data"
|
||||
target.mkdir(parents=True, exist_ok=True)
|
||||
for package in PACKAGES:
|
||||
print(f"Скачивание {package} ...")
|
||||
nltk.download(package, download_dir=str(target), quiet=False)
|
||||
print(f"Готово: {target}")
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
169
lab1/scripts/run_all_experiments.sh
Executable file
169
lab1/scripts/run_all_experiments.sh
Executable file
@@ -0,0 +1,169 @@
|
||||
#!/usr/bin/env bash
|
||||
# Полная серия экспериментов для лабораторной работы.
|
||||
#
|
||||
# Запуск из каталога lab1:
|
||||
# bash scripts/run_all_experiments.sh
|
||||
#
|
||||
# С другим интерпретатором:
|
||||
# PYTHON=../.venv/bin/python bash scripts/run_all_experiments.sh
|
||||
#
|
||||
# Быстрая проверка (1 повтор, без вложенной кросс-валидации):
|
||||
# QUICK=1 bash scripts/run_all_experiments.sh
|
||||
#
|
||||
# Все команды пишутся в results/logs/run_all_<timestamp>.log.
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
PROJECT_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)"
|
||||
cd "${PROJECT_ROOT}"
|
||||
|
||||
PYTHON="${PYTHON:-python}"
|
||||
if [[ "${PYTHON}" == */* ]]; then
|
||||
PYTHON_DIR="$(cd "$(dirname "${PYTHON}")" && pwd)"
|
||||
PYTHON="${PYTHON_DIR}/$(basename "${PYTHON}")"
|
||||
fi
|
||||
|
||||
if ! "${PYTHON}" -c "import sklearn, pandas, nltk" >/dev/null 2>&1; then
|
||||
echo "ОШИБКА: в интерпретаторе ${PYTHON} не установлены зависимости."
|
||||
echo "Выполните: ${PYTHON} -m pip install -r requirements.txt"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
REPEATS_MAIN="${REPEATS_MAIN:-5}"
|
||||
REPEATS_FAST="${REPEATS_FAST:-3}"
|
||||
REPEATS_LSA="${REPEATS_LSA:-3}"
|
||||
REPEATS_ABLATE="${REPEATS_ABLATE:-3}"
|
||||
REPEATS_DELTA="${REPEATS_DELTA:-2}"
|
||||
SKIP_NESTED="${SKIP_NESTED:-0}"
|
||||
|
||||
if [[ "${QUICK:-0}" == "1" ]]; then
|
||||
REPEATS_MAIN=1
|
||||
REPEATS_FAST=1
|
||||
REPEATS_LSA=1
|
||||
REPEATS_ABLATE=1
|
||||
REPEATS_DELTA=1
|
||||
SKIP_NESTED=1
|
||||
fi
|
||||
|
||||
LOG_DIR="${PROJECT_ROOT}/results/logs"
|
||||
mkdir -p "${LOG_DIR}"
|
||||
RUN_ID="${RUN_ID:-$(date +%Y%m%d_%H%M%S)}"
|
||||
LOG_FILE="${LOG_DIR}/run_all_${RUN_ID}.log"
|
||||
exec > >(tee -a "${LOG_FILE}") 2>&1
|
||||
|
||||
step() {
|
||||
echo
|
||||
echo "============================================================"
|
||||
echo "== $*"
|
||||
echo "============================================================"
|
||||
}
|
||||
|
||||
run() {
|
||||
echo
|
||||
echo ">>> $*"
|
||||
"$@"
|
||||
}
|
||||
|
||||
latest_dir() {
|
||||
local prefix="$1"
|
||||
ls -1dt results/"${prefix}"_* 2>/dev/null | head -n 1 || true
|
||||
}
|
||||
|
||||
echo "Начало серии: $(date)"
|
||||
echo "Интерпретатор: ${PYTHON}"
|
||||
echo "Повторы: main=${REPEATS_MAIN}, fast=${REPEATS_FAST}, lsa=${REPEATS_LSA}, ablate=${REPEATS_ABLATE}, delta=${REPEATS_DELTA}"
|
||||
echo "Вложенная кросс-валидация: ${SKIP_NESTED} (1 — пропустить)"
|
||||
echo "Лог: ${LOG_FILE}"
|
||||
|
||||
step "0. Проверка окружения и тесты"
|
||||
run "${PYTHON}" scripts/check_environment.py
|
||||
run "${PYTHON}" -m pytest -q
|
||||
|
||||
step "1. Аудит корпуса и ручные признаки (E0)"
|
||||
run "${PYTHON}" -m stylometry.cli audit
|
||||
run "${PYTHON}" -m stylometry.cli features
|
||||
|
||||
step "2. Нижние границы (E1)"
|
||||
run "${PYTHON}" -m stylometry.cli run --preset length --model dummy --repeats 1
|
||||
run "${PYTHON}" -m stylometry.cli run --preset length --model logreg --repeats "${REPEATS_MAIN}"
|
||||
run "${PYTHON}" -m stylometry.cli run --preset char --model multinomial_nb --repeats "${REPEATS_FAST}"
|
||||
run "${PYTHON}" -m stylometry.cli run --preset pos --model multinomial_nb --repeats "${REPEATS_FAST}"
|
||||
run "${PYTHON}" -m stylometry.cli run --preset fw --model logreg --repeats "${REPEATS_FAST}"
|
||||
|
||||
step "3. Классическая стилометрия (E2)"
|
||||
run "${PYTHON}" -m stylometry.cli run --preset stylometry --model logreg --repeats "${REPEATS_MAIN}"
|
||||
run "${PYTHON}" -m stylometry.cli run --preset stylometry --model linear_svc --repeats "${REPEATS_MAIN}"
|
||||
|
||||
step "4. Ручные признаки + n-граммы (E4)"
|
||||
run "${PYTHON}" -m stylometry.cli run --preset stylometry_pos --model logreg --repeats "${REPEATS_MAIN}"
|
||||
|
||||
step "5. Основной объединённый набор (E8)"
|
||||
run "${PYTHON}" -m stylometry.cli run --preset stylometry_pos_lsa --model logreg --repeats "${REPEATS_MAIN}"
|
||||
|
||||
step "6. Варианты LSA по отдельности (E5, E7)"
|
||||
for variant in word word2 content fw char pos; do
|
||||
run "${PYTHON}" -m stylometry.cli run --preset "lsa_${variant}" --model logreg --repeats "${REPEATS_LSA}"
|
||||
done
|
||||
|
||||
step "7. Перебор числа компонент LSA (E5)"
|
||||
run "${PYTHON}" -m stylometry.cli lsa-sweep \
|
||||
--variants word,content,fw,char,pos \
|
||||
--components 50,100,200 \
|
||||
--model logreg --repeats 1
|
||||
|
||||
step "8. Сравнение классификаторов (E6)"
|
||||
run "${PYTHON}" -m stylometry.cli benchmark \
|
||||
--preset stylometry_pos_lsa \
|
||||
--models logreg,linear_svc,random_forest,multinomial_nb,knn \
|
||||
--repeats "${REPEATS_FAST}"
|
||||
|
||||
step "9. Поочерёдное исключение групп признаков (E3)"
|
||||
run "${PYTHON}" -m stylometry.cli ablate \
|
||||
--preset stylometry_pos_lsa --model logreg --repeats "${REPEATS_ABLATE}"
|
||||
|
||||
step "10. Дельта Барроуза (E11)"
|
||||
run "${PYTHON}" -m stylometry.cli delta \
|
||||
--features 100,200,500 --metrics manhattan,cosine --repeats "${REPEATS_DELTA}"
|
||||
|
||||
step "11. Верификация «свой/чужой» (E12)"
|
||||
run "${PYTHON}" -m stylometry.cli verify --preset stylometry_pos_lsa
|
||||
|
||||
step "12. Кластеризация персонажей (E13)"
|
||||
run "${PYTHON}" -m stylometry.cli cluster --preset stylometry_pos_lsa
|
||||
|
||||
if [[ "${SKIP_NESTED}" != "1" ]]; then
|
||||
step "13. Вложенная кросс-валидация (E16)"
|
||||
run "${PYTHON}" -m stylometry.cli run --preset stylometry_pos_lsa --model logreg --nested
|
||||
run "${PYTHON}" -m stylometry.cli run --preset stylometry_pos_lsa --model linear_svc --nested
|
||||
else
|
||||
echo
|
||||
echo "Вложенная кросс-валидация пропущена (SKIP_NESTED=1 или QUICK=1)."
|
||||
fi
|
||||
|
||||
step "14. Анализ длины, подгрупп и сравнение запусков (E9, E14, E15)"
|
||||
MAIN_DIR="$(latest_dir stylometry_pos_lsa_logreg)"
|
||||
POS_DIR="$(latest_dir stylometry_pos_logreg)"
|
||||
if [[ -n "${MAIN_DIR}" ]]; then
|
||||
run "${PYTHON}" -m stylometry.cli length-report --run-dir "${MAIN_DIR}"
|
||||
for group in gender age_group species; do
|
||||
run "${PYTHON}" -m stylometry.cli subgroup-report \
|
||||
--run-dir "${MAIN_DIR}" --group "${group}"
|
||||
done
|
||||
if [[ -n "${POS_DIR}" ]]; then
|
||||
run "${PYTHON}" -m stylometry.cli compare \
|
||||
--run-a "${MAIN_DIR}" --run-b "${POS_DIR}" \
|
||||
--out "${MAIN_DIR}/comparison_main_vs_pos.json"
|
||||
fi
|
||||
else
|
||||
echo "Не найден каталог основного запуска; анализ пропущен."
|
||||
fi
|
||||
|
||||
step "Готово"
|
||||
echo "Конец серии: $(date)"
|
||||
echo
|
||||
echo "Основные каталоги результатов:"
|
||||
ls -1dt results/*/ 2>/dev/null | head -n 30 || true
|
||||
echo
|
||||
echo "Полный лог: ${LOG_FILE}"
|
||||
echo "Для упаковки результатов: bash scripts/collect_results.sh"
|
||||
31
lab1/scripts/run_core_experiments.sh
Executable file
31
lab1/scripts/run_core_experiments.sh
Executable file
@@ -0,0 +1,31 @@
|
||||
#!/usr/bin/env bash
|
||||
# Базовая серия экспериментов. Запускать из каталога lab1.
|
||||
# Для финального отчёта увеличьте --repeats до 5.
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
python -m stylometry.cli audit
|
||||
python -m stylometry.cli features
|
||||
|
||||
# E1: нижние границы
|
||||
python -m stylometry.cli run --preset length --model dummy --repeats 1
|
||||
python -m stylometry.cli run --preset length --model logreg --repeats 3
|
||||
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
|
||||
|
||||
# E2-E4: ручные признаки, POS и объединение
|
||||
python -m stylometry.cli run --preset stylometry --model logreg --repeats 3
|
||||
python -m stylometry.cli run --preset stylometry_pos --model logreg --repeats 3
|
||||
python -m stylometry.cli run --preset stylometry_pos_lsa --model logreg --repeats 3
|
||||
python -m stylometry.cli ablate --preset stylometry_pos_lsa --model logreg --repeats 2
|
||||
|
||||
# E5-E6: LSA и классификаторы
|
||||
python -m stylometry.cli lsa-sweep --variants word,pos,char --components 50,100,200 --model logreg --repeats 1
|
||||
python -m stylometry.cli benchmark --preset stylometry_pos_lsa \
|
||||
--models logreg,linear_svc,random_forest,multinomial_nb,knn --repeats 2
|
||||
|
||||
# E11-E13: дельта, верификация, кластеризация
|
||||
python -m stylometry.cli delta --features 100,200,500 --metrics manhattan,cosine --repeats 2
|
||||
python -m stylometry.cli verify --preset stylometry_pos_lsa
|
||||
python -m stylometry.cli cluster --preset stylometry_pos_lsa
|
||||
Reference in New Issue
Block a user