btc-chords-GGUF / README.md
cstr's picture
Upload README.md with huggingface_hub
aedf762 verified
|
Raw
History Blame Contribute Delete
6.81 kB
---
license: cc-by-nc-sa-4.0
library_name: crispasr
tags:
- chord-recognition
- music-information-retrieval
- audio
- gguf
- btc
- mir
base_model: jayg996/BTC-ISMIR19
pipeline_tag: audio-classification
---
# BTC chord recognition β€” GGUF
GGUF conversions of **BTC** (*A Bi-directional Transformer for Musical Chord
Recognition*, Park et al., ISMIR 2019) for use with
[CrispASR](https://github.com/CrispStrobe/CrispASR).
Audio in, a chord timeline out:
```
0.000 1.950 C
1.950 3.901 G
3.901 8.081 N
```
That is the `.lab` layout the standard chord datasets use, so the output drops
straight into `mir_eval`. `N` means "no chord".
---
## ⚠️ Licence β€” non-commercial weights
**These weights are CC-BY-NC-SA 4.0 and may NOT be used commercially.**
This matters because the surrounding software is not:
| | Licence |
|---|---|
| Upstream BTC code ([jayg996/BTC-ISMIR19](https://github.com/jayg996/BTC-ISMIR19)) | MIT |
| CrispASR | MIT |
| **These weights** | **CC-BY-NC-SA 4.0 β€” non-commercial** |
The checkpoints were trained on the Isophonics, Robbie Williams and UsPop2002
chord annotations, whose terms forbid commercial use. The MIT licence on the
code does not extend to them.
CrispASR enforces this: its model registry **refuses to download these files**
unless you explicitly accept the licence.
```bash
crispasr --chords -m auto --auto-download \
--accept-license cc-by-nc-sa-4.0 -f song.wav
```
If you are shipping a commercial product, you must train or supply your own
weights. The converter (`models/convert-btc-to-gguf.py`) works on any BTC
checkpoint.
---
## Files
| File | Size | Classes | Notes |
|---|---|---|---|
| `btc-chords-large-f16.gguf` | 5.6 MB | 170 | **default** |
| `btc-chords-large-q8_0.gguf` | 4.5 MB | 170 | smallest with no measurable loss |
| `btc-chords-large-f32.gguf` | 11.2 MB | 170 | reference precision |
| `btc-chords-f16.gguf` | 5.6 MB | 25 | maj/min + N |
| `btc-chords-q8_0.gguf` | 4.4 MB | 25 | |
| `btc-chords-f32.gguf` | 11.2 MB | 25 | reference precision |
**No q4_k is published, deliberately.** It builds and runs, but measured against
the torch reference on real music it costs real accuracy for very little space:
| | size | root | mirex | tetrads |
|---|---|---|---|---|
| f16 | 5.6 MB | 99.17 % | 98.81 % | 98.56 % |
| q8_0 | 4.5 MB | 99.17 % | 98.81 % | **98.52 %** |
| q4_k | 3.8 MB | 98.42 % | 97.34 % | **95.46 %** |
q8_0 is indistinguishable; q4_k gives up 3.1 points of tetrad accuracy to save
0.6 MB on an already-tiny model. Quantize from the **f16**, not the f32 β€” only
73 of 213 tensors are quantizable, so the rest inherit the source dtype and a
q8_0 built from f32 comes out at 7.5 MB, *larger* than the f16.
**Prefer the 170-class model.** It collapses to maj/min on demand with
`CRISPASR_BTC_MAJ_MIN=1`, whereas a 25-class model can never be expanded. The
two upstream checkpoints differ *only* in classifier width.
**f16 is the shipping default.** It passes the per-stage parity harness
identically to f32 (below), at half the size.
The 170-class vocabulary is `root * 14 + quality` for the 12 roots and 14
qualities (`min, maj, dim, aug, min6, maj6, min7, minmaj7, maj7, 7, dim7,
hdim7, sus2, sus4`), plus `X` (unknown) and `N` (no chord). Quality `maj`
renders as the bare root, so index 1 is `C`, not `C:maj`.
---
## Verification
Every file here was validated per-stage against the PyTorch reference, not just
spot-checked end-to-end.
| Check | Result |
|---|---|
| numpy spec vs PyTorch (170-class) | cos **1.00000004**, argmax agreement 1.0000 |
| numpy spec vs PyTorch (25-class) | cos **0.99999991**, argmax agreement 1.0000 |
| `crispasr-diff btc` β€” 170-class f32 | **13/13 stages**, cos 1.000000 |
| `crispasr-diff btc` β€” 170-class f16 | **13/13 stages**, cos 1.000000 |
| `crispasr-diff btc` β€” 25-class f32 | **13/13 stages**, cos 1.000000 |
| `crispasr-diff btc` β€” 25-class f16 | **13/13 stages**, cos 1.000000 |
| Chord agreement vs the torch reference, 257 s of real music | **98.6 – 99.2 %** (`mir_eval`) |
Stages compared: input projection + positional encoding, both attention
directions of layer 0, all 8 layer outputs, the final norm, and the logits.
Reproduce with:
```bash
python tools/btc_torch_parity.py model.gguf btc_model_large_voca.pt BTC-ISMIR19/ ref.gguf
crispasr-diff btc model.gguf ref.gguf audio.wav
```
End-to-end on the upstream test clip (4 min 17 s of real music), scored with
`mir_eval` against the PyTorch implementation's own output:
| metric | agreement |
|---|---|
| root | 99.17 % |
| majmin | 98.86 % |
| mirex | 98.81 % |
| thirds / triads | 98.88 % / 98.78 % |
| sevenths / tetrads | 98.70 % / 98.56 % |
**Known limitation:** the above measures fidelity **to the reference
implementation**, which is what a port owes. It is not an accuracy claim
against human chord annotations β€” the published MIREX figures are the model's
property, and were not re-measured here. On synthetic test tones the 25-class
model returns `N` where the 170-class model resolves chords; that is
out-of-distribution behaviour, not a conversion defect. Evaluate on your own
material before relying on it.
---
## Usage
```bash
# 170-class output (default)
crispasr --chords -m btc-chords-large-f16.gguf -f song.wav
# collapse to plain major/minor
CRISPASR_BTC_MAJ_MIN=1 crispasr --chords -m btc-chords-large-f16.gguf -f song.wav
# JSON, with per-span confidence
crispasr --chords --chords-format json -m btc-chords-large-f16.gguf -f song.wav
```
The backend is auto-detected from the GGUF (`general.architecture = btc`), and
audio is resampled internally to BTC's native 22.05 kHz mono.
Also available through the session C ABI (`crispasr_session_chords*`) and the
WASM binding (`sessionChords`).
---
## Model details
- 8 layers, hidden size 128, 4 heads, FFN width 128
- Blocks of exactly 108 frames β€” a fixed block, **not** a sliding window
- Each layer runs two attention blocks over the same input: one causal, one
with the mask transposed; outputs concatenated and projected 256 β†’ 128
- Front end: constant-Q transform, 144 bins, 24 per octave, log magnitude,
normalised by mean/std stored in the GGUF
- 213 tensors converted; 8 upstream tensors (`output_layer.lstm.*`) are dead
weight and are skipped
Conversion and runtime details, including the ten implementation details that
are not the obvious default, are documented in CrispASR under
`docs/music-transcription/`.
## Citation
```bibtex
@inproceedings{park2019bi,
title={A Bi-Directional Transformer for Musical Chord Recognition},
author={Park, Jonggwon and Choi, Kyoyun and Jeon, Sungwook and Kim, Dokyun and Park, Jonghun},
booktitle={Proceedings of the 20th International Society for Music Information Retrieval Conference (ISMIR)},
year={2019}
}
```