Instructions to use PearlLeeStudio/TheArtist-MusicTransformer-lora-classical with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- PEFT
How to use PearlLeeStudio/TheArtist-MusicTransformer-lora-classical with PEFT:
from peft import PeftModel from transformers import AutoModel base_model = AutoModel.from_pretrained("PearlLeeStudio/TheArtist-MusicTransformer-ft-pop80") model = PeftModel.from_pretrained(base_model, "PearlLeeStudio/TheArtist-MusicTransformer-lora-classical") - Notebooks
- Google Colab
- Kaggle
TheArtist Music Transformer — LoRA Adapter (Classical)
LoRA adapter that steers the F1 base toward classical chord progressions: Bach chorales. It is one of eleven per-genre adapters from the paper How Far Can Chord-Symbol Time-Series Adaptation Carry Genre Identity? (Lee, 2026), all trained on and loaded over the same F1 base. Use it to generate classical progressions or to compare genres over one base.
Paper · Code · Web app · Demo · All models
Accepted at ISMIR 2026 as a Late-Breaking Demo: Where Does Genre Stop Following the Chords? A Fixed-Frame Listening Probe of Bounded Chord-Symbol Genre Identity (TheArtist)
Model details
| Field | Value |
|---|---|
| Base model | PearlLeeStudio/TheArtist-MusicTransformer-ft-pop80 (F1). The released F1 is weight-identical to the Phase-0 pop baseline (a checkpoint-selection artifact). |
| Adapter | LoRA on the Q/K/V projections (w_q, w_k, w_v), r=64 (best of r ∈ {4, 8, 16, 32, 64}), α=128, dropout 0.05 |
| Trainable parameters | 1,940,480 (1,572,864 LoRA + 367,616 embedding and output), 7.5% of the 25,849,344 that full fine-tuning updates |
| Vocabulary | 359 tokens (351 base + 8 new genre tokens) |
| Files | adapter/ (LoRA weights, 6.0 MB) and embedding_extension.pt (the trained embedding and output matrices) |
Usage
Requires torch, huggingface_hub, peft, safetensors. Both repos bundle model.py and tokenizer.py, so nothing needs to be cloned from GitHub.
import sys
import torch
import torch.nn as nn
from huggingface_hub import snapshot_download
from peft import PeftModel
# 1. Download the base + LoRA repos. Both bundle model.py and tokenizer.py.
base_dir = snapshot_download(repo_id="PearlLeeStudio/TheArtist-MusicTransformer-ft-pop80")
lora_dir = snapshot_download(repo_id="PearlLeeStudio/TheArtist-MusicTransformer-lora-classical")
sys.path.insert(0, base_dir) # so the next two imports resolve
from model import MusicTransformer
from tokenizer import ChordTokenizer
# 2. Extended tokenizer (351 base + 8 new genre tokens = 359). The PAD id
# is unchanged across base and extended tokenizers.
tokenizer = ChordTokenizer(include_extra_genres=True)
# 3. Build the model at the BASE vocab size (351) so F1's state_dict loads
# cleanly; we grow the embedding rows immediately after. Passing the
# extended tokenizer's pad_id is safe because PAD is shared (see step 2).
BASE_VOCAB = 351
model = MusicTransformer(
vocab_size=BASE_VOCAB,
d_model=512, n_heads=8, d_ff=2048, n_layers=8,
max_seq_len=256, dropout=0.0, pad_id=tokenizer.pad_id,
)
ckpt = torch.load(f"{base_dir}/best.pt", map_location="cpu", weights_only=False)
model.load_state_dict(ckpt["model_state_dict"])
# 4. Grow token_emb + out_proj from 351 -> 359 (new rows init from
# [GENRE:none]), then overlay the LoRA's trained extension rows.
def _grow_to_extended_vocab(m, new_vocab, none_id):
d = m.token_emb.embedding_dim
new_emb = nn.Embedding(new_vocab, d, padding_idx=m.token_emb.padding_idx)
with torch.no_grad():
new_emb.weight[:m.token_emb.num_embeddings] = m.token_emb.weight
for i in range(m.token_emb.num_embeddings, new_vocab):
new_emb.weight[i] = m.token_emb.weight[none_id]
m.token_emb = new_emb
new_out = nn.Linear(d, new_vocab, bias=False)
with torch.no_grad():
new_out.weight[:m.out_proj.out_features] = m.out_proj.weight
for i in range(m.out_proj.out_features, new_vocab):
new_out.weight[i] = m.out_proj.weight[none_id]
m.out_proj = new_out
_grow_to_extended_vocab(model, tokenizer.vocab_size, tokenizer.encode_genre("none"))
ext = torch.load(f"{lora_dir}/embedding_extension.pt",
map_location="cpu", weights_only=False)
model.token_emb.load_state_dict(ext["token_emb_state"])
model.out_proj.load_state_dict(ext["out_proj_state"])
# 5. Apply the LoRA adapter (the adapter files live at lora_dir/adapter/).
model = PeftModel.from_pretrained(model, f"{lora_dir}/adapter")
model.eval()
# 6. Generate a classical continuation. With LoRA injected,
# PeftModel.forward routes through the adapted attention layers.
song = {
"key": "Cmaj", "time_signature": "4/4", "genre": "classical",
"bars": [["Cmaj7"], ["Fmaj7"]],
}
prompt_ids = tokenizer.encode_sequence(song)[:-1]
ids = torch.tensor([prompt_ids])
with torch.no_grad():
for _ in range(32):
logits = model(ids) # routed through LoRA via PeftModel
next_id = torch.multinomial(
torch.softmax(logits[:, -1, :] / 0.8, dim=-1), 1,
)
ids = torch.cat([ids, next_id], dim=-1)
if next_id.item() == tokenizer.eos_id:
break
print(tokenizer.decode(ids[0].tolist()))
Evaluation
All tables report teacher-forced next-token metrics. Top-1 counts every target, including the three header tokens that open each sequence (key, time signature, genre marker).
On this genre's val split (37 sequences, no key augmentation) both columns use the same dataloader and the same [GENRE:none]-initialised embedding extension, so only the adapter weights and the trained embedding rows differ.
| Metric | F1 base | F1 + this LoRA (r=64) | Δ |
|---|---|---|---|
| Top-1 accuracy (%) | 43.54 | 60.55 | +17.01 |
| Top-5 accuracy (%) | 72.82 | 86.09 | +13.27 |
| Cross-entropy loss | 2.8653 | 1.3071 | -1.5582 |
Rank sweep the released adapter was selected from (minimum val loss, top-1 tiebreak):
| Rank | val_loss | Top-1 (%) | Δ Top-1 vs F1 |
|---|---|---|---|
| r=4 | 1.3663 | 58.15 | +14.61 |
| r=8 | 1.3486 | 58.74 | +15.20 |
| r=16 | 1.3333 | 59.61 | +16.07 |
| r=32 | 1.3174 | 60.08 | +16.54 |
| r=64 | 1.3071 | 60.55 | +17.01 ← released |
Real-song check, mean over 10 held-out classical songs of 11–40 bars, this genre's slice of a 130-song eval set (10 per genre × 13 genres, seed 42) drawn only from the held-out val/test partitions. All 10 are named Bach chorales.
| Model | Top-1 (%) | Top-5 (%) | Loss |
|---|---|---|---|
| F1 base | 49.55 | 81.17 | 2.2389 |
| F1 + this LoRA | 61.88 | 88.87 | 1.2489 |
| Δ | +12.33 | +7.70 | -0.9900 |
Training data
371 sequences (237 songs) from Bach chorales curated from the public-domain music21 corpus, with a song-level 80/10/10 split (seed 42) and 12-key augmentation on train. The adapter trains on this subset alone, and the best checkpoint has the minimum val loss.
Limitations
- Chord symbols, automatic scores. Chord symbols only (no melody, rhythm, or audio), and every score here is automatic, not a listening result.
- Header tokens. Part of each Δ comes from header-token predictions: F1 does not predict the
[GENRE:classical]marker, and this adapter does. It also gets all 38 time-signature targets of the Bach-chorale test set right where F1 gets 18. - Bach chorales gain beyond the marker. F1 has never read
[GENRE:classical]as an input in training, but unlike in the ten Chordonomicon-derived genres the adapter adds more than a usable marker: scored without the header targets and with F1 given[GENRE:pop]instead, LoRA still leads by 9.6 pp, and by 10.2 pp on the real-song check (arXiv:2606.07334 v5). Bach chorale is also the only genre whose rank sweep shows a clear rank trend.
License
CC BY-NC 4.0 (matching Chordonomicon, the upstream training corpus). Research, paper replication, portfolio, and demo use are permitted; commercial use is not.
Citation
@misc{lee2026chordmix,
title = {Empirical Study of Pop and Jazz Mix Ratios for Genre-Adaptive Chord Generation},
author = {Lee, Jinju},
year = {2026},
eprint = {2605.04998},
archivePrefix = {arXiv}
}
@misc{lee2026chordtimeseries,
title = {How Far Can Chord-Symbol Time-Series Adaptation Carry Genre Identity?},
author = {Lee, Jinju},
year = {2026},
eprint = {2606.07334},
archivePrefix = {arXiv}
}
@inproceedings{lee2026wheregenre,
title = {Where Does Genre Stop Following the Chords? A Fixed-Frame Listening Probe of Bounded Chord-Symbol Genre Identity (TheArtist)},
author = {Lee, Jinju},
booktitle = {Late-Breaking/Demo Session of the 27th International Society for Music Information Retrieval Conference (ISMIR)},
address = {Abu Dhabi, United Arab Emirates},
year = {2026},
url = {https://ismir2026program.ismir.net/lbd_571.html#details}
}
- Downloads last month
- 44