旋律生成学习日记(一)

123因为自己做音频方向,偶然看见了旋律生成这个东西,就想自己学着做一做,网上现有文章不多,我自己瞎琢磨琢磨。

第一章转自:(64条消息) 基于深度学习LSTM算法生成音乐_lstm生成音乐_lbship的博客-CSDN博客

MID数据集下载地址:GitHub - bytedance/GiantMIDI-Piano

一、背景知识

1.概念 (来自百度百科):

notes(音符):用来记录不同长短的音的进行符号。全音符、二分音符、四分音符、八分音符、十六分音符是最常见的音符。是五线谱中最重要的元素

chord(和弦):和弦是乐理上的一个概念,指的是一定音程关系的一组声音。将三个和三个以上的音,按三度叠置的关系,在纵向上加以结合,就成为和弦

如果无法使用TensorFlow或者配置不够强大,可以使用Colaboratory网址,在线运行,里面已经集成了TensorFlow,pandas等包,很方便使用
 

二、读取MIDI文件

import tensorflow as tf
import os
from music21 import converter, instrument, note, chord, stream
import numpy as np


"""
notes(音符):用来记录不同长短的音的进行符号。全音符、二分音符、四分音符、八分音符、十六分音符是最常见的音符。是五线谱中最重要的元素

chord(和弦):和弦是乐理上的一个概念,指的是一定音程关系的一组声音。将三个和三个以上的音,按三度叠置的关系,在纵向上加以结合,就成为和弦
"""

# 读取训练数据的Notes
def get_notes():
    filepath = 'F:/GiantMIDI-Piano-master/midis_for_evaluation/giantmidi-piano/'
    #filepath =  'F:/GiantMIDI-Piano-master/midis_for_evaluation/data'
    files = os.listdir(filepath)
    Notes = []
    for file in files:
        try:
            stream = converter.parse(filepath + file)
            instru = instrument.partitionByInstrument(stream)
            if instru:  # 如果有乐器部分,取第一个乐器部分
                notes = instru.parts[0].recurse()
            else:  # 如果没有乐器部分,直接取note
                notes = stream.flat.notes
            for element in notes:
                # 如果是 Note 类型,取音调
                # 如果是 Chord 类型,取音调的序号,存int类型比较容易处理
                if isinstance(element, note.Note):
                    Notes.append(str(element.pitch))
                elif isinstance(element, chord.Chord):
                    Notes.append('.'.join(str(n) for n in element.normalOrder))
        except:
            pass
        # with open('Note', 'a+')as f:
        #     f.write(str(Notes))
    return Notes

原文有读取一个文件查看,但是我没有读取,我最后生成的note文件是一片空白,但是最后生成的音乐确实是有声的。后面我再研究研究。

三、构建神经网络

本次神经网络使用LSTM网络(Longshort term memory),它基于普通RNN在隐藏层各神经单元中增加记忆单元,从而使时间序列上的记忆信息可控,每次在隐藏层各单元间传递时通过几个可控门(遗忘门、输入门、候选门、输出门),可以控制之前信息和当前信息的记忆和遗忘程度,从而使RNN网络具备了长期记忆功能。

架构图:

def get_model(inputs, notes_len, weights_file=None):
    model = tf.keras.models.Sequential()
    model.add(tf.keras.layers.LSTM(512,input_shape=(inputs.shape[1], inputs.shape[2]),return_sequences=True))#512层神经元,return_sequences=True表示返回所有的输出序列
    model.add(tf.keras.layers.Dropout(0.3))  # 丢弃 30% 神经元,防止过拟合
    model.add(tf.keras.layers.LSTM(512, return_sequences=True))
    model.add(tf.keras.layers.Dropout(0.3))
    model.add(tf.keras.layers.LSTM(512))  # return_sequences 是默认的 False,只返回输出序列的最后一个
    model.add(tf.keras.layers.Dense(256))  # 256 个神经元的全连接层
    model.add(tf.keras.layers.Dropout(0.3))
    model.add(tf.keras.layers.Dense(notes_len))  # 输出的数目等于所有不重复的音调的数目
    model.add(tf.keras.layers.Activation('softmax'))
    model.compile(loss='categorical_crossentropy', optimizer='rmsprop')
 
    if weights_file is not None:
        model.load_weights(weights_file)
 
    return model

四 模型训练

