Mothersuperior commited on
Commit
65c5529
·
verified ·
1 Parent(s): f2278a2

Add safetensors releases (fp32 + bf16) with named LoRA tensors; scripts load either format

Browse files
README.md CHANGED
@@ -19,9 +19,18 @@ an artist, and generate new songs or covers.
19
  ## Files
20
  | file | what |
21
  |---|---|
22
- | `tokenizer_head_joint_v4.pt` | MERT-v2-FullSong layer-20 features (per-track instance-normalised, 25 Hz) → 32,768 YuE2 semantic codes. 8-layer transformer, d=512, 512-frame windows. Held-out exact match on YuE2's own songs: 16.1% top-1 (near-miss codes render almost identically; ear tests of NAR round-trips sit around 95%). |
23
- | `nar_lora_joint_v4.pt` | rank-32 LoRA on `nar_self_attn.{q,k,v,o}_proj` + `nar_mlp.{gate,up,down}_proj` (28 layers) + full `vae2llm`/`llm2vae`. Trained jointly with the head on real audio. |
24
- | `scripts/` | the training loop and inference scripts (below). |
 
 
 
 
 
 
 
 
 
25
 
26
  Trained on 4,765 YuE2 self-generated songs, then adapted to real audio. If your material sounds off, rerun `joint.py` on your own audio (step 3 below).
27
 
 
19
  ## Files
20
  | file | what |
21
  |---|---|
22
+ | `tokenizer_head_joint_v4.pt` / `.safetensors` / `.bf16.safetensors` | MERT-v2-FullSong layer-20 features (per-track instance-normalised, 25 Hz) → 32,768 YuE2 semantic codes. 8-layer transformer, d=512, 512-frame windows. Held-out exact match on YuE2's own songs: 16.1% top-1 (near-miss codes render almost identically; ear tests of NAR round-trips sit around 95%). |
23
+ | `nar_lora_joint_v4.pt` / `.safetensors` / `.bf16.safetensors` | rank-32 LoRA on `nar_self_attn.{q,k,v,o}_proj` + `nar_mlp.{gate,up,down}_proj` (28 layers) + full `vae2llm`/`llm2vae`. Trained jointly with the head on real audio. |
24
+ | `scripts/` | the training loop and inference scripts (below). `scripts/ckpt_io.py` loads either format. |
25
+
26
+ ### Safetensors layout
27
+ Same weights as the `.pt` files, bit-exact in fp32 (the `.bf16` variants are half the size; head top-1 agreement with fp32 is 98.6%). All scripts accept either
28
+ extension via `scripts/ckpt_io.load_ckpt(path)`, which returns the same dict the `.pt` files hold.
29
+
30
+ - **Head**: the plain `state_dict` of the 8-layer encoder (`inp.*`, `pos`, `enc.layers.{0..7}.*`, `norm.*`, `head.*`), 103 tensors.
31
+ - **NAR LoRA**: `layers.{0..27}.nar_self_attn.{q,k,v,o}_proj.lora_A` `[32, in]` and `.lora_B` `[out, 32]`, plus `layers.{i}.nar_mlp.{gate,up,down}_proj.lora_{A,B}`.
32
+ Apply as `W += lora_B @ lora_A` (scale 1.0, no alpha) to the matching `model.layers[i]` Linear of YuE2-3B. `vae2llm.{weight,bias}` and `llm2vae.{weight,bias}`
33
+ are **full replacement weights** for those two Linear layers, not LoRA deltas. Rank and the delta rule are also in the file metadata.
34
 
35
  Trained on 4,765 YuE2 self-generated songs, then adapted to real audio. If your material sounds off, rerun `joint.py` on your own audio (step 3 below).
36
 
