Press "Enter" to skip to content

用机器学习生成披头士的歌词 | 项目实战

本站内容均来自兴趣收集,如不慎侵害的您的相关权益,请留言告知,我们将尽快删除.谢谢.

 

来源 | Medium

 

译者 | Arno

 

披头士乐队是一个巨大的文化现象。他们永恒的音乐直到今天仍然与人们产生共鸣,无论老少。在我看来,他们是有史以来最伟大的乐队¹。他们的歌曲充满了有趣的歌词和深刻的思想。比如说下面的歌词:

 

When you’ve seen beyond yourself

 

Then you may find peace of mind is waitingthere²

 

是否觉得很强大? 披头乐队之所以伟大,是因为他们多才多艺。他们的一些歌曲深沉而有思想,另一些则有趣而轻松。毫无疑问,贯穿他们歌词的最大主题是爱。这里有这幺一段:

 

My sweet little darling can’t you see,

 

how wonderful it is when you’re mine.

 

If only I could be your lover forever,

 

I can never get my heart, I can’t get my mind.

 

事实上,如果你是披头士的忠粉的话,你会发现这些歌词并不是你所知道的披头士写的。不是 Lennon ,不是 McCartney ,不是 Harrison ,甚至也不是 RingoStarr 。它们实际上是由 OpenAI 的 GPT-2[1] 机器学习模型生成的。尽管这里使用了他们最小的模型,但结果却相当惊人。

 

在我们过于超前之前,让我们后退一步,看看这一切是如何实现的。之后,在 github 上,都有完整的代码可用。

 

语言模型

 

语言模型试图学习语言的结构 ( 如英语或披头士的歌词 ) 。它们是使用监督学习训练的生成模型。与其他监督学习任务一样,语言模型试图预测给定某些特征的标签。然而,与大多数有监督的学习任务不同,它没有明确的标签,而是语言本身同时扮演着特征和标签的角色。

 

在更高的层次上,语言模型所要做的是根据前面的单词序列预测下一个单词。一个好的语言模型能预测到像“ milk ”是“ to buy a gallon of____. ”这一短语的合乎逻辑的下文。

 

为了猜测下一个出现的单词,我们真正要做的是学习一个基于词汇表的概率分布,而这个词汇表上的单词是我们目前所能见到的。也就是说,我们想要学习

 

 

其中, W i 是词汇表上的单词。

 

因为我们正在显式地对这个分布建模,我们可以用它做一些很酷的事情,比如用它生成我们以前没有见过的单词。我们可以做到这一点的方法是从这个分布中反复采样下一个单词,然后当我们采样下下一个单词时,用它作为条件,以此类推。为了让它更具体,我们在 Python 中看看这可能是什幺样子。 如果我们有一个带有 采样 方法的 模型 对象,那幺我们可以通过这样做来生成新的样本 :

 

previous_words = [...] #已经生成的单词列表
while True:
    previous_words.append(model.sample(conditioned_on=previous_words))

 

当然,这上面跳过了一些细节,但随着我们下面的介绍,这些会变得更清晰。现在,让我们考虑世界上最简单的语言模型 –Unigram 模型。

 

Unigram 模型忽略任何条件,只是从训练数据中随机选择下一个单词。这相当于把我们的训练数据扔进搅拌机,搅拌 10 分钟后就把里面的内容倒出来。换句话说,我们不会创造出任何类似英语的东西。

 

Bigram 模型

 

在 Unigram 之上的是 Bigram 模型,正如你能从这个名字猜出来, Bigram 模型学习的分布 仅受前一个单词的限制 ,即

 

 

由于 Bigram 模型非常简单,所以很容易在 Python 中实现,这将使我们更深入地理解语言模型的工作原理。

 

数据收集

 

在开始实现之前,我们首先需要一些数据。我们的最终目标是创作出让披头士引以为豪的歌曲,所以让我们从收集他们所有已知的歌词开始。

 

