# Yakut Language Normalizer

Профессиональный нормализатор текста для якутского языка с использованием методов машинного обучения.

## Описание

Проект предназначен для нормализации якутского текста. **Нормализация** — это преобразование текста БЕЗ якутских символов в текст С якутскими символами.

**Направление нормализации:**
- Вход: текст без якутских символов (например, "МАННАЙГЫ")
- Выход: текст с якутскими символами (например, "МАҤНАЙГЫ")

**Правила нормализации:**
- `h` → `һ` (всегда)
- `ь` -> `һ`(в некоторых позициях)
- `г` → `ҕ` (в некоторых позициях)
- `н` → `ҥ` (в некоторых позициях)
- `о` → `ө` (в некоторых позициях)
- `у` → `ү` (в некоторых позициях)

**Подготовка данных:** Для обучения модели мы берем нормализованные тексты (с якутскими символами) и создаем денормализованные варианты (без символов), формируя пары (денормализованный, нормализованный) для обучения.

## Структура проекта

```
yakut_normalizer/
├── src/
│   ├── __init__.py
│   ├── data/          # Скрипты для обработки данных
│   ├── features/      # Извлечение признаков
│   ├── models/         # Модели машинного обучения
│   └── utils/          # Вспомогательные утилиты
├── data/               # Исходные данные
├── tests/              # Тесты
├── pyproject.toml      # Конфигурация проекта
└── README.md
```

## Быстрый старт

