一人工程 · solus opus

← 全部作品

simedw一人工程数据流水线MIDI清洗5 步18 万

数据流水线:300M note events → 18M

数据流水线: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。