这个网站收录了他们曾经发行的每首歌的歌词,它还有一个有用的索引页,其中有指向各个歌曲的链接,我们可以用它来抓取网站。这里编写了一个简单的脚本来遍历页面上的每个链接,解析其 HTML 以提取歌词,并将歌词转储到一个文件中,一行为一首歌曲。如果你打算跟着做,或者只是想要披头士的歌词,我强烈建议你使用它,因为抓取 HTML 是非常乏味和烦人的,即使使用像 Beautiful Soup 这样的工具。

 

一旦我们有了一个漂亮的、干净的格式的数据,剩下的就很简单了。但不要简单相信我的话,因为从下面的图表中可以看到其实数据清理和组织并不简单。

 

 

数据清理和组织是数据科学项目中最大的一块

 

 

如上所述, Bigram 模型只是根据前一个单词对下一个单词进行采样。我们可以做到这一点的一个简单方法是跟踪当前单词后面的单词,以及它们的出现频率。也就是说,我们在训练数据中为每个单词 current_word 保存一个字典,然后每次看到一个 next_word ,我们就更新 current_word[next_word]+= 1 。然后,为了生成单词,我们只需在 current_word 字典中查找所有单词和计数,并对一个与计数成正比的单词进行采样。这是一个完整模型在 Python 中的大概³样子:

 

import collections
import numpy as np
class ProbabilisticBigramLanguageModel(object):
"""一种二元概率语言模型
给定一个bigrams列表,该模型构建在列表上的概率分布。
 """

def __init__(self):
 self._model = collections.defaultdict(int)

def fit(self, bigrams):
for current_word, next_word in bigrams:
 self._model[current_word] += 1

def predict(self, current_word):
 words, counts = list(zip(*self._model[current_word].items()))
 counts = np.array(counts)
 probs = counts / np.sum(counts)
return np.random.choice(words, p=probs)

 

最后要注意的是,我们可能想通过添加一些特殊的标记来对歌词进行预处理,以表示行和歌曲的开始 / 结束。这就迫使我们的模型在生成新歌词时要维护一些歌曲结构,否则模型只会生成大量没有结尾的文本。 在我的代码中,我使用 XXSL 、 XXEL 、 XXSS 和 XXES 分别表示行开始、行结束、歌曲开始和歌曲结束。

 

最后,要生成歌曲时,我们可以从 XXSS 标记开始,并一直调用 model.predict() ,直到遇到 XXES 标记为止。

 

song = ['XXSS']
while song[-1] != 'XXES':
 song.append(model.predict(song[-1]))

 

理论上,一旦循环停止,我们将产生一首从未见过的披头士歌曲。但效果会很好吗?

 

一首从未见过的披头士的歌曲

 

下面是 Bigram 模型生成的歌曲的一小段 :

 

She’s so I love her heart.

 

Well they are; they said I’m so many,

 

She no surprise

 

When you’re mine

 

Sad and the Amsterdam Hilton

 

they make my way,

 

Yes I wait a boy been born with a rich man,

 

all share

 

生成的歌词听起来像疯子胡言乱语,只有极其幸运的情况下生成的歌词才感觉比较有意义。

 

因此我们可以继续扩展 Bigram 模型来考虑前面两个单词, 这就是所谓的 Trigram 模型。你可能会发现, trigram 模型实际上会产生更好的歌词。这是因为三个单词的组合比较少,所以模型在每一步有更少的选择,在某些步骤它只有一个选择。通常,我们可以通过考虑前面的 n 个单词来创建一个任意的 n-gram 模型,当 n 等于整首歌的长度时,你可能会发现这个模型生成的披头士的歌曲是完美的。不幸的是,它生成的歌曲已经存在了。

 

寻求更好的模型

 

