Pop2Piano 深度实战指南:用 Transformers 从流行音频直接生成钢琴翻弹 MIDI
【免费下载链接】transformers🤗 Transformers: the model-definition framework for state-of-the-art machine learning models in text, vision, audio, and multimodal models, for both inference and training.项目地址: https://gitcode.com/GitHub_Trending/tra/transformers
Pop2Piano 是首个无需旋律与和弦抽取模块、直接从流行歌曲音频波形端到端生成钢琴翻弹(Piano Cover)MIDI 的 Transformer 模型。本文以 Pop2Piano 模型文档 为主体,结合 🤗 Transformers 仓库内该模型的完整源码与测试,系统讲解其工作原理、环境安装、推理配置与多场景实战用法,帮助你直接复现"上传音频 → 生成可播放的钢琴 MIDI"的完整链路。
一、Pop2Piano 是什么
Piano covers of pop music are widely enjoyed, but generating them from music is not a trivial task. It requires great expertise with playing piano as well as knowing different characteristics and melodies of a song. With Pop2Piano you can directly generate a cover from a song's audio waveform. It is the first model to directly generate a piano cover from pop audio without melody and chord extraction modules.
论文摘要(原文):Piano covers of pop music are enjoyed by many people. However, the task of automatically generating piano covers of pop music is still understudied. This is partly due to the lack of synchronized {Pop, Piano Cover} data pairs, which made it challenging to apply the latest>pip install pretty-midi==0.2.9 essentia==2.1b6.dev1034 librosa scipy
安装完成后可能需要重启运行环境(runtime)。
依赖的深层原因在于:
essentia:提供RhythmExtractor2013节拍抽取算法(见 feature_extraction_pop2piano.py);librosa:负责音频加载与重采样;scipy:负责节拍插值(interp1d);pretty_midi:负责把 token 渲染成PrettyMIDI对象(音符起止、速度、删除非法音符等);torch:模型推理框架。上述库缺失时,对应的类会直接以装饰器方式在导入阶段声明硬依赖:
Pop2PianoFeatureExtractor声明需要essentia/librosa/scipy/torch,Pop2PianoTokenizer声明需要pretty_midi/torch,Pop2PianoProcessor声明需要全部五个(见各文件顶部@requires)。官方提供了一键式高层封装Pop2PianoProcessor(processing_pop2piano.py),它组合了特征提取器与分词器,同时支持音频→特征与音符→token两条通路。四、快速上手实战
4.1 使用 HuggingFace Dataset 示例
from datasets import load_dataset from transformers import Pop2PianoForConditionalGeneration, Pop2PianoProcessor model = Pop2PianoForConditionalGeneration.from_pretrained("sweetcocoa/pop2piano", device_map="auto") processor = Pop2PianoProcessor.from_pretrained("sweetcocoa/pop2piano") ds = load_dataset("sweetcocoa/pop2piano_ci", split="test") inputs = processor( audio=ds["audio"][0]["array"], sampling_rate=ds["audio"][0]["sampling_rate"], return_tensors="pt" ) model_output = model.generate(input_features=inputs["input_features"], composer="composer1") tokenizer_output = processor.batch_decode( token_ids=model_output, feature_extractor_output=inputs )["pretty_midi_objects"][0] tokenizer_output.write("./Outputs/midi_output.mid")
batch_decode后得到的BatchEncoding同时包含notes与pretty_midi_objects两类字段(见 tokenization_pop2piano.py)。取第 0 个PrettyMIDI对象调用.write()即导出.mid文件。4.2 使用自己的音频文件
import librosa from transformers import Pop2PianoForConditionalGeneration, Pop2PianoProcessor audio, sr = librosa.load("<your_audio_file_here>", sr=44100) # feel free to change the sr to a suitable value. model = Pop2PianoForConditionalGeneration.from_pretrained("sweetcocoa/pop2piano", device_map="auto") processor = Pop2PianoProcessor.from_pretrained("sweetcocoa/pop2piano") inputs = processor(audio=audio, sampling_rate=sr, return_tensors="pt").to(model.device) model_output = model.generate(input_features=inputs["input_features"], composer="composer1") tokenizer_output = processor.batch_decode( token_ids=model_output, feature_extractor_output=inputs )["pretty_midi_objects"][0] tokenizer_output.write("./Outputs/midi_output.mid")性能提示:加载音频时把采样率设为44.1 kHz(
sr=44100)通常能获得不错的生成效果。特征提取器内部若检测到输入采样率与自身目标采样率(默认 22050 Hz)不一致,会调用librosa.core.resample(res_type="kaiser_best")自动重采样(见 feature_extraction_pop2piano.py)。4.3 批量处理多个音频文件
import librosa from transformers import Pop2PianoForConditionalGeneration, Pop2PianoProcessor # feel free to change the sr to a suitable value. audio1, sr1 = librosa.load("<your_first_audio_file_here>", sr=44100) audio2, sr2 = librosa.load("<your_second_audio_file_here>", sr=44100) model = Pop2PianoForConditionalGeneration.from_pretrained("sweetcocoa/pop2piano", device_map="auto") processor = Pop2PianoProcessor.from_pretrained("sweetcocoa/pop2piano") inputs = processor(audio=[audio1, audio2], sampling_rate=[sr1, sr2], return_attention_mask=True, return_tensors="pt").to(model.device) # Since we now generating in batch(2 audios) we must pass the attention_mask model_output = model.generate( input_features=inputs["input_features"], attention_mask=inputs["attention_mask"], composer="composer1", ) tokenizer_output = processor.batch_decode( token_ids=model_output, feature_extractor_output=inputs )["pretty_midi_objects"] # Since we now have 2 generated MIDI files tokenizer_output[0].write("./Outputs/midi_output1.mid") tokenizer_output[1].write("./Outputs/midi_output2.mid")批量关键点:
- 传入多条音频时,
sampling_rate必须是与音频一一对应的列表([sr1, sr2]),否则Pop2PianoFeatureExtractor.__call__会直接抛出 ValueError(见 feature_extraction_pop2piano.py);- 必须显式传
return_attention_mask=True,并把attention_mask一并传给model.generate();batch_decode返回的pretty_midi_objects是列表,需按索引逐个写出文件。4.4 拆分使用 FeatureExtractor 与 Tokenizer
如果希望更细粒度地控制预处理,可以不使用
Pop2PianoProcessor,而分别显式使用Pop2PianoFeatureExtractor与Pop2PianoTokenizer:import librosa from transformers import Pop2PianoFeatureExtractor, Pop2PianoForConditionalGeneration, Pop2PianoTokenizer # feel free to change the sr to a suitable value. audio1, sr1 = librosa.load("<your_first_audio_file_here>", sr=44100) audio2, sr2 = librosa.load("<your_second_audio_file_here>", sr=44100) model = Pop2PianoForConditionalGeneration.from_pretrained("sweetcocoa/pop2piano", device_map="auto") feature_extractor = Pop2PianoFeatureExtractor.from_pretrained("sweetcocoa/pop2piano") tokenizer = Pop2PianoTokenizer.from_pretrained("sweetcocoa/pop2piano") inputs = feature_extractor( audio=[audio1, audio2], sampling_rate=[sr1, sr2], return_attention_mask=True, return_tensors="pt", ) # Since we now generating in batch(2 audios) we must pass the attention_mask model_output = model.generate( input_features=inputs["input_features"], attention_mask=inputs["attention_mask"], composer="composer1", ) tokenizer_output = tokenizer.batch_decode( token_ids=model_output, feature_extractor_output=inputs )["pretty_midi_objects"] # Since we now have 2 generated MIDI files tokenizer_output[0].write("./Outputs/midi_output1.mid") tokenizer_output[1].write("./Outputs/midi_output2.mid")
Pop2PianoProcessor.batch_decode本质上是转发调用Pop2PianoTokenizer.batch_decode(见 processing_pop2piano.py),因此两条路径结果一致。五、预处理流水线深度解析
Pop2PianoFeatureExtractor(feature_extraction_pop2piano.py)把一段音频变成模型可用的三个输入,顺序如下:
- 节拍抽取(Rhythm):调用 essentia 的
RhythmExtractor2013(method="multifeature"),返回 BPM、节拍时间点beat_times、置信度、速度估计与节拍区间。该算法仅处理原始音频,见extract_rhythm;- 节拍插值(Beatsteps):用
scipy.interpolate.interp1d(bounds_error=False, fill_value="extrapolate")按steps_per_beat(默认 2)与外推参数对beat_times插值,得到细粒度的beatsteps(见interpolate_beat_times);- Mel 预处理与频谱计算:
preprocess_mel按每num_bars * 4步切分音频片段、统一 pad 到最长片段(静音补零),并外推一个extrapolated_beatstep供分词器使用;mel_spectrogram使用 Hanning 窗 + STFT 得到 Mel 频谱:窗口大小window_size=4096、跳步hop_length=1024、Mel 滤波器个数feature_size=512、最低频率min_frequency=10.0;- 对频谱做
np.log(np.clip(mel_specs, a_min=1e-6))得到log-mel 频谱。最终
__call__返回BatchFeature,其中model_input_names = ["input_features", "beatsteps", "extrapolated_beatstep"]。关于 padding 的坑:处理批量输入时,
pad方法会在每个样本特征之间插入一整行全零数组用于"分隔"样本,因此attention_mask中会出现周期性为 0 的行(参见 docstring 中 mask 的分隔示例,feature_extraction_pop2piano.py);tokenizer.batch_decode正是依据attention_mask第 0 列中的 0 行来切分各样本生成结果(见 tokenization_pop2piano.py)。单样本且未请求 attention_mask 时,该分隔行会被自动移除。特征提取器的核心可调参数如下:
参数 默认值 说明 sampling_rate22050 送入模型的目标采样率;推理时应与音频采样率匹配并开启重采样 padding_value0 填充值,对应静音 window_size4096 傅里叶变换窗口长度(样本数) hop_length1024 相邻窗口步长(样本数) min_frequency10.0 log-mel 频谱使用的最低频率 feature_size512 特征维度(Mel 滤波器个数,同时等于模型 d_model)num_bars2 决定每个子序列的间隔长度(小节数)
steps_per_beat、resample、return_attention_mask、return_tensors是__call__的运行时参数:其中resample推理时必须为True;批量输入时return_attention_mask会被强制置True。六、模型配置与类参考
6.1 Pop2PianoConfig 全参数
配置类
Pop2PianoConfig(configuration_pop2piano.py)核心参数如下(括号内为默认值):
参数 默认值 说明 vocab_size2400 解码词汇表大小 composer_vocab_size21 作曲家数量(对应 composer_to_feature_token中的 composer 数)d_model512 隐藏层维度 d_kv64 注意力 Q/K/V 投影维度 d_ff2048 FFN 中间层维度 num_layers6 编码器层数(解码器默认取同值,见 num_decoder_layers)num_heads8 注意力头数 relative_attention_num_buckets32 每层相对位置注意力使用的桶(bucket)数量 relative_attention_max_distance128 桶划分的最大相对距离 dropout_rate0.1 Dropout 比率 layer_norm_epsilon1e-6 LayerNorm 的 epsilon initializer_factor1.0 初始化因子 feed_forward_proj"gated-gelu"FFN 类型,可选 "relu"或"gated-gelu"dense_act_fn"relu"DenseActDense与DenseGatedActDense中的激活函数is_encoder_decoderTrue 编码器-解码器架构标志 pad_token_id/eos_token_id0 / 1 特殊 token id(bos 为 2,unk 为 -1) tie_word_embeddingsTrue 权重绑定 几点源码细节值得注意:
attribute_map将num_hidden_layers→num_layers、hidden_size→d_model、num_attention_heads→num_heads做了别名映射,兼容 T5 系命名习惯(configuration_pop2piano.py);__post_init__中根据feed_forward_proj是否以"gated"开头自动推断is_gated_act;由于官方 checkpoint 只存shared.weight,权重实际始终绑定,scale_decoder_outputs由tie_word_embeddings决定(与 T5 相同的处理方式,configuration_pop2piano.py);keys_to_ignore_at_inference = ["past_key_values"],避免推理时 KV cache 相关键干扰。6.2 模型与生成
Pop2PianoForConditionalGeneration(modeling_pop2piano.py):整体入口,包含共享嵌入shared、mel_conditioner、T5 风格编码器与解码器、lm_head。generate(input_features, attention_mask=None, composer="composer1", generation_config=None, **kwargs)(modeling_pop2piano.py):Pop2Piano 定制的生成入口。input_features需是(batch, seq_len, feature_dim)的张量;传入 composer 名称时会自动从generation_config.composer_to_feature_token解析 composer token,并在批量输入时处理对应的 mask 拼接。forward(...):同时接受input_ids(训练时给解码器用)与input_features,返回标准Seq2SeqLMOutput;训练阶段标签可直接喂给labels,配对的prepare_decoder_input_ids_from_labels会自动做_shift_right构造解码器输入。模型权重可通过 convert_pop2piano_weights_to_hf.py 将官方仓库(sweetcocoa/pop2piano)的原始状态字典转换为 HF 格式(逐层映射 encoder/decoder 的相对注意力偏置、嵌入、
mel_conditioner.embedding、lm_head等)。七、测试验证与仓库证据
仓库针对该模型提供了四套独立测试,可作为行为契约参考:
- tests/models/pop2piano/test_modeling_pop2piano.py:模型前向与生成测试。slow 测试从
"sweetcocoa/pop2piano"加载真实权重执行generate,并断言输出sequences.ndim == 2;同时覆盖多 batch + 自定义composer及 attention_mask 的生成路径。- tests/models/pop2piano/test_feature_extraction_pop2piano.py:验证特征提取器对单/多音频的
input_features、beatsteps、extrapolated_beatstep输出与各种 padding/mask 行为。- tests/models/pop2piano/test_tokenization_pop2piano.py:验证 notes/token 互转、词汇表加载与 decode 一致性。
- tests/models/pop2piano/test_processing_pop2piano.py:验证
Pop2PianoProcessor对音频/音符双通路的组合与分发。八、使用建议与已知边界
以下结论均来自文档或源码可以确认的事实:
- 采样率:加载音频建议使用 44.1 kHz;特征提取器目标采样率默认 22050 Hz,不一致时会自动用
kaiser_best插值重采样,也可自行调低特征提取器sampling_rate减少输入长度。- Composer 选择:
generate(..., composer="composerX")中可用的 composer 集合取决于模型generation_config.json的composer_to_feature_token;传入不存在名称会报错并打印可用列表。切换 composer 是获得不同编曲风格最直接的手段。- 批量推理:多音频必须逐个传入采样率列表并返回 attention_mask;解码依赖 mask 中的零行分隔各样本,缺少 mask 会触发显式 ValueError。
- 训练侧重:模型主要针对韩国流行乐(K-Pop)训练,但对西方流行乐、Hip Hop 等也表现不错——这是官方文档给出的经验描述,具体效果请以实际试听为准。
- Token 后处理:
notes_to_midi会以resolution=384, initial_tempo=120.0初始化PrettyMIDI,用program=0(Acoustic Grand Piano)承载音符,并调用remove_invalid_notes()清理非法音符(tokenization_pop2piano.py)。生成的 MIDI 可直接用任意播放器或 DAW 打开试听。- 轻量推理起点:若想快速验证链路,仓库慢测试使用随机特征张量
(batch, seq, 512)也能驱动generate(test_modeling_pop2piano.py),可作为自建流水线冒烟测试的模板。结合本文的配置表、四段可运行示例与源码级原理说明,你现在可以基于 🤗 Transformers 从任意流行音频出发,探索"音频 → 钢琴翻弹 MIDI"的端到端生成,并通过 composer 参数解锁更多编曲风格。
【免费下载链接】transformers🤗 Transformers: the model-definition framework for state-of-the-art machine learning models in text, vision, audio, and multimodal models, for both inference and training.
项目地址: https://gitcode.com/GitHub_Trending/tra/transformers
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考