前段时间一个做独立音乐的朋友丢给我一段三百来字的歌词,问我能不能在本地把它变成一首带人声的完整歌,不要云端、不要按次计费、不要上传素材。这个问题放在两年前基本等于许愿,但在 YuE 这类开源音乐基础模型出来之后,它变成了一个"配置问题加耐心问题"。YuE 是一套面向全长歌曲生成的开源模型,输入是歌词加一组风格描述,输出是带人声和伴奏的完整音频,不是那种给个关键词吐八秒 loop 的玩具,而是奔着"整首三分钟左右的歌"去的。我按自己实际跑通的顺序把过程整理一遍:先讲清楚它的生成链路和背后的技术取舍,再落环境、落权重、落参数,然后聊歌词格式、采样调优和后处理,最后说说把它接进真实工作流时的几条边界。手里有一张二十四 G 显卡、想把音乐生成接到自己流程里的人可以直接照着做,只想搞明白"开源全曲生成到底走到哪一步"的人也可以扫一遍。
1. YuE 到底在做什么:把整首歌当成一个语言建模问题
1.1 它不解决编曲,它解决的是"从文本到有声"
先把预期摆正。YuE 不做混音,不做母带,不帮你改和弦走向,也不理解"这段副歌情绪再往上推一点"这种指令。它做的事情非常单一:吃进去一份带结构标记的歌词和一个风格描述,吐出来一条包含人声与伴奏的音频波形。你可以把它理解成一个"作曲加演唱加编曲一次性完成"的黑盒,但盒子的输入输出都是死板的文本和音频,中间没有可拖拽的轨道、没有可编辑的音符。
这个定位决定了它的使用方式。你不可能拿它当 DAW 用,正确的姿势是把它当成"小样生成器":先用它把一首歌从零变成有声,再决定哪一首值得投入真人和真乐器去重做。我自己的流程就是这样,以前写十首歌可能只挑一首做完整编曲,现在可以让模型先把十首全部唱出来听一遍,筛掉明显不行的,剩下的再进编曲环节。省下来的时间不是一点半点,特别是那种"旋律在脑子里但唱不出来"的状态,它能帮你快速验证这首歌到底成不成立。
从技术角度看,它走的是"大语言模型加音频离散编码"这条路。音频先被一个音频编解码器压成离散的 token 序列,模型再像预测下一个词那样预测下一个音频 token。所以它天然继承了大模型的一系列特性:能吃很长的上下文、能做上下文学习、能通过提示词控制风格,也天然继承了采样参数调优这一整套麻烦事。
1.2 两条 token 轨道:人声和伴奏分开建模的意义
YuE 在架构上最关键的一个设计,是把一首歌拆成"人声轨"和"伴奏轨"两条离散 token 流,分别建模又保持同步。这件事听起来是工程细节,实际直接决定听感。
如果把人声和伴奏混成一条流去做预测,模型会面临一个非常别扭的任务:它必须同时决定"这一秒唱什么字"和"这一秒用什么乐器",两者的节奏规律完全不同。人声的时间尺度是音节和乐句,伴奏的时间尺度是小节和和声进行,混在一起学,很容易出现"伴奏对了人声糊"或者"人声清楚但和弦乱飘"的情况。分开建模以后,每条轨道的预测难度下降,而且天然带出来一个好处:理论上你可以只换一边。比如保留伴奏轨、重跑人声轨换一版唱法,或者反过来只改编曲。我实测里最常用的是前者,同一份伴奏跑三遍人声,挑咬字最清楚的那一版。
代价是上下文长度被吃掉一半。两条轨意味着单位时间内的 token 数量翻倍,而模型的上下文窗口是固定的,所以单次能生成的音频时长是有上限的。这也是为什么它不能一次性生成十分钟的歌,得靠分段生成再拼接,或者干脆把歌写短一点。理解这一点以后,你再看后面所有关于"生成时长"和"分段"的参数就不会迷糊了。
另外,轨道分离也让后处理变得友好。模型直接输出的是一条混好的音频,但因为它内部是分轨的,用现成的音源分离工具再拆一遍人声和伴奏,效果比从纯混音里拆要好,尤其是人声的齿音和气息部分残留会少一些。
1.3 跑之前先算清楚的三笔账
在动手之前,把算力账、时间账、磁盘账先算一遍,可以省掉很多白折腾。
算力账的核心是显存。七 B 级别的权重用 bf16 加载,光权重就占十五六 G,加上激活值、KV cache 和音频解码器,二十四 G 显存是比较舒服的档位,三十二 G 以上基本可以不用管优化。十六 G 显存能跑,但要开低显存模式,速度会掉得比较明显。十二 G 以下就不建议折腾本地了,除非你愿意接受"一首歌跑一小时"这种节奏。
时间账要看你的目标时长。我的机器上一次完整的两阶段推理,生成三分钟左右的音频,包括模型加载、token 生成和解码,大概在十分钟上下,其中 token 生成阶段占大头。如果你一次要跑十几首候选,这就是两三个小时的机器占用,所以批量任务最好放在晚上挂着跑,白天来做筛选和后处理。
磁盘账容易被忽略。一份权重七 B 的模型,两个 stage 加起来就是三十多 G,加上音频编解码器的权重、缓存目录、输出音频,一台机器上留一百 G 空间是比较稳妥的。我第一跑就是因为临时目录所在的盘只剩四十 G,跑到解码阶段直接写失败,白等了十几分钟。
| 环节 | 主要占用 | 判断标准 |
|---|---|---|
| Stage1 token 生成 | 显存、算力 | 时长的主要瓶颈,占七成以上时间 |
| Stage2 音频解码 | 显存、内存 | 显存不够会退到内存交换,明显变慢 |
| 音频编解码器加载 | 显存 | 常被忽略的一小块固定开销 |
| 权重与缓存 | 磁盘 | 建议预留一百 G 以上 |
提示:把模型缓存目录和输出目录指到同一块大容量盘上,避免临时文件跨盘搬迁带来的额外等待。
2. 环境和权重准备:第一次跑通要绕开的几个硬坑
2.1 flash-attn 装不上时该走哪条路
YuE 的官方实现推荐使用 flash-attn 加速注意力计算,但这个包装起来是出了名的看运气,Python 版本、CUDA 版本、编译器版本、PyTorch 版本四者必须对上。我遇到过两次编译失败,一次是编译器版本过高导致的模板报错,一次是已经装了旧版本导致符号冲突。
处理思路按代价从低到高排:
- 优先尝试预编译好的 wheel,装的时候加
--no-build-isolation,让安装过程复用当前环境里已有的构建依赖,很多"莫名编译失败"都是构建隔离导致的。 - 如果预编译包装不上,检查 PyTorch 的主版本号,flash-attn 对 PyTorch 的小版本比较敏感,版本错配时宁可降 PyTorch 也不要硬刚编译。
- 实在装不上就放弃它,在加载模型时把注意力实现换成 SDPA 或 eager。SDPA 在多数显卡上速度损失可以接受,eager 会慢一些但兼容性最好。
# 优先方案:复用当前环境构建依赖安装预编译包 pip install flash-attn --no-build-isolation # 兜底方案:不装 flash-attn,加载时切到 SDPA # 在推理脚本的模型加载处改这个参数 # attn_implementation="sdpa"需要强调的是,换成 SDPA 之后输出音频的质量不会变差,只是慢。这类加速库影响的是速度不是结果,所以卡在安装上完全没必要死磕。
另外一个高频坑是 numpy 的版本。一些音频处理依赖对 numpy 二点零之后的接口变化还不适应,症状是导入阶段就报奇怪的属性错误。遇到这种情况,把 numpy 降到一点二六附近通常能解决,但要注意别把其他依赖一起带崩,改完立刻回头验证一遍主流程能不能跑。
2.2 CoT 版和 ICL 版权重到底选哪个
同一个模型会放出好几种权重变体,名字里带 CoT 和 ICL 的区别值得说清楚,因为这直接决定你该拿它干什么。
带 CoT 的版本针对"风格标签推理"做了调优,你给一串比较粗糙的风格描述,它会在内部先展开成更细的描述再开始生成。适合那种"我只知道要一首抒情的、慢的、带钢琴的歌"但说不清具体风格的人。带 ICL 的版本走的是上下文学习路线,你喂它一小段参考音频,它会顺着那段音频的风格往下写。适合"我想要这种感觉,你照着来"的场景,前提是你手里有干净的参考素材。
语言维度上还会细分,英文和地方语言的版本是分开的。中文歌词一定要用对应语言的权重,用英文版跑中文词,结果通常是咬字含混、声调乱飞。我一开始偷懒用英文版试了段中文词,听感像是外国人在念拼音,音节数量还对不上,白跑一轮。
Stage2 那边也有对应变体,通用版和特定语言版。我的做法是 Stage1 用语言匹配版,Stage2 用通用版,输出稳定性没有明显差异,但具体到你的素材最好各跑一遍对比。
2.3 最小可跑命令与参数逐项拆解
第一次跑通只需要关心五六个参数,其他都保持默认。命令大致长这样:
python infer.py \ --stage1_model /path/to/stage1-model \ --stage2_model /path/to/stage2-model \ --genre_txt ./prompt/genre.txt \ --lyrics_txt ./prompt/lyrics.txt \ --output_dir ./output \ --run_n_segments 2 \ --stage2_batch_size 2 \ --max_new_tokens 3000 \ --repetition_penalty 1.1逐项说。--run_n_segments控制把歌词切成几段来跑,段数越多单段越短、显存压力越小,但段与段之间的衔接痕迹越明显。--stage2_batch_size是解码阶段的批大小,显存紧就降到一。--max_new_tokens决定单段最多生成多少音频 token,这个值直接决定时长上限,调小了会在一句话中途截断,听起来像突然断电。--repetition_penalty是防止模型陷入循环的关键参数,后面单独展开。
我建议第一次跑不要追求完整歌曲,把歌词砍到四行,max_new_tokens设小一点,十分钟内看到有音频文件落在输出目录,就算环境通了。先跑通再跑好,这个顺序别反。
2.4 显存不够时的四档降级方案
显存紧张时按下面顺序逐级降,越往后代价越大:
- 第一档,开低显存模式。配置文件里一般有个开关,打开后模型会把部分模块临时换出显存,速度损失大概两三成,是最划算的一档。
- 第二档,减少分段长度。把一段歌词拆成更小的块,单次前向的激活值显著下降,代价是拼接处需要人工听一遍。
- 第三档,降低解码批大小到一,并缩短单段 token 上限。这一步会让总时长成倍增加,但能保证跑得完。
- 第四档,把 Stage1 和 Stage2 严格串行,跑完一个卸载一个再加载另一个。这是官方推荐的常规做法,很多人图省事同时加载两个模型,直接把显存顶爆。
注意:不要指望用系统内存做全量卸载来跑七 B 模型。数字上能跑,但每生成一个 token 都要在内存和显存之间搬几百兆权重,实际速度会慢到没有使用价值。
3. 歌词文件才是效果的下限:格式、时长配比与风格标签
3.1 结构标签的写法与真正的生效条件
歌词文件不是随便写一段文字丢进去就行,模型对结构标签的识别有比较严格的前提。
标签要单独占一行,放在它所描述的段落开头,用方括号包起来,常见的有主歌、副歌、桥段、前奏、尾奏、纯器乐段这几类。渲染成音频时,它影响的是段落的能量走向和配器密度,比如标了副歌的地方人声会更靠前、伴奏会更满,标了纯器乐段的地方会自然地把人声让出来。我第一次写歌词的时候把标签和歌词挤在同一行,结果模型完全无视,从头到尾一副平铺直叙的调子,副歌没有任何推力。
另一个前提是标签要符合音乐上的常识顺序。你写一段前奏接一段副歌再接一段前奏再接一段主歌,模型虽然不会报错,但生成出来的段落边界会很模糊,因为它内部的音乐结构先验被破坏了。老老实实按主歌、副歌、主歌、副歌、桥段、副歌的顺序写,出来的东西最稳。
还有一个细节是段落之间的空行。保留空行有助于模型对齐段落边界,尤其是当你想让某一段更短的时候,空行加上短句的组合比单纯写短句更容易被识别。
3.2 歌词字数和生成时长之间的换算关系
这是最容易翻车的地方,我甚至觉得它比参数调优更重要。
模型需要在有限的 token 预算里把歌词唱完。歌词给得太多、时长上限给得太小,结果是后半段被硬生生砍掉,或者为了塞进去而加快语速,听起来像在赶时间。反过来,歌词太少而时长上限给得太大,后半段会变成纯器乐,甚至开始循环前几句,听起来像卡带。
我的经验换算大致是这样:一段主歌四到六行、每行八到十二个字,大致对应三十到四十秒的音频。整首歌如果按主歌两段、副歌三段算,总歌词量控制在百字上下,对应时长三分钟左右比较舒服。这个比例不是精确公式,因为不同语言的音节密度差别很大,英文一个单词占的时间可能比中文一个字长,所以换语言的时候要重新试一遍。
实践里的做法是:先按经验值写好歌词,跑一遍听哪里被截断,然后只调max_new_tokens这一个变量,每次加一两百,直到不截断且末尾不空转。不要同时改歌词和参数,否则你永远搞不清是哪个在起作用。
3.3 风格描述词的粒度选择
风格描述的写法决定了"这首歌像什么",但它的最佳粒度跟直觉相反,不是越详细越好。
太短的描述比如只写一个流行,模型会给出一个非常中庸的结果,编曲往往偏保守,人声也偏模板化,听上去像广告配乐。太长的描述比如堆上十几个形容词加五种乐器加三种情绪,模型反而会顾此失彼,最后呈现出一个四不像。
比较有效的结构是三到五个维度,每个维度一个词:整体类型、人声特征、主要乐器、情绪基调。比如一首抒情歌可以写成整体偏流行、女声、钢琴主导、情绪克制这种组合。想再精确一点,可以加上节奏快慢或者年代感这类描述。关键是每个维度只给一个主选项,不要在同一维度上写互相冲突的词,比如同时写温柔和爆发,模型会随机挑一个,你两次跑出来的结果差异会很大。
另外,风格描述和歌词的语言最好统一。中文歌词配英文风格描述能跑,但模型在跨语言对齐上会弱一些,稳定做法是用歌词所在语言的描述词。
| 描述维度 | 推荐给几个词 | 常见写法 | 容易出问题的写法 |
|---|---|---|---|
| 整体类型 | 一至两个 | 流行、民谣、轻摇滚 | 同时写流行和重金属 |
| 人声特征 | 一个 | 女声、男声、童声 | 同时写男声和女声 |
| 主要乐器 | 一至两个 | 钢琴、木吉他 | 列五个以上乐器 |
| 情绪基调 | 一个 | 明亮、克制、忧伤 | 同时写忧伤和欢快 |
| 节奏 | 可选一个 | 中速、慢速 | 与整体类型冲突 |
4. 采样参数调优:糊、卡、跑调、重复都是怎么来的
4.1 重复惩罚和温度之间的相互拉扯
生成式模型都有一组采样参数,YuE 这套参数的作用方式和大语言模型很像,但表现出的症状完全不同。
重复惩罚负责压制"原地打转"。这个值设低了,模型会在某一句上反复循环,尤其是副歌的第一句,因为它在训练数据里高频出现,模型很容易陷进去。设高了呢,问题会更隐蔽:模型为了避免重复,会强行选择概率很低的 token,表现出来就是突然冒出一个奇怪的音、音高突然跳一下、或者人声破一瞬。我踩过的最典型的一个坑就是把重复惩罚从一点一拉到一点三想解决副歌循环,结果整首歌冒了七八处怪音,比循环本身更难听。后来稳定在一到一点一五这个区间,循环问题改用调整歌词段落的方式来治。
温度控制的是随机性。温度高,创意多但容易跑偏;温度低,输出稳但平。做完整歌曲我倾向设得接近默认值甚至略低一点,因为一首歌是一个整体,任何一段出格都会破坏整体感。如果你在找灵感阶段,可以临时调高温度多跑几版听个大概方向,定下来之后再用低温度精跑。
这两个参数最好不要同时动。固定一个、只调另一个,每次只跑一版,对比着听,这样才分得清因果。
4.2 咬字不清的三种成因和对症改法
人声糊是所有人都会遇到的第一类问题,但它其实有三种完全不同的原因,用错药方会越调越差。
第一种是歌词信息密度超过模型的演唱能力。中文尤其明显,同样时长里塞进太多字,模型为了赶节奏会把音节连读,听上去像含着一口水。改法只有一条:删字。把每行控制在十个字以内,把长句拆开。
第二种是风格描述和人声特征冲突。比如你写了低沉的男声,歌词却是密集高亢的句式,模型会在两个目标之间摇摆,输出的人声就会发虚。改法是让风格描述里明确人声特征,并且只写一个。
第三种是最容易被误判的,其实不是人声糊,而是伴奏把人声盖住了。因为模型输出的是混好的音频,编曲偏满的时候人声会被压下去,听感上像咬字不清。这种情况调参数没用,正确做法是用音源分离工具把人声轨拆出来单独听,确认人声本身是清楚的,然后在后处理阶段重新平衡两轨的音量。我有一版歌折腾了半天参数,最后发现人声轨拎出来非常干净,纯粹是伴奏太满。
4.3 结构塌陷和段落重复的处理思路
另一个高频问题是歌跑着跑着结构塌了,副歌和主歌的差别越来越小,最后变成一大段没有起伏的音频。
成因通常是歌词量超出了模型能维持结构的长度。模型的注意力是有限的,段落一多、每段又长,它就开始走捷径,把所有段落唱成同一个调子。解决办法是缩短单段长度、用分段生成的方式跑,然后在拼接处做一点过渡处理。分段生成的代价是段落衔接处可能有一点点不自然,但比起结构塌陷,这个代价完全值得。
还有一种情况是副歌被完整重复了两遍甚至三遍。如果歌词里本来就有重复的副歌,这属于正常;如果歌词里只有一段副歌而输出里出现多遍,说明重复惩罚需要稍微上调一点点,或者把副歌的歌词换几个字,打破完全一致的 token 序列。这个技巧很土但有效:把第二遍副歌的结尾改一两个字,模型就不会把它当成可以无限延续的模式。
5. Stage2 之后的收尾工作:分轨、响度与交付
5.1 拆人声和伴奏为什么比想象中重要
模型直接给的是混音成品,但真正拿去用的时候,你几乎一定需要分轨。
做视频配乐时,人声和伴奏的音量比需要按画面调;做小样时,你可能要保留伴奏替换人声,或者保留人声重做编曲;做纯音乐版本时,直接把伴奏轨拎出来就能用,比重新生成一首纯器乐效率高得多。这些需求都要求你手里有分离的两轨。
分离工具用现成的音源分离方案就行,选人声两轨模式。因为原始音频本身就是人声加伴奏的结构,没有太多现场环境音和复杂混响,分离质量通常不错。需要注意的一点是,分离之后的伴奏轨在低频部分会有一点损失,如果要用它做正式出版,建议还是在 DAW 里补一层低音。
实际操作里我会把分离出来的两轨都存成无损格式,然后再做后续处理,不要在有损格式上反复转码。
5.2 响度对齐和导出格式的选择
AI 生成的音频有一个通病,就是不同版本之间的响度差异很大,同一批生成五首,可能有两首明显比别的轻。如果直接放到同一个播放列表里,听感会非常割裂。
处理办法是做响度归一化,找一个目标响度值,把所有音频统一到同一个水平。命令行工具一行就能完成,不需要进 DAW。归一化的时候要注意别把动态压死,选那种只调增益不做压缩的模式,保住原来的起伏。
导出格式上,中间产物一律用无损格式,最终交付再看用途。上传到平台用常见的有损格式就够了,但码率别压得太低,人声的齿音部分对码率比较敏感。另外记得检查一下音频开头和结尾,模型生成的音频有时开头会带几十毫秒的空白,结尾会拖一小段静音,剪掉之后听感会紧凑很多。
# 响度归一化示例,只调增益不做动态压缩 ffmpeg -i input.wav -af "loudnorm=I=-14:TP=-1.5:LRA=11" -ar 44100 output.wav5.3 批量生成时的任务编排
单首跑通之后,真正的工作量在批量上。我的经验是不要在一台机器上并行跑多个实例,模型本身就吃满显存,多开只会互相抢资源,最后全都变慢。正确的做法是串行排队,把歌词和风格描述整理成一批任务清单,写个循环脚本依次跑,中间加上失败重试。
清单最好带一个编号和备注列,记清楚哪条歌词配了哪组风格描述。一次跑十几首之后,你根本记不住第三首用了什么参数,没有记录就只能重跑。我自己用的是一张表,每行一首歌,记录歌词文件路径、风格文件路径、生成的音频文件名、初步评价,跑完一轮之后按评价排序挑选。
跑批的时候建议分两阶段跑:先用很短的上限快速跑一遍所有歌词的前三十秒,听主歌第一句的咬字和整体感觉,筛掉明显不行的;再对留下来的完整跑。这样能省掉大量时间,因为一首歌的行不行,通常在第一个乐句里就能听出来。
6. 把 YuE 接进真实工作流:几种玩法和该守住的边界
6.1 当小样机用,把创作环节拆开
最实用的场景就是当小样机。写歌这件事的瓶颈往往不在旋律,而在"我脑子里的旋律到底是什么样"。有了全曲生成,你可以先不纠结编曲和演唱,把歌词写成能唱的格式,让模型给你一版有声的,然后坐在那儿听,判断这首歌的旋律骨架立不立得住。
这一步筛完之后,真正的编曲、录音、混音环节才有意义。我现在的习惯是每首歌至少让模型出三个版本,用不同的风格描述,因为同一份歌词在不同风格下的可塑性差很多,有时候一首本来打算做民谣的词,用偏摇滚的处理反而更带感。这种试错在传统流程里成本极高,需要找人编曲、找人唱,现在就是几分钟的事。
反过来说也要清楚它的边界。它生成的东西细节上是经不起推敲的,尤其是编曲的层次和人声的呼吸感,跟真人录出来的差距还是很明显。所以它适合做筛选和方向验证,不适合直接当成品交付。
6.2 风格注入和微调的现实预期
如果你手里有大量自己风格的素材,会自然想到微调。这件事能不能做,取决于你的目的。
如果只是想固定某个音色或者某种编曲习惯,上下文学习这条路更划算:准备几段干净、结构完整的参考音频,在提示里带上,模型会明显往那个方向靠。这种方式不需要训练,改参考素材就能换风格,迭代速度快。
如果要微调权重,投入产出比需要认真评估。数据方面你需要的是成对的高质量素材,歌词和对应音频严格对齐,格式干净没有杂音。计算方面七 B 模型的微调对显存的要求比推理高一个档次,普通消费级显卡基本只能做小规模参数高效微调,训练时长以天计。我的判断是,除非你要做一个持续产出的产品,否则先把提示词工程做到位,收益更快。
6.3 授权和商用这件事必须提前确认
最后说一件容易被跳过但很重要的事:能不能商用以权重文件随附的许可说明为准,不同版本、不同发布批次的条款可能不一样,用之前一定去仓库里把许可文件读一遍,不要看二手转述。
即便许可允许,模型生成内容的原创性、和你自己已有作品的相似度,这些都需要你自己判断和承担。实践中我会做两步:一是生成之后用音频指纹类工具过一遍,确认没有和已知作品高度重合;二是保留下生成过程的记录,包括歌词文件、风格描述、参数配置,万一有争议至少有据可查。
我个人最后的体会是,把它当成一个需要磨合的合作者而不是一个按钮。第一版永远不好听,把歌词结构调两轮、把风格描述收敛到三四个维度、把重复惩罚压在一到一点一五之间,第三版开始才会有能听的东西。还有个小技巧我一直在用:同一份歌词别只跑一遍就下结论,改掉其中一句的结尾两个字再跑一次,两版对比着听,往往能听出哪一版的人声走向更贴合你原来的想法,这比死磕参数快得多。