Instructions to use Dirdx/YuE2-3B-MLX-with-Hum-encoder with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- MLX
How to use Dirdx/YuE2-3B-MLX-with-Hum-encoder with MLX:
# Download the model from the Hub pip install huggingface_hub[hf_xet] hf download Dirdx/YuE2-3B-MLX-with-Hum-encoder --local-dir YuE2-3B-MLX-with-Hum-encoder
- Notebooks
- Google Colab
- Kaggle
- Local Apps Settings
- LM Studio
- Atomic Chat
Download test_parity.py from Dirdx/YuE2-3B-MLX-with-Hum-encoder: direct link, hf CLI and curl.
- Browser
- Download file 4.98 kB
-
https://huggingface.co/Dirdx/YuE2-3B-MLX-with-Hum-encoder/resolve/main/test_parity.py
- Command line
-
hf download hf://Dirdx/YuE2-3B-MLX-with-Hum-encoder/test_parity.py
-
curl -L -o test_parity.py https://huggingface.co/Dirdx/YuE2-3B-MLX-with-Hum-encoder/resolve/main/test_parity.py
4.98 kB
| #!/usr/bin/env python3 | |
| """Check the MLX port against the torch reference (needs torch, transformers, the HF snapshots). | |
| python test_parity.py --mlx ~/Desktop/Yue2-3B-MLX | |
| Compares AR logits, NAR velocity and VAE decode on tiny inputs. Exits non-zero on mismatch. | |
| """ | |
| from __future__ import annotations | |
| import argparse | |
| from pathlib import Path | |
| import mlx.core as mx | |
| import numpy as np | |
| import torch | |
| from generate import CODEC_OFFSET, MUSIC_END, Tokenizer, token_prefix | |
| from yue2_model import KVCache, load_model | |
| from yue2_vae import load_vae | |
| LATENT_START, LATENT_END, LATENT_PAD = 184621, 184622, 184623 | |
| def snapshot(repo): | |
| from huggingface_hub import snapshot_download | |
| return Path(snapshot_download(repo, allow_patterns=["*.json", "*.py", "model.safetensors", "qwen.tiktoken"])) | |
| def rel_err(a, b): | |
| a, b = np.asarray(a, np.float32), np.asarray(b, np.float32) | |
| return float(np.abs(a - b).max() / (np.abs(b).max() + 1e-6)) | |
| def main(): | |
| parser = argparse.ArgumentParser() | |
| parser.add_argument("--mlx", type=Path, default=Path.home() / "Desktop" / "Yue2-3B-MLX") | |
| parser.add_argument("--ref-model", default="m-a-p/YuE2-3B") | |
| parser.add_argument("--ref-vae", default="m-a-p/YuE2-Vae") | |
| args = parser.parse_args() | |
| from transformers import AutoModel, AutoModelForCausalLM | |
| ref_dir = snapshot(args.ref_model) if not Path(args.ref_model).is_dir() else Path(args.ref_model) | |
| vae_dir = snapshot(args.ref_vae) if not Path(args.ref_vae).is_dir() else Path(args.ref_vae) | |
| tok = Tokenizer(args.mlx / "qwen.tiktoken") | |
| prefix = token_prefix(tok, "pop, warm vocal", "[Verse]\nHello world\n", "off") | |
| codec = [7, 300, 5000, 12345, 32000, 9, 9, 42] | |
| ar_tokens = prefix + [c + CODEC_OFFSET for c in codec] + [MUSIC_END] | |
| frames = len(codec) | |
| rng = np.random.default_rng(0) | |
| state = rng.standard_normal((frames, 64)).astype(np.float32) | |
| raw_t = 0.3 | |
| # ── torch reference ── | |
| ref = AutoModelForCausalLM.from_pretrained(ref_dir, trust_remote_code=True, torch_dtype=torch.bfloat16, | |
| local_files_only=True).eval() | |
| with torch.inference_mode(): | |
| ref_logits = ref(torch.tensor([ar_tokens]), use_cache=False).logits[0].float().numpy() | |
| nar = [LATENT_START] + [LATENT_PAD] * frames + [LATENT_END] | |
| tokens = torch.tensor([ar_tokens + nar]) | |
| ar_mask = torch.tensor([[True] * len(ar_tokens) + [False] * len(nar)]) | |
| nar_mask = ~ar_mask | |
| content = torch.tensor([[False] * (len(ar_tokens) + 1) + [True] * frames + [False]]) | |
| ref_v = ref.nar_velocity(tokens, ar_mask, nar_mask, content, torch.tensor(state), raw_t).float().numpy() | |
| del ref | |
| ref_vae = AutoModel.from_pretrained(vae_dir, trust_remote_code=True, decoder_only=True, local_files_only=True).eval() | |
| z = rng.standard_normal((1, 64, 40)).astype(np.float32) | |
| with torch.inference_mode(): | |
| ref_audio = ref_vae.decode(torch.tensor(z))[0].T.numpy() # [L,2] | |
| ref_tiled = ref_vae.decode_tiled(torch.tensor(z), core_frames=16, halo_frames=16)[0].T.numpy() | |
| del ref_vae | |
| # ── mlx ── | |
| model = load_model(args.mlx) | |
| caches = [KVCache() for _ in model.model.layers] | |
| mlx_logits = np.array(model.ar_step(mx.array([ar_tokens]), caches, all_positions=True)[0].astype(mx.float32)) | |
| # incremental decode must equal the prefill logits for the same positions | |
| caches2 = [KVCache() for _ in model.model.layers] | |
| model.ar_step(mx.array([ar_tokens[:-3]]), caches2) | |
| step_logits = [np.array(model.ar_step(mx.array([[t]]), caches2).astype(mx.float32))[0] for t in ar_tokens[-3:]] | |
| ar_cache = model.nar_prefill(ar_tokens) | |
| mlx_v = np.array(model.nar_velocity(mx.array(state).astype(mx.bfloat16), raw_t, ar_cache, len(ar_tokens)).astype(mx.float32)) | |
| vae = load_vae(args.mlx) | |
| zc = mx.array(z[0].T) # [T,64] | |
| mlx_audio = np.array(vae(zc[None])[0]) | |
| mlx_tiled = np.array(vae.decode_tiled(zc, core=16, halo=16)) | |
| checks = { | |
| "ar_logits_rel_err": rel_err(mlx_logits, ref_logits), | |
| "ar_argmax_agree": float((mlx_logits.argmax(-1) == ref_logits.argmax(-1)).mean()), | |
| "ar_incremental_rel_err": rel_err(np.stack(step_logits), mlx_logits[-3:]), | |
| "nar_velocity_rel_err": rel_err(mlx_v, ref_v), | |
| "vae_rel_err": rel_err(mlx_audio, ref_audio), | |
| "vae_tiled_rel_err": rel_err(mlx_tiled, ref_tiled), | |
| "vae_len_ok": float(mlx_audio.shape == ref_audio.shape and mlx_tiled.shape == ref_tiled.shape), | |
| } | |
| for k, v in checks.items(): | |
| print(f"{k:26s} {v:.5f}") | |
| ok = (checks["ar_logits_rel_err"] < 0.05 and checks["ar_argmax_agree"] > 0.9 | |
| and checks["ar_incremental_rel_err"] < 0.05 and checks["nar_velocity_rel_err"] < 0.05 | |
| and checks["vae_rel_err"] < 1e-3 and checks["vae_tiled_rel_err"] < 1e-3 and checks["vae_len_ok"]) | |
| print("PASS" if ok else "FAIL") | |
| raise SystemExit(0 if ok else 1) | |
| if __name__ == "__main__": | |
| main() | |