前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >如何运用深度学习自动生成音乐

如何运用深度学习自动生成音乐

作者头像
人工智能小咖
修改2020-02-20 09:39:10
2.3K0
修改2020-02-20 09:39:10
举报
文章被收录于专栏:人工智能小咖人工智能小咖

文章综述

  • 学会如何开发一个用于自动模型生成的端到端模型
  • 了解WaveNet体系架构,并使用Keras从头开始实施
  • 比较WaveNet与LSTM的性能,以建立自动音乐生成模型

引言

如果我不是物理学家,可能会是音乐家。我整天沉浸在音乐之中,把我的生命当成乐章。——爱因斯坦

我可能不是爱因斯坦那样的物理学家,但我完全同意他的音乐思想!我每天都听音乐。我往返办公室的过程中就伴随着音乐的旋律,老实说,这有助于我专心工作。

我一直梦想着作曲,但不太懂乐器。直到我遇到了深度学习。使用某些技巧和框架,我能够在不真正了解任何音乐理论的情况下创作自己的原创音乐!

这是我最喜欢的专业项目之一。我将我的两种热情——音乐和深度学习——结合起来,创造了一个自动音乐生成模型。梦想成真了!

很高兴与大家分享我的方法,包括让你生成自己的音乐的整个代码!本文首先介绍快速理解自动音乐生成的概念,然后再深入探讨我们可以用来执行此操作的不同方法。最后,运用Python并设计自己的自动音乐生成模型。

目录

1. 什么是自动音乐生成?

2. 音乐的构成要素是什么?

3. 音乐生成的不同方法

3.1 使用WaveNet架构

3.2 使用LSTM

4. 使用Python构建自动音乐生成

1. 什么是自动音乐生成?

音乐是一门艺术,是一种通用 语言。

我把音乐定义为不同频率音调的集合。因此,自动音乐生成是一个在最少的人为干预下创作一首短曲的过程。

最简单的音乐形式是什么?

这一切都是从随机选择声音并将它们组合成一段音乐开始的。1787年,莫扎特为这些随机的声音选择提出了一个骰子游戏。他手写完成了近272个音调!然后,根据2个骰子的和选择了一个音调。

另一个有趣的想法是利用音乐语法来产生音乐。

音乐语法理解音乐声音的合理排列和组合以及音乐作品的恰当表现所必需的知识。——《音乐语法基础》

20世纪50年代初,伊恩尼斯·谢纳基斯(Iannis Xenakis)利用统计学和概率的概念创作了音乐,俗称随机音乐。他把音乐定义为一系列偶然发生的元素(或声音)。因此,他用随机理论来描述它。他对元素的随机选择严格依赖于数学概念。

近年来,深度学习架构已经成为自动生成音乐的最新技术。本文将讨论两种使用WaveNet和LSTM(Long-Short-Term Memory)架构的自动音乐创作方法。

2. 音乐的构成要素是什么?

音乐本质上是由音符和和弦组成的。让我从钢琴乐器的角度来解释这些术语:

  • 音符(Note):一个键发出的声音叫做音符。
  • 和弦(Chords):由两个或多个键同时产生的声音称为和弦。一般来说,大多数和弦至少包含3个关键音。
  • 八度(Octave):重复的模式称为八度。每个八度音阶包含7个白色和5个黑色键。

3. 自动生成音乐的不同方法

羡慕详细讨论自动生成音乐的两种基于深度学习的体系结构:WaveNetLSTM。但是,为什么只有深度学习架构?

深度学习是受神经结构启发的机器学习领域。这些网络从数据集中自动提取特征,能够学习任何非线性函数。这就是为什么神经网络被称为通用函数逼近器。

因此,深度学习模型是自然语言处理、计算机视觉、语音合成等领域的研究热点。来看看如何建立这些音乐创作模型。

方法1 :使用WaveNet

WaveNet是由Google DeepMind开发的一个基于深度学习的原始音频生成模型。

WaveNet的主要目标是根据原始数据分布生成新的样本。因此,被称为生成模型。

Wavenet就像NLP中的一个语言模型。

在语言模型中,给定一个单词序列,该模型尝试预测下一个单词:

类似于语言模型,在WaveNet中,给定一系列样本,它试图预测下一个样本。

方法2:使用LSTM模型

LSTM循环神经网络(RNNs)的一个变种,它能够捕获输入序列中的长期依赖关系。LSTM在语音识别、文本摘要、视频分类等序列到序列建模任务中有着广泛的应用。

下面详细讨论一下如何使用这两种方法来训练模型。

Wavenet: 训练阶段

这是一个多对一的问题,输入是一系列振幅值,输出是随后的值。

