Repository Wiki
bytedance/piano_transcription

端到端训练脚本 runme.sh 指南

runme.sh 是 bytedance/piano_transcription 仓库的一键式流水线入口脚本,它把"预训练模型推理演示 → MAESTRO 数据集打包 → 音符模型训练 → 踏板模型训练 → 双模型合并 → 论文指标评估"六个阶段串联成 8 条顺序执行的命令行调用,是复现论文训练与评估结果的最短路径。

Purpose and Scope

本页覆盖以下内容,并逐一给出对应的真实源码命令与参数说明:

  • runme.sh 脚本本身的完整结构、阶段划分与执行顺序
  • 脚本引用的各 Python 入口(utils/features.py、pytorch/main.py、pytorch/combine_note_and_pedal_models.py、pytorch/calculate_score_for_paper.py、pytorch/inference.py)的调用契约(即如何被脚本调用、传了哪些参数)
  • pytorch/main.py 中训练子命令 train 的命令行参数定义(已在源码中验证)
  • 训练前的环境/数据前置条件、失败模式与工程注意事项

以下相关主题有意留给兄弟页面,本页仅做交叉指引,不展开实现细节:

  • CRNN 网络结构与前向计算 —— 见「模型架构」相关页面
  • inference.py 推理后处理(音符/踏板事件解码)—— 见推理相关页面
  • utils/features.py 内部的 CQT/特征提取实现 —— 见特征工程相关页面
  • 评估指标的计算逻辑—— 见评估相关页面

Overview

runme.sh 位于仓库根目录,共 39 行,是官方 README 推荐的复现入口。它的设计意图是:把一次完整的"从零训练到论文指标"实验固化成一个线性脚本,让用户只需改 3~5 个路径变量即可跑通全流程,而不必逐条记忆每个 Python 入口的参数组合。

脚本分为两大块:

  1. 推理演示块(L3-L8):从 Zenodo 下载官方预训练 checkpoint(CRNN_note_F1=0.9677_pedal_F1=0.9186.pth),并对 resources/cut_liszt.mp3 做一次示例推理。这一块是独立的,不依赖训练块。
  2. 从零训练块(L10-L39):训练流水线本体,依次执行数据打包 → 音符模型训练 → 踏板模型训练 → 模型合并 → (可选)评估。

关键术语:

术语含义
Note 模型Regress_onset_offset_frame_velocity_CRNN,回归 onset / offset / frame / velocity 四类输出的 CRNN
Pedal 模型Regress_pedal_CRNN,回归延音/弱音踏板输出的 CRNN
两阶段训练音符与踏板是两个独立网络分别训练,最后通过 combine_note_and_pedal_models.py 合并为单一 checkpoint
DATASET_DIRMAESTRO 数据集的本地根目录(需用户自行下载)
WORKSPACE工作区目录,训练产物(HDF5 缓存、日志、checkpoint)都写在其下

Architecture

下面是 runme.sh 驱动的完整流水线架构图。每个节点都对应脚本中一条真实命令(或其产物),边表示文件/数据流向:

Loading diagram...

架构要点解读:

  • 单一数据源,双路训练:阶段 1 只执行一次 pack_maestro_dataset_to_hdf5,产出的 HDF5 特征缓存同时供音符模型与踏板模型两个训练进程读取,避免重复计算特征(这是该脚本将打包独立成阶段的核心设计动机)。
  • 合并阶段是"人工接力":脚本 L27 注释明确写着 Users should copy and rename the following paths to their trained model paths,即 NOTE_CHECKPOINT_PATH / PEDAL_CHECKPOINT_PATH 默认值是官方示例名,用户必须手工改成自己训练产物路径后重跑该行。
  • 评估与训练解耦:评估块标注为 optional,infer_prob 与 calculate_metrics 是两步操作——前者用合并后的 checkpoint 在测试集上推理概率,后者基于概率计算论文指标(maestro 与 maps 两个数据集各一次)。

流水线阶段详解

阶段 0:预训练模型推理演示(L3-L8)

脚本开头先做一次"零训练成本"的可用性验证:通过 wget 从 Zenodo(record 4034264)拉取官方发布的 CRNN_note_F1=0.9677_pedal_F1=0.9186.pth(checkpoint 文件名即其论文指标:音符 F1=0.9677、踏板 F1=0.9186),再用 MODEL_TYPE="Note_pedal" 调用 pytorch/inference.py 对一段李斯特音频做转写。这一块的价值在于:即使没有 GPU 训练资源,用户也能立刻验证环境正确性。

