
给钢琴接上 AI 自动续写:125M 参数模型从数据清洗到 DPO 的完整实录
一个 125M 参数的小模型如何在 iPhone 上实时续写钢琴演奏?作者复盘了 MIDI 表示设计、数据清洗、scheduled sampling、DPO 的全过程,以及一堆被验证无效的弯路。
原文来源:Training a 125M-parameter Model to Autocomplete Piano — 一位工程师用 125M 参数 Transformer 做出实时钢琴续写,并完整复盘了从 MIDI 表示到 DPO 的训练全流程。
把 MIDI 键盘连上手机,弹几个音,AI 帮你把曲子续写完——听起来像科幻,但一位叫 SimEdw 的工程师真的把它做成了。他训练的模型只有 125M 参数,却能在 iPhone 15 上以每秒 108 个音符的速度实时续写演奏。这个项目花了他近一年时间,中间经历了 14 轮实验迭代。他写下的这篇复盘非常坦诚:哪些设计有效、哪些彻底失败、为什么数据清洗比加数据更重要,全都讲清楚了。
核心难题:MIDI 怎么变成模型能读的序列
MIDI 文件和 MP3 完全不同。它不存储录音,而是存储一连串事件:某个键被按下(音高 + 力度)、某个键被释放、延音踏板踩下,诸如此类。要训练 Transformer 自动续写,第一步就是把事件流变成离散 token。
最直观的做法是给每个事件一个 token:NOTE_ON_60_80(音高 60、力度 80)、NOTE_OFF_60、TIME_SHIFT_12。但这样词表会爆炸——128 个音高 × 128 个力度组合,仅 note-on 就需要 16512 个 token,很多组合非常稀疏,模型要从大量罕见 token 里学结构,效率很低。
改进思路是用语法分解:[NOTE_ON, PITCH, VELOCITY] 拆成三个独立 token,生成时用掩码强制合法顺序。作者试过,模型确实不再产出语法错误,但慢——一个音符要消耗 4 个自回归步,上下文窗口也烧得快。
—— 广告 ——
最终方案:一个音符一个 token
绕了一圈,作者最终采用的表示非常优雅:让模型一次前进一个完整的音符,而不是逐步生成音符的属性。
NOTE(C4, delta=0, duration=12, velocity=80)
NOTE(D4, delta=24, duration=12, velocity=80)
音符之间不需要单独的 TIME_SHIFT 事件,休止时长直接编码进下一个音符的 delta(距上一个音符起始的时间步)。和弦就是多个 delta=0 的音符按音高排序。内部实现上,每个音符有 5 个分类字段(event_type、pitch、delta、duration、velocity),各字段有自己的词表和 embedding,音符 token 是所有 embedding 的和。模型有多个输出头,字段间夹一个小型嵌套 decoder 让后面的字段能条件化于前面已预测的字段——但昂贵的 Transformer 主干每个音符只跑一次。
这个设计把大模型的生成速度推到了每秒 108 个音符,远超真人演奏所需。节奏量化到每四分音符 24 步,既能覆盖常见的三连音,也能容纳作者自己弹琴时"差一点点踩在拍子上"的节奏瑕疵。
延音踏板的处理也值得一提:作者没有引入踏板事件,而是在预处理时把踏板的影响"烘"进音符时长里——键在踏板踩下时释放,就把音符延长到踏板抬起;同音高再次按下则截断前一个音符。这让模型只需预测音高、起始、时长、力度四件事,问题大大简化。
数据清洗:5 倍数据不如更干净的数据
数据集来自公开 MIDI 合集,最终约几十万首、3 亿个音符事件。但质量参差不齐,作者写了一大堆清洗脚本:
- 只保留钢琴向内容,剔除或弱化多轨混合
- 按密度、音高/时间覆盖过滤
- 用忽略全局移调和统一速度变化的指纹去重
- 同一首曲子的不同版本归入同一个数据划分,避免训练集/测试集泄漏
他试过把数据扩到 5 倍,结果模型反而更差。清洗和挑选比单纯堆数据重要得多。
训练技巧:scheduled sampling 反直觉地有效
训练目标是五个输出头的交叉熵之和(type + pitch + delta + duration + velocity),方便单独追踪各项准确率。
数据增强很重要,因为实际输入是"作者弹得乱七八糟的现场演奏":全局移调、速度缩放、时长/力度抖动、丢弃提示音符。
模型是标准的 decoder-only Transformer:RMSNorm、RoPE 旋转位置编码、因果注意力、SwiGLU。训练了三个尺寸:33M、64M、125M。
最有意思的是 scheduled sampling。训练时 duration 和 velocity 的预测通常能看到正确的 pitch,但推理时它们只能拿到模型自己预测的 pitch。作者于是在训练中逐步混入"喂模型自己的预测"——从 0% 逐渐升到 50%。结果非常反直觉:验证损失变差了(2.9998 vs 2.9495),但生成质量反而明显提升——Gemini 成对评测中,带 50% scheduled sampling 的模型 64.3% 被选为更好,对照组只有 35.7%。
评估与 DPO:让模型学会"好听"
音乐续写没有唯一正确答案,交叉熵只教模型"音符怎么接",不教"什么样的续写好听"。作者先用自动指标(重复音高 n-gram、音高熵、音符密度等)抓明显失败,然后用 Gemini 3.5 Flash 做成对偏好评测。给绝对分数不稳定,但问"A 和 B 哪个续写更好"就稳定得多——他还做了镜像对比消除位置偏差。
这个偏好数据集喂给 DPO 后效果显著:基座模型只有 24.55% 的续写被偏好,DPO 后达到 69.05%。关键超参数 β 不能太大:β=0.01 和 0.03 都有效,β=0.10 推得太狠反而变差。他还发现用"共识数据集"(只保留评测者一致同意的偏好对)效果最好。
什么没用:一条诚实的弯路清单
- Note-on/note-off 表示:小模型实时场景下漂移严重,会忘记发 note-off,音符悬挂
- 语法掩码 token 流:合法但太慢
- 更杂的数据:噪声数据扩量只会变差
- 更大模型:有帮助,但没魔法般解决循环问题
- Mirostat 采样:减少重复但输出常不连贯
- 额外的辅助损失:训练更慢,没有听得见的收益
- 绝对分数评测:不如成对评测
- 验证损失:单独看会误导,错过了 rollout 质量的差异
- Born-again networks(用自己的软预测重训):没提升
部署:Core ML + INT8 量化
模型导出到 Core ML,权重量化到 INT8。首次启动仍慢,因为 Apple 运行时要为当前硬件优化模型。模型只训练过最长 512 音符的上下文,为支持更长会话,上下文接近上限时保留最近 384 个音符重建——KV 缓存要重算,但模型够快所以问题不大。理论上可以用 RoPE 的位置偏移 + 环形缓冲区做更优雅的方案,但 Core ML 不暴露 Q、K、V,做不了。
作者的结语很实在:这个项目远不完美,偶尔会循环,短提示很难,但"我终于到了愿意坐在钢琴前弹几个音、看 AI 能和我一起创作出什么"的阶段。一个 125M 参数的小模型,配合干净的表示、克制的训练目标和诚实的评估,就能在端侧做出相当可用的创意工具——这本身就是一个关于"小模型如何被认真对待"的样本。
© 2026 四月
原文链接:https://www.aprilzz.com/ai/midi-autocomplete-piano-ai
相关文章
Anthropic 发布 Claude Opus 5:近 Fable 级别的智能,一半的价格
Anthropic 于 2026 年 7 月 24 日发布 Claude Opus 5,全新旗舰模型在编码、科学研究和知识工作等多项基准上超越前代 Opus 4.8 近两倍性能,定价仅为 Fable 5 的一半
拜耳与 Thoughtworks 联手打造 PRINCE:一个可靠的 Agentic RAG 系统从零到生产
从关键词搜索到多智能体协同,拜耳的临床前药物研发平台展示了构建生产级 AI 系统的工程实践:上下文工程、缰绳工程、回退机制与可观测性
LLM 不再「简单」:从 Transformer 堆叠到异质化架构的进化之路
Meta 前工程师 Ian Barber 撰文指出,LLM 架构已经走过了简单的 'Transformer 堆叠' 时代,进入了类似推荐系统的异质化复杂阶段。注意力变体激增、MoE 全面扩散、多模态深度集成——现代 LLM 的复杂度已不可同日而语。