Bigram 模型最突出的问题之一是,它只使用在训练数据中看到的单词和短语。虽然我们想要创作出听起来像披头士乐队自己写的歌词,但我们不想只局限于他们使用的词。例如,如果披头士从未使用过单词 “ parade ” ,那幺 Bigram 模型将不会生成任何关于 “ parade ” 的歌曲。 当然,由于我们只是在训练披头士的歌词,我们不可能指望我们的模型使用从未见过的单词。我们需要的是对大量的语料库进行训练,比如 Wikipedia 或 Reddit 。

 

然而,即使我们对所有的 Wikipedia 都进行了训练,并且看到了英语中的每一个单词,我们的 Bigram 模型仍然过于死板。以短语 “tall man” ,每个对英语有基本了解的人都会认识到, “tall” 只是 “man” 的修饰语,但两者并没有太大关系。相反, “tall” 可以用来修饰无数的其他事物,比如 “woman” 、 “boy” 、 “building” 、 “giraffe” 等等。然而,我们的 Bigram 模型不能学习到这个,它必须在使用 “tall” 之前至少看到一次 “tall” 的用法。所以如果模型只看到过 “tall man” 、 ”tall boy” 、 “tall woman” ,而没有看到过 “tall girl” ,那幺 “tall girl” 这个短语就不会被生成。

 

因此,我们想要的是一个拥有更丰富的词汇量和更深入理解词汇表中词汇之间关系的模型。幸运的是,聪明的研究人员已经发明了如此强大的模型,我们可以用它们来创作更好的歌曲。

 

GPT-2 模型

 

OpenAI 的 GPT-2 模型 [1] 最近因为 “too dangerous to release.” 而成为头条新闻。模型生成那样令人信服的文本 , 以至于作者担心被恶意使用 ⁴ 而没有发布完整的版本。相反,他们发布了两个更小的版本供人们玩耍和实验。我们将使用其中最小的一个来生成披头士的歌词。

 

GPT-2 是一个基于 transformer 的模型,它经过了数百个小时在 GPU 对海量的 Reddit 数据进行训练。在训练期间,它学习了一个非常好的英语模型 ( 或者至少是在 Reddit 上使用的英语模型 ) 。这意味着它能够理解像“ tall ”这样的单词可以用来描述人、建筑物或长颈鹿。

 

此外,由于它使用 Reddit 上的数据进行训练,意味者它很可能‘看到’了 99.9% 的英语单词和短语。这对我们来说是个好消息,因为这正是我们一直在寻找的丰富的词汇量和深入理解词汇之间的关系的模型。

 

然而,如果我们使用这个模型,让它生成一些东西,它几乎不可能产生类似披头士的歌词。这是因为模型不知道我们所关心的是生成披头士的歌词,毕竟这不是它被训练出来的目的。相反,我们需要推动模型去做我们想让它做的事情,我们可以通过迁移学习来做到这一点。

 

迁移学习

 

转移学习是指当我们通过做某一件事情获取到信息后,我们可以利用已获取信息并将其应用于解决相关的问题。例如,当你开始阅读这篇文章时,你不需要重新学习单词是什幺,哪些单词跟在哪些单词后面,或者它们是如何组合成句子的。想象一下那将是多幺乏味。相反,你利用了之前所学的语法知识来理解我现在在说什幺。

 

以类似的方式,我们可以利用 GPT-2 通过数百小时阅读 Reddit 上的帖子所学到的知识,并将其转移到生成披头士歌词的任务中。高层次的想法是采用预训练的模型,并将其训练更长时间。不过,我们将只使用抓取的披头士的歌曲歌词,而不是 Reddit 上的帖子进行训练,这将使模型严重偏向于生成类似披头士的歌曲。

 

这里我跳过如何具体实现,因为要讲它将需要采取另一个类似长度的文章来解释一切。如果你对具体细节感兴趣,我建议你参考 [2] ,这是一篇很棒的博客文章,详细介绍了如何将 GPT-2 模型转移到任何你关心的语言任务。这也是我所遵循的,以得到这里显示的结果。

 

