utils.py
包含的函數(shù)有
-
def convert_midi_to_mp3():: 將神經(jīng)網(wǎng)絡(luò)生成的MIDI文件轉(zhuǎn)換為MP3文件 -
def get_notes():: 從 music_midi 目錄中的所有 MIDI 文件里提取 note(音符)和 chord(和弦) return notes -
def create_music(prediction):: 用神經(jīng)網(wǎng)絡(luò)'預(yù)測'的音樂數(shù)據(jù)來生成 MIDI 文件,再轉(zhuǎn)成 MP3 文件,最后一句調(diào)用了convert_midi_to_mp3()
- 使用方式:
from utils import *,然后直接調(diào)用函數(shù)即可,比如notes = get_notes()
2.文件操作一般要引用import os, 文件操作建議寫法,同時(shí)積累形式字符串的寫法,如果用多個(gè),寫成format(xxx,xxx)
input_file = 'output.mid'
output_file = 'output.mp3'
if not os.path.exists(input_file):
raise Exception("MIDI 文件 {} 不在此目錄下,請確保此文件被正確生成".format(input_file))
print('將 {} 轉(zhuǎn)換為 MP3'.format(input_file))
- 在代碼中想執(zhí)行命令行命令的寫法,建議每一個(gè)比較大的操作都打一個(gè)log,需要引用
import subprocess.發(fā)現(xiàn)如果使用的時(shí)候用的是import,那么在使用時(shí)需要寫庫名
command = 'timidity {} -Ow -o - | ffmpeg -i - -acodec libmp3lame -ab 64k {}'.format(input_file, output_file)
return_code = subprocess.call(command, shell=True)
if return_code != 0:
print('轉(zhuǎn)換時(shí)出錯(cuò),請查看出錯(cuò)信息')
else:
print('轉(zhuǎn)換完畢. 生成的文件是 {}'.format(output_file))
- glob.glob匹配所有符合條件的文件,并以 List 的形式返回,提前引用庫
import glob
for midi_file in glob.glob("music_midi/*.mid"):
stream = converter.parse(midi_file)
- music21的操作見原代碼
- 判斷類型,用
isinstance( , ),append()用于在list后面添加元素。string有.join()。并且由于之前的note=[],所以相當(dāng)于是把list的符號轉(zhuǎn)成str輸出,奇妙的轉(zhuǎn)換 4.15.7
for element in notes_to_parse:
# 如果是 Note 類型,那么取它的音調(diào)
if isinstance(element, note.Note):
# 格式例如: E6
notes.append(str(element.pitch))
# 如果是 Chord 類型,那么取它各個(gè)音調(diào)的序號
elif isinstance(element, chord.Chord):
# 轉(zhuǎn)換后格式例如: 4.15.7
notes.append('.'.join(str(n) for n in element.normalOrder))
- 如果目錄不存在,創(chuàng)建目錄
if not os.path.exists("data"):
os.mkdir("data")
- 寫入文件,使用了
import picklestore the serialized object data into the file
with open('data/notes', 'wb') as filepath:
pickle.dump(notes, filepath)
network.py
使用keras框架搭建網(wǎng)絡(luò)
1.def network_model(inputs, num_pitch, weights_file=None): 輸入,最后輸出的個(gè)數(shù),以及是否有權(quán)重文件。默認(rèn)沒有,如果有的話,就載入
if weights_file is not None: # 如果是 生成 音樂時(shí)
# 從 HDF5 文件中加載所有神經(jīng)網(wǎng)絡(luò)層的參數(shù)(Weights)
model.load_weights(weights_file)
2.步驟
- 創(chuàng)建Sequential()類。
model = tf.keras.models.Sequential() - 添加每一層網(wǎng)絡(luò),比如
LSTM層
model.add(tf.keras.layers.LSTM(
512, # LSTM 層神經(jīng)元的數(shù)目是 512,也是 LSTM 層輸出的維度
input_shape=(inputs.shape[1], inputs.shape[2]), # 輸入的形狀,對第一個(gè) LSTM 層必須設(shè)置
return_sequences=True # 返回所有的輸出序列(Sequences)
))
Dropout層
model.add(tf.keras.layers.Dropout(0.3)) # 丟棄 30% 神經(jīng)元,防止過擬合
全連接層
model.add(tf.keras.layers.Dense(num_pitch))
- softmax激活函數(shù)算概率
model.add(tf.keras.layers.Activation('softmax')) - 交叉熵計(jì)算誤差,使用對 循環(huán)神經(jīng)網(wǎng)絡(luò)來說比較優(yōu)秀的 RMSProp 優(yōu)化器 compile: configure the model for training
model.compile(loss='categorical_crossentropy', optimizer='rmsprop')
train.py
1.會(huì)from utils import *以及from network import *
2.步驟
- 用utils.py中的函數(shù)得到數(shù)據(jù)及數(shù)據(jù)長度
- 為神經(jīng)網(wǎng)絡(luò)準(zhǔn)備好訓(xùn)練的序列prepare_sequence
- 字符串與整數(shù)的映射
- 序列長度,生成新的輸入序列
- 將輸入的形狀轉(zhuǎn)為神經(jīng)網(wǎng)絡(luò)接受的
- 歸一化(注意不是除以輸入的長度,而是輸入的最大值)
- 將期望輸出轉(zhuǎn)換成 {0, 1} 組成的布爾矩陣,為了配合 categorical_crossentropy 誤差算法使用
network_output = tf.keras.utils.to_categorical(network_output)
- 生成checkpoint文件
- 用fit方法生成模型
3.得到不重復(fù)的元素個(gè)數(shù)。set是不會(huì)有重復(fù)的數(shù)的
num_pitch = len(set(notes))
- notes中為字符串,要無重復(fù)并且按序
pitch_names = sorted(set(item for item in notes)) -
enumerate()同時(shí)得到索引和元素,創(chuàng)建字典用dict( , )。把set轉(zhuǎn)換為dict,如下,建立字符串與整數(shù)的映射關(guān)系
pitch_to_int = dict((pitch, num) for num, pitch in enumerate(pitch_names)) - range的用法
range(start, stop, step)stop is not include - 生成checkpoint文件
filepath = "weights-{epoch:02d}-{loss:.4f}.hdf5"
# 用 Checkpoint(檢查點(diǎn))文件在每一個(gè) Epoch 結(jié)束時(shí)保存模型的參數(shù)(Weights)
# 不怕訓(xùn)練過程中丟失模型參數(shù)??梢栽谖覀儗?Loss(損失)滿意了的時(shí)候隨時(shí)停止訓(xùn)練
checkpoint = tf.keras.callbacks.ModelCheckpoint(
filepath, # 保存的文件路徑
monitor='loss', # 監(jiān)控的對象是 損失(loss)
verbose=0,
save_best_only=True, # 不替換最近的數(shù)值最佳的監(jiān)控對象的文件
mode='min' # 取損失最小的
)
callbacks_list = [checkpoint] #后面的fit方法會(huì)調(diào)用
- 用 fit 方法來訓(xùn)練模型
model.fit(network_input, network_output, epochs=100, batch_size=64, callbacks=callbacks_list)
generate.py
用訓(xùn)練好的神經(jīng)網(wǎng)絡(luò)模型參數(shù)來作曲
1.用pickle加載之前保存的文件
with open('data/notes', 'rb') as filepath:
notes = pickle.load(filepath)
2.載入之前訓(xùn)練時(shí)最好的參數(shù)文件,來生成曲子
model = network_model(normalized_input, num_pitch, "best-weights.hdf5")
其中network_model就是寫的model,normalized_input是經(jīng)過prepare_sequential的