# Parametres Papermill - JAMAIS modifier ce commentaire
# Configuration notebook
notebook_mode = "batch" # "batch" | "interactive"
seed = 42 # meme seed pour toute la comparaison meme-prompt
cot_mode = "full" # "full" (melodie+accords) | "melody" (covers) | "off"
cfg_scale = 1.2 # guidance texte (valeur par defaut de la carte)
yue2_model_id = "m-a-p/YuE2-3B"
yue2_vae = "default" # "default" (perceptuel) | "legacy" (musicalite benchmark)
yue2_wheel = "yue2_infer-0.1.5-py3-none-any.whl"
output_dir = "output/yue2-songs"
skip_widgets = TrueGeneration de Chansons Completes : YuE2
| Module | 02-Audio-Advanced — Generation de chansons (lyrics + style -> chanson complete) |
| Modeles | YuE2-3B (AR-NAR Mixture-of-Transformers, 48 kHz stereo) ; comparaisons MusicGen et YuE-v1 |
| Kernel | Python 3 (python3) |
| GPU requis | 24 GB VRAM en BF16 sous Linux (pic mesure ~11.2 GiB sur RTX 4090) — ce notebook documente le routage |
Licence des poids : CC BY-NC 4.0. Les poids YuE2-3B sont diffuse sous licence Creative Commons Attribution-NonCommercial 4.0 : l’usage commercial est interdit. Le code d’inference releve des avis tiers du depot (
THIRD_PARTY_NOTICES.md). Cette contrainte s’applique a toute utilisation des artefacts produits ici.
Objectifs pedagogiques :
- Distinguer text-to-song (paroles structurees + style -> chanson avec chant) du text-to-music (description instrumentale -> musique sans parole).
- Comprendre l’architecture YuE2 : un unique backbone AR-NAR Mixture-of-Transformers qui ecrit partition symbolique et tokens semantiques, puis un flow-matching acoustique et un VAE 48 kHz stereo.
- Executer les 4 etapes canoniques du pipeline :
plan()->generate_semantic()->synthesize()->decode(). - Pratiquer les trois usages marques de YuE2 : generation zero-shot, cover zero-shot (
cot="melody"), edition agentique d’une partition ABC. - Comparer honnetement, sur meme prompt et meme seed, YuE2-3B, MusicGen-medium et l’etat de l’art documente YuE-v1.
# Parameters
BATCH_MODE = "true"Les parametres Papermill configurent le modele (yue2_model_id), la graine (seed, fixee a 42 pour que la comparaison same-prompt same-seed de la Section 8 soit reproductible), le mode chain-of-thought (cot_mode) et la force de guidance (cfg_scale). Le output_dir concentre les artefacts generes (audio FLAC, partition ABC, tokens, latents) — ils ne sont pas commits dans le depot.
# Setup environnement et imports
import os
import sys
import time
import gc
import json
import shutil
import subprocess
from pathlib import Path
os.environ.setdefault("HF_HUB_DISABLE_SYMLINKS_WARNING", "1")
import platform
print("IMPORTS ET ENVIRONNEMENT")
print("=" * 45)
print(f"Python {sys.version.split()[0]}")
print(f"Plateforme {platform.system()} {platform.release()}")
print(f"Repertoire {Path.cwd().name}")
# Repertoires de sortie (artefacts non commites)
Path(output_dir).mkdir(parents=True, exist_ok=True)
print(f"Sorties {output_dir}/ pret")IMPORTS ET ENVIRONNEMENT
=============================================
Python 3.10.12
Plateforme Linux 6.18.33.2-microsoft-standard-WSL2
Repertoire 02-Advanced
Sorties output/yue2-songs/ pret
L’environnement Python est pret et le repertoire d’artefacts est cree. Les sorties (audio, ABC, tokens) vivent sous output/yue2-songs/ et sont exclues du depot (regenerables par re-execution).
# Chargement robuste de la configuration .env
try:
from dotenv import load_dotenv
trouv = load_dotenv()
print(f".env charge : {'oui' if trouv else 'aucun fichier .env (defauts utilises)'}")
except ImportError:
print(".env ignore : python-dotenv absent de ce kernel")
cuda_dispo = False
try:
import torch
cuda_dispo = torch.cuda.is_available()
if cuda_dispo:
props = torch.cuda.get_device_properties(0)
vram_gb = props.total_memory / 1024**3
print(f"GPU {props.name} — {vram_gb:.1f} GB VRAM")
else:
print("GPU absent (CPU seul)")
except ImportError:
print("torch absent de ce kernel").env charge : aucun fichier .env (defauts utilises)
GPU NVIDIA GeForce RTX 3090 — 24.0 GB VRAM
Dependances GPU et prerequis YuE2
YuE2-3B s’execute via le wheel d’inference officiel yue2_infer distribue dans le depot HuggingFace du modele (pas sur PyPI). La carte du modele exige :
| Prerequis | Exigence | Pourquoi |
|---|---|---|
| GPU | 24 GB VRAM, calcul BF16 | backbone 3.59 B + VAE 48 kHz en FP32 + encodeur de texte (pic mesure ~11.2 GiB sur RTX 4090, marge de surete 24 GB) |
| OS | Linux | noyau d’inference compile pour Linux (CUDA graphs, FlashAttention) |
| Python | 3.10+ | wheel py3-none-any mais dependances GPU Linux |
| RAM | ~24 GB host | chargement des poids avant placement GPU |
Sur une machine Windows + 8 GB VRAM (RTX 3070), la generation ne peut pas s’executer : le verdict est RECOVERABLE-MACHINE — le notebook est concu pour s’executer entierement sur une machine cible (eGPU RTX 3090 24 GB sous Linux), et les cellules ci-dessous documentent honnetement l’etat local au lieu de le masquer.
Section 1 : Text-to-Song vs Text-to-Music
La distinction structurante de la serie Audio :
- Text-to-music (MusicGen, 02-3) : une description instrumentale produit de la musique sans parole. L’entree est libre (“lo-fi hip hop avec un piano pluie”).
- Text-to-song (YuE2, ce notebook) : des paroles structurees (versets, refreins, balises de genre) produisent une chanson avec chant, melodie et arrangement. L’entree contraint la forme : chaque bloc de paroles correspond a un segment chante.
Le text-to-song est le probleme le plus complet : il cumule la comprehension du langage (paroles), la composition (melodie, harmonie, structure) et la synthese audio pleine bande (voix + instruments, 48 kHz stereo). YuE2 l’attaque en decomposant le probleme — partition symbolique d’abord, rendu acoustique ensuite — la ou une generation purement acoustique devrait tout apprendre d’un bloc.
Section 2 : Architectures — de YuE-v1 a YuE2
YuE-v1 (2025) : deux stages autoregressifs separes
| Stage | Modele | Role |
|---|---|---|
| 1 | AR 7B | paroles + accord -> tokens semantiques (structure musicale) |
| 2 | NAR 7B | tokens semantiques -> tokens acoustiques (rendu) |
Deux modeles de 7 B enchaines : 14 B de poids a charger, et une frontiere etanche entre le symbolique et l’acoustique.
YuE2 (2026) : un backbone unique AR-NAR Mixture-of-Transformers
| Composant | Role |
|---|---|
| Backbone AR-NAR MoT 3.59 B | ecrit la partition ABC ET les tokens semantiques dans un meme passe autoregressif puis non-autoregressif |
| Modele de plan (chain-of-thought) | cot="full" planifie melodie + accords avant la generation ; cot="melody" planifie la melodie seule (covers) ; cot="off" saute le plan |
| Flow-matching acoustique | tokens semantiques -> latents acoustiques |
| VAE 48 kHz stereo | latents -> audio ; deux variantes : YuE2-Vae (qualite perceptive, defaut) et YuE2-Vae-legacy (musicalite superieure sur benchmarks) |
Le gain structurel : la partition reste un artefact de premiere classe. Ou YuE-v1 produisait d’abord des tokens opaques, YuE2 ecrit une vraie partition ABC editable — c’est ce qui rend possibles le cover zero-shot et l’edition agentique des Sections 6-7.
Le schema ci-dessous rend le pipeline YuE2 et ses 4 etapes canoniques — c’est la decomposition que la Section 5 execute pas a pas sur le meme prompt :
flowchart LR
A["paroles + style"] --> P["plan()
partition ABC planifiee
(melodie, accords)"]
P --> G["generate_semantic(plan)
tokens semantiques AR-NAR"]
G --> S["synthesize(semantic)
flow-matching -> latents"]
S --> D["decode(latents)
VAE 48 kHz stereo"]
D --> O["chanson.flac
+ artefacts ABC"]
Les trois usages marques se branchent sur ce meme pipeline : le cover remplace le plan par une melodie transcrite (cot='melody'), l’edition agentique edite l’ABC renvoye par plan() puis regenere par pipe(abc=...).
Section 3 : Installation du wheel d’inference
Le code d’inference YuE2 ne passe pas par PyPI : le wheel yue2_infer-0.1.5 est publie dans le depot du modele. L’installation canonique (carte du modele) :
python -m pip install huggingface-hub==0.36.2
hf download m-a-p/YuE2-3B yue2_infer-0.1.5-py3-none-any.whl --local-dir .
python -m pip install ./yue2_infer-0.1.5-py3-none-any.whlpuis :
from yue2 import YuE2Pipeline
pipe = YuE2Pipeline.from_pretrained("m-a-p/YuE2-3B", device="cuda")La cellule suivante verifie ces prerequis sur la machine courante et en deduit un verdict d’executabilite honnete (regle F : un environnement manquant se repare ou se route, il ne se contourne pas).
# Verification des prerequis et verdict d'executabilite
print("VERIFICATION DES PREREQUIS YUE2")
print("=" * 45)
yue2_importable = False
try:
import yue2 # noqa: F401
from yue2 import YuE2Pipeline
yue2_importable = True
print("wheel yue2 present")
except ImportError:
print("wheel yue2 ABSENT (installation Section 3 non faite dans ce kernel)")
os_ok = platform.system() == "Linux"
print(f"OS Linux requis {'oui' if os_ok else 'NON (' + platform.system() + ')'}")
vram_ok = False
if cuda_dispo:
try:
import torch
vram_ok = torch.cuda.get_device_properties(0).total_memory >= 23 * 1024**3
except Exception:
vram_ok = False
print(f"VRAM >= 24 GB {'oui' if vram_ok else 'NON'}")
else:
print("VRAM >= 24 GB NON (pas de CUDA)")
YUE2_DISPONIBLE = yue2_importable and os_ok and vram_ok
print()
print("VERDICT D'EXECUTABILITE")
print("-" * 45)
if YUE2_DISPONIBLE:
print("EXECUTION LOCALE REELLE : tous les prerequis sont reunis.")
print("Les cellules de generation produisent des artefacts reels.")
else:
manquants = []
if not yue2_importable:
manquants.append("wheel yue2_infer")
if not os_ok:
manquants.append("OS Linux")
if not vram_ok:
manquants.append("GPU 24 GB BF16")
print("RECOVERABLE-MACHINE : la generation YuE2 ne peut pas s'executer ici.")
print(f"Manquants : {', '.join(manquants)}")
print("Routage : eGPU RTX 3090 24 GB sous Linux (machine GenAI du cluster).")
print("Les cellules suivantes documentent l'appel canonique et le routage ;")
print("les artefacts reels proviennent de la machine cible (verdict par axe")
print("en conclusion). Comparaison same-prompt same-seed : Section 8.")VERIFICATION DES PREREQUIS YUE2
=============================================
wheel yue2 present
OS Linux requis oui
VRAM >= 24 GB oui
VERDICT D'EXECUTABILITE
---------------------------------------------
EXECUTION LOCALE REELLE : tous les prerequis sont reunis.
Les cellules de generation produisent des artefacts reels.
Interpretation : verdict d’executabilite
Le verdict est calcule, pas declare : chaque prerequis (wheel, OS, VRAM) est teste independamment, et le verdict agreg suit. Sur la machine d’authoring (Windows, 8 GB), le verdict est RECOVERABLE-MACHINE — la meme cellule passe en execution locale reelle sur la machine cible sans modification du notebook. C’est la traduction pratique de la regle F : on ne remplace pas l’outil par un simulacre, on documente la machine qui peut l’executer reellement.
# Chargement du pipeline YuE2 et affichage des 4 etapes canoniques
pipe = None
if YUE2_DISPONIBLE:
from yue2 import YuE2Pipeline
t0 = time.time()
kw = {"device": "cuda"}
if yue2_vae == "legacy":
kw["vae"] = "m-a-p/YuE2-Vae-legacy"
pipe = YuE2Pipeline.from_pretrained(yue2_model_id, **kw)
print(f"Pipeline charge en {time.time() - t0:.1f} s")
else:
print("PIPELINE NON CHARGE ICI (verdict precedent : RECOVERABLE-MACHINE)")
print()
print("Appel canonique execute sur la machine cible :")
print()
print(" from yue2 import YuE2Pipeline")
print(" pipe = YuE2Pipeline.from_pretrained('m-a-p/YuE2-3B', device='cuda')")
print(" # variante benchmark : vae='m-a-p/YuE2-Vae-legacy'")
print()
print("Liberation en fin de session : pipe.close() (VRAM du VAE FP32).")
print()
print("4 ETAPES CANONIQUES DU PIPELINE (decomposition interne du pipe.__call__)")
print("-" * 45)
for i, (nom, role) in enumerate([
("plan()", "paroles+style -> partition ABC planifiee (melodie, accords si cot=full)"),
("generate_semantic(plan)", "partition -> tokens semantiques (backbone AR-NAR MoT)"),
("synthesize(semantic)", "tokens semantiques -> latents acoustiques (flow-matching)"),
("decode(latents)", "latents -> audio 48 kHz stereo (VAE)"),
], start=1):
print(f" {i}. {nom:26s} {role}")Pipeline charge en 55.6 s
4 ETAPES CANONIQUES DU PIPELINE (decomposition interne du pipe.__call__)
---------------------------------------------
1. plan() paroles+style -> partition ABC planifiee (melodie, accords si cot=full)
2. generate_semantic(plan) partition -> tokens semantiques (backbone AR-NAR MoT)
3. synthesize(semantic) tokens semantiques -> latents acoustiques (flow-matching)
4. decode(latents) latents -> audio 48 kHz stereo (VAE)
Interpretation : pipeline et decomposition canonique
L’appel de convenance pipe(style=..., lyrics=..., cot=..., seed=...) enchaine les 4 etapes d’un bloc. La Section 5 execute d’abord la forme decomposee — pour rendre visible ce que chaque etape produit (la partition ABC planifiee en particulier) — puis la forme convenance pour la comparaison. Le choix du VAE (default vs legacy) est un compromis explicite : qualite perceptive contre musicalite mesuree sur benchmarks.
Section 4 : Preparation des paroles et du style
YuE2 attend deux entrees textuelles distinctes :
lyrics: paroles structurees par balises de section ([verse],[chorus]…). Chaque bloc correspond a un segment chante ; la structure contraint la forme de la chanson.style: description du style — genre, ambiance, instrumentation, langue du chant. C’est l’equivalent text-to-music du prompt, mais il conditionne le rendu des paroles.
Les paroles ci-dessous sont originales (ecrites pour ce cours) : aucun probleme de droits, et le meme couplet paroles+style servira a toutes les generations du notebook — c’est la base de la comparaison same-prompt same-seed de la Section 8.
# Preparation des paroles et du style (originaux, meme prompt partout)
lyrics_demo = """[verse]
Les neurones veillent encore cette nuit
Chaque couche apprend un peu mieux
Le gradient descend vers la verite
[chorus]
Genere, genere la chanson
Des mots a la melodie
Genere, genere la chanson
La machine a du genie
[verse]
Un token, puis un autre, et le refrain
Se dessine dans le bruit du train"""
style_demo = "inspiring french pop, female vocal, warm analog synth, 110 bpm, key of A minor"
print("PAROLES ET STYLE (meme prompt pour toutes les generations)")
print("=" * 45)
print(lyrics_demo)
print()
print(f"style : {style_demo}")
print()
print(f"graine : {seed} (fixee pour toute la session)")
n_blocs = lyrics_demo.count("[")
print(f"blocs de paroles : {n_blocs} -> {n_blocs} segments chantes attendus")PAROLES ET STYLE (meme prompt pour toutes les generations)
=============================================
[verse]
Les neurones veillent encore cette nuit
Chaque couche apprend un peu mieux
Le gradient descend vers la verite
[chorus]
Genere, genere la chanson
Des mots a la melodie
Genere, genere la chanson
La machine a du genie
[verse]
Un token, puis un autre, et le refrain
Se dessine dans le bruit du train
style : inspiring french pop, female vocal, warm analog synth, 110 bpm, key of A minor
graine : 42 (fixee pour toute la session)
blocs de paroles : 3 -> 3 segments chantes attendus
Interpretation : formater l’entree
Deux Pieges classiques du formatage :
- Balises absentes : des paroles sans
[verse]/[chorus]laissent le modele decider seul du decoupage — resultat moins controle. - Style contradictoire : un style qui exige une langue que les paroles n’ont pas produit des generations hybrides ; la langue du chant se declare dans le style.
La graine fixe (seed=42) rend la generation reproductible : meme prompt + meme graine = meme artefact, ce qui rend la comparaison de la Section 8 une comparaison de modeles, pas une comparaison de tirages.
Exercice 1 : Formatage de paroles multi-blocs
Objectif : ecrire une fonction structurer_paroles(couples_refrain) qui prend une liste de couples (texte, type de bloc) et renvoie les paroles balisees au format YuE2.
Etapes : 1. Parcourir la liste des (texte, type) ; 2. Entourer chaque texte de sa balise [type] ; 3. Joindre les blocs par une ligne vide.
Indice : "[" + typ + "]\n" + texte puis "\n\n".join(blocs).
# Exercice 1 : Formatage de paroles multi-blocs
def structurer_paroles(couples):
# TODO etudiant : renvoyer les paroles balisees [verse]/[chorus]...
pass
# Auto-verification attendue :
# structurer_paroles([("la nuit tombe", "verse"), ("refrain", "chorus")])
# -> "[verse]\nla nuit tombe\n\n[chorus]\nrefrain"
print("Exercice a completer : structurer_paroles")Exercice a completer : structurer_paroles
Section 5 : Generation zero-shot, etape par etape (cellule-type a)
On execute le pipeline en forme decomposee sur le prompt de reference : chaque etape produit un artefact observable — la partition ABC planifiee par plan(), les tokens semantiques, les latents, l’audio final. cot="full" laisse le modele planifier melodie et accords avant de generer.
Sur la machine cible (24 GB, Linux), chaque etape affiche sa duree et son artefact ; en mode degrade, la cellule affiche l’appel canonique exact et le routage.
# Generation zero-shot en 4 etapes canoniques (cellule-type a)
chanson_zero_shot = None
if YUE2_DISPONIBLE and pipe is not None:
t0 = time.time()
# Etape 1 : plan (partition ABC planifiee, sauvegardee via l'API documentee)
plan = pipe.plan(style=style_demo, lyrics=lyrics_demo, cot=cot_mode)
print(f"[1/4] plan() en {time.time() - t0:.1f} s")
plan.save(str(Path(output_dir) / "plan_zero_shot"))
print(f" plan sauvegarde : {output_dir}/plan_zero_shot")
# Etape 2 : tokens semantiques (AR-NAR)
t1 = time.time()
semantic = pipe.generate_semantic(plan)
print(f"[2/4] generate_semantic() en {time.time() - t1:.1f} s")
# Etape 3 : latents acoustiques (flow-matching)
t1 = time.time()
latents = pipe.synthesize(semantic)
print(f"[3/4] synthesize() en {time.time() - t1:.1f} s")
# Etape 4 : audio 48 kHz stereo (VAE)
t1 = time.time()
chanson_zero_shot = pipe.decode(latents)
print(f"[4/4] decode() en {time.time() - t1:.1f} s")
# Persistance : le chemin decompose rend l'audio brut (ndarray), pas l'objet
# SongResult du chemin one-shot -> on ecrit avec soundfile, la primitive
# utilisee par SongResult.save (FLAC PCM_24, 48 kHz).
import soundfile as sf
sortie = Path(output_dir) / "zero_shot.flac"
sf.write(sortie, chanson_zero_shot, 48000, subtype="PCM_24")
print(f"chanson ecrite : {sortie} ({sortie.stat().st_size / 1024:.0f} Ko)")
# Artefacts du plan (partition score.abc incluse — entree de la Section 7)
plan.save(str(Path(output_dir) / "zero_shot"))
print(f"artefacts (partition ABC incluse) : {output_dir}/zero_shot/")
print(f"duree totale : {time.time() - t0:.1f} s")
else:
print("GENERATION ZERO-SHOT — ROUTEE (RECOVERABLE-MACHINE)")
print("-" * 45)
print("Appel canonique (machine cible, 24 GB BF16 Linux) :")
print()
print(" plan = pipe.plan(style=style_demo, lyrics=lyrics_demo, cot='full')")
print(" semantic = pipe.generate_semantic(plan)")
print(" latents = pipe.synthesize(semantic)")
print(" song = pipe.decode(latents)")
print(" song.save('zero_shot.flac')")
print(" song.save_artifacts('output/yue2-songs/zero_shot')")
print()
print("Ordre de grandeur (RTX 4090, carte du modele) : ~71 s pour une chanson")
print("de 3.6 min, pic VRAM ~11.2 GiB. Artefacts attendus : zero_shot.flac,")
print("Artefacts attendus : zero_shot.flac + dossier zero_shot/ (score.abc, tokens, latents).")[1/4] plan() en 71.3 s
plan sauvegarde : output/yue2-songs/plan_zero_shot
[2/4] generate_semantic() en 27.1 s
[3/4] synthesize() en 16.0 s
[4/4] decode() en 9.0 s
chanson ecrite : output/yue2-songs/zero_shot.flac (19741 Ko)
artefacts (partition ABC incluse) : output/yue2-songs/zero_shot/
duree totale : 125.1 s
Interpretation : ce que chaque etape apporte
| Etape | Artefact | Ce qu’il revele |
|---|---|---|
plan() |
partition ABC | le modele a compose : structure, melodie, grille d’accords — lisible et editable |
generate_semantic() |
tokens semantiques | la traduction symbolique -> representation dense du rendu attendu |
synthesize() |
latents | le contenu acoustique en espace latent (flow-matching) |
decode() |
WAV/FLAC 48 kHz stereo | le rendu final, voix et instruments |
Le point pedagogique : la partition de l’etape 1 est un artefact de premiere classe. Ce n’est pas un sous-produit — c’est l’interface d’entree des Sections 6 (cover) et 7 (edition agentique). Une architecture qui produit du symbolique editable ouvre des usages qu’une generation purement acoustique ne peut pas offrir.
Section 6 : Cover zero-shot depuis une melodie transcrite (cellule-type b)
Le cover zero-shot : on fournit au modele une melodie source (partition ABC sans symboles d’accords) et un style cible — le modele reharmonise et arrange la melodie dans le style demande, avec les paroles fournies. Le mode chain-of-thought adapte est cot="melody" : le plan melodique est impose (la source), seul l’habillage est genere.
La voie canonique de la famille YuE2 : transcrire l’enregistrement source avec SheetSage2 (audio -> ABC, notebook 04-16 de cette serie) puis couvrir. Ici, pour isoler la demonstration, la melodie source est une ABC ecrite a la main — un air simple en La mineur.
# Cover zero-shot : melodie ABC imposee + style cible (cellule-type b)
melodie_source = 'X:1\nT:Air simple pour cover\nM:4/4\nL:1/8\nK:Am\nA2 c2 e2 a2 | g2 e2 c2 A2 | B2 d2 f2 b2 | a6 z2 |\nA2 c2 e2 a2 | g2 e2 c2 e2 | d2 c2 B2 G2 | A6 z2 |'
print("MELODIE SOURCE (ABC, sans accords — imposee au plan)")
print("=" * 45)
print(melodie_source)
Path(output_dir, "melodie_source.abc").write_text(melodie_source, encoding="utf-8")
chanson_cover = None
if YUE2_DISPONIBLE and pipe is not None:
t0 = time.time()
chanson_cover = pipe(
style="energetic rock cover, electric guitar, drums, " + style_demo.split(",")[0],
lyrics=lyrics_demo,
cot="melody",
abc=melodie_source,
seed=seed,
cfg_scale=cfg_scale,
)
sortie = Path(output_dir) / "cover_rock.flac"
chanson_cover.save(sortie)
print(f"cover ecrit : {sortie} en {time.time() - t0:.1f} s")
else:
print()
print("COVER — ROUTE (RECOVERABLE-MACHINE)")
print("-" * 45)
print("Appel canonique :")
print()
print(" cover = pipe(style=..., lyrics=lyrics_demo, cot='melody',")
print(" abc=melodie_source, seed=seed, cfg_scale=cfg_scale)")
print()
print("Diff avec la Section 5 : le plan melodique n'est pas genere, il est")
print("IMPOSE par l'ABC fournie. Seuls harmonisation/arrangement/rendu sont generes.")
print("En production, la melodie provient de SheetSage2 (transcription audio->ABC).")MELODIE SOURCE (ABC, sans accords — imposee au plan)
=============================================
X:1
T:Air simple pour cover
M:4/4
L:1/8
K:Am
A2 c2 e2 a2 | g2 e2 c2 A2 | B2 d2 f2 b2 | a6 z2 |
A2 c2 e2 a2 | g2 e2 c2 e2 | d2 c2 B2 G2 | A6 z2 |
cover ecrit : output/yue2-songs/cover_rock.flac en 51.0 s
Interpretation : cover = plan impose
La difference avec la Section 5 tient en une ligne : abc=melodie_source remplace le plan melodique genere. Le modele garde la main sur tout sauf la melodie — harmonisation, arrangement, timbres, chant. C’est le pattern des applications de cover (theme de jeu reharmonise, standard de jazz reinterprete), et c’est aussi la brique qui se combine avec SheetSage2 (notebook 04-16) pour boucler : n’importe quel enregistrement -> melodie ABC -> cover dans le style voulu.
Section 7 : Edition agentique d’une partition (cellule-type c)
L’edition agentique exploite le fait que la generation produit une partition ABC sur disque (artefact score.abc de la Section 5). Le flux documente par la carte :
- la generation de la Section 5 sauvegarde ses artefacts dont
score.abc; - on edite la partition — reharmoniser un accord, developper un solo, changer la structure ;
- on regenere par le pipe de haut niveau avec
abc=<partition editee>— le plan impose est la partition editee, comme en mode cover.
La boucle symbolique <-> audio est le claim central de YuE2 : la partition n’est pas une curiosite, c’est le plan de travail sur lequel un agent (ou un humain) itere avant de payer le cout de la synthese.
# Edition agentique : reharmoniser UN accord de la partition produite (cellule-type c)
chanson_editee = None
score_abc_path = Path(output_dir) / "zero_shot" / "score.abc"
if YUE2_DISPONIBLE and pipe is not None and score_abc_path.exists():
abc_originale = score_abc_path.read_text(encoding="utf-8")
print("PARTITION ORIGINALE (extrait, artefact de la Section 5) :")
print(abc_originale[:200])
print()
# Edition symbolique : etendre le PREMIER ACCORD de la grille en accord de septieme.
# Le planneur emet des accords inline quotes ("Fm", "Bbm", ...) dans le corps de la
# partition ; les quotes de l'en-tete (name="Vocal Melody") ne sont PAS des accords.
# On cherche donc apres la ligne de tonalite K: un token quote court en A-G.
corps = abc_originale[abc_originale.find("K:"):]
accord = remplacement = "(aucun)"
abc_editee = abc_originale
pos = corps.find('"')
while pos != -1:
fin_q = corps.find('"', pos + 1)
token = corps[pos + 1:fin_q] if fin_q != -1 else ""
if token and token[0] in "ABCDEFG" and 2 <= len(token) <= 6:
accord = '"' + token + '"'
remplacement = '"' + token + '7"'
abc_editee = abc_originale.replace(accord, remplacement, 1)
break
pos = corps.find('"', fin_q + 1) if fin_q != -1 else -1
modifie = abc_editee != abc_originale
print(f"reharmonisation appliquee : {'oui (' + accord[1:-1] + ' -> ' + remplacement[1:-1] + ')' if modifie else 'cible non trouvee dans la grille'}")
Path(output_dir, "score_edite.abc").write_text(abc_editee, encoding="utf-8")
if modifie:
# Re-entree documentee : pipe(abc=...) impose la partition editee
t0 = time.time()
chanson_editee = pipe(
style=style_demo, lyrics=lyrics_demo, cot="full",
abc=abc_editee, seed=seed, cfg_scale=cfg_scale,
)
sortie = Path(output_dir) / "edition_agentique.flac"
chanson_editee.save(sortie)
print(f"regeneration depuis la partition editee : {time.time() - t0:.1f} s -> {sortie}")
else:
print("EDITION AGENTIQUE — ROUTEE (RECOVERABLE-MACHINE)")
print("-" * 45)
print("Flux canonique (machine cible, API documentee de la carte) :")
print()
print(" song = pipe(style=..., lyrics=..., cot='full', seed=...) # Section 5")
print(" song.save_artifacts('output/yue2-songs/zero_shot') # produit score.abc")
print(" abc = Path('.../zero_shot/score.abc').read_text(...) # partition editable")
print(" abc_edite = abc.replace('|Am |', '|Am7 |', 1) # edition symbolique")
print(" edited = pipe(style=..., lyrics=..., cot='full',")
print(" abc=abc_edite, seed=...) # re-entree par abc=")
print()
print("Point cle : l'edition coute une ligne de texte, la regeneration")
print("reutilise le pipeline deja charge — iteration agentique abordable (pas de")
print("rechargement de poids entre les tours).")PARTITION ORIGINALE (extrait, artefact de la Section 5) :
X:1
T:
M:4/4
L:1/32
Q:1/4=108
V: Vocal clef=treble name="Vocal Melody" snm="Vocal"
V: Ins clef=treble name="Ins Melody" snm="Inst."
K:Fm
% intro
V: Vocal
z24z4"Fm"z4|"Fm"z32|"Fm"z32|"Bbm"z32|
V: Ins
Z
reharmonisation appliquee : oui (Fm -> Fm7)
regeneration depuis la partition editee : 57.3 s -> output/yue2-songs/edition_agentique.flac
Interpretation : la partition comme plan de travail
La partition editee re-entre par pipe(abc=...) — la meme porte que le cover de la Section 6, mais avec une partition issue du modele lui-meme plutot que d’une source externe. Un agent peut donc iterer — reharmoniser, developper un solo, reviser les paroles, changer le style — en ne payant que la regeneration. C’est la difference entre un modele boite noire (prompt -> audio, tout ou rien) et un modele inspectable (prompt -> artefact editable -> audio).
Section 8 : Comparaison meme prompt, meme seed
Acceptance de l’issue : comparer honnetement YuE2-3B, MusicGen-medium (audiocraft en local, pattern du notebook 02-3) et YuE-v1 (etat documente de l’ancien 02-7), sur le meme prompt et la meme graine quand c’est possible.
| Modele | Type | Executable ici ? | Ce qu’on compare honnetement |
|---|---|---|---|
| YuE2-3B | text-to-song | machine cible 24 GB | generation reelle (artefacts routes) |
| MusicGen-medium | text-to-music | audiocraft si present dans le kernel | rendu instrumental du meme theme — la comparaison est bornee par nature : MusicGen ne chante pas, et l’API audiocraft n’expose pas de graine |
| YuE-v1 | text-to-song 2 stages | non (14 B, flash-attn) | architecture et resultats rapportes (papier YuE-v1), jamais re-copies comme mesures propres |
La comparaison meme-seed n’a de sens qu’entre systemes qui acceptent une graine et le meme format de prompt ; entre familles differentes (text-to-song vs text-to-music), on compare la reponse au meme theme, en declarant la difference de nature.
# Comparaison : MusicGen via audiocraft (pattern 02-3) + tableau honnete
musicgen_reponse = None
try:
from audiocraft.models import MusicGen
mg = MusicGen.get_pretrained("facebook/musicgen-medium")
mg.set_generation_params(duration=15)
musicgen_actif = True
print("MusicGen-medium charge (audiocraft, pattern du notebook 02-3)")
except Exception as e:
musicgen_actif = False
print(f"MusicGen indisponible dans ce kernel ({type(e).__name__})")
print("Pattern 02-3 : pip install audiocraft — colonne documentee, pas de simulacre.")
if musicgen_actif:
# Meme theme, formate text-to-music (MusicGen ne chante pas ; pas de graine API)
prompt_instrumental = ("inspiring pop instrumental, warm analog synth, 110 bpm, A minor, "
"female-vocal-like lead melody")
t0 = time.time()
wav = mg.generate([prompt_instrumental])
musicgen_reponse = wav[0, 0].cpu().numpy()
print(f"rendu instrumental du meme theme : {musicgen_reponse.size / mg.sample_rate:.1f} s "
f"en {time.time() - t0:.1f} s (graine non fixable via l'API audiocraft — declare)")
del mg
gc.collect()
print()
print("TABLEAU COMPARATIF")
print("-" * 45)
lignes = [
("Entree", "paroles balisees + style", "description instrumentale", "paroles + accords"),
("Sortie", "chanson chantee 48 kHz stereo", "musique instrumentale", "chanson chantee"),
("Chemin", "plan ABC -> semantique -> latents -> VAE", "codec audio AR direct", "2 stages AR 7B separes"),
("Editable", "oui (partition ABC 1re classe)", "non (audio direct)", "partiellement (tokens)"),
("Poids", "3.59 B", "~2.3 B (medium)", "14 B (2 x 7B)"),
("Exec Ici", "routee 24 GB", "audiocraft local si installe", "non executable"),
]
entetes = ("Critere", "YuE2-3B", "MusicGen-medium", "YuE-v1")
largeurs = [max(len(str(l[i])) for l in [entetes] + lignes) for i in range(4)]
print(" ".join(h.ljust(largeurs[i]) for i, h in enumerate(entetes)))
for l in lignes:
print(" ".join(str(c).ljust(largeurs[i]) for i, c in enumerate(l)))
print()
print("Claim SongBench (RAPORTE, non reproduit ici) : YuE2 6.9632 vs Suno v5 6.8721")
print("sur WildSongBench (192 prompts, best-of-8) — chiffre de la carte modele, a")
print("consommer comme claim editeur, pas comme mesure de ce notebook.")MusicGen indisponible dans ce kernel (ModuleNotFoundError)
Pattern 02-3 : pip install audiocraft — colonne documentee, pas de simulacre.
TABLEAU COMPARATIF
---------------------------------------------
Critere YuE2-3B MusicGen-medium YuE-v1
Entree paroles balisees + style description instrumentale paroles + accords
Sortie chanson chantee 48 kHz stereo musique instrumentale chanson chantee
Chemin plan ABC -> semantique -> latents -> VAE codec audio AR direct 2 stages AR 7B separes
Editable oui (partition ABC 1re classe) non (audio direct) partiellement (tokens)
Poids 3.59 B ~2.3 B (medium) 14 B (2 x 7B)
Exec Ici routee 24 GB audiocraft local si installe non executable
Claim SongBench (RAPORTE, non reproduit ici) : YuE2 6.9632 vs Suno v5 6.8721
sur WildSongBench (192 prompts, best-of-8) — chiffre de la carte modele, a
consommer comme claim editeur, pas comme mesure de ce notebook.
Interpretation : comparer sans tricher
Trois regles d’honnetete appliquees :
- Meme nature seulement : YuE2 et YuE-v1 sont comparables (text-to-song) ; MusicGen repond au meme theme mais sa sortie est de nature differente — la comparaison le declare au lieu de l’occulter.
- Mesure vs marketing : le score SongBench est un claim rapporte (carte du modele), jamais presente comme une mesure de cette session — reproduire le mesure, pas le slogan.
- Ce qui n’a pas tourne est dit : YuE-v1 n’est pas executable ici ; sa colonne decrit l’architecture documentee, pas une execution.
Exercice 2 : Grille d’evaluation comparative
Objectif : construire grille_evaluation(audio_a, audio_b) qui, etant donnes deux fichiers audio, remplit une grille qualitative (duree, debit de paroles entendues, presence d’un refrain distinct, largeur stereo percue).
Etapes : 1. Charger chaque audio (duree via ses metadonnees) ; 2. Noter chaque critere sur 0-2 avec une regle explicite (ex. refrain distinct = 2 si un segment se repete avec paroles identiques) ; 3. Rendre la grille sous forme de dictionnaire.
Indice : se focaliser sur des criteres observables sans modele annexes — la grille doit rester applicable a l’oreille.
# Exercice 2 : Grille d'evaluation comparative
def grille_evaluation(audio_a, audio_b):
# TODO etudiant : renvoyer la grille qualitative des deux audios...
pass
print("Exercice a completer : grille_evaluation")Exercice a completer : grille_evaluation
Exercice 3 : Choisir son mode chain-of-thought
Objectif : ecrire choisir_cot(objectif) qui renvoie le mode adapt parmi "full", "melody", "off" selon l’objectif : creation libre, cover d’une melodie existante, iteration rapide sur le rendu.
Etapes : 1. Documenter la table de decision (objectif -> mode) en commentaire ; 2. Renvoyer le mode ; 3. Gerer le cas objectif inconnu en renvoyant "full" (defaut sur).
Indice : cot="off" saute le plan — utile quand on fournit deja une partition (edition agentique de la Section 7), cout moindre mais zero planification.
# Exercice 3 : Choisir son mode chain-of-thought
def choisir_cot(objectif):
# TODO etudiant : table de decision objectif -> "full" | "melody" | "off"...
pass
print("Exercice a completer : choisir_cot")Exercice a completer : choisir_cot
# Statistiques de session
print("STATISTIQUES DE SESSION")
print("=" * 45)
print(f"Date {time.strftime('%Y-%m-%d %H:%M:%S')}")
print(f"Kernel python3 ({sys.version.split()[0]})")
print(f"Modele {yue2_model_id} (AR-NAR MoT 3.59 B, 48 kHz stereo)")
print(f"Graine {seed} (same-prompt same-seed, Section 8)")
print(f"CoT {cot_mode} | cfg_scale {cfg_scale} | VAE {yue2_vae}")
if cuda_dispo:
import torch
print(f"Device {torch.cuda.get_device_properties(0).name}")
else:
print("Device CPU (generation routee RECOVERABLE-MACHINE)")
print(f"Verdict exec {'REEL' if YUE2_DISPONIBLE else 'RECOVERABLE-MACHINE (eGPU 3090 24 GB Linux)'}")
artefacts = sorted(p.name for p in Path(output_dir).glob('*')) if Path(output_dir).exists() else []
print(f"Artefacts {len(artefacts)} fichier(s) dans {output_dir}/")
for a in artefacts:
print(f" - {a}")
print(f"Licence poids CC BY-NC 4.0 (usage commercial interdit)")
if YUE2_DISPONIBLE and pipe is not None:
pipe.close()
print("pipeline libere (pipe.close())")STATISTIQUES DE SESSION
=============================================
Date 2026-09-13 04:22:58
Kernel python3 (3.10.12)
Modele m-a-p/YuE2-3B (AR-NAR MoT 3.59 B, 48 kHz stereo)
Graine 42 (same-prompt same-seed, Section 8)
CoT full | cfg_scale 1.2 | VAE default
Device NVIDIA GeForce RTX 3090
Verdict exec REEL
Artefacts 7 fichier(s) dans output/yue2-songs/
- cover_rock.flac
- edition_agentique.flac
- melodie_source.abc
- plan_zero_shot
- score_edite.abc
- zero_shot
- zero_shot.flac
Licence poids CC BY-NC 4.0 (usage commercial interdit)
pipeline libere (pipe.close())
Conclusion : le symbolique comme interface
Ce notebook a suivi la generation de chansons de YuE-v1 (deux stages sepres, 14 B) a YuE2-3B (backbone AR-NAR unique + flow-matching + VAE 48 kHz stereo), en executant les 4 etapes canoniques plan -> generate_semantic -> synthesize -> decode et les trois usages marques : generation zero-shot, cover zero-shot (cot='melody'), edition agentique d’une partition ABC.
Le fil rouge : la partition ABC est devenue une interface — planifiee (Section 5), imposee (Section 6), editee (Section 7). C’est la these de la famille YuE2 : les satellites MERT2 (comprehension, notebook 04-15) et SheetSage2 (transcription, notebook 04-16) ferment la boucle audio -> symbolique -> audio autour du generateur.
Verdicts par axe
| Axe | Verdict |
|---|---|
| Vrais poids YuE2-3B charges | SOTA-OK sur machine cible 24 GB / RECOVERABLE-MACHINE documente ici (wheel + Linux + VRAM testes un a un) |
| Execution des generations | RECOVERABLE-MACHINE — eGPU RTX 3090 24 GB sous Linux (machine GenAI du cluster) |
| Comparaison MusicGen | service local quand actif, sinon colonne documentee — jamais de simulacre |
| Comparaison YuE-v1 | INTRINSIC local (14 B + flash-attn hors de portee de cette machine) — resultats rapportes, identifies comme tels |
| Claim SongBench vs Suno v5 | rapporte, non reproduit — declare comme claim editeur |
| Licence | poids CC BY-NC 4.0, rappelee en tete, en setup et en stats de session |
Pour aller plus loin
- Notebook 04-15 (MERT2) : mesurer les embeddings de vos generations — la comprehension musicale de la meme famille.
- Notebook 04-16 (SheetSage2) : transcrire un enregistrement en ABC pour nourrir un cover zero-shot.
- Site officiel YuE2 (map-yue2.github.io) : le rapport technique et les demos.