阶段 1:MAESTRO 打包为 HDF5(L11-L18)

bash
1# ============ Train piano transcription system from scratch ============ 2# MAESTRO dataset directory. Users need to download MAESTRO dataset into this folder. 3DATASET_DIR="./datasets/maestro/dataset_root" 4 5# Modify to your workspace 6WORKSPACE="./workspaces/piano_transcription" 7 8# Pack audio files to hdf5 format for training 9python3 utils/features.py pack_maestro_dataset_to_hdf5 --dataset_dir=$DATASET_DIR --workspace=$WORKSPACE

Source: runme.sh

三个变量构成整个训练块的全部配置面:

  • DATASET_DIR:用户必须预先下载 MAESTRO 数据集到此目录(脚本不会下载,注释 L11 明确说明)。
  • WORKSPACE:后续所有阶段的输出根目录,也决定了评估阶段读取产物的位置。
  • 打包命令把音频统一转成 HDF5,为两个训练进程提供一次性的、可随机访问的特征存储。

阶段 2:训练音符(Note)转写系统(L20-L21)

bash
# --- 1. Train note transcription system --- python3 pytorch/main.py train --workspace=$WORKSPACE --model_type='Regress_onset_offset_frame_velocity_CRNN' --loss_type='regress_onset_offset_frame_velocity_bce' --augmentation='none' --max_note_shift=0 --batch_size=12 --learning_rate=5e-4 --reduce_iteration=10000 --resume_iteration=0 --early_stop=300000 --cuda

Source: runme.sh

这一行定义了论文的"标准训练配置":augmentation='none'(不使用数据增强)、max_note_shift=0(不做音高偏移)、batch_size=12、learning_rate=5e-4、每 10000 次迭代衰减学习率(reduce_iteration)、从迭代 0 开始(resume_iteration)、300000 次迭代早停(early_stop)、启用 CUDA。

阶段 3:训练踏板(Pedal)转写系统(L23-L24)

bash
# --- 2. Train pedal transcription system --- python3 pytorch/main.py train --workspace=$WORKSPACE --model_type='Regress_pedal_CRNN' --loss_type='regress_pedal_bce' --augmentation='none' --max_note_shift=0 --batch_size=12 --learning_rate=5e-4 --reduce_iteration=10000 --resume_iteration=0 --early_stop=300000 --cuda

Source: runme.sh

与阶段 2 唯一的差别是 model_type 与 loss_type 换成了踏板专用组合(Regress_pedal_CRNN + regress_pedal_bce),其余超参数完全一致——这保证两个模型在相同训练预算与学习率策略下训练,便于公平比较与合并。

阶段 4:合并音符与踏板模型(L26-L31)

bash
1# --- 3. Combine the note and pedal models --- 2# Users should copy and rename the following paths to their trained model paths 3NOTE_CHECKPOINT_PATH="Regress_onset_offset_frame_velocity_CRNN_onset_F1=0.9677.pth" 4PEDAL_CHECKPOINT_PATH="Regress_pedal_CRNN_onset_F1=0.9186.pth" 5NOTE_PEDAL_CHECKPOINT_PATH="CRNN_note_F1=0.9677_pedal_F1=0.9186.pth" 6python3 pytorch/combine_note_and_pedal_models.py --note_checkpoint_path=$NOTE_CHECKPOINT_PATH --pedal_checkpoint_path=$PEDAL_CHECKPOINT_PATH --output_checkpoint_path=$NOTE_PEDAL_CHECKPOINT_PATH

Source: runme.sh

设计意图:训练时两个网络独立(各自的损失与输出头不同),但推理与部署时只需要一个 Note_pedal 模型。此命令把两个 checkpoint 的权重装进同一个合并模型并另存为 output_checkpoint_path。注意脚本给出的三个路径都是官方示例名,必须手工替换为自己训练产物(L27 注释)。这正是本脚本"人工接力"的断点——若直接顺序执行整份脚本,合并步骤会因找不到文件而失败。

阶段 5:论文指标评估(L33-L39,可选)

