Python编曲实践(六):将MIDI文件转化成矩阵,继承PyTorch的Dataset类来构建数据集(附数据集网盘下载链接)

本文主要是介绍Python编曲实践(六):将MIDI文件转化成矩阵,继承PyTorch的Dataset类来构建数据集(附数据集网盘下载链接),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

前言

上篇文章中,我介绍了如何通过编写爬虫来从 Free Midi Files Download 网站上爬取海量的MIDI数据。本篇文章介绍的是使用 pretty_midi 库来将MIDI文件转化成矩阵,并通过PyTorch的Dataset类来构建数据集,为之后的训练与测试中传入张量做准备。

实施过程

将MIDI文件转化成稀疏矩阵信息并存储

构建数据集的第一步是将MIDI文件中的音乐信息以(时间,音高)的矩阵形式提取出来,并以稀疏矩阵的形式来保存到npz文件中。pretty_midi库提供了在每一个音轨中遍历音符(Note),并得到每个音符的音高(pitch),音符开始时间(note_on)和音符结束时间(note_off),将开始和结束时间分别除以十六分音符的长度(60秒 / 120BPM / 4),就可以得到开始和结束的时间在矩阵中对应的位置。

代码详见 MusicCritique/util/data/create_database.py

def generate_nonzeros_by_notes():root_dir = 'E:/merged_midi/'midi_collection = get_midi_collection()genre_collection = get_genre_collection()for genre in genre_collection.find():genre_name = genre['Name']print(genre_name)npy_file_root_dir = 'E:/midi_matrix/one_instr/' + genre_name + '/'if not os.path.exists(npy_file_root_dir):os.mkdir(npy_file_root_dir)for midi in midi_collection.find({'Genre': genre_name, 'OneInstrNpyGenerated': False}, no_cursor_timeout = True):path = root_dir + genre_name + '/' + midi['md5'] + '.mid'save_path = npy_file_root_dir + midi['md5'] + '.npz'pm = pretty_midi.PrettyMIDI(path)# segment_num = math.ceil(pm.get_end_time() / 8)note_range = (24, 108)# data = np.zeros((segment_num, 64, 84), np.bool_)nonzeros = []sixteenth_length = 60 / 120 / 4for instr in pm.instruments:if not instr.is_drum:for note in instr.notes:start = int(note.start / sixteenth_length)end = int(note.end / sixteenth_length)pitch = note.pitchif pitch < note_range[0] or pitch >= note_range[1]:continueelse:pitch -= 24for time_raw in range(start, end):segment = int(time_raw / 64)time = time_raw % 64nonzeros.append([segment, time, pitch])nonzeros = np.array(nonzeros)np.savez_compressed(save_path, nonzeros)midi_collection.update_one({'_id': midi['_id']}, {'$set': {'OneInstrNpyGenerated': True}})print('Progress: {:.2%}'.format(midi_collection.count({'Genre': genre_name, 'OneInstrNpyGenerated': True}) / midi_collection.count({'Genre': genre_name})), end='\n')
  • 为了方便存储,我将每个MIDI文件以四个小节为单位进行分割,考虑到的最短时长单位是十六分音符,这样每个矩阵的第一维度大小是64(4*16),代表音符在时间上的分布情况。
  • MIDI文件音高数值范围在0~127,可以存储从A0到G9的横跨10个八度的音高,对应关系可以参考 MIDI NOTE NUMBERS AND CENTER FREQUENCIES 。在这些音里面很多音符是几乎不会出现在真实的音乐中的。为了使得到的矩阵更为稠密,在处理的过程中忽略了过大和过小的数值,只提取了数值在24-108的音符,即C1-C8这84个音高,基本上与钢琴的音域相同。
  • 最后,同样为了矩阵更为稠密,提高训练效果,我将除去鼓轨外的所有乐器音轨合成到一起,统一记录音符,而不区分乐器种类。

考虑到以上三点,根据每一个MIDI文件得到的矩阵形式即[包含的四小节乐段数*1*64*84]。为了降低空间占用,保存在文件中的信息是矩阵中每一个非零点的坐标信息,后面可以通过这些坐标来构建稀疏矩阵。

合并某个风格的所有稀疏矩阵

通过上一步,我们已经将MIDI文件中的音乐信息以稀疏矩阵坐标的形式存储在了单独的npz文件中,为了方便构造数据集,我尝试将每个风格的所有稀疏矩阵统一存储。
代码详见 MusicCritique/util/data/create_database.py

