用 TensorFlow 让机器人唱首歌给你听


今天想来看看 AI 是怎样作曲的。

本文会用 TensorFlow 来写一个音乐生成器。

当你对一个机器人说:我想要一种能够表达出希望和奇迹的歌曲时,发生了什么呢?

计算机会首先把你的语音转化成文字,并且提取出关键字,转化成词向量。

然后会用一些打过标签的音乐的数据,这些标签就是人类的各种情感。接着通过在这些数据上面训练一个模型,模型训练好后就可以生成符合要求关键词的音乐。

程序最终的输出结果就是一些和弦,他会选择最贴近主人所要求的情感关键词的一些和弦来输出。

当然你不只是可以听,也可以作为创作的参考,这样就可以很容易地创作音乐,即使你还没有做到刻意练习 1 万小时。

机器学习其实是为了扩展我们的大脑,扩展我们的能力。


DeepMind 发表了一篇论文,叫做 WaveNet, 这篇论文介绍了音乐生成和文字转语音的艺术。

通常来讲,语音生成模型是串联。这意味着如果我们想从一些文字的样本中来生成语音的话,是需要非常大量的语音片段的 数据库,通过截取它们的一部分,并且再重新组装到一起,来组成一个完整的句子。

640?wx_fmt=png&wxfrom=5&wx_lazy=1

生成音乐也是同样的道理,但是它有一个很大的难点:就是当你把一些静止的组件组合到一起的时候,生成声音需要很自然,并且还要有情感,这一点是非常难的。

一种理想的方式是,我们可以把所有生成音乐所需要的信息存到模型的参数里面。也就是那篇论文里讲的事情。

640?wx_fmt=png

我们并不需要把输出结果传给信号处理算法来得到语音信号,而是直接处理语音信号的波。

他们用的模型是 CNN。这个模型的每一个隐藏层中,每个扩张因子,可以互联,并呈指数型的增长。每一步生成的样本,都会被重新投入网络中,并且用于产生下一步。

640?wx_fmt=png

我们可以来看一下这个模型的图。输入的数据,是一个单独的节点,它作为粗糙的音波,首先需要进行一下预处理,以便于进行下面的操作。

640?wx_fmt=png

接着我们对它进行编码,来产生一个 Tensor,这个 Tensor 有一些 sample 和 channel。

然后把它投入到 CNN 网络的第一层中。这一层会产生 channel 的数量,为了进行更简单地处理。

然后把所有输出的结果组合在一起,并且增加它的维度。再把维度增加到原来的 channel 的数量。

把这个结果投入到损失函数中,来衡量我们的模型训练的如何。

最后,这个结果会被再次投入到网络中,来生成下一个时间点所需要的音波数据。

重复这个过程就可以生成更多的语音。

这个网络很大,在他们的 GPU 集群上需要花费九十分钟,并且仅仅只能生成一秒的音频。


接下来我们会用一个更简单的模型在 TensorFlow 上来实现一个音频生成器。

1.引入 packages:

数据科学包 Numpy ,数据分析包 Pandas,tqdm 可以生成一个进度条,显示训练时的进度。

  1. import numpy as np

  2. import pandas as pd

  3. import msgpack

  4. import glob

  5. import tensorflow as tf

  6. from tensorflow.python.ops import control_flow_ops

  7. from tqdm import tqdm

  8. import midi_manipulation

我们会用到一种神经网络的模型 RBM-Restricted Boltzmann Machine 作为生成模型。

它是一个两层网络:第一层是可见的,第二层是隐藏层。同一层的节点之间没有联系,不同层之间的节点相互连接。每一个节点都要决定它是否需要将已经接收到的数据发送到下一层,而这个决定是随机的。

2.定义超参数:

先定义需要模型生成的 note 的 range

640?wx_fmt=png

  1. lowest_note = midi_manipulation.lowerBound #the index of the lowest note on the piano roll

  2. highest_note = midi_manipulation.upperBound #the index of the highest note on the piano roll

  3. note_range = highest_note-lowest_note #the note range

接着需要定义 timestep ,可见层和隐藏层的大小。

  1. num_timesteps  = 15 #This is the number of timesteps that we will create at a time

  2. n_visible      = 2*note_range*num_timesteps #This is the size of the visible layer.

  3. n_hidden       = 50 #This is the size of the hidden layer

训练次数,批量处理的大小,还有学习率。

  1. num_epochs = 200 #The number of training epochs that we are going to run. For each epoch we go through the entire data set.

  2. batch_size = 100 #The number of training examples that we are going to send through the RBM at a time.

  3. lr         = tf.constant(0.005, tf.float32) #The learning rate of our model

