跑完 model.generate() 那段代码,听到 AI 根据"80s synth pop with bassy drums and synth"生成的音乐时,我第一反应是:这东西底层到底是什么逻辑?花了一天时间扒源码,把整个流程清楚了。
一、MusicGen 是什么?
简单说:输入一段文字,输出一段音乐。
Meta(原 Facebook)2023 年开源的,核心架构就三块:
- T5 文本编码器:把你写的 prompt 转成 embedding
- MusicGen Transformer:自回归生成音频 token
- EnCodec 解码器:把 token 变回音频波形
整个流程跟 GPT 写文章一模一样,只不过 GPT 输出的是文字 token,MusicGen 输出的是音频 token。
二、架构长什么样?
先看图:
文本 Prompt ──→ T5 编码器 ──→ 文本 embedding
↓
MusicGen Transformer
(自回归生成 4 个并行的音频 token 流)
↓
EnCodec 解码器
↓
32kHz 音频 (.wav)
几个关键参数:
| 组件 | 架构 | 参数量 | 输入 | 输出 |
|---|---|---|---|---|
| MusicGen | Transformer Decoder | 0.3B-3.3B | 文本 embedding | 音频 token(4 码本) |
| T5/Flan-T5 | Encoder-only | 0.2B-3B | 文本 | 文本 embedding |
| EnCodec | CNN + RVQ | - | 音频 token | 32kHz 音频 |
这里有个细节:4 个码本(codebook)。
还记得上一章手搓的 Music Transformer 吗?也是多 codebook 的。MusicGen 用 4 个并行的 token 流来表示一个时间步的音频,分别对应不同的频率层。这不是拍脑袋定的,是 EnCodec 的 RVQ 量化器天然就输出多层 token。
三、环境搭建:5 分钟跑起来
我用的是魔搭社区(ModelScope),注册个账号就能用免费 GPU。
步骤 1:装依赖
pip install --upgrade pip
pip install --upgrade transformers scipy
pip install torch==2.6.0 torchvision==0.21.0 torchaudio==2.6.0 --index-url https://download.pytorch.org/whl/cpu
如果用 GPU 环境,直接选预装 torch>=2.6.0 的镜像就行,不用手动装。
步骤 2:加载模型
from modelscope import AutoProcessor, MusicgenForConditionalGeneration
processor = AutoProcessor.from_pretrained("facebook/musicgen-large")
model = MusicgenForConditionalGeneration.from_pretrained("facebook/musicgen-large")
这里有个坑:第一次加载会下载模型权重,musicgen-large 大概 12GB ,得等一会儿。如果网络慢,可以考虑用 musicgen-small(1.2GB)先跑通流程。
步骤 3:生成音乐
prompt = "80s pop track with bassy drums and synth"
tokens = 256
inputs = processor(
text=prompt,
padding=True,
return_tensors="pt",
)
audio_values = model.generate(**inputs, max_new_tokens=tokens)
tokens 控制生成长度,最大 1500,对应约 30 秒音乐。我试了 256,大概 5-6 秒。
步骤 4:听 + 保存
from IPython.display import Audio
from scipy.io import wavfile
sampling_rate = model.config.audio_encoder.sampling_rate
在线听
Audio(audio_values[0], rate=sampling_rate)
保存到文件
wavfile.write("musicgen_out.wav", rate=sampling_rate, data=audio_values.numpy())
跑完就能听到 AI 写的 80 年代合成器流行曲了。说实话,第一次听到生成的鼓点和贝斯时,我愣了几秒------这玩意儿居然真的能听懂"bassy drums"。
四、源码拆解:底层到底在干嘛?
扒了 audiocraft/models/musicgen.py,整个流程比我想象的干净。
1. 模型初始化
class MusicGen(BaseGenModel):
def init(self, name: str, compression_model: CompressionModel,
lm: LMModel, max_duration: tp.Optional[float] = None):
super().init(name, compression_model, lm, max_duration)
self.set_generation_params(duration=15) # 默认生成 15 秒
继承自 BaseGenModel,核心就三个东西:
- compression_model:EnCodec,负责编解码音频 token
- lm:Language Model,就是那个 Transformer Decoder
- max_duration:单次最大生成时长
加载预训练模型直接用静态方法:
model = MusicGen.get_pretrained("facebook/musicgen-small")
这个方法会干三件事:
- 从 HuggingFace 下载 LM 权重
- 下载 EnCodec 权重
- 如果是 melody 模型,会特殊处理
self_wav条件器(推理时对齐长度、关掉训练时的 mask)
2. 生成参数配置
model.set_generation_params(
use_sampling=True,
top_k=250,
top_p=0.0,
temperature=1.0,
duration=30.0,
cfg_coef=3.0,
extend_stride=18
)
这几个参数得拆开说:
temperature(温度):控制随机性。1.0 是默认,调高到 1.5 会更"疯",调到 0.5 会更保守。
top_k:每一步只在概率最高的 250 个 token 里采样。不设的话就是全词表(1024 个)里采,容易出奇怪的东西。
top_p:核采样。设 0.9 就是只在前 90% 累积概率的 token 里采。跟 top_k 二选一就行。
cfg_coef(Classifier-Free Guidance 系数):这个最有意思。
五、CFG 在自回归模型里怎么玩?
CFG 原本是 Diffusion 模型的技术,MusicGen 把它搬到了 Transformer 自回归生成里。
核心公式:
logits = uncond + cfg_coef * (cond - uncond)
翻译成人话:
cond:带文本 prompt 跑出来的 logitsuncond:空文本("")跑出来的 logitscond - uncond:文本带来的"改变方向"cfg_coef:放大这个改变方向的强度
举个例子:
prompt = "epic orchestral battle music"
- cond logits:模型看到这个 prompt,倾向于生成史诗交响乐
- uncond logits:模型啥提示都没有,按"音乐统计规律"瞎生成
- cond - uncond:史诗感 - 随机感 = 史诗感的方向
- cfg_coef=3.0:把这个方向放大 3 倍
效果:
- cfg_coef 越大,越贴近文本描述,但音质可能变差(过度拟合 prompt)
- cfg_coef 越小,越"自由发挥",但可能跟 prompt 没关系
默认 3.0 是个经验值,我试了 5.0,生成的音乐确实更"听话",但高频噪声也多了。
六、输入预处理:三种条件怎么组合?
MusicGen 支持三种输入:
- 文本描述(descriptions):必须
- 音频续写开头(prompt):可选
- 旋律参考(melody_wavs):可选(只有 melody 模型支持)
场景 1:纯文本生成
descriptions = ["A calm piano melody with soft strings"]
prompt = None
melody_wavs = None
从零开始生成一段温柔钢琴曲。
场景 2:音频续写
descriptions = ["Lo-fi hip hop beat"]
prompt = torch.randn(1, 1, 160000) # 已有 5 秒音频
melody_wavs = None
给模型一段 lo-fi 开头,让它接着往下写。内部会先把 prompt 音频用 EnCodec 编码成 token,然后接在生成序列前面。
场景 3:旋律 + 文本
descriptions = ["Epic orchestral trailer music"]
prompt = None
melody_wavs = [torch.randn(32000)] # 1 秒旋律
给一段旋律,让模型围绕这个旋律生成史诗管弦乐。注意:这是完全按照给定旋律的音高走向来生成,不是参考风格。
如果是 musicgen-style 模型,那就是参考风格而不是旋律。
场景 4:三者都用
descriptions = ["Jazz piano trio"]
prompt = 已有钢琴片段
melody_wavs = 给定旋律
模型会综合文本风格、开头音频、指定旋律一起生成。这种场景适合"续写 + 旋律约束"的复杂任务。
预处理函数 _prepare_tokens_and_attributes 会把这些输入打包成 ConditioningAttributes 对象,里面包含 text embedding 和 wav conditioning。
七、长音频生成:滑动窗口策略
MusicGen 单次最多生成 30 秒(max_duration)。如果想生成 60 秒怎么办?
不能直接 lm.generate(max_gen_len=4500),会报错。
原因:Transformer 有三个硬限制:
- 位置编码长度固定
- attention mask 尺寸固定
- KV cache 分配大小固定
解决方案:分段滑动生成。
假设:
- max_duration = 30 秒
- duration = 70 秒(你想生成的总时长)
- extend_stride = 10 秒(每次前进的步长)
- frame_rate = 50 token/秒
计算:
- max_gen_len = 1500 tokens(30 秒 × 50)
- stride_tokens = 500 tokens(10 秒 × 50)
- overlap = 1000 tokens(重叠部分,防止音乐断裂)
生成流程:
- 第 1 轮:生成前 30 秒(1500 tokens)
- 第 2 轮:输入最后 20 秒(1000 tokens overlap),再生成 30 秒,但只取新前进的 10 秒
- 第 3 轮:重复...直到 70 秒
overlap 的作用是保持上下文连贯,不然每一段开头都会很突兀。
八、完整生成流程
把上面所有部分串起来,generate_with_chroma 函数干的事:
def generate_with_chroma(self, descriptions, melody_wavs, melody_sample_rate, ...):
1. 重采样 + 声道转换(44.1kHz stereo → 32kHz mono)
melody_wavs = [convert_audio(wav, melody_sample_rate, self.sample_rate, ...) ...]
2. 构造条件(text + wav)
attributes, prompt_tokens = self._prepare_tokens_and_attributes(...)
3. 生成 token(经过 Transformer + Attention)
tokens = self._generate_tokens(attributes, prompt_tokens, progress)
4. 解码为音频
return self.generate_audio(tokens)
四步走完,输出 [B, C, T] 形状的音频张量。
九、踩过的坑
1. 显存爆炸
musicgen-large 跑 30 秒生成,GPU 要 10GB+ 显存。如果爆显存:
- 换
musicgen-small(1.2GB) - 缩短 duration(15 秒试试)
- 关掉 CFG(
cfg_coef=1.0)
2. 生成全是噪声
temperature 设太高(>2.0),模型会疯掉。调回 1.0-1.2 就正常了。
3. 旋律条件不生效
用了错误的模型。musicgen-melody 才支持旋律输入,musicgen-small 只支持文本。加载前看清楚模型名。
4. 下载慢
HuggingFace 国内网络慢,可以加镜像:
os.environ["HF_ENDPOINT"] = "https://hf-mirror.com"
十、跟手搓 Music Transformer 对比
第三章我们自己手搓了一个 Music Transformer,对比一下:
| 维度 | 手搓 Transformer | MusicGen |
|---|---|---|
| 模型规模 | 6 层、512 维、0.3B | 12-32 层、1024 维、3.3B |
| 训练数据 | 72 首钢琴曲 | 2 万小时音乐 |
| 条件控制 | 无条件 | 文本 + 旋律 + 音频续写 |
| CFG | 无 | 有(cfg_coef=3.0) |
| 长音频 | 不支持 | 滑动窗口支持 |
| 生成质量 | 能听,单调 | 商业级,丰富 |
差距在哪?
- 数据量:72 首 vs 2 万小时,差了 300 倍
- 模型容量:6 层 vs 32 层,差了 5 倍
- 条件控制:无条件自回归 vs 文本/旋律多条件
- 训练 trick:我们用的基础 AdamW + warmup,MusicGen 还有 CFG、多阶段训练、数据增强
但核心思路是一样的:音频→token→自回归建模→生成→解码。MusicGen 就是把这个流程做到了极致。
十一、下一步能搞什么?
- 微调(Fine-tuning):用自己的音乐数据集微调 MusicGen,生成特定风格的音乐
- 旋律控制 :用
musicgen-melody模型,输入一段 hum(哼唱),生成完整编曲 - 长音频生成 :调
duration=180,生成 3 分钟完整歌曲 - 批量生成:循环生成不同 prompt 的音乐,建自己的 AI 音乐库
- 源码魔改 :把
musicgen.py里的 CFG 逻辑抽出来,加到自己手搓的模型里
十二、写在最后
扒完 MusicGen 源码,我最大的感受是:没有什么黑魔法,都是工程细节堆出来的。
- CFG 不是 Diffusion 专属,自回归模型也能用
- 长音频生成不是什么新技术,就是滑动窗口 + overlap
- 多条件控制就是把不同模态的输入打包成 conditioning attributes
但工程细节决定上限。怎么调 cfg_coef、怎么设 extend_stride、怎么处理 overlap、怎么做重采样,这些琐碎的东西堆起来,才有一段能用的 AI 音乐。
Meta 开源 MusicGen,等于把音乐生成的天花板掀开了。接下来就是看社区怎么玩:微调、魔改、加新条件、接 MIDI 控制...
AI 写歌这条路,现在才真正开始。
有问题欢迎提 issue 或者评论区留言。
标签 :AI音乐 MusicGen Meta 音频生成 Transformer EnCodec 深度学习 开源模型