让我们看看如何准备输入和输出序列。

WaveNet的输入:

WaveNet将原始音频波形的块作为输入。原始音频波是指波在时间序列域中的表示。

在时间序列域中,声波以振幅值的形式表示,振幅值以不同的时间间隔记录:

WaveNet的输出:

给定振幅值的序列,WaveNet尝试预测连续的振幅值。

通过一个例子来理解这一点。假设音频波形为5秒,采样率为16000(即每秒16000个采样)。现在,有80000个样本以不同的间隔记录了5秒钟。把音频分成大小相等的块,比如1024(这是一个超参数)。

下图说明了模型的输入和输出序列:

前3块的输入和输出

我们可以对其余的块执行类似的过程。

从上面可以推断出,每个块的输出只依赖于过去的信息(即以前的时间步),而不依赖于将来的时间步。因此,此任务称为自回归任务,模型称为自回归模型。

推段阶段

在推断阶段,我们将尝试生成新的样本。看看如何做到这一点:

1.选择样本值的随机数组作为建模的起点

2.现在,模型输出所有样本的概率分布

3.选择具有最大概率的值并将其追加到一个样本数组中。

4.删除第一个元素并作为下一个迭代的输入传递

5.对一定数量的迭代重复步骤2和4

理解WaveNet结构

WaveNe的构建块是因果扩展的一维卷积层。首先了解一下相关概念的重要性。

为什么是卷积?什么是卷积?

使用卷积的主要原因之一是从输入中提取特征。

例如,在图像处理的情况下,用滤波器卷积图像可以得到特征映射。

卷积是一种结合两个函数的数学运算。在图像处理的情况下,卷积是图像的某些部分与核的线性组合。

什么是一维卷积?

一维卷积的目标类似于LSTM模型。它用于解决与LSTM相似的任务。在一维卷积中,核或滤波器仅沿一个方向移动:

卷积的输出取决于内核的大小、输入形状、填充类型和步幅。现在,我将带您了解不同类型的填充,以了解使用扩展的因果一维卷积层的重要性。

当我们设置填充有效时,输入和输出序列的长度会变化。输出的长度小于输入:

当我们将padding设置为相同时,将在输入序列的任一侧填充零,以使输入和输出的长度相等:

一维卷积的优点:

  • 捕获输入序列中的顺序信息
  • 与GRU或LSTM相比,由于缺乏反复的联系,训练要快得多

一维卷积的缺点:

  • 当padding设置为相同时,时间步t的输出也与前一个t-1和未来的时间步t+1相卷积。因此,它违反了自回归原理。
  • 当padding设置为有效时,输入和输出序列的长度会变化,这是计算剩余连接所需的(稍后将介绍)。

这为因果卷积扫清了道路。

注意:我在这里提到的正反两面都是针对这个问题的。

什么是1维因果卷积?

这被定义为卷积,其中在时间t时的输出仅与来自时间t和先前层中的元素卷积。

简单地说,正常卷积和因果卷积只在填充方面有所不同。在因果卷积中,在输入序列的左边加零以保持自回归原理:

因果一维卷积的优点:

  • 因果卷积不考虑未来的时间步长,这是建立生成模型的标准。

因果一维卷积的缺点:

  • 因果卷积不能回顾过去或序列中较早发生的时间步。因此,因果卷积有一个非常低的感受域。网络的感受野(Receptive field)是指影响输出的输入数目:

如您所见,输出仅受5个输入的影响。因此,网络的感受野为5,非常低。网络的接收域也可以通过增加大尺寸的内核来增加,但是要记住计算复杂度增加。

这就驱使我们进入到了扩张的一维因果卷积的概念。

什么是扩展一维因果卷积?

具有核值之间的空穴或空间的因果一维卷积层称为扩展一维卷积。

要添加的空间数由膨胀率给出。它定义了网络的接收域。在核函数k的每一个值之间都有d-1个空穴。

如您所见,在7*7输入上卷积一个3*3内核函数,其伸缩率为2,感受野为5*5。

扩展一维因果卷积的优点:

  • 扩张的一维卷积网络通过指数增加每个隐藏层的扩张率来增加感受野:

正如您所看到的,输出受所有输入的影响。因此,网络的接收场为16。

WaveNet的残差块:

构建基块包含刚添加的剩余连接和跳过连接,以加快模型的收敛:

WaveNet的工作流:

  • 输入被输入到因果1D卷积中。
  • 输出被馈送到具有sigmoidtanh激活的两个不用的扩张1D卷积层。
  • 两个不同激活值的元素相乘导致跳过连接。
  • 在元件上添加跳跃连接,并输出因果1D。