3.定义变量:

x 是投入网络的数据 
w 用来存储权重矩阵,或者叫做两层之间的关系 
此外还需要两种 bias,一个是隐藏层的 bh,一个是可见层的 bv

  1. x  = tf.placeholder(tf.float32, [None, n_visible], name="x") #The placeholder variable that holds our data

  2. W  = tf.Variable(tf.random_normal([n_visible, n_hidden], 0.01), name="W") #The weight matrix that stores the edge weights

  3. bh = tf.Variable(tf.zeros([1, n_hidden],  tf.float32, name="bh")) #The bias vector for the hidden layer

  4. bv = tf.Variable(tf.zeros([1, n_visible],  tf.float32, name="bv")) #The bias vector for the visible layer

接着,用辅助方法 gibbs_sample 从输入数据 x 中建立样本,以及隐藏层的样本:

gibbs_sample 是一种可以从多重概率分布中提取样本的算法。 
640?wx_fmt=png

它可以生成一个统计模型,其中,每一个状态都依赖于前一个状态,并且随机地生成符合分布的样本。

640?wx_fmt=png

  1. #The sample of x

  2. x_sample = gibbs_sample(1)

  3. #The sample of the hidden nodes, starting from the visible state of x

  4. h = sample(tf.sigmoid(tf.matmul(x, W)   bh))

  5. #The sample of the hidden nodes, starting from the visible state of x_sample

4.更新变量:
  1. size_bt = tf.cast(tf.shape(x)[0], tf.float32)

  2. W_adder  = tf.mul(lr/size_bt, tf.sub(tf.matmul(tf.transpose(x), h), tf.matmul(tf.transpose(x_sample), h_sample)))

  3. bv_adder = tf.mul(lr/size_bt, tf.reduce_sum(tf.sub(x, x_sample), 0, True))

  4. bh_adder = tf.mul(lr/size_bt, tf.reduce_sum(tf.sub(h, h_sample), 0, True))

  5. #When we do sess.run(updt), TensorFlow will run all 3 update steps

  6. updt = [W.assign_add(W_adder), bv.assign_add(bv_adder), bh.assign_add(bh_adder)]

5.接下来,运行 Graph 算法图:
1.先初始化变量
  1. with tf.Session() as sess:

  2.    #First, we train the model

  3.    #initialize the variables of the model

  4.    init = tf.initialize_all_variables()

  5.    sess.run(init)

首先需要 reshape 每首歌,以便于相应的向量表示可以更好地被用于训练模型。

  1.    for epoch in tqdm(range(num_epochs)):

  2.        for song in songs:

  3.            #The songs are stored in a time x notes format. The size of each song is timesteps_in_song x 2*note_range

  4.            #Here we reshape the songs so that each training example is a vector with num_timesteps x 2*note_range elements

  5.            song = np.array(song)

  6.            song = song[:np.floor(song.shape[0]/num_timesteps)*num_timesteps]

  7.            song = np.reshape(song, [song.shape[0]/num_timesteps, song.shape[1]*num_timesteps])

2.接下来就来训练 RBM 模型,一次训练一个样本
  1.            for i in range(1, len(song), batch_size):

  2.                tr_x = song[i:i batch_size]

  3.                sess.run(updt, feed_dict={x: tr_x})

模型完全训练好后,就可以用来生成 music 了。

3.需要训练 Gibbs chain

其中的 visible nodes 先初始化为 0,来生成一些样本。 
然后把向量 reshape 成更好的格式来 playback。

  1.    sample = gibbs_sample(1).eval(session=sess, feed_dict={x: np.zeros((10, n_visible))})

  2.    for i in range(sample.shape[0]):

  3.        if not any(sample[i,:]):

  4.            continue

  5.        #Here we reshape the vector to be time x notes, and then save the vector as a midi file

  6.        S = np.reshape(sample[i,:], (num_timesteps, 2*note_range))

4.最后,打印出生成的和弦
  1.       midi_manipulation.noteStateMatrixToMidi(S, "generated_chord_{}".format(i))


综上,就是用 CNN 来参数化地生成音波, 
用 RBM 可以很容易地根据训练数据生成音频样本, 
Gibbs 算法可以基于概率分布帮我们得到训练样本。 
640?wx_fmt=png

∞∞∞


640?wx_fmt=jpeg&wx_lazy=1&wxfrom=5

IT派 - {技术青年圈} 持续关注互联网、区块链、人工智能领域 640?wx_fmt=jpeg&wx_lazy=1&wxfrom=5



公众号回复“机器学习”

邀你加入{ IT派AI机器学习群 } 


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值