之前在看Andrew Ng 的deep learning 视频教程,在RNN 这一节的课后作业里,实现了一个基于deepjazz的music generator,实验之后发现产生的结果还有模有样的,这激发了我的兴趣,于是我就查阅了一些资料,看看音乐的自动生成方面最近有哪些进展,特别是深度学习在这一块的应用.在这里稍微总结一下,并且写一写一些有趣的应用.
---------------------------------------------------- Part I : deep jazz 的简单介绍-------------------------------------------------------------
1.What is deepjazz?
以下内容搬运自deepjazz的官网:
deepjazz 是一个使用theano 和keras的基于深度学习的jazz music 生成器.我在编程马拉松(hackathon)使用36个小时创建了deepjazz.它使用了theano和keras这两个库来生成jazz music.具体地说,它构建了两层的LSTM,从midi files 中学习.它使用了深度学习技术,以及AI技术,这个技术创造了著名的google AlphaGo和IBM的Watson,来产生音乐.而音乐被认为是非常human的.
2.怎样使用deepjazz?
2.1 训练模型并产生midi文件
我们首先去deepjazz的github首页看一下.github上面说的比较简单,我们首先git clone https://github.com/jisungk/deepjazz,然后 cd deepjazz,再运行python generator.py 应该就可以产生 学习出来的.mid 文件了.但是在测试的过程里,首先会报错(python3):\'\'\' from itertools import groupby, izip_longest ImportError: cannot import name \'izip_longest\' \'\'\'.这是由于原来的脚本是用python2写的,我们需要将原来所有的"from itertools import izip_longest" 替换为"from itertools import zip_longest".
继续运行,还是由于兼容性的问题,仍然会报错:
"melody_voice.insert(0, key.KeySignature(sharps=1,mode=\'major\'))TypeError: init() got an unexpected keyword argument \'mode\'
这里我们只需要将mode = \'major\'移除就可以了,后面还需要将generator.py中的 _sample 函数修改为:
def __sample(a, temperature=1.0):
a = np.log(a) / temperature
# a = np.exp(a) / np.sum(np.exp(a))
# return np.argmax(np.random.multinomial(1, a, 1))
dist = np.exp(a)/np.sum(np.exp(a))
choices = range(len(a))
return np.random.choice(choices, p=dist)
做了如上的修改之后,再运行python generator.py 应该就可以正常run起来了,运行结束之后,在midi 文件夹下,会多出一个deepjazz_on_metheny...128_epochs.midi 文件,这个就是我们通过 original_metheny.mid 训练学习得到的生成文件了.需要注意的是,无论我们训练还是生成的都不是常见的.mp3,.wav格式的音频文件,而是.mid(或者.midi)格式的文件.那么这两者有什么区别呢?这里引用百度百科的定义简单说明一下:
与波形文件不同,MIDI文件不对音乐进行抽样,而是对音乐的每个音符记录为一个数字,所以与波形文件相比文件要小得多,可以满足长时间音乐的需要。MIDI标准规定了各种音调的混合及发音,通过输出装置可以将这些数字重新合成为音乐。
MIDI音乐的主要限制是它缺乏重现真实自然声音的能力,因此不能用在需要语音的场合。此外,MIDI只能记录标准所规定的有限种乐器的组合,而且回放质量受到声音卡的合成芯片的限制。近年来,国外流行的声音卡普遍采用波表法进行音乐合成,使MIDI的音乐质量大大提高。
MIDI文件有几个变通格式,如RMI和CIF等。其中CMF文件(creative music format)是随声霸卡一起使用的音乐文件。RMI文件是Windows使用的RIFF(resource interchange file format)文件的一种子格式,称为RMID,即包含MIDI文件的格式。
简单来说就是:常见的.mp3,.wav格式的文件记录的都是真实的音频内容,因此一般体积会比较大(几兆到几十兆不等),而midi格式的文件没有记录真实的音频信息,它只是记录了一种代表格式的数字,计算机可以按照一定的标准识别出这种数字,然后把它转化为对应的音频播放出来.因此一般.midi格式的文件体积非常小,这是它的一个很大的优点,其缺点就是对于真实的声音还原较差(因为它只能通过有限种指定的乐器来模拟声音).
2.2 如何播放midi文件?
言归正传,在得到生成的midi文件之后,我们当然需要播放它好好欣赏一番啦.在windows下有不少软件可以播放midi格式的文件,但是我使用的系统是ubuntu16.04,默认的播放器不支持midi格式,在查阅了资料以后发现需要安装timidity,在ubuntu 下
直接sudo apt-get install timidity 即可.我找了一个python脚本play_midi.py(基于pygame),可以播放midi 文件,代码如下:
import pygame
import pygame as pg
def play_music(music_file):
\'\'\'
stream music with mixer.music module in blocking manner
this will stream the sound from disk while playing
\'\'\'
clock = pg.time.Clock()
try:
pg.mixer.music.load(music_file)
print("Music file {} loaded!".format(music_file))
except pygame.error:
print("File {} not found! {}".format(music_file, pg.get_error()))
return
pg.mixer.music.play()
# check if playback has finished
while pg.mixer.music.get_busy():
clock.tick(30)
# pick a midi or MP3 music file you have in the working folder
# or give full pathname
music_file = input("Please input the midi file path:")
#music_file = "Drumtrack.mp3"
freq = 44100 # audio CD quality
bitsize = -16 # unsigned 16 bit
channels = 2 # 1 is mono, 2 is stereo
buffer = 2048 # number of samples (experiment to get right sound)
pg.mixer.init(freq, bitsize, channels, buffer)
# optional volume 0 to 1.0
pg.mixer.music.set_volume(0.8)
try:
play_music(music_file)
except KeyboardInterrupt:
# if user hits Ctrl/C then exit
# (works only in console mode)
pg.mixer.music.fadeout(1000)
pg.mixer.music.stop()
raise SystemExit
运行python play_midi.py ,然后输入midi文件的路径,就可以播放啦.试着播放我们生成的midi文件,你会发现听起来是相当不错的!
当然其实安装了timidity之后,我们就可以直接播放midi文件了,直接运行 timidity xxx.midi 就可以了.但是有可能会出问题,因为我们还需要一些额外的配置文件,运行命令\'sudo apt-get install fluid-soundfont-gm fluid-soundfont-gs\' 安装好soundfont(声音字体,用于解析midi,并且播放),然后打开/etc/timidity/timidity.cfg 文件,将最后一行\'source freepats.cfg\' 注释掉,如果是ubuntu系统的话改为:
dir /usr/share/sounds/sf2/
soundfont FluidR3_GM.sf2
如果是centos系统的话改为:
dir /usr/share/soundfonts/
soundfont FluidR3_GM.sf2
然后重启timidity,执行命令:sudo /etc/init.d/timidity restart
这样我们再执行timidity xxx.midi应该就可以正常播放啦!
2.3 如何将midi文件转化为一般的音频文件(mp3,wav等格式)
现在我们可以正常播放midi文件了.但是还有一个问题,一般我们使用的音频格式是wav,mp3这种格式的,因为它们更易于被一般的播放器识别并且播放.那么有没有上面办法可以将midi文件转化为这样的格式呢?当然是有办法的,最简单的办法就是使用timidity(之前我们已经安装过啦),运行下面的命令:
timidity --output-24bit --output-mono -A120 source.mid -Ow -o source.wav
就可以把source.mid 转化为source.wav 了.其中 --output指定输出的格式,-A指定音量(volume),-Ow 表示转化为RIFF WAVE file输出格式,-o指定输出音频文件的名字,具体可以timidity --help 查看各个参数的含义.
如果没有问题的话,我们就得到一个.wav文件啦,这样你使用任何一个音乐播放器都可以播放它啦!
顺便提一个小问题.wav文件一般体积比较大(质量较好),而在网络上更常见的是mp3文件,那么这两者该如何转化呢?这里我提供两种解决办法:
1.使用ffmpeg 这个音视频库来进行转化.运行命令
ffmpeg -i source.wav -acodec libmp3lame source.mp3
就可以将source.wav 转化为source.mp3了.这里 -i 表示输入音频,-acodec 表示设置 audio codec(音频编码)格式,是"-codec:a"的别名,更多的信息可以输入 ffmpeg --help 或者 man ffmpeg查看
2.也可以安装python的音频库pydub进行转化,这个我在之前的博客介绍几个python的音频处理库介绍过,有兴趣可以自行查看.
如何将wav,mp3文件转化为midi文件?
这个问题我一开始以为是挺容易实现的一个任务,哪知道查了资料以后才发现是一个很hard的问题,目前仍然有很多人研究music transcription(音乐转换)的问题.我没有找到一个很好地解决这个问题的api,具体可以参看stackoverflow的这个讨论,目前也有很多的plugin可以做这个事情,比如Sonic Annotator等,但是就涉及到很专业的知识了,我想了一个还是放弃了...总之如果要做批量的从wav,mp3到midi的转化还是很困难的,特别是要求比较高的质量的话,如果有兴趣,大家可以自行研究了.但是如果不要求大规模的自动转换,还是有不少软件可以完成wav(mp3)到midi的转化的,比如这个网址可以在线将mp3转换为midi格式.
如何训练自己的midi文件?
之前我们是拿作者给的一个original_metheny.mid文件进行训练然后生成mid文件的.那么我们可以拿自己的mid文件进行训练吗?这里有一个网址可以打包下载很多的midi文件,或者访问这个网址可以下载自己喜欢的流行音乐的midi格式.我们发现我们下载的midi文件的format,tracks,divisions都和deepjazz作者提供的original_metheny.mid格式不同,所以如果只是把mid文件换成我们自己的是没有办法顺利train的,总是会报错.我大概看了一下代码,主要是使用music21处理midi格式转换的代码部分有问题.我尝试了半天,因为自己对于music21以及midi格式不是很熟悉,所以这个问题暂时没有解决.如果我后面有时间了会好好再分析一下deepjazz的源码,解决这个问题.
---------------------------------------------------------------------------Part II magenta ---------------------------------------------------------------
1.What is magenta?
下面是magenta官方github的介绍.
magenta是一个旨在探索使用机器学习来创造艺术和音乐的研究项目.目前它主要使用新兴的深度学习技术以及强化学习技术来产生歌曲,绘画,图片等等.同时它也旨在探索构建智能化的工具和接口,这样艺术家可以使用这些模型来扩展(而不是取代)他们的部分工作.
magenta最初是由Google Brain 的一些研究员发起的,但是其他的很多研究人员也为这个项目做出了巨大的贡献.我们使用tensorflow在github上发布我们的模型和代码.如果你想要了解更多关于magenta的事情,你可以查看我们的博客,我们在那里介绍了很多技术上的细节.你也可以加入讨论组.
2.How to install magenta and use it?
安装magenta非常简单,可以直接使用pip install magenta 安装,但是要注意在此之前你需要安装好了tensorflow.
magenta支持gpu加速(你只需要安装gpu版本的tensorflow),使用pip install magenta-gpu 安装即可.magenta其实提供了非常多的models,包含了语音,图片等.这里我们主要关注音乐生成方面的模型.
2.1 drums_rnn model
这是一个训练得到drums 风格音乐的模型.这个模型使用了LSTM将语言模型应用在drum track 生成上.和melodies不一样,drum tracks是多音的,多个drums可能会同时存在.尽管这样,我们还是通过以下手段将drum track 作为一个single sequence 来处理:
a)将所有不同的midi drums 映射到一个更小的drum classes上去
b)将每一个event表达为一个单一值,该值代表了该次struck(敲击)所属的drums classes 类别
这里model 提供了两个configurations:one drum 和drum kit.具体可以参考原网址的说明
下面来说明如何训练drums_rnn model.magenta其实已经提供了pre trained model,我们可以首先快速来inference一下.首先下载drum_kit文件,然后将下载的drum_kit_rnn.mag文件放入某一个文件夹下(比如model/下).然后我们写一个脚本generate_drums_rnn.sh:
#!/bin/bash
drums_rnn_generate \\
--config=\'drum_kit\' \\
--bundle_file=../data/drum_kit_rnn.mag \\
--output_dir=../output \\
--num_outputs=5 \\
--num_steps=256 \\
--primer_drums="[(36,)]"
这里 --config 是配置 configuration,有\'drum_kit\'和\'one_drum\'两个选项
--bundle_file 指定我们bundle file的地址(就是刚才下载的drum_kit_rnn.mag文件)
--output_dir 指定输出midi文件的地址
--num_outputs 指定输出midi文件的个数(默认是10个)
--num_steps 指定训练的epochs(训练轮数)
--primer_drums 指定开始的一些音节(必填)
上面的脚本会以一个bass drum hit(低音)开始,如果你愿意的话,你也可以使用其他的字符串形式的python list,但是list中的元素必须是一个tuple,而且必须要是代表drum 的midi 音节的整数.比如说:--primer_drums="[(36, 42), (), (42,)]"表示的意思就是一个bass 和一个hit-hat,然后是一个silence,最后是一个hit-hat.如果你不使用--primer_drums参数,你也可以使用--primer_midi参数,来使用一个drum midi 文件来作为primer(开头).
如果按照上面的方式来进行尝试的话,你会得到一些midi文件.然后播放它吧,有些还是相当不错的!
上面我们使用了pre trained model,然后可以直接得到生成的midi文件,那么该如何来训练自己的model呢?训练自己的model有些复杂,我们可以按照如下的steps 进行操作:
step1:build your dataset
参考网址首先我们需要准备自己的midi datasets,可以在这个网址打包下载,或者在这个midiworld自己手动下载,然后我们需要将这些midi files 转化为NoteSequences.使用如下的脚本convert_midi.sh进行转换:
#!/bin/bash
convert_dir_to_note_sequences \\
--input_dir=$INPUT_DIRECTORY \\
--output_file=$SEQUENCES_TFRECORD \\
--recursive
上面的参数:
--input_dir表示输入midi files 的文件夹地址(可以包含子文件夹)
--output_file 表示输出.tfrecord文件的地址
--recursive 表示递归遍历midi files
注意如果你使用的是前一个midi datasets的话,由于这个数据集非常大(有1.6G左右),包含了非常多的midi文件,所以训练起来可能会非常耗时,我大概训练了两个小时还没训练完最后提前终止了,当然如果你的计算机性能非常好,你也可以尝试训练完.
训练完之后,我们会得到一个lmd_matched_notesequences.tfrecord文件.接下来进入step2
step2:create sequenceExamples
注意我们输入模型进行训练和评估的是SequenceExamples.每一个SequenceExample都会包含一个序列输入和一个序列标签,代表了一个drum track.可以运行下面的命令将之前得到的NoteSequences 转化为SequenceExamples.这样将会产生两个部分的SequenceExamples,一个用于training,一个用于evaluation.具体可以使用--eval_ratio来指定两者的比例.比如指定eval_ratio = 0.1(或者10%),会将提取出的drums tracks 的10%用于evaluation,剩下的90%用于training.
drums_rnn_create_dataset \\
--config=<one of \'one_drum\' or \'drum_kit\'> \\
--input=/tmp/notesequences.tfrecord \\
--output_dir=/tmp/drums_rnn/sequence_examples \\
--eval_ratio=0.10
上面的参数中:
--config 只能取值为\'one_drum\'或者\'drum_kit\'
--input 为step1得到的tfrecord文件地址
--output_dir 为输出SequenceExamples的文件夹地址
--eval_ratio 指定evaluation 和training的比例
step3:train and evaluate the model
运行下面的代码(train.sh)就可以进行train了.
#!/bin/bash
drums_rnn_train \\
--config=drum_kit \\
--run_dir=/tmp/drums_rnn/logdir/run1 \\
--sequence_example_file=/tmp/drums_rnn/sequence_examples/training_drum_tracks.tfrecord \\
--hparams="batch_size=64,rnn_layer_sizes=[64,64]}" \\
--num_training_steps=20000
各个参数的含义如下:
--config:\'one_drum\' or \'drum_kit\'
--run_dir 是运行tensorflow训练模型checkpoints存放的文件夹地址
--sequence_example_file是用于训练模型的SequenceExamples tfrecord文件地址
--num_training_steps 指定训练的steps(轮数),如果不指定的话,会一直运行直到手动终止(CTRL-C或者CTRL-Z)
--hparams 用于指定其他的超参数,比如这里我们指定了batch_size = 64,而不是默认的128.使用更小的batch size 有助于降低OOM(内存溢出)的风险,当然,如果你的内存够大,也可以设置较大的batch_size.这里还设定使用2 layers的RNN,每一个layer 的hidden units都是64,而不是默认的3 layers,每个layer有256个hidden units.这样可以加速训练(当然损失了一定的精度),如果你的计算机性能很高,你可以尝试更大的hidden units以获得更好的结果.我们还可以设定--attn_length 参数来指定多少个steps进行一次attention machanism.这里我们使用的是默认值32.
运行下面的代码(eval.sh)就可以进行evaluation.
!/bin/bash
drums_rnn_train \\
--config=drum_kit \\
--run_dir=/tmp/drums_rnn/logdir/run1 \\
--sequence_example_file=/tmp/drums_rnn/sequence_examples/eval_drum_tracks.tfrecord \\
--hparams="batch_size=64,rnn_layer_sizes=[64,64]" \\
--num_training_steps=20000 \\
--eval
和train.sh差不多,唯一区别是--sequence_example_file需要指定eval的tfrecord file 了,还有就是多了一个--eval 用于指定这是一个eval过程,而不是train.注意eval过程不会改变任何一个参数,它只是用于评估模型的性能.
当然我们也可以运行:tensorboard --logdir=/tmp/drums_rnn/logdir 来使用tensorboard来查看train 和eavl的结果,只要在浏览器打开:
http://localhost:6006 就可以了.
step4:generate drum tracks
完成了step1~step3之后我们就可以来产生自己的midi 文件了.运行的脚本为:
#!/bin/bash
drums_rnn_generate \\
--config=drum_kit \\
--run_dir=/tmp/drums_rnn/logdir/run1 \\
--hparams="batch_size=64,rnn_layer_sizes=[64,64]" \\
--output_dir=/tmp/drums_rnn/generated \\
--num_outputs=10 \\
--num_steps=128 \\
--primer_drums="[(36,)]"
大部分参数上面都已经解释了,这里不再赘述.
2.2 melody_rnn model
melody_rnn model 和上面的 drums_rnn 非常类似,只不过这里产生的是melody,而上面产生的是drums.这里不再赘述,具体可以参见melody_rnn
2.3 其他的模型
除了上面说的drums_rnn 和melody_rnn之外,magenta还有很多其他有趣的模型,比如neural style transfer(神经风格迁移,可以产生指定风格的图片)等,有兴趣的可以去magenta详细了解.
------------------------本文完,感谢阅读!-----------------------------------------------------------------------