bash
1# ============ Evaluate (optional) ============ 2# Inference probability for evaluation 3python3 pytorch/calculate_score_for_paper.py infer_prob --workspace=$WORKSPACE --model_type='Note_pedal' --checkpoint_path=$NOTE_PEDAL_CHECKPOINT_PATH --augmentation='none' --dataset='maestro' --split='test' --cuda 4 5# Calculate metrics 6python3 pytorch/calculate_score_for_paper.py calculate_metrics --workspace=$WORKSPACE --model_type='Note_pedal' --augmentation='aug' --dataset='maestro' --split='test' 7python3 pytorch/calculate_score_for_paper.py calculate_metrics --workspace=$WORKSPACE --model_type='Note_pedal' --augmentation='aug' --dataset='maps' --split='test'

Source: runme.sh

评估分两步三命令:

  1. infer_prob:用合并模型(model_type='Note_pedal')在 maestro 测试集上推理概率并落盘到 WORKSPACE;注意此步传 --cuda 且 --augmentation='none'。
  2. calculate_metrics × 2:读取上一步概率,分别在 maestro 与 maps 数据集上计算指标;注意此步不传 --cuda 且 --augmentation='aug'(按论文的增强设定统计)。两个数据集共用同一 --split='test'。

一个值得留意的参数差异:infer_prob 只对 maestro 做推理,而 calculate_metrics 却对 maestro 和 maps 都计算——意味着 maps 的概率文件应在 WORKSPACE 中已有来源(本脚本未包含为 maps 生成概率的命令,属于需要用户按需补充的环节)。

Core Flow:一次完整执行的时序

Loading diagram...

时序中的三个关键点:

  1. 阶段 0 与训练块相互独立:删除 L3-L8 也不影响从零训练。
  2. HDF5 只打包一次:两个 train 调用共享同一份特征缓存,WORKSPACE 是唯一的数据交换媒介。
  3. 唯一的人工断点在 L26-L31:脚本不可全自动跑完,用户须在两次训练结束后回填 checkpoint 路径。

训练参数 API Reference(pytorch/main.py train 子命令)

脚本 L21/L24 的训练命令,其参数在 pytorch/main.py 中通过 argparse 严格定义。以下定义已逐条核对源码:

python
1parser_train.add_argument('--workspace', type=str, required=True) 2parser_train.add_argument('--model_type', type=str, required=True) 3parser_train.add_argument('--loss_type', type=str, required=True) 4parser_train.add_argument('--augmentation', type=str, required=True, choices=['none', 'aug']) 5parser_train.add_argument('--max_note_shift', type=int, required=True) 6parser_train.add_argument('--batch_size', type=int, required=True) 7parser_train.add_argument('--learning_rate', type=float, required=True) 8parser_train.add_argument('--reduce_iteration', type=int, required=True) 9parser_train.add_argument('--resume_iteration', type=int, required=True) 10parser_train.add_argument('--early_stop', type=int, required=True) 11parser_train.add_argument('--mini_data', action='store_true', default=False)

Source: pytorch/main.py

参数速查表(含 runme.sh 中实际取值):

参数类型必填runme.sh 取值说明
--workspacestr✅./workspaces/piano_transcription工作区根目录,HDF5/日志/checkpoint 均写于此
--model_typestr✅Regress_onset_offset_frame_velocity_CRNN / Regress_pedal_CRNN网络结构选择(音符四头回归 vs 踏板回归)
--loss_typestr✅regress_onset_offset_frame_velocity_bce / regress_pedal_bce与 model_type 配套的损失函数
--augmentationstr(枚举)✅none只允许 none / aug,超出取值 argparse 直接报错
--max_note_shiftint✅0音高偏移增强幅度;0 表示禁用
--batch_sizeint✅12训练批大小
--learning_ratefloat✅5e-4初始学习率
--reduce_iterationint✅10000学习率衰减周期(迭代数)
--resume_iterationint✅0恢复训练的起始迭代号;0 表示全新训练
--early_stopint✅300000早停迭代上限(即总训练迭代预算)
--mini_dataflag—未使用仅用小样本数据的调试开关(默认 False)
--cudaflag—已传入启用 GPU;infer_prob/inference.py 同样支持