LSTM方法

记忆(LSTM)模型。输入和输出序列的准备与WaveNet类似。在每个时间步,一个振幅值被输入到LSTM单元中,然后计算隐藏向量并将其传递到下一个时间步。

在时间步ht处的当前隐藏向量是基于在和先前隐藏向量ht-1处的当前输入计算的。这就是在任何递归神经网络中捕获序列信息的方式:

LSTM的优点:

  • 捕获输入序列中的顺序信息。

LSTM的缺点:

  • 由于它按顺序处理输入,因此需要花费大量时间进行培训。

使用Python自动生成音乐

等待结束了!让我们开发一个自动生成音乐的端到端模型。启动Jupyter Notebook或Colab(或任何你喜欢的IDE)。

下载数据集:

我从大量资源中下载并组合了一架数码钢琴的多个古典音乐文件。您可以从这里下载最终的数据集。

让我们先为可重复的结果埋下种子。这是因为深度学习模型在执行时由于随机性可能会输出不同的结果。这确保每次都能产生相同的结果。

代码语言:javascript
复制
from numpy.random import seed
seed(1)
from tensorflow import set_random_seed
set_random_seed(2)

重要的库:

Music 21是麻省理工学院开发的用于理解音乐数据的Python库。MIDI是存储音乐文件的标准格式(它代表乐器数字接口)。MIDI文件包含指令,而不是实际的音频。因此,它占用很少的记忆。这就是为什么在传输文件时通常首选它的原因。

代码语言:javascript
复制
 #dealing with midi   files
from music21 import * 
 
#array   processing
import numpy as np  
import os
 
#random   number generator
import random 
 
#keras   for building deep learning model
from keras.layers import * 
from keras.models import *
import keras.backend as K

读取数据:

定义读取MIDI文件的函数。它返回音乐文件中的一组音符和和弦。

代码语言:javascript
复制
def read_midi(file):
 notes=[]
 notes_to_parse = None
 
 #parsing a midi   file
 midi = converter.parse(file)
 #grouping based on   different instruments
 s2 = instrument.partitionByInstrument(midi)
 
 #Looping over all   the instruments
 for part in s2.parts:
 #select elements   of only piano
 if 'Piano' in str(part): 
 notes_to_parse = part.recurse() 
 #finding   whether a particular element is note or a chord
 for element in notes_to_parse:
 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))
 
 return notes

从目录中读取MIDI文件:

代码语言:javascript
复制
#read   all the filenames
files=[i for i in os.listdir() if i.endswith(".mid")]
 
#reading   each midi file
all_notes=[]
for i in files:
 all_notes.append(read_midi(i))
 
#notes   and chords of all the midi files
notes = [element for notes in all_notes for element in notes]

准备文章中提到的输入和输出序列:

代码语言:javascript
复制
#length   of a input sequence
no_of_timesteps = 128 
 
#no. of   unique notes
n_vocab = len(set(notes)) 
 
#all   the unique notes
pitch = sorted(set(item for item in notes)) 
 
#assign   unique value to every note
note_to_int = dict((note, number) for number, note in enumerate(pitch)) 
 
#preparing   input and output sequences
X = []
y = []
for notes in all_notes:
 for i in range(0, len(notes) - no_of_timesteps,   1):
 input_ = notes[i:i + no_of_timesteps]
 output = notes[i + no_of_timesteps]
 X.append([note_to_int[note] for note in input_])
 y.append(note_to_int[output])

卷积1D或LSTM的输入必须是(样本、时间步长、特征)的形式。所以,根据所需的形状重塑输入数组。请注意,每个时间步中的功能数为1:

代码语言:javascript
复制
#reshaping
X = np.reshape(X, (len(X), no_of_timesteps, 1))
#normalizing   the inputs
X = X / float(n_vocab) 

在这里定义了两种架构——WaveNet和LSTM。请尝试这两种架构,以了解WaveNet架构的重要性。

代码语言:javascript
复制
def lstm():
 model = Sequential()
 model.add(LSTM(128,return_sequences=True))
 model.add(LSTM(128))
 model.add(Dense(256))
 model.add(Activation('relu'))
 model.add(Dense(n_vocab))
 model.add(Activation('softmax'))
 model.compile(loss='sparse_categorical_crossentropy', optimizer='adam')
 return model

由于这些层的作用是提高更快的收敛速度(而WaveNet以原始音频波为输入),因此我简化了WaveNet的结构,而没有添加剩余连接和跳过连接。但在例子中,输入将是一组节点和和弦,因为我们正在生成音乐:

代码语言:javascript
复制
K.clear_session()
def simple_wavenet():
 no_of_kernels=64
 num_of_blocks= int(np.sqrt(no_of_timesteps)) - 1 #no. of stacked conv1d layers
 
 model = Sequential()
 for i in range(num_of_blocks):
 model.add(Conv1D(no_of_kernels,3,dilation_rate=(2**i),padding='causal',activation='relu'))
 model.add(Conv1D(1, 1, activation='relu', padding='causal'))
 model.add(Flatten())
 model.add(Dense(128, activation='relu'))
 model.add(Dense(n_vocab, activation='softmax'))
 model.compile(loss='sparse_categorical_crossentropy', optimizer='adam')
 return model

定义回调以在每50个阶段之后保存模型:

代码语言:javascript
复制
import keras
mc = keras.callbacks.ModelCheckpoint('model{epoch:03d}.h5', save_weights_only=False, period=50)

实例化并训练批大小为128的模型:

代码语言:javascript
复制
model = simple_wavenet()
model.fit(X,np.array(y), epochs=300, batch_size=128,callbacks=[mc])

这是本文中提到的推理阶段的实现。它预测特定迭代次数下最有可能的元素:

代码语言:javascript
复制
def generate_music(model, pitch, no_of_timesteps, pattern):
 
 int_to_note = dict((number, note) for number, note in enumerate(pitch))
 prediction_output = []
 
 # generate 50   elements
 for note_index in range(50):
 #reshaping   array to feed into model
 input_ = np.reshape(pattern, (1, len(pattern), 1))
 #predict   the probability and choose the maximum value
 proba = model.predict(input_, verbose=0)
 index = np.argmax(proba)
 #convert   integer back to the element
 pred = int_to_note[index]
 prediction_output.append(pred)
 pattern = list(pattern)
 pattern.append(index/float(n_vocab))
 #leave   the first value at index 0
 pattern = pattern[1:len(pattern)]
 
 return prediction_output

下面是一个将合成音乐转换为MIDI文件的函数:

代码语言:javascript
复制
def convert_to_midi(prediction_output):
 offset = 0
 output_notes = []
 
 # create note and   chord objects based on the values generated by the model
 for pattern in prediction_output:
 #   pattern is a chord
 if ('.' in pattern) or pattern.isdigit():
 notes_in_chord = pattern.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)
 #   pattern is a note
 else:
  new_note = note.Note(pattern)
 new_note.offset = offset
 new_note.storedInstrument = instrument.Piano()
 output_notes.append(new_note)
 
 #   Specify duration between 2 notes
 offset+ = 0.5
  # offset += random.uniform(0.5,0.9)
 
 midi_stream = stream.Stream(output_notes)
 midi_stream.write('midi', fp='music.mid')

现在进行编曲。

代码语言:javascript
复制
#Select   random chunk for the first iteration
start = np.random.randint(0, len(X)-1)
pattern = X[start]
#load   the best model
model=load_model('model300.h5')
#generate   and save music
music = generate_music(model,pitch,no_of_timesteps,pattern)
convert_to_midi(music)

以下是由我们的模型创作的几首曲子。是时候欣赏音乐了!

音乐播放器

00:00

00:00

Use Up/Down Arrow keys to increase or decrease volume.

音乐播放器

00:00

00:00

Use Up/Down Arrow keys to increase or decrease volume.

音乐播放器

00:00

00:00

Use Up/Down Arrow keys to increase or decrease volume.

太棒了,对吧?但学习并不止于此。以下是进一步提高模型性能的几种方法:

  • 增加训练数据集的大小会产生更好的旋律,因为深度学习模型在大型训练数据集上很好地推广。
  • 在构建具有大量层的模型时添加跳过和剩余连接。

结尾

深度学习在日常生活中有着广泛的应用。解决任何问题的关键步骤是理解问题陈述、制定问题陈述和定义解决问题的体系结构。

来源:https://www.analyticsvidhya.com/blog/2020/01/how-to-perform-automatic-music-generation/

本文系外文翻译,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文系外文翻译前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 引言
  • 1. 什么是自动音乐生成?
  • 2. 音乐的构成要素是什么?
  • 3. 自动生成音乐的不同方法
    • Wavenet: 训练阶段
      • 推段阶段
        • 理解WaveNet结构
          • LSTM方法
          • 使用Python自动生成音乐
          • 结尾
          相关产品与服务
          NLP 服务
          NLP 服务(Natural Language Process,NLP)深度整合了腾讯内部的 NLP 技术,提供多项智能文本处理和文本生成能力,包括词法分析、相似词召回、词相似度、句子相似度、文本润色、句子纠错、文本补全、句子生成等。满足各行业的文本智能需求。
          领券
          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档