项目笔记(一):实验——用神经网络实现midi音乐旋律音轨的确定
零、寫在前面
計劃要用seq2seq模型做一個交響樂編曲程序,encoder network的輸入是一個樂句旋律,decoder network的目標target是這個樂句完整的管弦配樂版本。本文記錄的實驗的目的是自動提取出midi樂句的旋律音軌。
一、原理
參考這篇文獻中的方法:提取出這個樂句中各個音軌(樂器)的以下特征:
每個音軌的五個特征疊在一起,作為一個樂句的神經網絡輸入。
對應于主旋律的音軌的one-hot向量作為神經網絡的目標輸出。
之前文獻中,使用了其他的特征作為輸入,但那些特征并不十分合理。
二、技術細節
1.midi_to_features.py
這個文件用于提取出一個樂句各個音軌的5個特征值,用了python的pretty_midi包作為處理midi文件的工具。
import pretty_mididef get_avg_velocity(instrument):# 平均力度velocity = 0for note in instrument.notes:velocity += note.velocityvelocity /= len(instrument.notes)return velocitydef get_sum_duration(instrument):# 所有音符累加總時值duration = 0for note in instrument.notes:duration += note.end - note.startreturn durationdef get_types_of_duration(instrument):# 時值類型duration = []for note in instrument.notes:duration.append(note.end - note.start)types = len(set(duration))return typesdef get_pitch_range(instrument):# 最高音與最低音之間的音程pitch = []for note in instrument.notes:pitch.append( note.pitch )sorted_pitch = sorted( set(pitch) )range = sorted_pitch[-1] - sorted_pitch[0]return rangedef get_second_pitch_range(instrument):# 第二高音與第二低音之間的音程pitch = []for note in instrument.notes:pitch.append( note.pitch )sorted_pitch = sorted( set(pitch) )range = sorted_pitch[-2] - sorted_pitch[1]return range上面的代碼中五個函數對應于提取一個樂句中某一音軌(樂器)的五個特征值。
我們接下來將每一個音軌(樂器)的這五個值,壓縮到到一個向量中。
接下來還要寫函數來遍歷數據集,提取出各個midi樂句的(一個樂句一個midi文件)特征值。將他們疊在一起,作為訓練集的輸入。
def get_X_train():X_train = [[]]for i in range(13): # 一共切取了14個midi片段,其中13個作為訓練樣本midi_file = 'data/x'+str(i+1)+'.mid'train_example = get_train_example(midi_file)if i==0:X_train[0]=train_exampleelse:X_train.append(train_example)return X_train類似地,再寫測試集:
def get_X_test():X_test = [[]]midi_file = 'data/x14.mid'train_example = get_train_example(midi_file)X_test[0]=train_examplereturn X_test2. features_to_melody.py
這個文件里用tensorflow構建一個二層神經網絡,用matplotlib繪圖。我們使用鋼琴曲作為數據集,左右手各一個音軌,每個音軌5個特征值,所以一個樂句共有10個特征值。對應的y值顯然也只有兩種可能。
from midi_to_features import * import tensorflow as tf import matplotlib.pyplot as plt import numpy as npn_x, n_y = 10, 2接下來我們制作訓練集和測試集的輸入和輸出,我使用的數據中都是旋律再右手的,所以所有的輸出都相同。
# get training data X_train = get_X_train() Y_example = [1, 0] Y_train = [[]] for i in range (13):if i == 0:Y_train[0] = Y_exampleelse:Y_train.append( Y_example )#行列互換,使其符合tensorflow的輸入格式 X_train = list(map(list, zip(*X_train))) Y_train = list(map(list, zip(*Y_train))) print("X_train:", X_train) print("Y_train:", Y_train)# get testing data X_test = get_X_test() X_test = list(map(list, zip(*X_test))) Y_test = [[1],[0]] print("X_test:", X_test) print("Y_test:", Y_test)接下來是構建神經網絡的準備工作:創建placeholder和初始化參數(用xavier來初始化所有w,用全零初始化b)。
# Create placeholders def create_placeholders(n_x, n_y):X = tf.placeholder(tf.float32, shape=[n_x, None])Y = tf.placeholder(tf.float32, shape=[n_y, None])return X, Y# Initialize the parameters def initialize_parameters():W1 = tf.get_variable("W1", [25, n_x], initializer=tf.contrib.layers.xavier_initializer())b1 = tf.get_variable("b1", [25, 1], initializer=tf.zeros_initializer())W2 = tf.get_variable("W2", [12, 25], initializer=tf.contrib.layers.xavier_initializer())b2 = tf.get_variable("b2", [12, 1], initializer=tf.zeros_initializer())W3 = tf.get_variable("W3", [n_y, 12], initializer=tf.contrib.layers.xavier_initializer())b3 = tf.get_variable("b3", [n_y, 1], initializer=tf.zeros_initializer())parameters = {"W1": W1,"b1": b1,"W2": W2,"b2": b2,"W3": W3,"b3": b3}return parameters神經網絡的正向傳播,激活函數使用relu。
# Forward propagation def forward_propagation(X, parameters):W1 = parameters['W1']b1 = parameters['b1']W2 = parameters['W2']b2 = parameters['b2']W3 = parameters['W3']b3 = parameters['b3']Z1 = tf.add(tf.matmul(W1, X), b1)A1 = tf.nn.relu(Z1)Z2 = tf.add(tf.matmul(W2, A1), b2)A2 = tf.nn.relu(Z2)Z3 = tf.add(tf.matmul(W3, A2), b3)return Z3使用softmax作為輸出層,定義交叉熵損失。
# Compute Cost def compute_cost(Z3, Y):logits = tf.transpose(Z3)labels = tf.transpose(Y)cost = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(logits = logits, labels = labels))return cost現在我們可以開始構建整個模型了。
# the model def model(X_train, Y_train, X_test, Y_test,learning_rate = 0.0001, num_epochs = 1000,print_cost = True):n_x, n_y, = 10, 2, 13costs = []# 創建placeholderX, Y = create_placeholders(n_x, n_y)# 初始化參數parameters = initialize_parameters()# 神經網絡前向傳播Z3 = forward_propagation(X, parameters)# 計算損失函數cost = compute_cost(Z3, Y)#用adam算法最小化損失函數optimizer = tf.train.AdamOptimizer(learning_rate=learning_rate).minimize(cost)init = tf.global_variables_initializer()# 創建tensorflow的sessionwith tf.Session() as sess:sess.run(init)for epoch in range(num_epochs):_, Cost = sess.run([optimizer, cost], feed_dict={X: X_train, Y: Y_train})# 打印出cost的變化if print_cost == True and epoch % 100 == 0:print ("Cost after epoch %i: %f" % (epoch, Cost))costs.append(Cost)# 用matplotlib繪制 時間-損失圖像plt.plot(np.squeeze(costs))plt.ylabel('cost')plt.xlabel('iterations (per tens)')plt.title("Learning rate =" + str(learning_rate))plt.show()parameters = sess.run(parameters)print ("Parameters have been trained!")correct_prediction = tf.equal(tf.argmax(Z3), tf.argmax(Y))# 計算準確率accuracy = tf.reduce_mean(tf.cast(correct_prediction, "float"))print("Train Accuracy:", accuracy.eval({X: X_train, Y: Y_train}))print("Test Accuracy:", accuracy.eval({X: X_test, Y: Y_test}))return parametersparameters = model(X_train, Y_train, X_test, Y_test)3.數據集
這個實驗只是為了驗證可行性,用了很小很小的數據集(對應無比強烈的過擬合)。我手工提取了莫扎特土耳其進行曲(這是我寫過的這首曲子的樂理分析)的一共14旋律材料(14個樂句)。13個作為訓練集,最后一個作為測試集。他們可以在這里下載。
我使用的是降b小調的版本,但樂句整體的移調不會影響輸出,所以,never mind。
三、結論
Cost after epoch 0: 9.850606 Cost after epoch 100: 0.593675 Cost after epoch 200: 0.267264 Cost after epoch 300: 0.170038 Cost after epoch 400: 0.108876 Parameters have been trained! Train Accuracy: 1.0 Test Accuracy: 1.0我們看到,神經網絡對于訓練集擬合地很好,正確率100%(其實是過擬合),測試樣本Test Accuracy: 1.0,即算法正確地分類了測試樣本。
這說明,用神經網絡實現midi音樂旋律音軌的確定,這一方法是可行的。
總結
以上是生活随笔為你收集整理的项目笔记(一):实验——用神经网络实现midi音乐旋律音轨的确定的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 研究GigE Vision(未完待续)
- 下一篇: F28M35调用IQmath库出错