feat: remove BAR token; bump spec to v2.3; fix max_seq_len
Bar boundaries are now implicit — the detokenizer counts positions per bar using TIME × SUB, and the generator gates EOS to bar boundaries only. Removing the deterministic BAR token reduces vocab size from 85 to 84 and lets the model focus on meaningful predictions. - src/tokenizer.py: drop BAR from VOCAB (85→84); replace BAR-based detokenize_to_period with position-counting logic; add write_chord_file; fix _tokens_to_symbol for add9/m(add9) qualities - tests/test_tokenizer.py: update vocab-size assertions to 84, structural token test, remove bar-count test, add test_no_bar_token_in_vocab - docs/chord_format_spec.md: bump to v2.3; document BAR removal in §5.2, §5.3, §5.4, §5.5, §5.6, §6.2, and changelog - CLAUDE.md: remove stale BAR reference, update vocab size to 84 - scripts/pretrain.py: raise max_seq_len 256→320 to cover regenerated McGill data (mean=83, max=283 tokens with BAR-free tokenizer) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
+31
-27
@@ -1,6 +1,6 @@
|
||||
# Спецификация формата данных hamori
|
||||
|
||||
**Версия:** 2.2
|
||||
**Версия:** 2.3
|
||||
**Дата:** 2026-05-20
|
||||
|
||||
---
|
||||
@@ -18,7 +18,7 @@
|
||||
Формат двухуровневый:
|
||||
|
||||
- **Исходный (`.chord`)** — то, что пишется руками. Близок к лид-шиту, человекочитаем, легко правится в любом текстовом редакторе.
|
||||
- **Токенизированный** — то, что подаётся в модель. Факторизованное представление с фиксированным словарём (85 токенов).
|
||||
- **Токенизированный** — то, что подаётся в модель. Факторизованное представление с фиксированным словарём (84 токена).
|
||||
|
||||
Между уровнями стоит детерминированный парсер.
|
||||
|
||||
@@ -238,13 +238,14 @@ D/F# ← D, бас F♯
|
||||
- `EXT_none`, `EXT_9`, `EXT_b9`, `EXT_#9`, `EXT_11`, `EXT_#11`, `EXT_13`, `EXT_b13` — 8 токенов
|
||||
- `BASS_root`, `BASS_C`, `BASS_C#`, ..., `BASS_B` — 13 токенов
|
||||
|
||||
**Временные/структурные:**
|
||||
**Временные/структурные (2):**
|
||||
|
||||
- `HOLD` — позиция продолжает предыдущий аккорд
|
||||
- `NC` — пауза в гармонии
|
||||
- `BAR` — конец такта
|
||||
|
||||
**Итого:** 4 + 2 + 9 + 2 + 5 + 9 + 12 + 18 + 8 + 13 + 3 = **85 токенов**.
|
||||
Граница такта **не является токеном** — детокенизатор восстанавливает её по счётчику позиций на основе `TIME` и `SUB`.
|
||||
|
||||
**Итого:** 4 + 2 + 9 + 2 + 5 + 9 + 12 + 18 + 8 + 13 + 2 = **84 токена**.
|
||||
|
||||
### 5.3 Структура обучающей последовательности
|
||||
|
||||
@@ -256,19 +257,19 @@ ROOT_<x> QUAL_<x> EXT_<x> BASS_<x> ← новый аккорд = 4 ток
|
||||
HOLD ← удержание = 1 токен
|
||||
HOLD
|
||||
HOLD
|
||||
BAR
|
||||
|
||||
← граница такта не токенизируется
|
||||
ROOT_<x> QUAL_<x> EXT_<x> BASS_<x>
|
||||
HOLD
|
||||
ROOT_<x> QUAL_<x> EXT_<x> BASS_<x>
|
||||
HOLD
|
||||
BAR
|
||||
|
||||
...
|
||||
|
||||
<EOS>
|
||||
```
|
||||
|
||||
Детокенизатор считает позиции самостоятельно: как только накоплено `positions_per_bar(TIME, SUB)` позиций, текущий такт закрывается и открывается новый. `<EOS>` допускается только в начале такта (на нулевой позиции).
|
||||
|
||||
### 5.4 Алгоритм токенизации (источник → токены)
|
||||
|
||||
1. Прочитать шапку.
|
||||
@@ -281,14 +282,19 @@ BAR
|
||||
- Если `.`: выпустить `HOLD`.
|
||||
- Если `NC`: выпустить `NC`.
|
||||
- Если `?`: выпустить `<UNK>`.
|
||||
- После последней позиции — `BAR`.
|
||||
- Границу такта не токенизировать.
|
||||
6. Выпустить `<EOS>`.
|
||||
|
||||
### 5.5 Алгоритм детокенизации (токены → MIDI)
|
||||
### 5.5 Алгоритм детокенизации (токены → период)
|
||||
|
||||
1. Считать метатокены, восстановить параметры периода.
|
||||
2. Группировать аккордовые токены по 4 (root, quality, extension, bass).
|
||||
3. Развернуть в последовательность аккордов с длительностями, считая HOLD-ы как продолжение предыдущего.
|
||||
2. Вычислить `positions_per_bar` = число позиций на такт по `TIME` и `SUB`.
|
||||
3. Читать тело последовательности, поддерживая счётчик `pos_in_bar`:
|
||||
- `ROOT_*` + следующие 3 токена (QUAL, EXT, BASS) → новый аккорд, `pos_in_bar += 1`.
|
||||
- `HOLD` → удержать аккорд, `pos_in_bar += 1`.
|
||||
- `NC` → пауза, `pos_in_bar += 1`.
|
||||
- Когда `pos_in_bar == positions_per_bar` → закрыть текущий такт, сбросить `pos_in_bar = 0`.
|
||||
- `<EOS>` → завершить (только при `pos_in_bar == 0`).
|
||||
4. Транспонировать обратно из C/Am в целевую тональность (задаваемую пользователем на инференсе).
|
||||
5. Сгенерировать MIDI через `pretty_midi`: для каждого аккорда выложить ноты в один трек, бас — отдельной линией в нижней октаве.
|
||||
|
||||
@@ -297,10 +303,10 @@ BAR
|
||||
Период 8 тактов в 4/4 с subdivision=4, в среднем 2 смены аккорда на такт:
|
||||
|
||||
- Метатокены: 1 (BOS) + 5 = 6 токенов
|
||||
- На такт: 2 аккорда × 4 + 2 HOLD-а + 1 BAR = 11 токенов
|
||||
- 8 тактов: ~88 токенов
|
||||
- На такт: 2 аккорда × 4 + 2 HOLD-а = 10 токенов
|
||||
- 8 тактов: ~80 токенов
|
||||
- EOS: 1
|
||||
- **Итого: ~95 токенов на типичный период.**
|
||||
- **Итого: ~87 токенов на типичный период.**
|
||||
|
||||
Длинный период (16 тактов с частой сменой): редко превышает 250 токенов. Контекстное окно 512 токенов более чем достаточно.
|
||||
|
||||
@@ -349,46 +355,40 @@ BAR
|
||||
<BOS>
|
||||
MODE_major TIME_4/4 SUB_4 STYLE_H1K0 FUNC_chorus
|
||||
|
||||
ROOT_C QUAL_maj7 EXT_none BASS_root
|
||||
HOLD HOLD HOLD
|
||||
BAR
|
||||
ROOT_C QUAL_maj7 EXT_none BASS_root ← такт 1, поз. 0
|
||||
HOLD ← такт 1, поз. 1
|
||||
HOLD ← такт 1, поз. 2
|
||||
HOLD ← такт 1, поз. 3 (4/4 → новый такт)
|
||||
|
||||
ROOT_E QUAL_m7 EXT_none BASS_root
|
||||
HOLD HOLD HOLD
|
||||
BAR
|
||||
|
||||
ROOT_A QUAL_m7 EXT_none BASS_root
|
||||
HOLD HOLD HOLD
|
||||
BAR
|
||||
|
||||
ROOT_F QUAL_maj EXT_none BASS_root
|
||||
HOLD
|
||||
ROOT_G QUAL_maj EXT_none BASS_root
|
||||
HOLD
|
||||
BAR
|
||||
|
||||
ROOT_F QUAL_maj7 EXT_none BASS_root
|
||||
HOLD HOLD HOLD
|
||||
BAR
|
||||
|
||||
ROOT_C QUAL_maj EXT_none BASS_E
|
||||
HOLD HOLD HOLD
|
||||
BAR
|
||||
|
||||
ROOT_D QUAL_m7 EXT_none BASS_root
|
||||
HOLD
|
||||
ROOT_G QUAL_maj EXT_none BASS_root
|
||||
HOLD
|
||||
BAR
|
||||
|
||||
ROOT_C QUAL_maj EXT_none BASS_root
|
||||
HOLD HOLD HOLD
|
||||
BAR
|
||||
|
||||
<EOS>
|
||||
```
|
||||
|
||||
(Переносы строк здесь для читаемости; в реальности — один поток.)
|
||||
(Переносы строк здесь для читаемости; в реальности — один поток. Граница такта определяется счётчиком позиций — каждые 4 позиции при `TIME_4/4 SUB_4`.)
|
||||
|
||||
---
|
||||
|
||||
@@ -477,7 +477,11 @@ sea_glass-bridge.chord
|
||||
|
||||
## 11. История изменений
|
||||
|
||||
- **v2.2** (текущая)
|
||||
- **v2.3** (текущая)
|
||||
- Удалён токен `BAR`. Граница такта теперь восстанавливается детокенизатором по счётчику позиций (`TIME` × `SUB`). Генератор отслеживает `pos_in_bar` и разрешает `<EOS>` только на нулевой позиции.
|
||||
- Размер словаря: 85 → 84 токена.
|
||||
|
||||
- **v2.2**
|
||||
- Добавлены тактовые размеры `5/4`, `7/4`, `7/8`, `9/8` — в допустимые значения поля `time` и в словарь (`TIME_*`).
|
||||
- Размер словаря: 81 → 85 токенов.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user