本项目南京邮电大学大学生创新创业训练计划项目
测试使用Transformer架构生成音乐中的和弦进行的可行性的方案
我们的目标是训练一个可以生成具有流畅自然的听感的和弦进行的模型。它可以直接生成和弦进行,也可以在用户写好旋律以后为他的旋律来生成对应的和弦进行。
这个模型可以被运用于给音乐制作人提供灵感,或者简化编曲的过程,也可以被用做给普通人提供一个把自己的歌曲变得更加精美的巧妙黑盒。
试想你可曾有过这样的经历——你某一天突发灵感创作出了一段旋律,可由于你没有学习过作曲,你只能单调地哼着这一段旋律,却没有办法把它写成完整的乐曲。今天,有了我们的模型,你就可以把你的灵感快速转变成一首有模有样的曲子。就算没有作曲知识,你也可以创作出属于你自己的曲子!
自从 Attention is All You Need (arXiv:1706.03762) 发布以来,它被运用于各行各业,其中颇为出圈的就是ChatGPT。似乎,这种注意力机制格外适合自然语言处理(NLP)任务。
我们生成和弦的思路其实和NLP任务是一脉相承的。为什么?语言是一个一个词讲出来的,讲完一句话,它就有一个完整的含义。注意力机制恰好能够阐明语言前后的关联。那音乐呢?我们常说音乐是一种世界性的语言,你可能会认为这是一种比喻或类比……但它真的仅仅是比喻吗?音乐难道不本来就是一种“语言”吗?
对于我们人类而言,平常的语言用来传达确切的信息,而音乐则更像是一种抽象的情绪表现,它们有着很大的差别,但是如果我们脱离开人的感官,假设我们就是一堆神经网络,仅仅从数据层面上看,音乐其实和语言没什么区别。甚至,音乐比像汉语、英语这样的语言来得更简单,也更自由。这也就意味着,只需要更少的训练就可以达到一个和语言模型同等组织力的音乐生成模型。
由于我们的目标是生成一个偏向流行歌曲的和弦进行,我们的数据集应当能够较好地反应歌曲的和弦进行。起初我试图使用业界流行的maestro、adl-piano-midi、BACH DOODLE、MusicScore等数据集,然而,我发现这些数据集并不能完美地契合我们的需要。
事实上,最近Transformer也有被运用于音乐生成领域,例如MusicTransformer (arXiv:1809.04281) 项目,这是一个使用了maestro MIDI数据集训练而成的Transformer模型,生成的结果好像还像那么回事。只是MIDI看上去是一团乱麻。作为音乐制作人,我观察了他们所做的工作,发现他们使用的数据集内的很多歌曲的MIDI要不就是古典钢琴曲,那种旋律和和弦不是很明确区分的,本来作为人都不好寻找它的规律,又怎么去训练出一个辅助音乐人的AI呢?而另一个游戏音乐的MIDI集,其中又有很多写法奇奇怪怪的MIDI,这种技法用来流行音乐是不现实的,也是一种干扰性质的数据。
好在,在查找了各种音乐类的数据集之后,我们发现有一个宝藏网站HookTheory竟然收录了现成的两万多首流行歌曲的乐谱!不仅如此,它甚至还是每首歌严格分为旋律和和弦轨的。我勒个去,这不就是我想要的理想数据集吗?!于是,我们使用HookTheory数据集作为我们的训练用数据集,开始了我们的模型设计。
为了便于模型提取特征,减少干扰和参数量,我们决定所有和弦使用调式内的级数表示法,而非绝对表示。
例如,在G自然大调中出现的 G Major 和弦(组成音为G、B、D),我们只表示为Ⅰ级和弦,而其中出现的 B minor 和弦(组成音为B、D、F#),我们只表示为Ⅲ级和弦,而不会表示出它的具体组成音。
这种表示方式看似很美好,如果它直接成立,那么我们的Token总数将只有7种!试想,一个纯英文语境的语言模型就算只是单纯地将出现的字符集作为Token,都会有几十种。
然而,事实并非如此——作曲家们可不会都乖乖地固定着调式内的七个最基本的自然音三和弦来写所有的歌。在一首乐曲中,出现七和弦、九和弦、增减、挂留和弦那是家常便饭,尤其是在一些日式ACG音乐中(爵士和古典暂不考虑),还经常借用其他调式内的和弦。
因此,我们需要将和弦合理地量化,以能够很好地将其编码为张量。恰好HookTheory的数据集内的歌曲数据本就是首调表示法的。于是,我们打算借其原本的表示法,做一个转换以变成模型输入的张量。
在自然语言处理中,为了让我们的模型“认识”我们的文字,我们通常使用Tokenizer来处理文字。例如,OpenAI使用的是tiktoken。同样地,为了让模型“认识”乐谱,我们也需要通过某种方式把乐谱转化为一个个的Token。
在HookTheory的原始数据中,每一个和弦采用例如如下数据结构表示:
{
"root": 1,
"beat": 13,
"duration": 4,
"type": 5,
"inversion": 0,
"applied": 0,
"adds": [],
"omits": [],
"alterations": [],
"suspensions": [],
"pedal": null,
"alternate": "",
"borrowed": null,
"isRest": false,
"recordingEndBeat": null
}
经过我的研究和确认,这些内容分别代表:和弦根音级数、持续时间、类型(5为三和弦、7为七和弦)、转位(0表示不转位,三和弦有第一转位和第二转位,七和弦有第一、第二、第三转位)、(不知道?功能性应用和弦)、加音、省略音、变音或音程修饰(不确定)、挂留音(不确定)、借用其他调式的和弦、
borrowed
这个值有三种表示方法,如果没有借用就是null
。如果借用了,一种是直接写借用的和弦来自哪个调式,例如"locrian"
,但也有个别歌曲会用一个数组来表示,例如[-1, 1, 3, 4, 6, 8, 9]
和[1, 2, 4, 6, 8, 9, 11]
,后者被用于在F Major调内表示D大三和弦。我组织数据的时候看到这样的数据感到非常疑惑。
我们要考虑的是这样的数据结构该如何转化为嵌入向量。这是这个工程的一大难点——如果我只是简单地把上面所示这样的结构的每一项变成一个数字,那我就将面临一个问题:怎么把adds
omits
编码器使用和语言模型类似的Transformer Decoder架构。
首先,和弦序列将被划分为多个块,每个块内包含连续的block_size
个和弦组成的进行。嵌入矩阵会将每个和弦转化为一个embedding_dim
维度的向量。
为了使生成的和弦更加准确,我们希望引入更详细的、具有歌曲特色的外界信息,例如:
- 根据已有的旋律来生成和弦。由于我们的模型是使用首调模式来生成和弦的,因此用户需要在写下旋律的同时再指定所处的调式。(后续可能我们会再做识别调式调号来自动对齐首调的模块,但那不是我们现阶段的主要目标)
- 歌曲的曲速和曲风也会在一定程度上影响和弦的选择。
- 允许用户指定特定位置的和弦。
对于已有旋律的编码比和弦要复杂。
TODO:
在大模型的嵌入向量计算和注意力模式的探索中,我们发现了奇妙的一点,它竟然在机理上与华萃康老师的色彩和声理论的运作方式不谋而合!
下面,我将借由色彩和声-小田田发表的图文:自动化和声编写程序(构想)中的表述方式来阐述注意力机制和色彩和声的相通之处。
TODO: 待完善
- Rainbow-Dreamer/musicpy
- karpathy/minGPT
- PyTorch
- HookTheory Dataset: chrisdonahue/sheetsage
TODO: 待完善