数据流水线:300M note events → 18M
simedw 的数据流水线把 300M note events 砍到 18M,砍掉 94%,模型反而更好。
输入
- 原始 MIDI 文件池:60 万文件
- 总 note events:~300M
- 来源:BitMidi + Classical Archives + 自家私藏
输出
- 高质量 MIDI 文件:18 万
- 总 note events:~80M(不是 18M,这是个错)
实际数字:以 MIDI 文件数计 60 万 → 18 万,砍 70%。以 note events 计 300M → 80M,砍 73%。
我标题写的「300M → 18M」是简化版,不精确。真实是「300M → 80M」或「60 万文件 → 18 万文件」。
5 步流水线
第 1 步:钢琴主导筛选
每个 MIDI 文件跑音色识别,留 piano track 占比 > 80% 的。
实现:
- 用 pretty_midi 库解析。
- 统计每个 track 的 instrument program(General MIDI 标准)。
- piano = program 0-7(acoustic / electric piano / harpsichord / clavinet)。
- 计算 piano track 时长 / 总时长。
80% 保留,否则砍。
60 万 → 35 万(砍 42%)。
第 2 步:多轨病理混录砍掉
同时有 > 4 个 track 的录音通常是大混录,砍。
实现:
- 统计每个 MIDI 的 track 数。
4 个 track 的砍(歌剧 / 交响 / 室内乐)。
35 万 → 28 万(砍 20%)。
第 3 步:维度筛选
按密度 / 音域 / 时间覆盖筛。
密度(每分钟 note 数):30 - 300 保留。
- 太稀疏:环境音乐 / 练习 demo。
- 太密集:鼓机 pattern / 特效。
音域:A0-C8 保留。
- A0 = MIDI 21,C8 = MIDI 108。
- 超出钢琴音域的 MIDI 几乎都是合成器 / 鼓机。
时间覆盖:30 秒 - 15 分钟保留。
- 太短:碎片 demo。
- 太长:交响乐总谱。
28 万 → 24 万(砍 14%)。
第 4 步:指纹去重
忽略全局移调(+12 / -12 半音视为同一首)+ 等比 tempo(1.5x / 0.5x tempo 视为同一首),算哈希去重。
实现:
- 把每个 MIDI 的所有音高减去中位数音高,得到相对音高序列。
- 把每个 MIDI 的 onset 时间归一化到总时长 1。
- 算编辑距离(Levenshtein 变种),低于阈值视为同一首。
- 保留时长大 / 演奏 quality 高的版本。
这一步把「同曲不同版本」从 12% 砍到 0.3%。
24 万 → 21 万(砍 12.5%)。
第 5 步:同曲不同版本归到同一 split
避免一首练习曲的 5 个版本分别进 train / test 造成数据泄漏。
实现:
- 第 4 步的「同一首」hash → 同一 split。
- 同一 hash 的 MIDI 都进 train 或都进 test。
这一步不删 MIDI,但改变它们的 split 归属。
21 万 → 21 万(不删数,只调 split)。
流水线总效果
| 步骤 | 文件数 | 砍掉比例 |
|---|---|---|
| 输入 | 60 万 | - |
| 1. 钢琴主导 | 35 万 | 42% |
| 2. 多轨砍 | 28 万 | 20% |
| 3. 维度筛 | 24 万 | 14% |
| 4. 指纹去重 | 21 万 | 12.5% |
| 5. split 归一 | 21 万 | 0% |
| 输出 | 21 万 | 65% |
note events 总数:300M → ~95M(砍 68%)。
5× 失败对照
simedw 试过扩到 5× 数据(60 万原始 MIDI 扩到 300 万)。结果模型更差:
- 验证 loss:略升
- rollout 质量:明显变差(更多重复 loop)
- 训练时间:5×
原因:300 万 MIDI 里大量是非钢琴主导、多轨病理、维度异常的噪声数据。
5× 失败 → 回到 5 步流水线 → 21 万高质量 → 模型偏好 +5%。
一人工程的数据工程
OpenAI 的数据工程:
- 100+ 数据工程师。
- 多个爬虫 + 多个清洗流水线 + 多个 dedup 任务。
- A/B 评估每一步的边际效果。
- 几百亿 token 的清洗能力。
simedw 的数据工程:
- 1 个作者。
- 1 个 5 步流水线(Python 脚本 ~500 行)。
- 跑一遍 60 万 MIDI → 21 万。
- 没有 A/B 评估每一步(5 步是「常识组合」不是「逐步优化」)。
一人工程的数据工程 = 一个人 + 一个 Python 脚本 + 几小时。
OpenAI 不可能这么干——他们的数据池是几百亿 token,必须工业流水线。simedw 的 60 万 MIDI 是「人能在自己电脑上迭代」的规模。
5 步的工程哲学
5 步的选择不是「最优 5 步」,是「一人工程能写出来的 5 步」:
- 第 1 步钢琴主导:常识筛选。
- 第 2 步多轨砍:常识筛选。
- 第 3 步维度筛:常识筛选。
- 第 4 步指纹去重:常识筛选。
- 第 5 步 split 归一:常识筛选。
没有「自研新算法」,没有「专门训练的去重模型」。每一步都是「数据工程教科书的标准做法」。
simedw 的优势不是「发明了新的数据清洗方法」,是「把教科书做法组合成 5 步流水线 + 跑完 60 万 MIDI」。
一人工程的方法学 = 「已知方法 + 组合 + 跑完」。
14 次实验里数据流水线的位置
- 实验 0(实验前):60 万 MIDI → 21 万 = 5 步流水线。
- 实验 1-3:MIDI 表征(在 21 万 MIDI 上训)。
- 实验 4-7:DPO β 调优(在 21 万 MIDI 上训)。
- 实验 8:scheduled sampling(在 21 万 MIDI 上训)。
- 实验 9-14:蒸馏 / 量化 / 部署 / 审核(在 21 万 MIDI 上训)。
5 步流水线是「14 次实验的地基」。实验 0 不是 simedw 的「正式开始实验」,是「给实验 1-14 准备数据」。
没有 5 步流水线,13 个实验都是空中楼阁。
流水线输出 → 模型表现
- 60 万原始 MIDI 训出来的模型:偏好率 75%(vs base 24.55%)。
- 21 万清洗 MIDI 训出来的模型:偏好率 82%(vs base 24.55%)。
「砍 65% 数据,偏好率涨 7 个百分点」。直觉上不可思议——直觉是「数据多 = 模型好」。
但「数据少 + 高质量」>「数据多 + 噪声」。
清洗胜过堆量的数学:
- E_loss ∝ |D_clean| / σ²(D_clean) + 常数
- 砍 65% 数据 → |D| × 0.35
- 砍 90% 噪声 → σ² × 0.10
- 净效果:E_loss × 0.35 / 0.10 = 0.35 × 10 = 3.5× 改善
实测偏好率从 75% → 82%(绝对值 +7)。3.5× 改善对应偏好率「在 sigmoid 中间段」的 +7% 是数学一致的。
signature
solus opus。