utils.py
包含的函数有
-
def convert_midi_to_mp3():
: 将神经网络生成的MIDI文件转换为MP3文件 -
def get_notes():
: 从 music_midi 目录中的所有 MIDI 文件里提取 note(音符)和 chord(和弦) return notes -
def create_music(prediction):
: 用神经网络'预测'的音乐数据来生成 MIDI 文件,再转成 MP3 文件,最后一句调用了convert_midi_to_mp3()
- 使用方式:
from utils import *
,然后直接调用函数即可,比如notes = get_notes()
2.文件操作一般要引用import os
, 文件操作建议写法,同时积累形式字符串的写法,如果用多个,写成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('将 {} 转换为 MP3'.format(input_file))
- 在代码中想执行命令行命令的写法,建议每一个比较大的操作都打一个log,需要引用
import subprocess
.发现如果使用的时候用的是import
,那么在使用时需要写库名
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('转换时出错,请查看出错信息')
else:
print('转换完毕. 生成的文件是 {}'.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=[]
,所以相当于是把list的符号转成str输出,奇妙的转换 4.15.7
for element in notes_to_parse:
# 如果是 Note 类型,那么取它的音调
if isinstance(element, note.Note):
# 格式例如: E6
notes.append(str(element.pitch))
# 如果是 Chord 类型,那么取它各个音调的序号
elif isinstance(element, chord.Chord):
# 转换后格式例如: 4.15.7
notes.append('.'.join(str(n) for n in element.normalOrder))
- 如果目录不存在,创建目录
if not os.path.exists("data"):
os.mkdir("data")
- 写入文件,使用了
import pickle
store the serialized object data into the file
with open('data/notes', 'wb') as filepath:
pickle.dump(notes, filepath)
network.py
使用keras框架搭建网络
1.def network_model(inputs, num_pitch, weights_file=None):
输入,最后输出的个数,以及是否有权重文件。默认没有,如果有的话,就载入
if weights_file is not None: # 如果是 生成 音乐时
# 从 HDF5 文件中加载所有神经网络层的参数(Weights)
model.load_weights(weights_file)
2.步骤
- 创建Sequential()类。
model = tf.keras.models.Sequential()
- 添加每一层网络,比如
LSTM层
model.add(tf.keras.layers.LSTM(
512, # LSTM 层神经元的数目是 512,也是 LSTM 层输出的维度
input_shape=(inputs.shape[1], inputs.shape[2]), # 输入的形状,对第一个 LSTM 层必须设置
return_sequences=True # 返回所有的输出序列(Sequences)
))
Dropout层
model.add(tf.keras.layers.Dropout(0.3)) # 丢弃 30% 神经元,防止过拟合
全连接层
model.add(tf.keras.layers.Dense(num_pitch))
- softmax激活函数算概率
model.add(tf.keras.layers.Activation('softmax'))
- 交叉熵计算误差,使用对 循环神经网络来说比较优秀的 RMSProp 优化器 compile: configure the model for training
model.compile(loss='categorical_crossentropy', optimizer='rmsprop')
train.py
1.会from utils import *
以及from network import *
2.步骤
- 用utils.py中的函数得到数据及数据长度
- 为神经网络准备好训练的序列prepare_sequence
- 字符串与整数的映射
- 序列长度,生成新的输入序列
- 将输入的形状转为神经网络接受的
- 归一化(注意不是除以输入的长度,而是输入的最大值)
- 将期望输出转换成 {0, 1} 组成的布尔矩阵,为了配合 categorical_crossentropy 误差算法使用
network_output = tf.keras.utils.to_categorical(network_output)
- 生成checkpoint文件
- 用fit方法生成模型
3.得到不重复的元素个数。set是不会有重复的数的
num_pitch = len(set(notes))
- notes中为字符串,要无重复并且按序
pitch_names = sorted(set(item for item in notes))
-
enumerate()
同时得到索引和元素,创建字典用dict( , )
。把set转换为dict,如下,建立字符串与整数的映射关系
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(检查点)文件在每一个 Epoch 结束时保存模型的参数(Weights)
# 不怕训练过程中丢失模型参数。可以在我们对 Loss(损失)满意了的时候随时停止训练
checkpoint = tf.keras.callbacks.ModelCheckpoint(
filepath, # 保存的文件路径
monitor='loss', # 监控的对象是 损失(loss)
verbose=0,
save_best_only=True, # 不替换最近的数值最佳的监控对象的文件
mode='min' # 取损失最小的
)
callbacks_list = [checkpoint] #后面的fit方法会调用
- 用 fit 方法来训练模型
model.fit(network_input, network_output, epochs=100, batch_size=64, callbacks=callbacks_list)
generate.py
用训练好的神经网络模型参数来作曲
1.用pickle加载之前保存的文件
with open('data/notes', 'rb') as filepath:
notes = pickle.load(filepath)
2.载入之前训练时最好的参数文件,来生成曲子
model = network_model(normalized_input, num_pitch, "best-weights.hdf5")
其中network_model就是写的model,normalized_input是经过prepare_sequential的
网友评论