def train():
    notes=get_notes()
    notes_len=len(set(notes))
    note_name=sorted(set(i for i in notes))#获得排序的不重复的音符名字
    sequence_length = 100 #序列长度
    note_dict=dict((j,i) for i,j in enumerate(note_name))#设计一个字典,把音符转换成数字,方便训练
    network_input = []#创建输入序列
    network_output = []#创建输出序列
    for i in range(0, len(notes) - sequence_length):
        #输入100个,输出1个
        sequence_in = notes[i: i + sequence_length]
        sequence_out = notes[i + sequence_length]
        network_input.append([note_dict[k] for k in sequence_in])
        network_output.append(note_dict[sequence_out])
    network_input = np.reshape(network_input, (len(network_input), sequence_length, 1))
    network_input = network_input / float(notes_len) #归一化
    network_output = tf.keras.utils.to_categorical(network_output)#输出布尔矩阵,配合categorical_crossentropy 算法使用
    model =get_model(network_input,notes_len)
    filepath = "weights-{epoch:02d}-{loss:.2f}.hdf5"
    checkpoint = tf.keras.callbacks.ModelCheckpoint(
        filepath,
        monitor='loss',  # 监控的对象是loss
        verbose=0,
        save_best_only=True,
        mode='min'  # 如果监控对象是val_acc则取max,是loss则取min
    )
    callbacks_list = [checkpoint]
    model.fit(network_input, network_output, epochs=100, batch_size=128, callbacks=callbacks_list) #整体迭代100次,每小批128个

原文博主说训练时间非常长,我还不信,喵的我错了,我真的错了,一个epoch我的电脑耗时一个小时,根本跑不动,最后拿52个样本跑了十个epoch跑了个模型出来,看看是否能实现。路径直接给mid文件的路径就行。不需要其余另外的标签。

五、生成音乐

# 生成mid音乐
def create_music():
    notes = get_notes()
    notes_len = len(set(notes))
    note_name = sorted(set(i for i in notes))
    sequence_length = 100  # 序列长度
    note_dict = dict((j, i) for i, j in enumerate(note_name))  # 设计一个字典,把音符转换成数字,方便训练
    network_input = []  # 创建输入序列
    network_output = []  # 创建输出序列
    for i in range(0, len(notes) - sequence_length):
        # 输入100个,输出1个
        sequence_in = notes[i: i + sequence_length]
        sequence_out = notes[i + sequence_length]
        network_input.append([note_dict[k] for k in sequence_in])
        network_output.append(note_dict[sequence_out])
    network_input = np.reshape(network_input, (len(network_input), sequence_length, 1))
    normal_network_input = network_input / float(notes_len)  # 归一化
    # print(len(network_input)) #1541019
    # network_input, normal_network_input,notes_len,note_name=train()
    # 寻找loss最小的weight文件,作为训练参数
    files = os.listdir()
    minloss = {}
    for i in files:
        if 'weights' in i:
            num = i[11:15]
            minloss[num] = i
    best_weights = minloss[min(minloss.keys())]
    print('最佳模型文件为:' + best_weights)
    model = get_model(normal_network_input, notes_len, best_weights)
    predictions = generate_notes(model, network_input, note_name, notes_len)
    offset = 0
    output_notes = []
    # 生成 Note(音符)或 Chord(和弦)对象
    for data in predictions:
        if ('.' in data) or data.isdigit():
            notes_in_chord = data.split('.')
            notes = []
            for current_note in notes_in_chord:
                new_note = note.Note(int(current_note))
                new_note.storedInstrument = instrument.Piano()
                notes.append(new_note)
            new_chord = chord.Chord(notes)
            new_chord.offset = offset
            output_notes.append(new_chord)
        else:
            new_note = note.Note(data)
            new_note.offset = offset
            new_note.storedInstrument = instrument.Piano()
            output_notes.append(new_note)
        offset += 1
    # 创建音乐流(Stream)
    midi_stream = stream.Stream(output_notes)
    # 写入 MIDI 文件
    midi_stream.write('midi', fp='output1.mid')

博主源代码有个报错,ValueError: Failed to convert a NumPy array to a Tensor (Unsupported object type numpy.ndarray).这个是由于数据格式不对引起的,我添加了.astype(np.float)搞定,最后也成功生成了十个模型以及一个音频,给大伙长长眼!!!

 怀揣着炽热的心走向最宽容的刑场~~~~

妈的啥玩意,叮叮叮一直叮,就一个调???

查看了评论区注释,生成音乐一直当当当不变是因为样本太多,准确的来说是训练次数太少。

总结,没有一点基础第一次上手,跟我想象的不一样,我还以为会是和图像一样能给标签描述就会给我音频,但是这个方法的实现貌似不是这样的,我开始对这个感兴趣,也是因为chatgpt,提问请给我一段欢快的音频,它也确实能给你一段小调。

接下来有空试试其他的。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值