分享

使用LSTM神经网络进行音乐合成(数据格式,模型构建,完整源码)

 openlog 2019-09-30

本文章将讲解如何借用机器学习框架Tensorflow和Keras,构建LSTM神经网络模型,通过学习音乐数据,来自动合成一段音乐。

训练的原始音乐数据为:下载试听

合成的音乐例子:下载试听

完整代码(包含训练数据集):源码下载。其中主方法在MusicGenerator.py文件中。

在这里插入图片描述

1. 神经网络结构模型

从下图可以看出,我们的网络模型是经典的LSTM循环神经网络模型,该模型的优点就是能够建模序列的数据,有效解决长依赖问题。下图中没有把状态c画出来。
在这里插入图片描述

1.2. 模型的输入数据

每一次输入的是一个“音符”,这里为了便于理解,把一个音符抽象成一个数值,然后我们把这个数值编码成one-hot的格式。我们的数据集中总共有78个不同的“音符”,所以一个one-hot向量的长度为78,也就是使用一个78长度的向量表示一个音符,在这个向量中,有77个元素为0,另外一个为1. 举一个例子,如果向量[3,2,5] 表示三个音符,那么如何使用one-hot格式表示这三个音符呢?

[[0,0,1,0,0,0,...],
[0,1,0,0,0,0,...],
[0,0,0,0,1,0,...]]
省略号表示省略了72个0,其中一行向量表示一个音符。

如果输入的是长度为30的音符(可以类比输入单词数为30的句子),那么,需要使用shape为(30,78)的二维矩阵来表示。也就是一段音符(音乐)需要使用(30,78)的矩阵来表示。又因为我们使用了很多段音乐作为输入数据来训练网络,比如60段长度都为30的音乐,这时候所有的数据需要使用(60,30,78)维度的三维矩阵来表示。这便是本文中我们的输入数据的格式了。

1.3. 模型的输出格式

因为我们的目的是合成音乐,所以输出数据的维度跟输入的数据的格式应该相似。输入数据为(60,30, 78),输出数据为(30, 60, 78)。其中60表示有60小段音乐,每一小段音乐的长度为30(即30个音符),使用长度为78的one-hot向量表示一个音符。为什么输出数据的30在60的前面,是因为方便数据表示,后面会讲到。

使用下面的代码可以加载和查看输入和输出数据的格式

X, Y, n_values, indices_values = load_music_utils()
print('shape of X:', X.shape)
print('number of training examples:', X.shape[0])
print('Tx (length of sequence):', X.shape[1])
print('total # of unique values:', n_values)
print('Shape of Y:', Y.shape)

for i in range(10):
    print(X[0, i, :])
for i in range(10):
    print(Y[i, 0, :])

上面代码的输出的部分结果如下所示,one-hot向量使用True和False代替0和1了。

shape of X: (60, 30, 78)
number of training examples: 60
Tx (length of sequence): 30
total # of unique values: 78
Shape of Y: (30, 60, 78)
[False False False False False False False False False False False False
 False False False False False False False False False False False False
 False False False False False False False False False False False False
 False False False False False False False False False False False False
 False False False False False False False False False False False False
 False False False False False False False False False False False False
 False False False False False False]
[False False False False False False False False False False False False
 False False False False False False False False False False False False
 False False False False False False  True False False False False False
 False False False False False False False False False False False False
 False False False False False False False False False False False False
 False False False False False False False False False False False False
 False False False False False False]

2. 模型

我们需要构建两个不同的模型,分别是训练模型和用于生成音乐的模型。

先来讲第一个模型:神经网络的训练模型。构建该模型可以结合上图来理解。对于每一小段音乐中的每一个时间点,输入表示一个音符的向量,在每一步中使用a并结合Dense函数来计算输出out,同时把输出out保存到一个列表中。最后在构建model( model = Model(inputs=[X_in, a0, c0], outputs=outputs)
)的时候我们传入X_in, a0, c0, outputs,分别表示模型的输入数据,第一个状态数据集a0和c0,以及模型的输出数据集,它们表示的是具有特定维度的“占位符”,告诉模型这里将输入具备这种维度的数据,占位符这个词来自tensorflow。该条代码的输入数据的参数跟这个代码model.fit([X, a0, c0], list(Y), epochs=100)对应(这句代码表示训练拟合数据,在主方法中)。在这两行代码中,变量outputs和变量list(Y)的维度应该一一对应,在构建变量outputs的时候它的维度表示为(30,60,78),所以,模型训练的时候的变迁数据Y也是(30,60,78)维度的。

