自成一派,这个作曲大师确实名副其实!

image

AWS DeepComposer 是一项教育型的 AWS 服务,可以训练出生成式人工智能,并利用 GAN(Generative Adversarial Network)转换输入的旋律,创作出完整的原创乐曲。

之前我们曾有篇文章详细介绍过该服务,感兴趣的童鞋可以在这里查看:当音乐遇上人工智能 -- 歌手们都在「云竞演」,作曲家就不想试试「云谱曲」?

简单来说,借助 AWS DeepComposer,我们可以使用预先训练好的音乐流派模型(如爵士、摇滚、流行、交响乐等)将我们提供的旋律转换为相应流派的曲子。那么当你玩转这个服务后,有没有想过自己也「开宗立派」,通过一定的参数训练出自己的流派选项?

当然可以!我们只需要将音乐数据文件存储在 NumPy 对象中,随后结合 GitHub 上的 Lab 2—— 训练一套自定义 GAN 模型中的训练步骤进行操作即可。下文将介绍如何将 MIDI 文件转换为适用于 AWS Deepomposer 的格式,而训练出自己的流派模型。

在下文中,我们可以使用自己的 MIDI 文件训练出雷鬼音乐流派模型。雷鬼音乐诞生于牙买加岛,常用乐器包括低音吉他、鼓以及各类打击乐器。不过本文介绍的方法也是通用的,大家完全可以借此训练任何其他音乐流派。

数据处理:生成训练数据

MIDI(.mid)文件是训练数据的最初始状态。MIDI 文件由软件生成(并读取),文件中包括关于音符及声音回放的数据。在数据处理过程中,我们需要将 MIDI 文件转换为 NumPy 数组,并将其持久化保存成磁盘上的一个单独的.npy 文件。下图展示了数据的转换过程:
image

尽管机器学习普遍使用.csv文件存储数据,但.npy文件是为加速训练过程中的读取速度高度优化的。.npy文件的最终形态应为(x, 32, 128, 4),其含义分别为(样本数量,每个样本的时间步数, 音高范围, 乐器)。

若将 MIDI 文件转换为适当的格式,我们需要完成以下步骤:

  1. 读取 MIDI 文件以生成一个 Multitrack 对象。
  2. 确定要使用的四条乐器音轨。
  3. 检索各音轨中的 Pianoroll 矩阵,并将其调整为正确的格式。
  4. 将 Pianoroll 对象与给定的乐器相匹配,并将其存储在.npy文件中。

读取 MIDI 文件以生成一个 Multitrack 对象

数据处理的第一步,是解析各 MIDI 文件并生成一个 Multitrack 对象。下图即为 Multitrack 对象的基本结构。
image
这个过程用需要用到 Pypianoroll 库,它以 Python 代码实现读取及写入 MIDI 文件的功能。具体参见以下代码:

#init with beat resolution of 4
music_tracks = pypianoroll.Multitrack(beat_resolution=4)
#Load MIDI file using parse_midi
#returns Multitrack object containing Track objects
music_tracks.parse_midi(your_midi_file_directory + your_midi_filename)

music_tracks 是一个 Mulitrack 对象,包含一个从 MIDI 文件中读取到的 Track 对象列表。每个 Mulitrack 对象都包含速度(Tempo)、节拍(Downbeat)、节拍分辨率(Beat resolution)以及名称(Name),具体如以下代码所示:

tracks: [FRETLSSS, ORGAN 2, CLAVINET, MUTED GTR, CLEAN GTR, VIBRAPHONE, DRUMS],
tempo: [120. 120. 120. ... 120. 120. 120.],
downbeat: [ True False False False False False False False False . . .
 False False False False False False False False False False False False],
beat_resolution: 4,
name: “reggae1”

确定要使用的四条乐器音轨

如果要解析的 Mulitrack 对象中恰好包含四种乐器,则可以直接跳过此步骤。

我们之前解析的 Mulitrack 对象共包含7条乐器音轨(Fretless 电贝司、风琴、竖笛、静音电吉他、清音电吉他、颤音琴和鼓)。模型需要学习的乐器种类越多,训练时间就越长,成本自然也越高。有鉴于此,这里我们选择的 GAN 只支持最多4种乐器。如果 MIDI 文件中的音轨包含4种以上乐器,请直接从所选择的音乐流派中选择最重要的4种乐器进行模型训练。相应的,如果乐器数量不足4种,则需要扩充 MIDI 文件。乐器数量错误会导致 NumPy 的形制出现错误。

给定音轨上的每一种乐器都拥有自己的编号。编号由通用 MIDI 规范规定,相当于乐器的唯一标识符。以下代码示例就用相关编号提取到钢琴、风琴、贝司以及吉他四种乐器:

instrument1_program_numbers = [1,2,3,4,5,6,7,8] #Piano
instrument2_program_numbers = [17,18,19,20,21,22,23,24] #Organ
instrument3_program_numbers = [33,34,35,36,37,38,39,40] #Bass
instrument4_program_numbers = [25,26,27,28,29,30,31,32] #Guitar
if track.program in instrument1_program_numbers:
 collection['Piano'].append(track)
elif track.program in instrument2_program_numbers:
 collection['Organ'].append(track)
elif track.program in instrument3_program_numbers:
 collection['Bass'].append(track)
elif track.program in instrument4_program_numbers:
 collection['Guitar'].append(track)

检索每条音轨中的 Pianoroll 矩阵,并将其调整为正确的格式

Multitrack 对象为每种乐器提供一个 Track 对象。每个 Track 对象中都包含一个 Pianoroll 矩阵、一个编号、人个表示音轨是否为鼓的布尔值以及一个名称。

以下代码,为单一 Track 的具体示例:


pianoroll:
[[0 0 0 ... 0 0 0]
 [0 0 0 ... 0 0 0]
 [0 0 0 ... 0 0 0]
 ...
 [0 0 0 ... 0 0 0]
 [0 0 0 ... 0 0 0]
 [0 0 0 ... 0 0 0]],
program: 7,
is_drum: False,
name: CLAVINET

在训练当中,单一 Pianoroll 对象应具有32个离散的时间步长,代表一首歌曲的片段与128个音高。所选乐器音轨的 Pianoroll 对象的起始形态为(512,128),我们需要将其调整为正确的格式。每个 Pianoroll 对象将重新调整为2个小节(32个时间步长)、音高为128。输入以下代码后,单一 Pianoroll 对象的最终形制为 (16, 32, 128):

#loop through chosen tracks
for index, track in enumerate(chosen_tracks): 
 try:
 #reshape pianoroll to 2 bar (i.e. 32 time step) chunks 
 track.pianoroll = track.pianoroll.reshape( -1, 32, 128)
 #store reshaped pianoroll per instrument
 reshaped_piano_roll_dict = store_track(track, reshaped_piano_roll_dict) 
 except Exception as e:
 print("ERROR!!!!!----> Skipping track # ", index, " with error ", e)

为了简洁起见,以下代码示例仅显示钢琴音轨的制作示例:

{'Piano': [Track(pianoroll=array([[[0, 0, 0, ..., 0, 0, 0],
 [0, 0, 0, ..., 0, 0, 0],
 [0, 0, 0, ..., 0, 0, 0],
 ...,
 [0, 0, 0, ..., 0, 0, 0],
 [0, 0, 0, ..., 0, 0, 0],
 [0, 0, 0, ..., 0, 0, 0]],
 ...,
 [0, 0, 0, ..., 0, 0, 0],
 [0, 0, 0, ..., 0, 0, 0],
 [0, 0, 0, ..., 0, 0, 0]]], dtype=uint8), program=7, is_drum=False, name=CLAVINET)]

将 Pianoroll 对象与给定乐器相匹配,并将其存储在.npy 文件中。

下一步是按乐器将所有音轨连结起来,并将其存储在.npy 训练文件当中。我们可以将这个过程理解为各 Pianoroll 对象间的彼此堆叠。我们需要为4种所选乐器分别重复这个过程,具体请参见以下代码:


def get_merged(music_tracks, filename):
...
#will hold all merged instrument tracks
merge_piano_roll_list = []
for instrument in reshaped_piano_roll_dict:
 try:
 merged_pianorolls = np.empty(shape=(0,32,128))
 #concatenate/stack all tracks for a single instrument
 if len(reshaped_piano_roll_dict[instrument]) > 0:
 if reshaped_piano_roll_dict[instrument]: 
 merged_pianorolls = np.stack([track.pianoroll for track in reshaped_piano_roll_dict[instrument]], -1)
 merged_pianorolls = merged_pianorolls[:, :, :, 0]
 merged_piano_rolls = np.any(merged_pianorolls, axis=0)
 merge_piano_roll_list.append(merged_piano_rolls)
 except Exception as e:
 print("ERROR!!!!!----> Cannot concatenate/merge track for instrument", instrument, " with error ", e)
merge_piano_roll_list = np.stack([track for track in merge_piano_roll_list], -1)
return merge_piano_roll_list.reshape(-1,32,128,4)

现在,我们将合并后的 Pianoroll 存储在.npy 文件当中,详见以下代码:

#holds final reshaped tracks that will be saved to training .npy file
track_list = np.empty(shape=(0,32,128,4))
. . .
#merge pianoroll objects by instrument
merged_tracks_to_add_to_training_file = get_merged(music_tracks, filename)
#concatenate merged pianoroll objects to final training data track list
track_list = np.concatenate((merged_tracks_to_add_to_training_file, track_list))
# binarize data
track_list[track_list == 0] = -1
track_list[track_list >= 0] = 1
#save the training data to reggae-train.npy
save(train_dir + '/reggae-train.npy', np.array(track_list))

结果

以上代码将一组存储在 your_midi_file_directory 中的 MIDI 文件生成 reggae-train.npy。相应 Jupyter notebook 与完整代码可通过GitHub repo获取。

现在,我们已经拥有了训练数据文件,接下来就可以根据 AWS DeepComposer 示例 notebook 中 Lab 2—— 训练自定义 GAN 模型提出的步骤训练自定义音乐流派模型。

本文在 SoundCloud 上提供两个由 AI 生成的雷鬼风格音轨:Summer BreezeMellow Vibe

技巧与秘诀

我们可以使用以下技巧与秘诀理解你的数据内容,并生成更悦耳的 AI 乐曲。

在 GarageBand 中查看及收听 MIDI 文件

如果你使用 Mac 设备,可以使用 GarageBand 收听 MIDI 文件并查看其中使用的乐器。如果没有 Mac 设备,则可使用任何其他支持 MIDI 文件的Digital Audio Workstation(DAW)。在通过 GarageBand 收听 AI 生成的乐曲时,音质要明显更好,甚至可以连接专业级扬声器以获得极好的听觉效果。

使用乐器编号更改伴奏的乐器

在运行Lab 2—— 训练自定义 GAN 模型提供的推理代码时,大家可能会注意到,所有由 AI 生成的音轨都会在 GarageBand 中显示为“Steinway Grand Piano”。如果熟悉 AWS DeepComposer 控制台,则可随时调整乐器种类。要在训练自定义模型时更改伴奏的乐器种类,请在调用midi_utils中的save_pianoroll_as_midi函数时使用programs参数,具体请见以下代码:

#use programs to provide the program numbers for the instruments I care about
#17 = Drawbar Organ, 28 = Electric Guitar, 27 = Electric Guitar, 11 = Music Box
midi_utils.save_pianoroll_as_midi(fake_sample_x[:4], programs=[17, 28, 27, 11], destination_path=destination_path)

使用 GarageBand 添加其他伴奏

在使用AI生成一首乐曲(带伴奏)之后,我们可以使用GarageBand(或者其他类似的工具)进一步添加更多伴奏。我们可以调整音轨的速度,甚至让某些乐器静音。我们也可以添加任意数量的其他伴奏乐器以创造出独特的声音表现。

在 AWS DeepComposer 控制台上创建推理旋律

在运行推理时,我们需要一段 MIDI 格式的自定义旋律。我们还可以添加伴奏乐器配合该自定义旋律生成一首独特的乐曲。在训练自定义模型时,最简单的旋律创建方法就是使用 AWS DeepComposer 控制台。我们可以使用虚拟键盘或者 AWS DeepComposer 键盘记录下旋律,而后选择“Download”将其下载为 MIDI 文件。
image

使用 Matplotlib 绘制 Pianoroll

大家可以使用 Track 上的 Plot 函数绘制 Pianoroll 对象,这样就能直观查看 Pianoroll 对象了。具体请参见以下代码:

import matplotlib.pyplot as plt
...
fig, ax = track.plot()
plt.show()

下图所示,为 Pianoroll 对象的基本观感。
image

数据二值化

代码包含了一段对数据进行二进制化的部分。这项更新非常重要,因为在处理二进制化输入时,该模型实际处理的是-1与1(而非0与1)。Track_list 中包含最终训练数据,在继续使用 Reggae-train.npy 之前,应将其设置为-1或者1。具体请参见以下代码:

# binarize data
track_list[track_list == 0] = -1
track_list[track_list >= 0] = 1

总结

AWS DeepComposer 不只是一款普通的键盘,同时也是一种有趣的互动方式,帮助我们了解生成式的 AI 与 GAN 的复杂性。我们可以在它的帮助下学习演奏简单的旋律,甚至可能激发出创作全新乐曲的灵感、训练出自己的自定义音乐流派模型、最终创造出前所未有的声音。我们也可以将两种流派融合起来以创造出新的音乐类型!

image

阅读 1.8k

推荐阅读
AWS_AI开发社区
用户专栏

AWS_AI 开发者社区是专注于人工智能领域 IT 人士交流与互动的平台。在这里,你可以分享和获取一切有关人...

727 人关注
77 篇文章
专栏主页