nar_lora_joint_v4.bf16.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:e67ca682f1070d2abb45cf9ac90d542fe4ce5286b87aed97f02d9a4acd6b6f36
3
+ size 70301856
nar_lora_joint_v4.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:7c99b788ca1167a1532b35fff2085d8d14549c8eaa87f9eda7ce68c041841fcc
3
+ size 140560552
scripts/ar_generate.py CHANGED
@@ -1,5 +1,6 @@
1
  """Generate songs with AR LoRA + joint_v1 NAR LoRA through YuE2's own pipeline (cot=off).
2
  usage: ar_generate.py <ar_lora.pt> <nar_lora.pt> <out_tag> <style_from_track_name> <lyrics_file> <seed>"""
 
3
  import os, sys, glob, math, json, numpy as np, torch, torch.nn as nn
4
  os.environ.setdefault("HF_HOME","/workspace/hf")
5
  from yue2 import YuE2Pipeline
@@ -19,10 +20,10 @@ def merge(attn_name, mlp_name, tensors, scale=1.0):
19
  return n_merged
20
  with torch.no_grad():
21
  AR_SCALE=float(os.environ.get("AR_SCALE","1.0"))
22
- if AR_CK!="none": ar=torch.load(AR_CK,map_location=dev); print(f"merged AR linears (scale {AR_SCALE}):", merge("self_attn","mlp",ar["lora"],AR_SCALE), flush=True)
23
  else: print("AR: stock (no LoRA)", flush=True)
24
  if NAR_CK!="none":
25
- nar=torch.load(NAR_CK,map_location=dev); print("merged NAR linears:", merge("nar_self_attn","nar_mlp",nar["lora"]), flush=True)
26
  model.vae2llm.load_state_dict({k:v.to(torch.bfloat16) for k,v in nar["io"]["vae2llm"].items()}); model.llm2vae.load_state_dict({k:v.to(torch.bfloat16) for k,v in nar["io"]["llm2vae"].items()})
27
  else: print("NAR: stock (no LoRA)", flush=True)
28
  model.eval(); print("LoRAs loaded", flush=True)
 
1
  """Generate songs with AR LoRA + joint_v1 NAR LoRA through YuE2's own pipeline (cot=off).
2
  usage: ar_generate.py <ar_lora.pt> <nar_lora.pt> <out_tag> <style_from_track_name> <lyrics_file> <seed>"""
3
+ import sys, os as _os; sys.path.insert(0,_os.path.dirname(_os.path.abspath(__file__))); from ckpt_io import load_ckpt
4
  import os, sys, glob, math, json, numpy as np, torch, torch.nn as nn
5
  os.environ.setdefault("HF_HOME","/workspace/hf")
6
  from yue2 import YuE2Pipeline
 
20
  return n_merged
21
  with torch.no_grad():
22
  AR_SCALE=float(os.environ.get("AR_SCALE","1.0"))
23
+ if AR_CK!="none": ar=load_ckpt(AR_CK,dev); print(f"merged AR linears (scale {AR_SCALE}):", merge("self_attn","mlp",ar["lora"],AR_SCALE), flush=True)
24
  else: print("AR: stock (no LoRA)", flush=True)
25
  if NAR_CK!="none":
26
+ nar=load_ckpt(NAR_CK,dev); print("merged NAR linears:", merge("nar_self_attn","nar_mlp",nar["lora"]), flush=True)
27
  model.vae2llm.load_state_dict({k:v.to(torch.bfloat16) for k,v in nar["io"]["vae2llm"].items()}); model.llm2vae.load_state_dict({k:v.to(torch.bfloat16) for k,v in nar["io"]["llm2vae"].items()})
28
  else: print("NAR: stock (no LoRA)", flush=True)
29
  model.eval(); print("LoRAs loaded", flush=True)
scripts/ar_prep.py CHANGED
@@ -1,5 +1,6 @@
1
  """AR dataset in score-free (cot=off) format: prefix ids (instructions+tags+lyrics) + codec tokens.
2
  Artist tracks: tokens from the joint_v1 head. Minted: true semantic tokens. -> /workspace/real/ar/dataset.pt"""
 
