📌 隶属于架构蓝图: 企业级 AI Agent 与本地 GPU 算力自动化 → L4: 多模态 AIGC 工业化生成流水线

为什么我的 AI 歌曲总是丢结尾:在 12GB 显卡上跑 YuE2

· 31 分钟 · 人工智能 yue2comfyuiai-musiclocal-aigpuvrammandarin

YuE2 是一个 30 亿参数的开源音乐模型,能按风格提示词和歌词唱中文。我把它放在自己那台 12GB 显存的 RTX 3060 上用 ComfyUI 本地跑,一次排了 12 首原创歌 —— 结果第一批出来,每一首的时长都恰好是 240.000 秒。这个数字不是巧合,而且是个坏消息。

每首都恰好 240.000 秒

12 个文件,时长精确到毫秒完全一致:

01_A1_..._糖画小仙.mp3   duration=240.000000
02_A2_..._溪边捉云记.mp3  duration=240.000000
03_A3_..._寒露拾桂子.mp3  duration=240.000000
...

我设的是 max_duration=240,因为参考指南说 12 段结构大约会落在三分半左右。结果它精确落在 240 秒 —— 这意味着模型从来没有”决定结束”,它是把时间用光了。

整数是一种自白

YuE2 用 ABC 规划器来编排:读取歌词,给每一段分配小节数,生成一份符号化的曲谱草稿。音乐生成器照着这份草稿唱,草稿结束它就结束。

所以一首真正唱完的歌,输出时长一定是小于你设的上限的 —— 它自己停下来了。

判断方法只要一条命令:

d=$(ffprobe -v error -show_entries format=duration -of csv=p=0 take.mp3)
python -c "print(abs(float('$d') - 240.0) <= 0.005)"   # True = 被硬切

如果时长和 max_duration 相差不到几毫秒,这次渲染就是被截断的。歌词需要的时间超过了上限允许的范围,切点那一句被拦腰砍断。

确认尾奏真的丢了

光看时长只能知道”被切了”,不知道”丢了什么”。所以我把每首的尾巴切出来,用本地语音模型(SenseVoice,跑在 CPU 上,GPU 渲染时不用停)转写:

ffmpeg -v error -y -i take.mp3 -ss 232 -t 20 -ar 16000 -ac 1 -c:a pcm_s16le t.wav
curl -s -X POST http://127.0.0.1:20129/v1/audio/transcriptions \
  -F "[email protected]" -F "language=zh"

(对于连末遍副歌都没唱到的曲目,我将切片窗口向前扩展到了最后 30 秒即 -ss 210,以准确定位截断发生时的唱段。)结果没有歧义:

歌唱到哪缺了什么
糖画小仙末遍副歌最后一句整段 [Outro]
溪边捉云记215 秒还停在 [Bridge]Solo + 末遍副歌 + Outro
寒露拾桂子215 秒还停在 [Bridge]末遍副歌 + Outro
纸鸢与旧木偶末遍副歌第 5 句副歌后半 + Outro

所以不是混音问题、也不是淡出 —— 是结构不完整。

试过但没用的三件事,和半有用的一件

1. 提高 BPM —— 完全无效

直觉上:节奏更快、每秒塞进更多字、同样的窗口能装更多歌词。我在整批上验过:117 BPM 的国风组和 140 BPM 的 DJ 燃曲组,吞吐几乎一样。

YuE2 的时长来自 ABC 规划器分配的小节数,不看你风格提示词里的 BPM 标签。BPM 只影响”感觉”,买不到时间。别在它身上调时长。

2. 同 seed 重跑 —— 静悄悄地什么都没做

ComfyUI 是用 --cache-classic 启动的,所以重新提交一个逐字节相同的任务(同样的风格、同样的歌词、同样的 seed)会直接命中节点缓存。任务”完成”于 10 秒左右,还你一份一模一样的截断音频,文件名加个 _00002。

看起来像一次又快又成功的重试。实际上是命中了一个已知有问题 take 的缓存。

3. 换 seed —— 四次里能救三次

规划器分配小节的方式依赖 seed,所以换个 seed 确实可能给出更短、能唱完的版本。这一招救回了我四首里的三首:

歌原始新 seed结果
溪边捉云记卡在 281 秒20002279.7 秒,完整
外婆的绿搪瓷碗卡在 300 秒20007251.9 秒,完整
北溟有小鱼卡在 300 秒20005、30005再次撞顶

预算是”每秒几个字”,而这个数还在变

我试过把它做成算术:数字数、除以吞吐、得出时长。实测不配合。

歌汉字数上限实际装下了吗
糖画小仙556272 秒258.7 秒✅
纸鸢与旧木偶572300 秒279.9 秒✅
溪边捉云记578281 秒281.0 秒❌ 被切
小楼檐下听雨眠580300 秒256.3 秒✅
画卷小锦鲤634300 秒285.9 秒✅
三楼窗台的蒲公英693329 秒247.4 秒✅
弄堂口的竹蜻蜓692300 秒250.0 秒✅

把最后两行和第三行对比着看:693 个字在 247 秒内唱完了,578 个字却撑爆了 281 秒。 字数换算时长的公式在这里根本不存在 —— 实测吞吐落在每秒 2.0 到 2.8 个汉字之间,取决于规划器怎么切段落。