def merge_all_sparse_matrices():midi_collection = get_midi_collection()genre_collection = get_genre_collection()root_dir = 'E:/midi_matrix/one_instr/'time_step = 64valid_range = (24, 108)for genre in genre_collection.find({'DatasetGenerated': False}):save_dir = 'd:/data/' + genre['Name']if not os.path.exists(save_dir):os.mkdir(save_dir)print(genre['Name'])whole_length = genre['ValidPiecesNum']shape = np.array([whole_length, time_step, valid_range[1]-valid_range[0]])processed = 0last_piece_num = 0whole_num = midi_collection.count({'Genre': genre['Name']})non_zeros = []for midi in midi_collection.find({'Genre': genre['Name']}, no_cursor_timeout=True):path = root_dir + genre['Name'] + '/' + midi['md5'] + '.npz'valid_pieces_num = midi['PiecesNum'] - 1f = np.load(path)matrix = f['arr_0'].copy()print(valid_pieces_num, matrix.shape[0])for data in matrix:try:data = data.tolist()if data[0] < valid_pieces_num:piece_order = last_piece_num + data[0]non_zeros.append([piece_order, data[1], data[2]])except:print(path)last_piece_num += valid_pieces_numprocessed += 1print('Progress: {:.2%}\n'.format(processed / whole_num))non_zeros = np.array(non_zeros)print(non_zeros.shape)np.savez_compressed(save_dir + '/data_sparse' + '.npz', nonzeros=non_zeros, shape=shape)genre_collection.update_one({'_id': genre['_id']}, {'$set': {'DatasetGenerated': True}})

这个函数中genre的ValidPiecesNum域是之前添加的,意义是某一类的所有MIDI文件的四小节数目之和,并从这之中扣除了最后不满一小节的部分。

将稀疏矩阵转化为矩阵

由于所有的非零的坐标信息已经保存在了npz文件中,通过遍历这些坐标信息并将这些坐标点的数值设置为1.0,就可以得到矩阵。

def generate_sparse_matrix_of_genre(genre):npy_path = 'D:/data/' + genre + '/data_sparse.npz'with np.load(npy_path) as f:shape = f['shape']data = np.zeros(shape, np.float_)nonzeros = f['nonzeros']for x in nonzeros:data[(x[0], x[1], x[2])] = 1.return data

继承Dataset类,编写自定义数据集

通过继承PyTorch的Dataset类,并对几个重要函数进行重写,参考官方文档
代码详见 MusicCritique/util/data/dataset.py

class SteelyDataset(data.Dataset):def __init__(self, genreA, genreB, phase, use_mix):assert phase in ['train', 'test'], 'not valid dataset type'sources = ['metal', 'punk', 'folk', 'newage', 'country', 'bluegrass']genre_collection = get_genre_collection()self.data_path = 'D:/data/'numA = genre_collection.find_one({'Name': genreA})['ValidPiecesNum']numB = genre_collection.find_one({'Name': genreB})['ValidPiecesNum']train_num = int(min(numA, numB) * 0.9)test_num = min(numA, numB) - train_numif phase is 'train':self.length = train_numif use_mix:dataA = np.expand_dims(generate_sparse_matrix_of_genre(genreA)[:self.length], 1)dataB = np.expand_dims(generate_sparse_matrix_of_genre(genreB)[:self.length], 1)mixed = generate_sparse_matrix_from_multiple_genres(sources)np.random.shuffle(mixed)data_mixed = np.expand_dims(mixed[:self.length], 1)self.data = np.concatenate((dataA, dataB, data_mixed), axis=1)else:dataA = np.expand_dims(generate_sparse_matrix_of_genre(genreA)[:self.length], 1)dataB = np.expand_dims(generate_sparse_matrix_of_genre(genreB)[:self.length], 1)self.data = np.concatenate((dataA, dataB), axis=1)else:self.length = test_numdataA = np.expand_dims(generate_sparse_matrix_of_genre(genreA)[:self.length], 1)dataB = np.expand_dims(generate_sparse_matrix_of_genre(genreB)[:self.length], 1)self.data = np.concatenate((dataA, dataB), axis=1)def __getitem__(self, index):return self.data[index, :, :, :]def __len__(self):return self.length

继承的重点是重写初始化函数、getitem函数和len函数。在构建数据库的时候,为了方便调用数据,我将dataA和dataB合并到了一起,并取较小数据集的数目来确定总体数据集数目,以保证两种数据大小一致,在这过程中使用了Numpy库中的expand_dims函数来增加维度,concatenate函数来把两个矩阵合并到新增的维度上。

数据集分享

大家需要的话可以通过 百度云 下载这一数据集,提取码:nsfi。如在使用过程中遇到问题,请在下面评论,感谢阅读!

这篇关于Python编曲实践(六):将MIDI文件转化成矩阵,继承PyTorch的Dataset类来构建数据集(附数据集网盘下载链接)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/927549

相关文章

大模型研发全揭秘:客服工单数据标注的完整攻略