3
  import os, glob, json, hashlib, numpy as np, torch, torch.nn as nn, sys
4
  os.environ.setdefault("HF_HOME","/workspace/hf")
5
  from yue2.protocol import SongRequest, token_prefixes
@@ -12,7 +13,7 @@ class Tok(nn.Module):
12
  super().__init__(); s.inp=nn.Linear(din,D); s.pos=nn.Parameter(torch.zeros(1,WIN,D))
13
  layer=nn.TransformerEncoderLayer(D,H,4*D,dropout=0.1,batch_first=True,norm_first=True,activation="gelu"); s.enc=nn.TransformerEncoder(layer,L); s.norm=nn.LayerNorm(D); s.head=nn.Linear(D,VOCAB)
14
  def forward(s,x): return s.head(s.norm(s.enc(s.inp(x)+s.pos[:,:x.shape[1]])))
15
- head=Tok(1024).to(dev).eval(); head.load_state_dict(torch.load(HEAD_CK,map_location=dev)["model"])
16
  def instnorm(x): x=x.astype(np.float32); return (x-x.mean(0))/(x.std(0)+1e-5)
17
  @torch.no_grad()
18
  def predict(x):
 
1
  """AR dataset in score-free (cot=off) format: prefix ids (instructions+tags+lyrics) + codec tokens.
2
  Artist tracks: tokens from the joint_v1 head. Minted: true semantic tokens. -> /workspace/real/ar/dataset.pt"""
3
+ import sys, os as _os; sys.path.insert(0,_os.path.dirname(_os.path.abspath(__file__))); from ckpt_io import load_ckpt
4
  import os, glob, json, hashlib, numpy as np, torch, torch.nn as nn, sys
5
  os.environ.setdefault("HF_HOME","/workspace/hf")
6
  from yue2.protocol import SongRequest, token_prefixes
 
13
  super().__init__(); s.inp=nn.Linear(din,D); s.pos=nn.Parameter(torch.zeros(1,WIN,D))
14
  layer=nn.TransformerEncoderLayer(D,H,4*D,dropout=0.1,batch_first=True,norm_first=True,activation="gelu"); s.enc=nn.TransformerEncoder(layer,L); s.norm=nn.LayerNorm(D); s.head=nn.Linear(D,VOCAB)
15
  def forward(s,x): return s.head(s.norm(s.enc(s.inp(x)+s.pos[:,:x.shape[1]])))
16
+ head=Tok(1024).to(dev).eval(); head.load_state_dict(load_ckpt(HEAD_CK,dev)["model"])
17
  def instnorm(x): x=x.astype(np.float32); return (x-x.mean(0))/(x.std(0)+1e-5)
18
  @torch.no_grad()
19
  def predict(x):
