【CS191】【lab1】part2


基于PyTorch和RNN的音乐生成实验笔记

实验目标

本实验旨在通过构建循环神经网络(RNN),学习音乐数据中的模式,并生成新的音乐。我们将使用PyTorch框架,以ABC符号表示的爱尔兰民间音乐数据集为基础,训练模型并生成音乐。


2.1 实验依赖与环境配置

依赖安装

实验需要安装以下Python库:

  • PyTorch:深度学习框架,用于构建和训练RNN模型。
  • Comet ML:用于跟踪模型训练过程和实验结果。
  • NumPy、SciPy:用于数据处理和音频文件生成。
  • MIT Introduction to Deep Learning package:提供实验所需的数据集和辅助函数。

安装命令如下:

1
2
!pip install comet_ml
!pip install mitdeeplearning

Comet ML配置

Comet ML用于记录实验过程和结果。需要注册Comet账户并获取API Key,然后将其设置为全局变量COMET_API_KEY

GPU检查

实验需要使用GPU加速,确保运行环境已启用GPU:

1
assert torch.cuda.is_available(), "Please enable GPU from runtime settings"

2.2 数据集准备与分析

数据集下载

实验使用了包含817首爱尔兰民间歌曲的数据集,歌曲以ABC符号格式表示。数据集通过以下代码下载:

1
songs = mdl.lab1.load_training_data()

数据集格式

每首歌曲以文本形式存储,包含音符、调式、节奏等信息。例如:

1
2
3
4
5
6
7
8
X:1
T:Alexander's
Z:id:dc-hornpipe-1
M:C|
L:1/8
K:D Major
(3ABc|dAFA DFAd|fdcd FAdf|gfge fefd|(3efe (3dcB A2 (3ABc|!
dAFA DFAd|fdcd FAdf|gfge fefd|(3efe dc d2:|!

数据集特点

  • 数据集中共有83个唯一字符,包括音符、符号和元信息(如标题、调式等)。
  • 字符数量的多样性增加了学习问题的复杂性。

数据集合并

将所有歌曲合并为一个长字符串,用于后续处理:

1
songs_joined = "\n\n".join(songs)

2.3 数据预处理

文本向量化

为了将文本数据输入到神经网络中,需要将其转换为数值形式。我们创建两个映射表:

  • char2idx:将字符映射为唯一的整数索引。
  • idx2char:将整数索引映射回字符。

向量化函数如下:

1
2
3
def vectorize_string(string):
vector = [char2idx[char] for char in string]
return np.array(vector)

将所有歌曲向量化:

1
vectorized_songs = vectorize_string(songs_joined)

训练样本生成

为了训练RNN模型,需要将文本数据分割为输入序列和目标序列。每个输入序列包含seq_length个字符,目标序列是输入序列向右移动一个字符的结果。

生成训练样本的函数如下:

1
2
3
4
5
6
7
8
def get_batch(vectorized_songs, seq_length, batch_size):
n = vectorized_songs.shape[0] - 1
idx = np.random.choice(n - seq_length, batch_size)
input_batch = [vectorized_songs[i:i+seq_length] for i in idx]
output_batch = [vectorized_songs[i+1:i+seq_length+1] for i in idx]
x_batch = torch.tensor(input_batch, dtype=torch.long)
y_batch = torch.tensor(output_batch, dtype=torch.long)
return x_batch, y_batch

2.4 RNN模型构建

模型架构

RNN模型基于长短期记忆网络(LSTM)架构,包含以下三个主要组件:

  1. Embedding层:将字符索引转换为固定维度的密集向量。
  2. LSTM层:处理序列数据,捕捉字符之间的时序关系。
  3. 全连接层(Linear层):将LSTM的输出映射到词汇表大小的维度,输出每个字符的概率分布。

模型定义如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class LSTMModel(nn.Module):
def __init__(self, vocab_size, embedding_dim, hidden_size):
super(LSTMModel, self).__init__()
self.embedding = nn.Embedding(vocab_size, embedding_dim)
self.lstm = nn.LSTM(input_size=embedding_dim, hidden_size=hidden_size, batch_first=True)
self.fc = nn.Linear(hidden_size, vocab_size)

def forward(self, x, state=None, return_state=False):
x = self.embedding(x)
if state is None:
state = self.init_hidden(x.size(0), x.device)
out, state = self.lstm(x, state)
out = self.fc(out)
return out if not return_state else (out, state)

模型参数

  • vocab_size:词汇表大小(83)。
  • embedding_dim:嵌入层维度(256)。
  • hidden_size:LSTM隐藏层大小(1024)。
  • batch_size:训练时的批量大小(8)。

2.5 模型训练

损失函数

使用PyTorch的CrossEntropyLoss计算模型输出与真实标签之间的交叉熵损失:

1
2
3
4
5
def compute_loss(labels, logits):
batched_labels = labels.view(-1)
batched_logits = logits.view(-1, logits.size(-1))
loss = cross_entropy(batched_logits, batched_labels)
return loss

优化器

使用Adam优化器,学习率为5e-3

1
optimizer = torch.optim.Adam(model.parameters(), lr=params['learning_rate'])

训练循环

训练过程如下:

  1. 随机抽取一批序列作为输入。
  2. 计算损失并进行反向传播。
  3. 更新模型参数。

训练代码如下:

1
2
3
4
5
for iter in tqdm(range(params["num_training_iterations"])):
x_batch, y_batch = get_batch(vectorized_songs, params["seq_length"], params["batch_size"])
x_batch = torch.tensor(x_batch, dtype=torch.long).to(device)
y_batch = torch.tensor(y_batch, dtype=torch.long).to(device)
loss = train_step(x_batch, y_batch)

2.6 音乐生成

生成过程

使用训练好的模型生成音乐,步骤如下:

  1. 提供一个起始字符串(如“X”)作为种子,初始化LSTM的隐藏状态。
  2. 在每一步中,模型根据当前输入和隐藏状态生成下一个字符的概率分布,通过采样得到下一个字符。
  3. 将生成的字符作为下一次输入,重复上述过程,生成指定长度的ABC格式文本。

生成代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
def generate_text(model, start_string, generation_length=1000):
input_idx = [char2idx[s] for s in start_string]
input_idx = torch.tensor([input_idx], dtype=torch.long).to(device)
state = model.init_hidden(input_idx.size(0), device)
text_generated = []
for i in tqdm(range(generation_length)):
predictions, state = model(input_idx, state, return_state=True)
predicted_logits = predictions[0, -1, :]
predicted_probs = torch.nn.functional.softmax(predicted_logits, dim=-1)
predicted_idx = torch.multinomial(predicted_probs, num_samples=1)
predicted_char = idx2char[predicted_idx.item()]
text_generated.append(predicted_char)
input_idx = predicted_idx.unsqueeze(0)
return (start_string + ''.join(text_generated))

音乐播放

将生成的ABC格式文本转换为音频文件并播放:

1
2
3
4
5
6
generated_songs = mdl.lab1.extract_song_snippet(generated_text)
for i, song in enumerate(generated_songs):
waveform = mdl.lab1.play_song(song)
if waveform:
print("Generated song", i)
ipythondisplay.display(waveform)

2.7 实验优化与总结

实验优化方向

  1. 训练迭代次数:增加迭代次数以提升模型性能。
  2. 学习率调整:尝试不同的学习率以找到最优值。
  3. 数据集扩展:增加数据集的多样性和规模。
  4. 模型架构改进:尝试更复杂的RNN架构,如双向LSTM或GRU。

实验总结

本实验通过构建RNN模型,成功实现了基于ABC符号的音乐生成。通过调整训练参数和优化模型架构,可以进一步提升生成音乐的质量。未来可以探索多模态融合等方向,以生成更具表现力的音乐作品。


文章作者: MIKA
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 MIKA !
  目录