# 构建训练模型
def djmodel(Tx, n_a, n_values):
    """
    Implement the model

    Arguments:
    Tx -- length of the sequence in a corpus
    n_a -- the number of activations used in our model
    n_values -- number of unique values in the music data

    Returns:
    model -- a keras model with the
    """

    # Define the input of your model with a shape
    X_in = Input(shape=(Tx, n_values))

    # Define s0, initial hidden state for the decoder LSTM
    a0 = Input(shape=(n_a,), name='a0')
    c0 = Input(shape=(n_a,), name='c0')
    a = a0
    c = c0

    ### START CODE HERE ###
    # Step 1: Create empty list to append the outputs while you iterate (≈1 line)
    outputs = []

    # Step 2: Loop
    for t in range(Tx):
        # Step 2.A: select the "t"th time step vector from X.
        x = Lambda(lambda x: x[:, t, :])(X_in)

        # Step 2.B: Use reshapor to reshape x to be (1, n_values) (≈1 line)
        x = reshapor(x)
        # Step 2.C: Perform one step of the LSTM_cell
        a, _, c = LSTM_cell(x, initial_state=[a, c])
        # Step 2.D: Apply densor to the hidden state output of LSTM_Cell
        out = densor(a)
        # Step 2.E: add the output to "outputs"
        outputs.append(out)

    # Step 3: Create model instance
    model = Model(inputs=[X_in, a0, c0], outputs=outputs)

    ### END CODE HERE ###

    return model

第二个模型用于生成音乐。该模型跟上面第一个模型的不同点是输入数据的方式不一样。并且第一个模型是为了训练,而第二个模型是为了预测。在该模型中,在t0时间点,我们输入第一个x1,得到y1,然后将y1作为x2来输入模型中,依次类推。从下面的代码块中我们看出,我们还把每一步的输出out保存到一个数组中,作为这行代码inference_model = Model(inputs=[x0, a0, c0], outputs=outputs)的参数,以生成一个预测模型。该行代码跟文件data-utils.py中的pred = inference_model.predict([x_initializer, a_initializer, c_initializer])这行代码对应。因为是预测,所以,在第二行代码中没有标签数据y值的输入。
在这里插入图片描述

# 构建生成音乐的模型
def music_inference_model(LSTM_cell, densor, n_values=78, n_a=64, Ty=100):
    """
    Uses the trained "LSTM_cell" and "densor" from model() to generate a sequence of values.

    Arguments:
    LSTM_cell -- the trained "LSTM_cell" from model(), Keras layer object
    densor -- the trained "densor" from model(), Keras layer object
    n_values -- integer, umber of unique values
    n_a -- number of units in the LSTM_cell
    Ty -- integer, number of time steps to generate

    Returns:
    inference_model -- Keras model instance
    """

    # Define the input of your model with a shape
    x0 = Input(shape=(1, n_values))

    # Define s0, initial hidden state for the decoder LSTM
    a0 = Input(shape=(n_a,), name='a0')
    c0 = Input(shape=(n_a,), name='c0')
    a = a0
    c = c0
    x = x0

    ### START CODE HERE ###
    # Step 1: Create an empty list of "outputs" to later store your predicted values (≈1 line)
    outputs = []

    # Step 2: Loop over Ty and generate a value at every time step
    for t in range(Ty):
        # Step 2.A: Perform one step of LSTM_cell (≈1 line)
        a, _, c = LSTM_cell(x, initial_state=[a, c])

        # Step 2.B: Apply Dense layer to the hidden state output of the LSTM_cell (≈1 line)
        out = densor(a)

        # Step 2.C: Append the prediction "out" to "outputs". out.shape = (None, 78) (≈1 line)
        outputs.append(out)

        # Step 2.D: Select the next value according to "out", and set "x" to be the one-hot representation of the
        #           selected value, which will be passed as the input to LSTM_cell on the next step. We have provided
        #           the line of code you need to do this.
        x = Lambda(one_hot)(out)

    # Step 3: Create model instance with the correct "inputs" and "outputs" (≈1 line)
    inference_model = Model(inputs=[x0, a0, c0], outputs=outputs)

    ### END CODE HERE ###
    return inference_model

3. 原始的音乐数据的处理

音乐数据的处理包含两部分的内容,一个是从原始音乐中提取出目标数据并进行适当的预处理,用于输入模型的数据x;二是将生成的数据y还原成音频数据。该部分的功能实现借助了第三方库,具体请查看源码。

4. 总结:

本文讲解了如何构建一个LSTM神经网络模型训练一个能够合成音乐的模型。重点讲了输入输出数据的格式以及两个模型的构建过程。谢谢

Reference:

  1. 吴恩达老师深度学习课程, recurrent neural networks
  2. Ji-Sung Kim, 2016, deepjazz
  3. Jon Gillick, Kevin Tang and Robert Keller, 2009. Learning Jazz Grammars
  4. Robert Keller and David Morrison, 2007, A Grammatical Approach to Automatic Improvisation
  5. François Pachet, 1999, Surprising Harmonies

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多