audio-cpp commited on
Commit
d81ea87
·
verified ·
1 Parent(s): 44e93ef

Add VocalRender VoxCPM2 finetune and conversion script

Browse files
.gitattributes CHANGED
@@ -249,3 +249,4 @@ Voxtral-Mini-4B-Realtime-2602-GGUF/voxtral-mini-4b-realtime-2602-q4_k.gguf filte
249
  Voxtral-Mini-4B-Realtime-2602-GGUF/voxtral-mini-4b-realtime-2602-q8_0.gguf filter=lfs diff=lfs merge=lfs -text
250
  Kokoro-82M-GGUF/kokoro-82m-multilingual-q8_0.gguf filter=lfs diff=lfs merge=lfs -text
251
  LatentSync-1.6-GGUF/latentsync-1.6-f32-f16.gguf filter=lfs diff=lfs merge=lfs -text
 
 
249
  Voxtral-Mini-4B-Realtime-2602-GGUF/voxtral-mini-4b-realtime-2602-q8_0.gguf filter=lfs diff=lfs merge=lfs -text
250
  Kokoro-82M-GGUF/kokoro-82m-multilingual-q8_0.gguf filter=lfs diff=lfs merge=lfs -text
251
  LatentSync-1.6-GGUF/latentsync-1.6-f32-f16.gguf filter=lfs diff=lfs merge=lfs -text
252
+ VoxCPM2-GGUF/finetune/vocalrender-pro-orig.gguf filter=lfs diff=lfs merge=lfs -text
VoxCPM2-GGUF/finetune/convert_vocalrender.py ADDED
@@ -0,0 +1,96 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """Convert a VocalRender checkpoint into a standalone VoxCPM2 GGUF.
3
+
4
+ Requires torch, safetensors, and an audiocpp_gguf executable built with VoxCPM2.
5
+ Example:
6
+ python convert_vocalrender.py --input ./VocalRender-Pro \
7
+ --output ./vocalrender-pro-orig.gguf --converter ./audiocpp_gguf
8
+ """
9
+
10
+ import argparse
11
+ import json
12
+ import os
13
+ from pathlib import Path
14
+ import shutil
15
+ import subprocess
16
+ import tempfile
17
+
18
+ import torch
19
+ from safetensors import safe_open
20
+ from safetensors.torch import save_file
21
+
22
+
23
+ def main():
24
+ parser = argparse.ArgumentParser(description=__doc__)
25
+ parser.add_argument("--input", type=Path, required=True, help="Source checkpoint directory.")
26
+ parser.add_argument("--output", type=Path, required=True, help="Output orig-precision GGUF.")
27
+ parser.add_argument("--converter", default="audiocpp_gguf", help="Converter executable path or PATH command.")
28
+ parser.add_argument("--model-spec", type=Path, help="Optional voxcpm2 spec; otherwise use the converter catalog.")
29
+ parser.add_argument("--overwrite", action="store_true")
30
+ args = parser.parse_args()
31
+ source = args.input.expanduser().resolve(strict=True)
32
+ output = args.output.expanduser().resolve()
33
+ converter = shutil.which(os.path.expanduser(args.converter))
34
+ if converter is None:
35
+ parser.error("Converter not found; provide --converter or add audiocpp_gguf to PATH.")
36
+ if output.exists() and not args.overwrite:
37
+ parser.error("Output already exists; use --overwrite to replace it.")
38
+ weights = source / "model.safetensors"
39
+ with safe_open(weights, framework="pt", device="cpu") as tensors:
40
+ shape = tensors.get_slice("base_lm.embed_tokens.weight").get_shape()
41
+ config = json.loads((source / "config.json").read_text(encoding="utf-8"))
42
+ tokenizer = json.loads((source / "tokenizer.json").read_text(encoding="utf-8"))
43
+ token_config = json.loads((source / "tokenizer_config.json").read_text(encoding="utf-8"))
44
+ ids = [*tokenizer["model"]["vocab"].values(), *[t["id"] for t in tokenizer["added_tokens"]]]
45
+ if len(shape) != 2 or shape[1] != config["lm_config"]["hidden_size"]:
46
+ raise ValueError("Embedding shape does not match the model configuration.")
47
+ if min(ids) < 0 or max(ids) >= shape[0]:
48
+ raise ValueError("Tokenizer IDs exceed the checkpoint embedding capacity.")
49
+ config["lm_config"]["vocab_size"] = shape[0]
50
+ tokenizer["model"]["merges"] = [
51
+ " ".join(merge) if isinstance(merge, list) else merge
52
+ for merge in tokenizer["model"]["merges"]
53
+ ]
54
+ token_config["added_tokens_decoder"] = {
55
+ str(t["id"]): {key: value for key, value in t.items() if key != "id"}
56
+ for t in tokenizer["added_tokens"]
57
+ }
58
+ special = {key: token_config[key] for key in ("bos_token", "eos_token", "unk_token")}
59
+ output.parent.mkdir(parents=True, exist_ok=True)
60
+ with tempfile.TemporaryDirectory(prefix="vocalrender-convert-", dir=output.parent) as temporary:
61
+ root = Path(temporary) / source.name
62
+ root.mkdir()
63
+ for name, data in (("config.json", config), ("tokenizer.json", tokenizer),
64
+ ("tokenizer_config.json", token_config), ("special_tokens_map.json", special)):
65
+ (root / name).write_text(json.dumps(data, ensure_ascii=False, indent=2) + "\n", encoding="utf-8")
66
+ checkpoint = torch.load(source / "audiovae.pth", map_location="cpu", weights_only=True)
67
+ state = checkpoint.get("state_dict", checkpoint)
68
+ tensors = {}
69
+ for name, tensor in state.items():
70
+ if not isinstance(name, str) or not isinstance(tensor, torch.Tensor):
71
+ raise ValueError("AudioVAE state_dict must contain named tensors only.")
72
+ tensors[name] = tensor.detach().cpu().contiguous()
73
+ if not tensors:
74
+ raise ValueError("AudioVAE checkpoint contains no tensors.")
75
+ vae = root / "audiovae.safetensors"
76
+ save_file(tensors, vae)
77
+ with safe_open(vae, framework="pt", device="cpu") as saved:
78
+ if set(saved.keys()) != set(tensors):
79
+ raise ValueError("AudioVAE tensor names changed during conversion.")
80
+ for name, expected in tensors.items():
81
+ actual = saved.get_tensor(name)
82
+ if actual.dtype != expected.dtype or actual.shape != expected.shape or not torch.equal(actual, expected):
83
+ raise ValueError(f"AudioVAE tensor changed during conversion: {name}")
84
+ command = [str(Path(converter).resolve()), "--family", "voxcpm2", "--type", "orig",
85
+ "--input", f"weights={weights}", "--input", f"audiovae_weights={vae}",
86
+ "--root", str(root), "--output", str(output), "--log"]
87
+ if args.model_spec:
88
+ command.extend(["--model-spec", str(args.model_spec.expanduser().resolve(strict=True))])
89
+ if args.overwrite:
90
+ command.append("--overwrite")
91
+ subprocess.run(command, check=True)
92
+ print(f"Converted: {output}")
93
+
94
+
95
+ if __name__ == "__main__":
96
+ main()
VoxCPM2-GGUF/finetune/vocalrender-pro-orig.gguf ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:1c9148d67d3df67a0b04a8dbf9201d61bb7cac346bbfc9650b42f9d387a3df05
3
+ size 9544158368