在人工智能领域,音乐生成一直是个令人着迷的方向。最近我完成了一个使用GPT-2模型生成多轨音乐的项目,整个过程从数据集准备到模型部署,收获了不少实战经验。与常见的音频生成不同,这个项目采用了符号音乐(Symbolic Music)的方法,将MIDI音乐指令转化为类似自然语言的"伪单词",然后利用NLP领域的Transformer模型进行训练。
音乐生成通常有两种主流方法:
我选择后者有几个重要原因:
提示:对于个人开发者和研究者,符号音乐方法是在有限资源下探索AI音乐生成的理想选择。
我使用了Lakh MIDI数据集(LMD)的Clean子集,包含14,751个MIDI文件。这些文件已经过初步清洗,文件名包含艺术家和曲名信息,这对后续按流派分类很有帮助。
数据集处理的关键步骤:
python复制# Spotify API获取流派示例代码
genres = {}
for i,artist in enumerate(artists):
try:
results = sp.search(q=artist, type='artist', limit=1)
items = results['artists']['items']
genre_list = items[0]['genres'] if len(items) else items['genres']
genres[artist] = (genre_list[0]).replace(" ","_")
if i <5:
print("预览 {}/5".format(i+1), artist, genre_list[:5])
except Exception as e:
genres[artist] = "MISC"
print("错误: ", artist,"未包含: ", e)
为了训练效果,我将每首曲子分割成8小节的片段。这个长度既能保持音乐结构的完整性,又不会使输入序列过长。
分块处理的注意事项:
python复制# MIDI分块处理核心逻辑
for i, midi_path in enumerate(tqdm(midi_paths, desc="处理MIDI")):
try:
midi = MidiFile(midi_path)
ticks_per_cut = MAX_NB_BAR * midi.ticks_per_beat * 4
nb_cuts = ceil(midi.max_tick / ticks_per_cut)
for j, track in enumerate(midi.instruments):
track.notes.sort(key=lambda x: x.start)
for cut_id in range(nb_cuts):
# 处理音符时间偏移
...
except Exception as e:
print(f"处理{midi_path}时出错:{e}")
经过比较,我采用了MMM(Multi-Track Music Machine) tokenization方案,它有以下几个优势:
MMM的基本结构:
code复制PIECE_START GENRE=CLASSICAL
TRACK_START INST=0 (钢琴)
BAR_START
NOTE_ON=60 TIME_DELTA=4 NOTE_OFF=60
NOTE_ON=62 TIME_DELTA=4 NOTE_OFF=62
BAR_END
TRACK_END
...
PIECE_END
使用修改版的MMM-JSB代码库进行tokenization,主要调整包括:
处理后的数据集示例:
code复制PIECE_START GENRE=JAZZ TRACK_START INST=32 BAR_START
NOTE_ON=65 TIME_DELTA=4.0 NOTE_OFF=65
NOTE_ON=63 TIME_DELTA=2.0 NOTE_OFF=63
BAR_END TRACK_END PIECE_END
GPT-2原有的tokenizer是为英语设计的,直接用于音乐符号会出现问题:
python复制from tokenizers import Tokenizer, models, pre_tokenizers
from tokenizers.trainers import WordLevelTrainer
# 初始化tokenizer
tokenizer = Tokenizer(models.WordLevel(unk_token="[UNK]"))
tokenizer.pre_tokenizer = pre_tokenizers.WhitespaceSplit()
# 训练配置
trainer = WordLevelTrainer(
special_tokens=["[UNK]", "[CLS]", "[SEP]", "[PAD]", "[MASK]"]
)
# 训练并保存
tokenizer.train_from_iterator(get_training_corpus(), trainer=trainer)
tokenizer.save("tokenizer.json")
训练后的tokenizer能完美处理音乐符号:
code复制输入: "PIECE_START GENRE=POP NOTE_ON=60 TIME_DELTA=4.0"
输出: ["PIECE_START", "GENRE=POP", "NOTE_ON=60", "TIME_DELTA=4.0"]
基于Chinchilla论文的缩放定律,我选择了适中的模型规模:
python复制from transformers import AutoConfig, GPT2LMHeadModel
config = AutoConfig.from_pretrained(
"gpt2",
vocab_size=len(tokenizer),
n_positions=context_length,
n_layer=6,
n_head=8,
n_embd=512,
pad_token_id=tokenizer.pad_token_id
)
model = GPT2LMHeadModel(config)
使用Weights & Biases监控训练,关键配置:
自定义Trainer类实现训练中生成音乐样本:
python复制class CustomTrainer(Trainer):
def evaluation_loop(...):
# 常规评估逻辑
eval_output = super().evaluation_loop(...)
# 生成音乐样本
input_ids = tokenizer.encode("PIECE_START GENRE=JAZZ", return_tensors="pt").cuda()
generated_ids = model.generate(input_ids, max_length=512, temperature=0.75)
# 转换为音频并记录到W&B
token_sequence = tokenizer.decode(generated_ids[0])
note_sequence = token_sequence_to_note_sequence(token_sequence)
audio = synthesize(note_sequence)
wandb.log({"生成音频": wandb.Audio(audio, sample_rate=44100)})
return eval_output
使用W&B Sweeps进行自动化超参数优化:
yaml复制method: random
metric:
name: eval/loss
goal: minimize
parameters:
learning_rate:
distribution: log_uniform
min: 5e-4
max: 3e-3
gradient_accumulation_steps:
values: [1, 2, 4]
搜索发现的最佳配置:
使用Gradio构建交互界面,通过Docker部署。关键组件:
Dockerfile核心配置:
dockerfile复制FROM ubuntu:20.04
RUN apt-get install -y fluidsynth libasound2-dev ffmpeg
COPY requirements.txt .
RUN pip install -r requirements.txt
python复制def generate_music(genre, temperature=0.7):
input_text = f"PIECE_START GENRE={genre.upper()}"
input_ids = tokenizer.encode(input_text, return_tensors="pt").to(device)
output_ids = model.generate(
input_ids,
max_length=512,
temperature=temperature,
pad_token_id=tokenizer.pad_token_id
)
token_sequence = tokenizer.decode(output_ids[0])
note_sequence = token_sequence_to_note_sequence(token_sequence)
audio = synthesize_audio(note_sequence)
return audio, note_sequence
这个项目最让我惊喜的是,即使是相对小型的GPT-2模型(约8000万参数),经过恰当训练后也能生成结构合理、风格鲜明的音乐作品。关键在于高质量的数据准备和针对音乐特性的模型调整。