tabcnn-GGUF / README.md
cstr's picture
Upload README.md with huggingface_hub
ce88315 verified
|
Raw
History Blame Contribute Delete
7.5 kB
---
license: cc-by-4.0
library_name: gguf
tags:
- guitar
- tablature
- music-information-retrieval
- audio-to-tab
- crispasr
- gguf
pipeline_tag: audio-classification
---
# TabCNN β€” guitar tablature emission scorer (GGUF)
GGUF conversion of **TabCNN** for [CrispASR](https://github.com/CrispStrobe/CrispASR)'s
`--tab` surface. 833,982 parameters.
Per frame, the model emits **six independent distributions over 21 fret classes**
β€” one per guitar string. It contains no decoder: no inter-string coupling, no
temporal model, no search.
> ⚠️ **These are emission SCORES, not a decided tablature.** Turning them into a
> playable fingering needs a constrained decoder (one note per string, fret
> range, capo, hand span). Taking the argmax of this grid ignores every
> playability constraint. CrispASR ships the scorer and leaves the decoder to
> the caller.
## Provenance and licence β€” verifiable, not asserted
**Source artifact** (fetch it and check the hashes yourself):
| | |
|---|---|
| file | `best_TabCNN_tablature_trancription_model` (sic β€” typo is upstream's) |
| direct URL | <https://zenodo.org/records/11406378/files/best_TabCNN_tablature_trancription_model?download=1> |
| record | <https://zenodo.org/records/11406378> |
| size | 3,345,122 bytes |
| md5 (upstream, from the Zenodo API) | `ce168b2cd426f81a2a78499214e40605` |
| sha256 (computed on the bytes converted) | `1470a308896629352a811082843eb708cbc2f1aa3092757340055ef76a53ed0c` |
Every GGUF here carries these as metadata (`general.source.url`,
`general.source.record_url`, `general.source.md5_upstream`,
`general.source.sha256`), so provenance travels with the artifact.
⚠️ **There is no Zenodo DOI for this record.** `10.5281/zenodo.11406378` looks
plausible and **404s** β€” it does not exist. The record's actual DOI is the arXiv
one, `10.48550/arXiv.2405.14679` (resolves). Cite what resolves.
**Licence evidence.** The Zenodo API reports, for record 11406378:
```json
"metadata": { "license": {"id": "cc-by-4.0"}, "access_right": "open" }
```
Zenodo has no per-file licensing (a file object exposes only `checksum`, `id`,
`key`, `links`, `size`), so the record licence governs every deposited file. And
the record description states explicitly that the weights are part of the
deposit:
> "The weights for the best performing model (TabCNN trained with
> \"GuitarProFX\") in the paper are also provided."
That sentence is also what confirms this is the **GuitarProFX-augmented**
variant rather than the baseline β€” the distinction that matters, since the
baseline collapses from tablature F1 0.748 to 0.447 on real electric guitar
while the augmented one recovers to 0.585 (DAFx-24).
**Required citation** (the record asks for this explicitly):
> Pedroza HE, Abreu W, Corey R, Roman IR. "Leveraging real electric guitar tones
> and effects to improve robustness in guitar tablature transcription modeling."
> In *27th International Conference on Digital Audio Effects (DAFx)*, 2024.
**Upstream chain:**
| | |
|---|---|
| **Model** | TabCNN β€” Wiggins & Kim, *Guitar Tablature Estimation with a Convolutional Neural Network*, ISMIR 2019 |
| **Reference implementation** | [`amt-tools`](https://github.com/cwitkowitz/amt-tools) (Cwitkowitz) β€” MIT |
| **Training corpus** | [GuitarSet](https://zenodo.org/records/3371780) β€” CC BY 4.0 |
## Files
| file | size | tablature F1 | vs f32 | notes |
|---|---|---|---|---|
| `tabcnn-f16.gguf` | 1.78 MB | 0.7732 | **0.0000** | **default** β€” lossless, 100 % argmax agreement |
| `tabcnn-q8_0.gguf` | 1.10 MB | 0.7749 | +0.0017 | `dense0` q8_0, `head` f32 |
| `tabcnn-q4_k.gguf` | 0.72 MB | 0.7749 | +0.0017 | `dense0` Q4_0, `head` f32 β€” smallest, no measurable loss |
| `tabcnn-f32.gguf` | 3.34 MB | 0.7732 | β€” | full precision, for parity work |
(F1 on EGSet12 track 01 against its JAMS ground truth; the +0.0017 is noise on a
single 10 s clip β€” read it as "no loss", not "better".)
### The output layer must stay at full precision
Only **two** tensors are quantizable at all: the conv stack is 3Γ—3, so `ne0=3`,
far below any block size, and the biases are 32–126 wide. `dense0.weight`
(761 k of 834 k params) is effectively the whole model.
Quantizing `dense0` costs nothing β€” but quantizing `head.weight` alongside it is
catastrophic:
| | head quantized | head preserved |
|---|---|---|
| q8_0 | 0.7676 (βˆ’0.0057) | 0.7749 |
| Q4_0 | **0.7153 (βˆ’0.0579)** | 0.7749 |
`head.weight` is 16 k params, 1.6 % of the file, and directly determines the
21-way per-string softmax. Preserving it makes Q4_0 as accurate as q8_0.
`crispasr-quantize` encodes this as an arch rule; anyone converting these
weights by another route should do the same.
⚠️ **`q4_k` here is really Q4_0.** No tensor has `ne0 % 256 == 0`
(`dense0.weight` is 5952; 5952 % 256 = 64), so k-quants cannot apply and the
quantizer falls back to Q4_0. The filename keeps the requested name; the content
is Q4_0.
## Front end β€” read it from the file, do not assume it
```
CQT: sr 22050, hop 512, 192 bins, 24 per octave, fmin C1 (32.70 Hz)
-> amplitude_to_db(ref = max of the WHOLE clip) -> [-80, 0]
-> /80 + 1 -> [0, 1]
-> 9-frame centred context window
```
⚠️ **`fmin` is C1, not the guitar's low E.** Assuming E2 is the obvious guess and
it is wrong β€” and every wrong value still *runs*, producing plausible tensors
that pass shape and cosine checks while the model emits garbage. Measured on
EGSet12 track 01: fmin C1 β†’ tablature **F1 0.771**, E1 β†’ 0.040, E2 at 44.1 kHz β†’
**0.001**. All of these constants are stored as GGUF metadata
(`tabcnn.sample_rate`, `tabcnn.fmin_hz`, …) precisely so a consumer never has to
guess.
⚠️ `ref = max of the whole clip` is a **per-clip normalisation**, so features
cannot be computed streaming or chunked without changing them. This model is
two-pass by construction.
## Usage
```bash
crispasr --tab -m tabcnn-f16.gguf -f guitar.wav
crispasr --tab -m tabcnn-f16.gguf -f guitar.wav --tab-format json
```
For real use, take the log-probabilities through the C ABI
(`crispasr_session_tab` β†’ `crispasr_session_tab_emissions`) and run your own
constrained decoder. `crispasr_session_tab_silent_class()` tells you which class
means "not played" β€” read it rather than assuming it is the highest index.
## Validation
`crispasr-diff tabcnn` against a reference dump from the `amt-tools` model, run
from the **waveform** (not replayed features, so the CQT is covered):
| stage | cos |
|---|---|
| `cqt_db` | 0.998925 |
| `conv0_relu` | 0.991401 |
| `conv1_relu` | 0.987366 |
| `conv2_relu` | 0.972109 |
| `pool` | 0.975496 |
| `dense0_relu` | 0.991824 |
| `logits` | 0.999661 |
End to end on EGSet12 track 01 against its JAMS ground truth:
| | P | R | F1 |
|---|---|---|---|
| torch reference | 0.7183 | 0.8315 | 0.7708 |
| CrispASR (this GGUF) | 0.7179 | 0.8378 | **0.7732** |
Ξ”F1 **+0.0024**, argmax agreement **98.57 %**. The residual difference is the
front end β€” CrispASR uses direct Brown-kernel CQT against librosa's recursive
downsampling β€” and it costs nothing on the task metric.
## Limitations
- Trained on solo guitar. Mixed music is out of domain.
- GuitarSet numbers overstate real-world performance; EGSet12 is the honest
reference point.
- The scorer is per-frame and per-string independent, so it can emit physically
impossible combinations. That is expected β€” the decoder is where playability
is enforced.