新的披头士

 

就像所有优秀的深度学习成果一样,我在一开始发布的歌词也是精心挑选的。生成的歌曲并不都那幺好,它们的质量取决于微调阶段时候。当模型对训练数据的拟合仍然严重不足,大约 100 个小批进行微调后,您可能会得到以下结果:

 

Ev ’ ry I Love You

 

Lennon & McCartney

 

Ev ’ ry I will always be

 

Ev ’ ry you love me.

 

Ev ’ ry you love me,

 

Ev ’ ry you love me too.

 

Ev ’ ry I will always be

 

这样再写个 10 到 15 行,可能至少比 Lil ‘Pump 好吧,哈哈。

 

说正经的,我最感兴趣的是前两行。在训练数据中,每首歌的开头第一行是标题,第二行是作者,下面几行是歌词。即使在这个早期阶段,该模型也设法了解了我们数据的结构 : 第一行和第二行是特殊的 ; 在第二行中,可能出现的单词组合很少,最有可能出现的是 Lennon & McCartney。

 

如果我们微调约 350 个小批量,模型开始产生更可信的歌词,像我一开始所给出的或是下面的这幺一段 :

 

Woman in Black

 

Lennon & McCartney

 

I ’ d make a scene

 

If you don ’ t want me to appear

 

You might as well leave me alone.

 

I ’ m near death and I ’ m in love

 

可能不完美,但感觉还不错,最后,如果我们持续微调 ( 大约 2800 个小批量 ) ,会发生以下情况 :

 

Get Back (Get Back)

 

Lennon & McCartney

 

Yellow Submarine

 

Lennon & McCartney

 

On a Saturday night as the sun shines down onme

 

The sun is out, the sails are clear

 

The sun is in, the sails are clear

 

Oooh — Hey

 

模型开始过度拟合,生成的样本很可能出现在训练数据中,如重复的“ Lennon & McCartney ”行、“ yellow Submarine ”等,我发现大约 300-500 步的微调能产生最好的歌词。

 

结论

 

好了,希望你现在对语言模型的工作原理有了更好的了解,并且了解我们如何利用最先进的模型来极大地改进后续任务。

 

话虽如此, GPT-2 模型还有很多值得探索的地方。我生成的示例使用默认的 ( 可能的 ) 次优超参数。如果把更多的时间花在正确的微调上,看看生成的歌词能有多好会很有趣。我也只使用了发布的最小模型,因为我是用笔记本电脑训练的,我相信更大的模型会产生更壮观的结果。最后, OpenAI 最近发布了 MuseNet ,它能够生成非常逼真的音乐。如果将 GPT-2 和 MuseNet 放在一起 ( 它们基本上是相同的模型 ) ,并生成歌词和伴奏音乐,那该有多棒 ? 如果我有更多的时间、金钱,或者对我正在做的事情有任何想法,我愿意用机器学习来创作一首成熟的歌曲,然后让真正有天赋的人来表演。

 

脚注

 

¹ 尽管我认为最好的独唱音乐家应该是另一个 60 年代的传奇人物, Bob Dylan 。 他的单曲《 Like a Rolling Stone 》可能是有史以来最好的歌曲,这也不仅仅是我的观点。

 

² 来自歌曲《 Within You Without You 》

 

³ 为了简洁起见,略过了一些小细节。 可以在 GitHub 上看到所有细节 .

 

⁴ 有些人认为这只是一个巨大的宣传噱头,但事实并非如此。

 

参考文献

 

[1] A. Radford et al., LanguageModels are Unsupervised Multitask Learners (2019)

 

[2] S. Todorov, Generating FakeConversations by fine-tuning OpenAI’s GPT-2 on data from Facebook Messenger (2019)

Be First to Comment

发表评论

邮箱地址不会被公开。 必填项已用*标注