在人工智能(AI)领域,数据标注是模型训练过程中至关重要的一步。无论你是新手还是有经验的从业者,掌握数据标注的技术细节和常见问题的解决方案都能为你的AI项目增添不少价值。在电信运营商的客服系统中,工单数据是客户问题和解决方案的重要记录。通过对这些工单数据进行有效标注,不仅能够帮助提升客服自动化系统的智能化水平,还能优化客户服务流程,提高客户满意度。本文将详细介绍如何在电信运营商客服工单的背景下进行

基于MySQL Binlog的Elasticsearch数据同步实践

一、为什么要做 随着马蜂窝的逐渐发展,我们的业务数据越来越多,单纯使用 MySQL 已经不能满足我们的数据查询需求,例如对于商品、订单等数据的多维度检索。 使用 Elasticsearch 存储业务数据可以很好的解决我们业务中的搜索需求。而数据进行异构存储后,随之而来的就是数据同步的问题。 二、现有方法及问题 对于数据同步,我们目前的解决方案是建立数据中间表。把需要检索的业务数据,统一放到一张M

关于数据埋点,你需要了解这些基本知识

产品汪每天都在和数据打交道,你知道数据来自哪里吗? 移动app端内的用户行为数据大多来自埋点,了解一些埋点知识,能和数据分析师、技术侃大山,参与到前期的数据采集,更重要是让最终的埋点数据能为我所用,否则可怜巴巴等上几个月是常有的事。   埋点类型 根据埋点方式,可以区分为: 手动埋点半自动埋点全自动埋点 秉承“任何事物都有两面性”的道理:自动程度高的,能解决通用统计,便于统一化管理,但个性化定

python: 多模块(.py)中全局变量的导入

文章目录 global关键字可变类型和不可变类型数据的内存地址单模块(单个py文件)的全局变量示例总结 多模块(多个py文件)的全局变量from x import x导入全局变量示例 import x导入全局变量示例 总结 global关键字 global 的作用范围是模块(.py)级别: 当你在一个模块(文件)中使用 global 声明变量时,这个变量只在该模块的全局命名空

使用SecondaryNameNode恢复NameNode的数据

1)需求: NameNode进程挂了并且存储的数据也丢失了,如何恢复NameNode 此种方式恢复的数据可能存在小部分数据的丢失。 2)故障模拟 (1)kill -9 NameNode进程 [lytfly@hadoop102 current]$ kill -9 19886 (2)删除NameNode存储的数据(/opt/module/hadoop-3.1.4/data/tmp/dfs/na

异构存储(冷热数据分离)

异构存储主要解决不同的数据,存储在不同类型的硬盘中,达到最佳性能的问题。 异构存储Shell操作 (1)查看当前有哪些存储策略可以用 [lytfly@hadoop102 hadoop-3.1.4]$ hdfs storagepolicies -listPolicies (2)为指定路径(数据存储目录)设置指定的存储策略 hdfs storagepolicies -setStoragePo

Hadoop集群数据均衡之磁盘间数据均衡

生产环境,由于硬盘空间不足,往往需要增加一块硬盘。刚加载的硬盘没有数据时,可以执行磁盘数据均衡命令。(Hadoop3.x新特性) plan后面带的节点的名字必须是已经存在的,并且是需要均衡的节点。 如果节点不存在,会报如下错误: 如果节点只有一个硬盘的话,不会创建均衡计划: (1)生成均衡计划 hdfs diskbalancer -plan hadoop102 (2)执行均衡计划 hd

嵌入式QT开发:构建高效智能的嵌入式系统

摘要: 本文深入探讨了嵌入式 QT 相关的各个方面。从 QT 框架的基础架构和核心概念出发,详细阐述了其在嵌入式环境中的优势与特点。文中分析了嵌入式 QT 的开发环境搭建过程,包括交叉编译工具链的配置等关键步骤。进一步探讨了嵌入式 QT 的界面设计与开发,涵盖了从基本控件的使用到复杂界面布局的构建。同时也深入研究了信号与槽机制在嵌入式系统中的应用,以及嵌入式 QT 与硬件设备的交互,包括输入输出设

【Prometheus】PromQL向量匹配实现不同标签的向量数据进行运算

✨✨ 欢迎大家来到景天科技苑✨✨ 🎈🎈 养成好习惯,先赞后看哦~🎈🎈 🏆 作者简介:景天科技苑 🏆《头衔》:大厂架构师,华为云开发者社区专家博主,阿里云开发者社区专家博主,CSDN全栈领域优质创作者,掘金优秀博主,51CTO博客专家等。 🏆《博客》:Python全栈,前后端开发,小程序开发,人工智能,js逆向,App逆向,网络系统安全,数据分析,Django,fastapi

常用的jdk下载地址

jdk下载地址 安装方式可以看之前的博客: mac安装jdk oracle 版本:https://www.oracle.com/java/technologies/downloads/ Eclipse Temurin版本:https://adoptium.net/zh-CN/temurin/releases/ 阿里版本: github:https://github.com/