设计意图解读:train 子命令把所有超参数都设为 required=True(除 --mini_data),不含任何隐式默认值。这是刻意的——让每次实验的完整配置都显式落在命令行(进而落在 runme.sh)里,保证实验可复现、可追溯,避免"默认值漂移"导致的静默差异。runme.sh 因此成为论文配置的单一事实来源。

Failure Modes, Edge Cases & Concurrency

以下事项均可直接从脚本源码推导(无源码可查的部分已明确标注):

场景触发条件脚本行为 / 后果缓解方式
MAESTRO 未下载DATASET_DIR 目录不存在或为空阶段 1 打包命令失败预先下载数据集到 ./datasets/maestro/dataset_root(脚本注释 L11 明确要求)
checkpoint 路径未手工替换顺序执行整份脚本阶段 4 合并命令因找不到 Regress_onset_offset_frame_velocity_CRNN_onset_F1=0.9677.pth 等示例文件而失败训练完成后回填 L28-L30 的三个路径变量
Zenodo 网络不可达wget 下载预训练模型失败仅影响阶段 0 推理演示,不影响训练块阶段 0 可整体跳过
CUDA 不可用无 GPU 环境传了 --cuda 的训练/推理命令无法启用 GPU需移除 --cuda 或改用 CPU 模式(本页不展开具体差异)
maps 指标缺概率文件直接跑 L39calculate_metrics --dataset='maps' 找不到对应概率需额外执行一次针对 maps 的 infer_prob(脚本未提供该命令)
脚本中途失败后重跑任一阶段出错退出脚本无 set -e,默认 shell 下后续命令仍会继续执行建议以 bash runme.sh 分段执行,或逐条复制命令

并发与幂等性说明:

  • 脚本是纯顺序、单进程的:两个 train 命令先后执行,不会同时占用同一 GPU。
  • 阶段 1 的 HDF5 打包是一次性的预计算——若目录已存在同名 HDF5,其覆盖行为取决于 utils/features.py 内部实现(此细节未在本次源码阅读中验证,见该文件对应页面)。
  • resume_iteration=0 表明脚本的默认路径是"全新训练";若要断点续训,需手工修改该值并重跑 L21/L24。

Performance & Operational Notes

  • 总训练预算:两个模型各 early_stop=300000 次迭代、batch_size=12,reduce_iteration=10000 意味着学习率在整个训练过程中按万级迭代阶梯衰减,长尾收敛需要较长时间。
  • 磁盘规划:WORKSPACE 需要容纳 MAESTRO 全量特征的 HDF5 缓存 + 两个模型的 checkpoint + 评估概率文件;MAESTRO 本体(约数百 GB 的 WAV)需另行预留 DATASET_DIR 空间。
  • checkpoint 命名即指标:官方发布的 checkpoint 文件名内嵌 F1 值(如 CRNN_note_F1=0.9677_pedal_F1=0.9186.pth),这是仓库的命名惯例,便于在合并/评估时直接读出模型质量。
  • 评估阶段的 CPU/GPU 分工:infer_prob 带 --cuda(概率推理是 GPU 任务),而两条 calculate_metrics 不带(指标统计在 CPU 上做即可)。
  • 运维建议:把 runme.sh 当作"配置模板 + 命令清单"而非严格自动化脚本使用——先跑阶段 0 验证环境,再逐段执行训练块,最后手工完成合并与评估。

Extension Points

  • 换数据集:流水线对 MAESTRO 的依赖集中在阶段 1 的 pack_maestro_dataset_to_hdf5;替换数据集需提供等价的打包入口(属 utils/features.py 页面范畴)。
  • 启用增强:训练命令中的 --augmentation='none' 与 --max_note_shift=0 是论文基准设定;argparse 允许改为 aug / 非 0 值做增强实验(choices=['none','aug'] 已在源码验证)。
  • 小样本调试:--mini_data 开关(默认关)用于快速冒烟测试整条训练链路,适合在改动代码后验证可跑通。
  • 断点续训:--resume_iteration 是唯一的续训入口,配合 WORKSPACE 中既有的迭代产物即可恢复。
  • 合并模型复用:combine_note_and_pedal_models.py 的三参数接口(note/pedal/output 路径)是独立的通用工具,可脱离 runme.sh 单独调用,用于任意两个训练产物的组合。

Sources

(1 files)