### Требования
- Python 3.11+
- [uv](https://github.com/astral-sh/uv) (рекомендуется) или pip

### Установка с uv

```bash
# Если uv не установлен, установите его:
# Windows: powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex"
# Linux/Mac: curl -LsSf https://astral.sh/uv/install.sh | sh

# Установка зависимостей
uv sync

# Активация виртуального окружения
uv run python

# Или запуск скриптов напрямую
uv run python -m src.data.prepare_data --help
```

### Установка с pip

```bash
pip install -e .
pip install -e ".[dev]"
```

### Настройка pre-commit

```bash
# Установка pre-commit hooks
pre-commit install

# Ручной запуск проверок
pre-commit run --all-files
```

## Использование

### Нормализаторы

Проект предоставляет различные реализации нормализаторов через абстрактный интерфейс:

```python
from src.models.naive import NaiveNormalizer

# Создание нормализатора
normalizer = NaiveNormalizer()

# Нормализация текста
text = "МАННАЙГЫ БАhА"
normalized = normalizer.normalize(text)
# Результат: "МАҤНАЙГЫ БАҺА"

# Нормализатор можно вызывать как функцию
normalized = normalizer(text)
```

**Доступные нормализаторы:**
- `NaiveNormalizer` - наивная реализация, заменяет все вхождения символов без учета контекста
- `ContextAwareNormalizer` - контекстно-зависимый нормализатор, использующий n-граммы
- `Word2VecNormalizer` - нормализатор на основе word2vec embeddings (рекомендуется)

**Рекомендуемые параметры для Word2VecNormalizer:**
После оптимизации с помощью Optuna найдены оптимальные параметры (см. `best_params.txt`):
- Character Accuracy: **0.9715**
- Exact Match: **0.6177**
- Word Accuracy: **0.9209**

### Интерактивная нормализация

Для быстрой нормализации текста можно использовать интерактивный скрипт:

```bash
# Интерактивный режим
python scripts/normalize_interactive.py

# Нормализация текста из командной строки
python scripts/normalize_interactive.py --text "МАННАЙГЫ БАhА"

# Нормализация файла
python scripts/normalize_interactive.py --input input.txt --output output.txt
```

Подробнее см. [scripts/README.md](scripts/README.md).

### Подготовка данных

```python
from src.data.augmentation import (
    denormalize_text,
    generate_denormalized_variants,
    create_normalization_pairs,
)

# Денормализация текста (для подготовки данных)
normalized_text = "МАҤНАЙГЫ БАҺА"
denormalized = denormalize_text(normalized_text, replacement_prob=1.0, random_seed=42)
print(denormalized)  # "МАННАЙГЫ БАhА" или "МАННАЙГЫ БАьА"

# Генерация нескольких денормализованных вариантов
variants = generate_denormalized_variants(normalized_text, num_variants=3)
for variant in variants:
    print(variant)

# Создание пар для обучения: (денормализованный, нормализованный)
normalized_texts = ["МАҤНАЙГЫ БАҺА", "Күн киирдэ"]
pairs = create_normalization_pairs(normalized_texts, num_variants_per_text=2)
# Пары: [("МАННАЙГЫ БАhА", "МАҤНАЙГЫ БАҺА"), ...]
```

### Подготовка данных

Скрипт берет нормализованные тексты (с якутскими символами) и создает денормализованные варианты для обучения модели.

```bash
# Создать пары (денормализованный, нормализованный) из исходного файла
uv run python -m src.data.prepare_data \
    --input data/yakut_razdel.txt \
    --output data/augmented/yakut_pairs.txt \
    --num-variants 2 \
    --replacement-prob 1.0 \
    --random-seed 42

# Для тестирования на небольшом количестве строк
uv run python -m src.data.prepare_data \
    --input data/yakut_razdel.txt \
    --output data/augmented/yakut_pairs_test.txt \
    --max-lines 100 \
    --num-variants 1
```

**Формат выходного файла:** `denormalized<tab>normalized`
- Первая колонка: текст без якутских символов (input для модели)
- Вторая колонка: текст с якутскими символами (target для модели)

### Разделение на train и test

Данные разделяются на train и test с учетом доли специальных символов для сохранения похожего распределения:

```bash
# Разделить данные на train и test
python -m src.data.split_data \
    --input data/processed/yakut_pairs.txt \
    --train-output data/processed/train_pairs.txt \
    --test-output data/processed/test_pairs.txt \
    --test-ratio 0.2

# Сравнить метрики на train и test
python scripts/compare_train_test.py src.models.naive.NaiveNormalizer
```

Разделение использует стратификацию по доле специальных символов, чтобы обеспечить похожее распределение в train и test наборах.

### Валидация и сравнение подходов

Валидатор позволяет сравнивать разные подходы к нормализации с помощью различных метрик:

```python
from src.utils.validator import compare_normalizers, load_pairs

# Загрузка тестовых данных
test_pairs = load_pairs("data/processed/yakut_pairs.txt")

# Определение нормализаторов для сравнения
def my_normalizer(text: str) -> str:
    # Ваша реализация нормализации
    return normalized_text

normalizers = {
    "Baseline": lambda x: x,  # Identity
    "My Approach": my_normalizer,
}

# Сравнение подходов
results = compare_normalizers(normalizers, test_pairs[:100])
```

**Доступные метрики:**
- **Levenshtein Distance** - минимальное количество редактирований
- **Normalized Levenshtein** - нормализованное расстояние (0.0-1.0)
- **Character Accuracy** - точность на уровне символов
- **Word Accuracy** - точность на уровне слов
- **Character Error Rate (CER)** - частота ошибок на символах
- **Word Error Rate (WER)** - частота ошибок на словах
- **Exact Match** - процент полностью совпадающих текстов

**Командная строка:**
```bash
# Оценка одного нормализатора
uv run python -m src.utils.validator \
    --test-data data/processed/yakut_pairs.txt \
    --max-samples 100 \
    --verbose
```

**Скрипт для валидации нормализаторов:**
```bash
# Валидация нормализатора с параметрами
python scripts/validate_normalizer.py src.models.naive.NaiveNormalizer \
    --max-samples 100 \
    --compare-baseline

# С параметрами инициализации
python scripts/validate_normalizer.py src.models.naive.NaiveNormalizer \
    --params '{"param1": "value1"}' \
    --max-samples 50
```

## Разработка

### Pre-commit hooks

```bash
pre-commit install
pre-commit run --all-files
```

### Запуск тестов

```bash
pytest
```

### Оптимизация гиперпараметров

Для поиска оптимальных параметров нормализатора можно использовать Optuna:

```bash
# Базовая оптимизация (50 trials)
python scripts/optimize_hyperparameters.py

# С большим количеством trials
python scripts/optimize_hyperparameters.py --n-trials 100

# Сохранение study в файл для продолжения позже
python scripts/optimize_hyperparameters.py --storage sqlite:///study.db --n-trials 100
```

Подробнее см. [scripts/README.md](scripts/README.md).

## Лицензия

MIT