scripts/ckpt_io.py ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Load our checkpoints from either the original .pt files or the .safetensors releases, returning the same dict layout the scripts expect.
2
+ head : {"model": state_dict, "cfg": {...}}
3
+ lora : {"lora": [A0,B0,A1,B1,...] (layer-major: nar_self_attn q,k,v,o then nar_mlp gate,up,down), "io": {"vae2llm": sd, "llm2vae": sd}, "rank": int}"""
4
+ import torch
5
+ NAR_MODS=[("nar_self_attn",n) for n in ("q_proj","k_proj","v_proj","o_proj")]+[("nar_mlp",n) for n in ("gate_proj","up_proj","down_proj")]
6
+ def load_ckpt(path, map_location="cpu"):
7
+ if not str(path).endswith(".safetensors"): return torch.load(path, map_location=map_location, weights_only=False)
8
+ from safetensors.torch import load_file; from safetensors import safe_open
9
+ t=load_file(path, device=str(map_location))
10
+ with safe_open(path, "pt") as f: meta=f.metadata() or {}
11
+ if any(k.endswith(".lora_A") for k in t):
12
+ layers=sorted({int(k.split(".")[1]) for k in t if k.startswith("layers.")})
13
+ lora=[]
14
+ for L in layers:
15
+ for blk,proj in NAR_MODS: lora+=[t[f"layers.{L}.{blk}.{proj}.lora_A"], t[f"layers.{L}.{blk}.{proj}.lora_B"]]
16
+ io={m:{k.split(".",1)[1]:v for k,v in t.items() if k.startswith(m+".")} for m in ("vae2llm","llm2vae")}
17
+ return {"lora":lora, "io":io, "rank":int(meta.get("rank", lora[0].shape[0]))}
18
+ return {"model":t, "cfg":{"instnorm": meta.get("input","").find("instnorm=true")>=0}}
scripts/joint.py CHANGED
@@ -3,6 +3,7 @@ usage: joint.py <name> <steps> <train_head 0|1> <train_lora 0|1> <init_head.pt>
3
  Real window: MERT -> head -> straight-through tokens -> (LoRA'd) NAR flow loss on true VAE latents. Head also gets minted soft-CE each step;
4
  in LoRA mode 25% of flow windows are minted (true tokens). Eval = held-out real flow loss (fixed windows/t/noise), minted top-1, repeat rate.
5
  Ends by rendering the held-out track with the best head+NAR."""
 
6
  import os, sys, glob, json, math, time, random, hashlib, numpy as np, torch, torch.nn as nn, torch.nn.functional as F, soundfile as sf
7
  from torch.utils.checkpoint import checkpoint
8
  os.environ.setdefault("HF_HOME","/workspace/hf"); torch.backends.cuda.matmul.allow_tf32=True
@@ -27,7 +28,7 @@ for layer in bb.layers:
27
  for n in names: l=LoRALinear(getattr(mod,n),RANK); setattr(mod,n,l); lora_params+=[l.A,l.B]
28
  model.vae2llm.float(); model.llm2vae.float(); io_params=list(model.vae2llm.parameters())+list(model.llm2vae.parameters())
29
  def load_lora(path):
30
- ck=torch.load(path,map_location=dev)
31
  with torch.no_grad():
32
  for p,v in zip(lora_params,ck["lora"]): p.copy_(v.to(dev))
33
  model.vae2llm.load_state_dict({k:v.float() for k,v in ck["io"]["vae2llm"].items()}); model.llm2vae.load_state_dict({k:v.float() for k,v in ck["io"]["llm2vae"].items()})
@@ -39,7 +40,7 @@ class Tok(nn.Module):
39
  super().__init__(); s.inp=nn.Linear(din,D); s.pos=nn.Parameter(torch.zeros(1,WIN,D))
40
  layer=nn.TransformerEncoderLayer(D,H,4*D,dropout=0.1,batch_first=True,norm_first=True,activation="gelu"); s.enc=nn.TransformerEncoder(layer,L); s.norm=nn.LayerNorm(D); s.head=nn.Linear(D,VOCAB)
41
  def forward(s,x): return s.head(s.norm(s.enc(s.inp(x)+s.pos[:,:x.shape[1]])))
42
- head=Tok(1024).to(dev); head.load_state_dict(torch.load(INIT_HEAD,map_location=dev)["model"]); head.requires_grad_(bool(TRAIN_HEAD))
43
  groups=[]
44
  if TRAIN_HEAD: groups.append({"params":list(head.parameters()),"lr":LR_HEAD,"weight_decay":0.05})
45
  if TRAIN_LORA: groups+=[{"params":lora_params,"lr":LR_LORA,"weight_decay":0.0},{"params":io_params,"lr":LR_IO,"weight_decay":0.0}]
@@ -132,7 +133,7 @@ for st in range(1,STEPS+1):
132
  save_all("last")
133
  print(f"RESULT {NAME}: best real_nar {best:.4f} (start {e0:.4f})", flush=True)
134
  # ---- render held-out with best
