docs: add README, architecture, glossary, requirements; update CLAUDE.md

Add four Russian-language project documents:
- README.md: user-facing guide (install, quick start, data prep, training,
  evaluation, limitations)
- docs/architecture.md v1.0: system architecture, data flow diagrams,
  module interfaces, 7 architectural decision records, extension points
- docs/glossary.md v1.0: musical, ML, and project-specific term definitions
- docs/requirements.md v1.0: functional/non-functional requirements,
  acceptance criteria, four use-case scenarios

Update CLAUDE.md with project name etymology (hamori / ハモリ) and rename
repo root reference from chord-gen to hamori. Refine chord_format_spec.md.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-05-19 11:00:21 +03:00
parent 9929209bcf
commit 75fa07bf6c
6 changed files with 2312 additions and 5 deletions
+915
View File
@@ -0,0 +1,915 @@
# Архитектура системы hamori
**Версия документа:** 1.0
**Дата:** 2026-05-19
Документ описывает архитектуру проекта _hamori_ — генератора гармонических
периодов: высокоуровневую структуру, потоки данных, состав модулей, ключевые
проектные решения и их обоснование, а также точки расширения.
---
## Содержание
1. [Высокоуровневая архитектура](#1-высокоуровневая-архитектура)
2. [Потоки данных](#2-потоки-данных)
3. [Состав модулей](#3-состав-модулей)
4. [Модель машинного обучения](#4-модель-машинного-обучения)
5. [Конвейер обучения](#5-конвейер-обучения)
6. [Конвейер инференса и оценки](#6-конвейер-инференса-и-оценки)
7. [Ключевые проектные решения](#7-ключевые-проектные-решения)
8. [Точки расширения](#8-точки-расширения)
---
## 1. Высокоуровневая архитектура
Система состоит из шести логических уровней.
**Уровень человекочитаемых данных.** Текстовые `.chord`-файлы лид-шит-нотации,
с которыми работает автор-композитор при ручной транскрипции. Каждый файл
описывает один гармонический период.
**Уровень парсинга и валидации.** Модули, преобразующие `.chord`-файлы в
структурированные внутренние представления и проверяющие их корректность.
**Уровень токенизации.** Модули, преобразующие структурированные представления
в последовательности целочисленных идентификаторов и обратно. Здесь же
выполняется нормализующая транспозиция в каноническую тональность.
**Уровень обучения и инференса.** Реализация нейросетевой модели, циклы
обучения и сэмплирования, работа с чекпоинтами.
**Уровень оценки.** Расчёт метрик, построение распределений, формирование
графических артефактов для отчёта.
**Уровень внешних адаптеров.** Конвертеры публичных корпусов в формат `.chord`,
экспорт периодов в MIDI.
Схема информационных связей между уровнями:
```
автор-композитор
.chord-файлы (raw_user)
│ публичный корпус
│ │
│ ▼
│ внешний конвертер
│ │
│ ▼
│ .chord-файлы (raw_external)
│ │
└────────┬────────┘
парсер + валидатор
транспозиция в C/Am
токенизатор
.pt-файлы (processed)
┌──────────────────┼──────────────────┐
▼ ▼ ▼
train выборка val выборка holdout выборка
│ │ │
└────────┬─────────┘ │
▼ │
трансформер: pre-train + fine-tune │
│ │
▼ │
чекпоинты ◄───────────────────────┘
│ │
┌────────────────┼────────────────────────────┘
▼ ▼
инференс оценка
│ │
▼ ▼
.chord + MIDI метрики + графики + образцы
│ │
▼ ▼
автор-композитор отчёт
```
---
## 2. Потоки данных
### 2.1 Поток подготовки собственного корпуса
```
DAW-проект (REAPER)
│ ручная транскрипция
.chord-файл в data/raw_user/
│ валидация формата
│ (опционально: MIDI-санитарная проверка)
.chord-файл прошёл проверку
│ скрипт prepare_data.py
│ ├── чтение
│ ├── транспозиция в C major / A minor
│ ├── токенизация
│ └── разбиение train / val
.pt-файлы в data/processed/user/{train,val}/
```
### 2.2 Поток подготовки публичного корпуса
```
McGill Billboard (Harte-аннотации)
│ скрипт mcgill_to_chord.py
│ ├── парсинг Harte-нотации
│ ├── разрезание на периоды по секциям
│ ├── определение тональности
│ └── конвертация в .chord
.chord-файлы в data/raw_external/mcgill_converted/
│ скрипт prepare_data.py
.pt-файлы в data/processed/mcgill/{train,val}/
```
### 2.3 Поток обучения
```
data/processed/mcgill/ ◄── предобучение
checkpoints/pretrained.pt
│ инициализация весов
data/processed/user/ ◄── дообучение
checkpoints/finetuned.pt
```
### 2.4 Поток инференса
```
пользовательские параметры (CLI)
│ построение prompt-токенов
│ ▼
prompt = <BOS> + метатокены + опциональный prefix
│ авторегрессионная генерация (top-p sampling)
последовательность токенов до <EOS>
│ детокенизация
ChordPeriod в канонической тональности C/Am
│ транспозиция в целевую тональность
ChordPeriod в требуемой тональности
│ сериализация + MIDI-экспорт
.chord и .mid файлы
```
### 2.5 Поток оценки
```
data/processed/holdout/
вычисление перплексии для base и target
извлечение признаков (типы аккордов, инверсии, интервалы корня)
построение гистограмм и таблиц
reports/figures/, reports/metrics.json
```
---
## 3. Состав модулей
### 3.1 `src/chord_parser.py`
**Назначение.** Разбор отдельных аккордовых символов в строго типизированное
представление.
**Публичный интерфейс.**
- `parse_chord_symbol(symbol: str) -> ChordTokens` — парсит строку вида `Am7`,
`Cmaj9`, `F/G`, `Bb7b9/D` в dataclass `ChordTokens(root, quality, extension, bass)`.
- `ChordParseError` — исключение, поднимаемое при невалидном символе.
**Ключевые соображения реализации.** Модуль не использует регулярные
выражения для парсинга качеств: вместо этого выполняется последовательное
распознавание из таблицы альтернативных написаний по принципу самого длинного
совпадения. Это упрощает добавление новых качеств в будущем и снижает риск
тонких ошибок с приоритетами совпадений.
Бемольные написания корня и баса нормализуются к диезной форме на этапе парсинга.
**Связи.** Используется модулем `tokenizer.py` для разбора аккордов внутри
периода. Не имеет зависимостей внутри проекта, кроме стандартной библиотеки
Python и опционально `music21` (как fallback для нетипичных написаний).
### 3.2 `src/tokenizer.py`
**Назначение.** Преобразование `.chord`-файлов в последовательности
целочисленных идентификаторов и обратно. Реализация словаря токенов.
Реализация нормализующей транспозиции.
**Публичный интерфейс.**
- Константа `VOCAB: list[str]` — словарь токенов в порядке, описанном в
спецификации формата (81 токен).
- Константа `TOKEN_TO_ID: dict[str, int]` — обратное отображение.
- Функция `parse_chord_file(path: Path) -> ChordPeriod` — парсинг
`.chord`-файла в структурированное представление.
- Функция `transpose_to_canonical(period: ChordPeriod) -> ChordPeriod`
транспозиция мажорных периодов в C major, минорных в A minor.
- Функция `tokenize_period(period: ChordPeriod) -> list[int]` — последовательно
выполняет транспозицию и преобразование в токены.
- Функция `detokenize_to_period(token_ids: list[int]) -> ChordPeriod`
обратная операция, возвращает период в канонической тональности.
- Функция `transpose_period(period, target_key) -> ChordPeriod`
транспозиция в произвольную целевую тональность (используется на этапе
инференса для возврата результата в требуемую тональность).
- Исключение `ChordFormatError` — для ошибок формата файла.
**Ключевые соображения реализации.** Словарь токенов является константой
модуля; его изменение приводит к несовместимости с ранее обученными моделями,
поэтому любые изменения должны сопровождаться инкрементом версии спецификации
формата и переобучением моделей.
Транспозиция реализуется через расчёт интервала в полутонах между исходным и
целевым тонами, после чего к каждому корневому тону и бассу применяется
циклический сдвиг по 12-тоновой системе. Качество и расширения аккорда при
транспозиции не меняются.
**Связи.** Используется всеми остальными модулями для входа и выхода из
токенизированного пространства. Зависит от `chord_parser.py`.
### 3.3 `src/midi_export.py`
**Назначение.** Преобразование `.chord`-файлов в стандартные MIDI-файлы для
прослушивания в DAW и для использования сгенерированных периодов в
композиторской работе.
**Публичный интерфейс.**
- `chord_file_to_midi(chord_path, midi_path, tempo=90)` — основная функция.
- `period_to_midi(period: ChordPeriod, midi_path, tempo=90)` — вариант,
принимающий уже распарсенный период.
**Ключевые соображения реализации.** MIDI-файл содержит два инструментальных
трека: трек аккордов и трек баса. Аккорды раскладываются в средней октаве
(C4–B5) тремя или четырьмя одновременными нотами, бас — в нижней октаве (C2–B2)
одной нотой. Длительности соответствуют длительностям удержания аккордов в
исходном `.chord`-файле.
Voicing внутри аккорда выполняется минимально — простое расположение нот в
тесном расположении от корня. Это не задача данного модуля и сознательно
оставлено простым.
**Связи.** Зависит от `tokenizer.py` (для парсинга `.chord`) и `pretty_midi`.
### 3.4 `src/dataset.py`
**Назначение.** Реализация PyTorch-датасета над предварительно
токенизированными `.pt`-файлами.
**Публичный интерфейс.**
- Класс `ChordDataset(torch.utils.data.Dataset)`.
- Конструктор принимает путь к директории с `.pt`-файлами и максимальную
длину последовательности.
- `__getitem__` возвращает тензор токенов, обрезанный или дополненный
паддингом до максимальной длины.
- Функция `make_dataloader(dataset, batch_size, shuffle) -> DataLoader`
удобная фабрика.
**Ключевые соображения реализации.** Все `.pt`-файлы загружаются в память при
создании датасета. Это допустимо при текущем размере данных (тысячи периодов
максимум) и существенно ускоряет обучение по сравнению с подгрузкой с диска.
Паддинг выполняется специальным токеном `<PAD>` с индексом 2 в словаре.
В функции потерь этот индекс игнорируется через параметр `ignore_index`.
### 3.5 `src/model.py`
**Назначение.** Определение нейросетевой архитектуры.
**Публичный интерфейс.**
- Класс `ChordTransformer(nn.Module)` с параметрами конструктора:
`vocab_size`, `d_model`, `n_layers`, `n_heads`, `d_ff`, `max_seq_len`,
`dropout`.
**Архитектурные детали.** Декодер-only трансформер с pre-normalization
(нормализация перед остаточной связью, а не после). Эмбеддинги токенов и
позиционные эмбеддинги — обучаемые. Веса входного эмбеддинга и финальной
проекции на словарь связаны (tied weights), что сокращает число параметров
и стабилизирует обучение на малых данных.
Каждый блок трансформера состоит из:
- LayerNorm
- Causal multi-head self-attention с маскированием будущих позиций
- Residual connection
- LayerNorm
- Feedforward с активацией GELU
- Residual connection
После последнего блока — финальная LayerNorm и линейная проекция на размер
словаря.
**Связи.** Используется в модулях обучения и инференса.
### 3.6 `src/train.py`
**Назначение.** Логика обучения, общая для предобучения и дообучения.
**Публичный интерфейс.**
- Функция `train_model(config: TrainConfig) -> Path` — основная точка
входа. Возвращает путь к лучшему чекпоинту.
- Dataclass `TrainConfig` с полями для всех гиперпараметров.
**Особенности.** Один общий цикл обучения параметризуется аргументом
`init_from`. Если этот аргумент задан, веса модели инициализируются из
указанного чекпоинта, иначе — случайно. Это позволяет использовать один и
тот же код для предобучения и дообучения, различающихся только параметрами
запуска (низкий learning rate, меньшее число эпох для дообучения).
Логирование: после каждой эпохи в stdout выводится строка с номером эпохи,
тренировочной потерей, валидационной потерей и валидационной перплексией.
Параллельно строка добавляется в CSV-лог. Лучший по валидационной потере
чекпоинт сохраняется отдельно.
Ранняя остановка: если валидационная потеря не улучшается на протяжении N
эпох (по умолчанию 5), обучение завершается досрочно.
### 3.7 `src/generate.py`
**Назначение.** Сэмплирование из обученной модели.
**Публичный интерфейс.**
- Функция `generate_period(model, mode, time, subdivision, style, function,
key, prefix=None, temperature=1.0, top_p=0.9, max_tokens=300, seed=None)
-> ChordPeriod`.
**Ключевые соображения реализации.** Авторегрессионная генерация выполняется
по одному токену за раз. Для каждого шага:
1. Прогон последовательности через модель, получение распределения над
следующим токеном.
2. Деление логитов на температуру.
3. Применение nucleus sampling: оставляем минимальный по числу элементов
набор кандидатов с накопленной вероятностью не менее top_p.
4. Маскирование грамматически невалидных кандидатов (например, токена
расширения сразу после токена удержания).
5. Сэмплирование из оставшегося распределения.
6. Останов при появлении `<EOS>` или при достижении лимита токенов.
После завершения генерации последовательность детокенизируется, получившийся
период транспонируется из канонической тональности в целевую и возвращается
вызывающему.
### 3.8 `src/evaluate.py`
**Назначение.** Расчёт метрик качества и построение распределений.
**Публичный интерфейс.**
- `compute_perplexity(model, dataloader) -> float`.
- `extract_features(period: ChordPeriod) -> dict` — извлекает гармонические
признаки периода: список типов качеств, доли инверсий, интервалы движения
корня, биграммы корней.
- `compare_distributions(baseline_features, target_features) -> dict` —
агрегирует признаки и формирует структуры для построения графиков.
- `plot_comparison(distributions, output_dir)` — рисует и сохраняет графики.
### 3.9 `src/external_converters/mcgill_to_chord.py`
**Назначение.** Конвертация аннотаций McGill Billboard Project в формат
`.chord`.
**Публичный интерфейс.**
- `convert_directory(input_dir, output_dir, log_path=None)` — конвертирует
все пьесы из исходной директории.
- `convert_song(song_dir, output_dir) -> list[Path]` — конвертирует одну
пьесу, возвращает список путей к созданным файлам периодов.
**Ключевые соображения реализации.** Harte-нотация McGill отличается от
формата проекта по ряду признаков: использует другие имена качеств, явно
указывает интервальный состав в скобках, имеет иную систему обозначения
длительностей. Конвертер реализует таблицу соответствий между Harte и форматом
проекта и приводит к ближайшему допустимому аккорду в случаях, когда точное
соответствие отсутствует.
Разрезание на периоды выполняется по разметке секций в исходных файлах
(`verse`, `chorus`, `bridge` и т.д.). Периоды длиной менее 4 или более 16
тактов пропускаются.
---
## 4. Модель машинного обучения
### 4.1 Выбор архитектуры
Архитектура декодер-only трансформера выбрана по следующим причинам.
**Соответствие задаче.** Гармоническая последовательность — это
последовательность дискретных символов с сильными локальными
зависимостями (соседние аккорды связаны функциональными отношениями) и
менее сильными глобальными зависимостями (начало и конец периода связаны
тонально). Self-attention отражает оба типа зависимостей естественным
образом.
**Совместимость со схемой предобучения + дообучения.** Архитектуры
семейства трансформеров — стандартный выбор для задач с малой целевой
выборкой и большим объёмом предобучающих данных.
**Простота реализации с нуля.** При выбранном масштабе модели (несколько
блоков, небольшая размерность) реализация умещается в нескольких сотнях
строк кода и не требует тяжёлых зависимостей.
Альтернатива в виде LSTM была рассмотрена и отвергнута на основании того,
что:
- При сопоставимом числе параметров трансформер обычно работает не хуже на
задачах с дискретными последовательностями.
- Параллелизация обучения трансформера эффективнее.
- Стандартное предобучение языковых моделей через next-token prediction
легче переносится на трансформер, чем на рекуррентные сети.
### 4.2 Параметры модели
Размер модели сознательно выбран небольшим — порядка одного-трёх миллионов
параметров. Это обусловлено объёмом обучающих данных: при тысячах примеров
крупная модель неизбежно переобучится, а компактная сохранит способность
к обобщению. Рекомендуемая конфигурация:
| Параметр | Значение |
| ---------------------------- | ----------- |
| Число слоёв | 3 |
| Размерность модели (d_model) | 192 |
| Число голов внимания | 6 |
| Размерность FFN | 768 |
| Длина контекста | 512 токенов |
| Размер словаря | 81 |
| Dropout | 0.1 |
При необходимости конфигурация может быть пересмотрена в сторону уменьшения
(если модель не сходится) или увеличения (если результаты явно недостаточны
и есть запас времени на эксперимент).
### 4.3 Функция потерь и оптимизация
Стандартная кросс-энтропия с игнорированием `<PAD>`-токена. Оптимизатор —
AdamW. Расписание learning rate — косинусное снижение с линейным разогревом
на 5% от общего числа шагов.
**Предобучение.** Стартовый learning rate 3·10⁻⁴, 50 эпох (с возможностью
ранней остановки).
**Дообучение.** Стартовый learning rate 1·10⁻⁵, 15 эпох с ранней остановкой.
Двухпорядковая разница в learning rate между предобучением и дообучением —
ключевой приём для предотвращения катастрофического забывания: на этапе
дообучения веса модели изменяются медленно, что сохраняет общие
гармонические закономерности, выученные на крупном корпусе.
### 4.4 Генерация
Используется nucleus sampling (top-p) с температурой 1.0 по умолчанию.
Параметры регулируются на этапе инференса.
Beam search отвергнут на основании опыта генеративных задач: он склонен
порождать монотонные, многократно повторяющиеся последовательности, что
особенно нежелательно в задаче создания творческих идей.
---
## 5. Конвейер обучения
### 5.1 Подготовка данных
```
сырьё (.chord)
парсинг и валидация
транспозиция в каноническую тональность
токенизация
случайное разбиение на train/val (90/10)
сохранение .pt-файлов
```
Разбиение train/val выполняется на уровне периодов, а не на уровне исходных
пьес. Для собственного корпуса это компромиссное решение: разбиение по
пьесам было бы методологически чище, но при 20–25 пьесах привело бы к
слишком высокой дисперсии валидационной потери. Holdout-выборка, в свою
очередь, специально формируется на уровне пьес, что обеспечивает честность
итоговой оценки.
### 5.2 Цикл предобучения
```
инициализация модели случайными весами
для каждой эпохи (1..50):
├── проход по train: forward, loss, backward, optimizer step
│ │
│ ▼
│ агрегация train_loss за эпоху
├── проход по val (без градиентов): forward, loss
│ │
│ ▼
│ агрегация val_loss и val_perplexity
├── запись строки в CSV-лог
├── если val_loss улучшилась — сохранение чекпоинта
└── если val_loss не улучшалась 5 эпох подряд — выход
```
### 5.3 Цикл дообучения
Идентичен циклу предобучения по структуре, отличается:
- Инициализация модели из чекпоинта предобучения.
- Меньший learning rate.
- Меньшее максимальное число эпох (15).
- Опционально: меньший patience для ранней остановки.
### 5.4 Контроль качества обучения
В процессе обучения отслеживаются следующие признаки нормального хода:
- Train loss монотонно снижается.
- Val loss снижается синхронно с train loss до точки, после которой
начинается расхождение (типичное переобучение). Лучший чекпоинт
сохраняется до точки расхождения.
- Val perplexity на сошедшейся модели находится в диапазоне 2–6 для нашей
задачи. Существенно меньшие значения указывают на ошибку (например,
пересечение train и val выборок). Существенно большие — на плохую
сходимость или несоответствие модели данным.
Если эти признаки нарушаются, необходимо в первую очередь проверить
корректность подготовленных данных: токенизацию случайных файлов руками,
отсутствие пересечений между выборками, баланс распределения метаданных.
---
## 6. Конвейер инференса и оценки
### 6.1 Инференс
Подробное описание процесса генерации приведено в разделе 3.7. Ключевые
особенности:
- Все параметры запроса передаются через CLI-аргументы.
- Случайное зерно фиксируется, что обеспечивает воспроизводимость отдельных
семплов.
- Невалидные грамматические последовательности маскируются на каждом шаге
сэмплирования.
- Результат сразу сохраняется в двух форматах: `.chord` (для возможного
редактирования или подачи модели как затравки в дальнейшем) и MIDI (для
прослушивания).
### 6.2 Количественная оценка
**Перплексия** на отложенной выборке рассчитывается как экспонента средней
кросс-энтропии. Сравнение перплексий базовой и целевой моделей на одной
выборке показывает, насколько сильно дообучение сместило распределение
вероятностей модели в сторону распределения собственного корпуса автора.
Снижение перплексии на отложенной выборке после дообучения является
основным численным индикатором успеха проекта. Ожидаемая величина снижения —
от 10% до 50% относительно базовой модели.
### 6.3 Качественная оценка через распределения
Качественная сторона эффекта дообучения оценивается через сравнение
гистограмм по следующим признакам.
**Типы качеств аккордов.** Распределение по 18 базовым качествам. На малых
данных авторский стиль часто проявляется в смещении этого распределения:
например, повышенная частота больших септаккордов и нонаккордов или,
напротив, преобладание простых трезвучий.
**Доля инверсий.** Процент аккордов с явно указанным басом, отличным от
корня. Этот признак особенно характерен для индивидуального стиля и для
конкретных жанров (J-Pop, например, активно использует слэш-аккорды).
**Интервалы движения корня.** Распределение интервалов между корнями
соседних аккордов в полутонах. Например, доминирование интервала –5
полутонов (квинтовый ход вниз) характерно для барочной и классической
гармонии; преобладание интервалов –2, +2 — для более поп-ориентированных
стилей.
**Биграммы корней.** Частоты пар «текущий корень → следующий корень». Эти
биграммы захватывают функциональные предпочтения автора: например,
характерные переходы IV → V или V → vi.
Графики строятся как наложение двух гистограмм (baseline-распределение и
target-распределение) на одной координатной плоскости. Визуальный сдвиг
target относительно baseline — прямое подтверждение того, что дообучение
сработало.
### 6.4 Качественная оценка через прослушивание
Для отчёта формируются три специально подобранные («cherry-picked») пары
сгенерированных образцов: для каждой из выбранных гармонических затравок —
по одному примеру от базовой и от дообученной модели с одним и тем же
случайным зерном. Эти примеры конвертируются в MIDI и прилагаются к отчёту
(в виде ссылок и описаний).
Слепой listening-тест с привлечением сторонних слушателей не проводится из
соображений ограничения по времени.
---
## 7. Ключевые проектные решения
В этом разделе фиксируются проектные решения, принятые на этапе
проектирования, и обоснования к ним. Решения изложены в виде записей в
стиле Architectural Decision Records.
### 7.1 ПР-01. Юнит обработки — гармонический период, а не пьеса целиком
**Контекст.** Изначально рассматривался вариант обучения модели на целых
пьесах. При объёме собственного корпуса 20–25 пьес и средней длине каждой
40–100 тактов это давало бы датасет из 20–25 длинных последовательностей —
крайне малый объём для генеративной модели.
**Решение.** Единицей обработки и генерации является гармонический период —
замкнутая фраза 4–16 тактов. Из одной пьесы извлекается 4–8 периодов.
**Последствия.**
- Эффективный объём датасета увеличивается в 4–8 раз.
- Проблема обработки модуляций между секциями исчезает: внутри периода
модуляций нет.
- Длина обучающей последовательности становится меньшей и более однородной
(50–250 токенов вместо 500–1500), что упрощает обучение.
- Юнит хорошо соответствует реальному композиторскому воркфлоу: помощник
выдаёт идеи периодами, а не целыми пьесами.
### 7.2 ПР-02. Нормализующая транспозиция в C major / A minor
**Контекст.** Если каждый период хранится в исходной тональности,
функционально эквивалентные последовательности в разных тональностях
становятся для модели разными последовательностями. Это резко увеличивает
эффективное разнообразие данных в 12 раз и затрудняет обобщение.
**Решение.** Перед токенизацией все периоды транспонируются: мажорные — в
C major, минорные — в A minor. Тональность в словарь модели не входит.
На инференсе результат транспонируется обратно в требуемую тональность
постпроцессингом.
**Последствия.**
- Эффективное увеличение датасета в 12 раз.
- Сокращение словаря на 24 токена.
- Цвет конкретной тональности (характерное звучание Fis-dur против C-dur)
теряется. Это исполнительское свойство, не функционально-гармоническое,
и для задачи генерации прогрессий не релевантно.
- Внутренние модуляции и тонизации записываются обычными функциональными
аккордами и обрабатываются единообразно.
### 7.3 ПР-03. Факторизованная токенизация аккордов
**Контекст.** Каждый аккорд можно представить либо одним атомарным токеном
(`Cmaj7`, `Am7`, `F/G` как отдельные элементы словаря), либо разложенным
на несколько токенов (корень, качество, расширение, бас).
**Решение.** Каждый аккорд представляется ровно четырьмя токенами:
`ROOT_x`, `QUAL_x`, `EXT_x`, `BASS_x`. Словарь содержит 81 токен против
нескольких сотен в случае атомарной токенизации.
**Последствия.**
- Существенно меньший словарь, легче обучаемый на малых данных.
- Модель видит общность между, например, всеми минорными септаккордами,
а не учит их как 12 несвязанных слов.
- Каждый аккорд занимает в последовательности четыре позиции вместо одной,
что увеличивает длину последовательности и нагрузку на attention. При
выбранной длине контекста 512 это не создаёт проблем.
- Появляется необходимость грамматического маскирования при генерации:
не любой токен может следовать за любым.
### 7.4 ПР-04. Двухстадийное обучение
**Контекст.** Прямое обучение модели на собственном корпусе автора
невозможно из-за крайне малого объёма данных.
**Решение.** Двухстадийная схема: предобучение на крупном публичном
корпусе (McGill Billboard Project) и последующее дообучение на собственном
корпусе с пониженным learning rate.
**Последствия.**
- Базовые гармонические закономерности (функциональная гармония,
стандартные каденции) выучиваются на этапе предобучения.
- Индивидуальный стиль автора подмешивается на этапе дообучения без
необходимости заново выучивать общие законы.
- Появляется естественная схема сравнения «до и после» дообучения для
отчёта.
- Существует риск катастрофического забывания на этапе дообучения, что
митигируется низким learning rate и небольшим числом эпох.
### 7.5 ПР-05. Минималистичная реализация без тяжёлых фреймворков
**Контекст.** Существует ряд готовых фреймворков для обучения трансформеров
(PyTorch Lightning, HuggingFace Trainer, fastai), которые скрывают
boilerplate кода тренировочного цикла.
**Решение.** Использовать чистый PyTorch с явным циклом обучения.
**Последствия.**
- Код полностью прозрачен и поддаётся пошаговой отладке, что важно для
учебного проекта.
- Снижается риск проблем с совместимостью версий и сложным поведением
фреймворков «из коробки».
- Объём кода тренировочного цикла остаётся небольшим (порядка двух сотен
строк).
- Теряется доступ к некоторым удобствам фреймворков (готовые callbacks,
логирование в TensorBoard и т.п.). Для масштабов проекта это
несущественно.
### 7.6 ПР-06. Ручная транскрипция собственного корпуса
**Контекст.** Альтернатива — автоматическое извлечение аккордов из аудио
с помощью библиотек вроде Chordino, librosa, или нейросетевых детекторов.
**Решение.** Транскрипция выполняется автором вручную, на основе
DAW-проектов с использованием абсолютного слуха.
**Последствия.**
- Качество транскрипции существенно выше автоматического: тонкие гармонические
решения, нестандартные расширения, точные инверсии — всё это передаётся
без потерь.
- Существенные временные затраты (10–15 часов). Это самая трудозатратная
часть проекта.
- Невозможность масштабирования на большой корпус. Для текущей задачи
(80–150 периодов) это приемлемо.
### 7.7 ПР-07. Английский язык в коде, русский — в документации и отчёте
**Контекст.** Учебное заведение требует оформления отчёта на русском
языке. С другой стороны, стандарты разработки и совместимость с
инструментами вроде Claude Code предполагают английский язык в коде.
**Решение.** Чёткое разделение по слоям:
- Код, идентификаторы, комментарии, сообщения логов, коммиты — английский.
- Документация (README, спецификация, требования, архитектура,
глоссарий) — русский.
- Итоговый отчёт — русский с оформлением по ГОСТу.
**Последствия.** Однозначность для всех участников разработки.
Двуязычность не создаёт неудобств, поскольку слои разделены.
---
## 8. Точки расширения
Перечисленные ниже направления развития проекта оставлены явно за рамками
текущей версии. Их реализация может рассматриваться в будущем.
### 8.1 Дообучение на корпусе японской поп-музыки
**Описание.** После защиты курсовой работы планируется собрать второй
авторский корпус — гармонические периоды из японских поп-песен (Royal Road
прогрессии, mu-аккорды, характерные секундовые надстройки, on-аккорды) — и
выполнить дополнительное дообучение модели на этом материале с тегом
`STYLE_jpop`.
**Что уже подготовлено для этого расширения.** В словаре токенов
зарезервирован токен `STYLE_jpop`. Формат `.chord` поддерживает любые
характерные для J-Pop приёмы (расширенные аккорды, инверсии, слэш-аккорды).
В шапке файла предусмотрено поле `style`.
**Что нужно дополнительно сделать.** Собрать и транскрибировать корпус
J-Pop периодов. Выполнить дообучение существующей модели на смешанном
корпусе (свой + J-Pop) или последовательное дообучение (свой → J-Pop).
Сравнить генерации с разными значениями стилевого conditioning.
### 8.2 Генерация мелодии
**Описание.** Расширение модели на генерацию монофонической мелодической
линии, привязанной к гармонической последовательности.
**Что нужно сделать.** Расширить формат `.chord` дополнительным полем для
мелодической линии (или ввести отдельный формат). Расширить словарь
токенов мелодическими токенами (вероятно, через раздельное представление
ступени, длительности, артикуляции). Архитектура модели может остаться
прежней.
**Сложность.** Существенная: задача мелодизации сложнее, чем гармонизации,
требует больше данных, имеет другие критерии оценки.
### 8.3 Voicing внутри аккорда
**Описание.** Автоматическое расположение нот внутри аккорда выше баса
с учётом голосоведения (минимизация суммарного движения голосов, запрет
параллельных квинт и октав, разрешение тяготеющих ступеней).
**Что нужно сделать.** Эта задача может быть решена rule-based методом без
машинного обучения. Простой алгоритм минимизации суммарного межаккордового
смещения голосов с дополнительными правилами укладывается в несколько
сотен строк кода.
**Сложность.** Низкая, выполнима за день-два после защиты курсовой.
### 8.4 Графический пользовательский интерфейс
**Описание.** Веб- или десктоп-приложение, позволяющее задавать параметры
генерации интерактивно, прослушивать результат прямо в браузере, сохранять
понравившиеся варианты.
**Что нужно сделать.** Любой современный веб-фреймворк (FastAPI на backend,
любой минимальный frontend) поверх существующего CLI. Воспроизведение
MIDI в браузере через `Tone.js` или подобные библиотеки.
**Сложность.** Невысокая по нынешним стандартам, но требует существенного
времени.
### 8.5 Интеграция с REAPER
**Описание.** Плагин или внешний инструмент, который при работе в REAPER
позволяет запрашивать генерацию следующего фрагмента прямо из проекта,
учитывая текущий гармонический контекст.
**Сложность.** REAPER предоставляет ReaScript для расширений на Lua и
Python. Реализация возможна, но требует погружения в API REAPER.
### 8.6 Обработка модуляций внутри периода
**Описание.** Текущая версия требует разрезания периодов по точке
модуляции. Альтернатива — введение inline-токена `MODULATE_<note>_<mode>`,
переключающего тонический центр в середине последовательности.
**Что нужно сделать.** Расширить словарь токенов на 24 модуляционных
токена. Дополнить парсер и токенизатор поддержкой inline-меток модуляции.
Накопить достаточное число обучающих примеров с модуляциями (что
проблематично при малом исходном корпусе).
**Сложность.** Средняя, основное ограничение — данные.
### 8.7 Поддержка большего числа альтераций в аккорде
**Описание.** Текущая версия поддерживает один слот расширения на аккорд.
Альтерированные доминанты с несколькими альтерациями одновременно
(`C7♯9♭13`) сворачиваются до одной альтерации.
**Что нужно сделать.** Перейти от единственного `EXT_x` токена к множеству
одновременных токенов расширений. Это требует пересмотра грамматики
последовательности и формата представления одного аккорда (теперь его
описание становится не четырёхтокеновым, а переменной длины).
**Сложность.** Средняя, в основном проектная — требуется аккуратное
обновление формата с инкрементом версии.
---
## 9. История изменений
- **1.0** (2026-05-19) — первоначальная редакция документа.