所以字数只适合用来预警预算,不适合用来掐时间。

真正卡住的瓶颈是显存

一旦接受”给它足够空间、让它自己收尾”,上限自然想设大一点。但在 12GB 卡上设不大。

渲染过程中每 5 秒采一次 nvidia-smi 得到的峰值显存:

max_duration峰值显存
240 秒10.3 GB
272 秒11.65 GB
278 秒11.07 GB
281 秒12.06 GB
300 秒12.03 GB

显存总量为 12,288 MiB(12 GB)。300 秒就是实际天花板 —— 再往上,你离”渲染到第六分钟时因为一个浏览器标签页而 OOM 崩掉”只差一步。

解法

三处改动,都很便宜:

  1. 把上限设到 300 秒,让规划器自己收尾。 不要追目标时长;给足余量,它会在音乐结束的地方结束。
  2. 被切的 take 归档,不要混进成品。 时长等于上限的 take 移到 未自然结尾\ 目录。成品目录里就只有完整的歌,而且重跑永远不会覆盖掉一个可用的 take。
  3. 换 seed 最多试三次。 换 seed 有用,但如果一首歌的歌词真的超过显存能撑的时长,它会永远失败。三次不成就动歌词 —— 别烧一小时 GPU 假装下一个 seed 会中。

那首始终装不下的歌

12 首里有一首没做成。北溟有小鱼,593 字,失败了五次:

尝试参数结果
1seed 10005,上限 287 秒撞 287 秒
2seed 10005,上限 300 秒撞 300 秒
3seed 20005,上限 300 秒撞 300 秒
4seed 30005,上限 300 秒撞 300 秒
5seed 40005,删掉两段纯器乐撞 300 秒

我甚至把 [Interlude] 和 [Instrumental Solo] 两个标记删掉 —— 从 12 段降到 10 段,一个唱词都没动 —— 它还是溢出。它的规划器要的时间超过了这张卡能给的上限。诚实的解法是砍掉大约 40 个字再重跑;我宁可告诉你这件事,也不想交一首结尾被咬掉一块的第十二首歌。

在这之前踩到的一个坑

值得单独警告:YuE2 会把你歌词里括号内的中文制作备注唱出来。

如果你贴的歌词长这样(大多数歌词稿就是这么写的):

[Intro]
(重鼓点起,急促琵琶轮指,唢呐破空,合成器铺底)
嘿!哈!山海震!

那么那行括号内容就成了一句歌词。模型不知道那是舞台说明。提交前先清掉:

clean = "\n".join(l for l in raw.split("\n")
                  if not re.match(r"^\s*[((].*[))]\s*$", l))

配器和动态写进风格提示词。歌词框里只留段落标签和要唱的字。

我会怎么改

十一首歌

下面这些是自然收尾的成品,女童声、中文、跑在一张 12GB 显卡上,成本比一年的云端音乐额度还低。每个播放器都带同步歌词 —— 因为模型不输出时间戳,时间轴是用转写结果和歌词原文对齐反推出来的。

1. 糖画小仙 — Sugar-Painting Fairy

Guofeng electronic · 556 characters · 259 s

2. 溪边捉云记 — Catching Clouds by the Stream

Guofeng electronic · 578 characters · 280 s

3. 寒露拾桂子 — Picking Osmanthus at Cold Dew

Guofeng electronic · 578 characters · 290 s

4. 纸鸢与旧木偶 — The Kite and the Old Puppet

Guofeng ballad · 572 characters · 280 s

5. 小楼檐下听雨眠 — Listening to Rain Under the Eaves

Guofeng ballad · 580 characters · 256 s

6. 外婆的绿搪瓷碗 — Grandma’s Green Enamel Bowl

Urban folk · 679 characters · 252 s

7. 弄堂口的竹蜻蜓 — The Bamboo Dragonfly at the Alley Mouth

Urban folk · 692 characters · 250 s

8. 三楼窗台的蒲公英 — Dandelion on the Third-Floor Windowsill

Urban folk · 693 characters · 247 s

9. 小哪吒借红绫 — Little Nezha Borrows the Red Sash

Guofeng DJ remix · 612 characters · 271 s

10. 画卷小锦鲤 — The Koi That Left the Painting

Guofeng DJ remix · 634 characters · 286 s

11. 山海小当家 — Little Boss of the Classic of Mountains and Seas

Guofeng DJ remix · 647 characters · 237 s

🚀 企业智能化与业务自动化

想要一个 24/7 自动接单、发试用、不漏单的业务自动化系统?

别让夜间和周末的客户询盘因为“没人回”而流失给竞争对手。我帮中小企业定制 Telegram / WhatsApp 智能客服与全自动履约系统(打通商品库、CRM 与自动发货,无高昂 SaaS 人头费)。

Lee Teong Hoe

Lee Teong Hoe · Mr Hoelee

Full-stack developer & DevOps engineer based in Malaysia. I run my own production infrastructure and build web systems for small businesses — this blog is the living record.

Hiring? Profile & stack · Malaysian SME? Website design & development ↗ · All posts