135
- head.load_state_dict(torch.load(f"{OUT}/head_best.pt",map_location=dev)["model"]); head.eval()
136
  if TRAIN_LORA: load_lora(f"{OUT}/lora_best.pt")
137
  model.vae2llm.to(torch.bfloat16); model.llm2vae.to(torch.bfloat16)
138
  @torch.no_grad()
 
3
  Real window: MERT -> head -> straight-through tokens -> (LoRA'd) NAR flow loss on true VAE latents. Head also gets minted soft-CE each step;
4
  in LoRA mode 25% of flow windows are minted (true tokens). Eval = held-out real flow loss (fixed windows/t/noise), minted top-1, repeat rate.
5
  Ends by rendering the held-out track with the best head+NAR."""
6
+ import sys, os as _os; sys.path.insert(0,_os.path.dirname(_os.path.abspath(__file__))); from ckpt_io import load_ckpt
7
  import os, sys, glob, json, math, time, random, hashlib, numpy as np, torch, torch.nn as nn, torch.nn.functional as F, soundfile as sf
8
  from torch.utils.checkpoint import checkpoint
9
  os.environ.setdefault("HF_HOME","/workspace/hf"); torch.backends.cuda.matmul.allow_tf32=True
 
28
  for n in names: l=LoRALinear(getattr(mod,n),RANK); setattr(mod,n,l); lora_params+=[l.A,l.B]
29
  model.vae2llm.float(); model.llm2vae.float(); io_params=list(model.vae2llm.parameters())+list(model.llm2vae.parameters())
30
  def load_lora(path):
31
+ ck=load_ckpt(path,dev)
32
  with torch.no_grad():
33
  for p,v in zip(lora_params,ck["lora"]): p.copy_(v.to(dev))
34
  model.vae2llm.load_state_dict({k:v.float() for k,v in ck["io"]["vae2llm"].items()}); model.llm2vae.load_state_dict({k:v.float() for k,v in ck["io"]["llm2vae"].items()})
 
40
  super().__init__(); s.inp=nn.Linear(din,D); s.pos=nn.Parameter(torch.zeros(1,WIN,D))
41
  layer=nn.TransformerEncoderLayer(D,H,4*D,dropout=0.1,batch_first=True,norm_first=True,activation="gelu"); s.enc=nn.TransformerEncoder(layer,L); s.norm=nn.LayerNorm(D); s.head=nn.Linear(D,VOCAB)
42
  def forward(s,x): return s.head(s.norm(s.enc(s.inp(x)+s.pos[:,:x.shape[1]])))
43
+ head=Tok(1024).to(dev); head.load_state_dict(load_ckpt(INIT_HEAD,dev)["model"]); head.requires_grad_(bool(TRAIN_HEAD))
44
  groups=[]
45
  if TRAIN_HEAD: groups.append({"params":list(head.parameters()),"lr":LR_HEAD,"weight_decay":0.05})
46
  if TRAIN_LORA: groups+=[{"params":lora_params,"lr":LR_LORA,"weight_decay":0.0},{"params":io_params,"lr":LR_IO,"weight_decay":0.0}]
 
133
  save_all("last")
134
  print(f"RESULT {NAME}: best real_nar {best:.4f} (start {e0:.4f})", flush=True)
135
  # ---- render held-out with best
136
+ head.load_state_dict(load_ckpt(f"{OUT}/head_best.pt",dev)["model"]); head.eval()
137
  if TRAIN_LORA: load_lora(f"{OUT}/lora_best.pt")
138
  model.vae2llm.to(torch.bfloat16); model.llm2vae.to(torch.bfloat16)
139
  @torch.no_grad()
tokenizer_head_joint_v4.bf16.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:ce52f7e4b3e437f204bdac71db685d86f3912e4085659a711387de87cfeb5127
3
+ size 85644544
tokenizer_head_joint_v4.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:0117394bb7db3dc88e4cb62a5e6e909283e1042240a3678780dcf9396502327e
3
+ size 171278496