独家 | 利用深度学习来预测Spotify上的Hip-Hop 流行程度

344bcc87bebd773ebd67f79f6050be8c.png

作者:Nicholas Indorf翻译:Gabriel Ng校对:zrx

本文约10000字,建议阅读13分钟
项目中收集并使用了 Spotify 数据库中最近发布的hip-hop曲目的音频预览样本和相关的流行度分数。

摘要

在这个项目里面,我想构建一个工具来帮助我的表弟,一位名叫“KC Makes Music”的Hip-Hop艺术家。这个工具将会评估他尚未发布的歌曲是否有在Spotify上流行的潜力。

项目中只收集并使用了 Spotify 数据库中最近发布的hip-hop曲目的音频预览样本和相关的流行度分数。这些数据被处理成频谱图(spectrogram)和二进制响应目标(流行/不流行),然后被输入到神经网络中,该网络随着迭代构建,其复杂度也会逐渐提高。

其中,基于循环神经网络的最复杂的模型表现最好,准确率为 59.8%,而ROC-AUC 为 0.646。这意味着比起基准情况,其准确度提高了 9.1% 和 0.146。考虑到这个模型只使用到曲目的预览音频,这虽然不是一个令人兴奋的结果,但它也并不是一无是处!尽管我不建议将此模型用于单一乐曲的层面,但对于大量样本来说,其仍然是能够发挥一定作用的。它可以把艺术家的注意力引导到要关注的子集上,从而节省精力,体现出模型的价值。相信进一步的改进会使模型可供客户使用的更加强大的工具。

https://github.com/Nindorph/HipHopPopularity

问题背景

音频分类(Audio classification)是神经网络模型的经典用例。例如在之前的项目中,我所在的一个团队构建了一个分类器来区分来自该数据集的音乐类别。这已经在多个博客文章(博客 1、博客 2)中完成,并且似乎是一个相当有知名度的项目。此外,Kaggle 上也进行着音频分类比赛,其参赛模型将会是未来类似项目的主要候选对象。两个重要的例子是Google Brain的语音识别比赛和康奈尔鸟类学实验室的鸟叫识别比赛。

考虑到这些,我想知道音频分类能走多远。音乐流派、人类语言和鸟叫声所涉及的类别当中的差异对人耳来说是显然的。爵士乐听起来不同于金属,不同的音节听起来不同,不同的鸟有不同的叫声。这些对于人类观察者来说都是显而易见的。我知道机器有着比人类更高的区分能力,奇怪的是,我发现在音频领域中,给机器提供的问题是人耳可以轻松解决的。我能找到的最困难的音频分类问题是将区分 COVID-19 的咳嗽声与正常咳嗽声,然而一个训练有素的呼吸科医生很大机率能很好地完成这项任务。相比之下,我们有着可以根据大量关于水井的数据点来预测坦桑尼亚的水井是否正常运行的模型。在神经网络领域,Nature 发表了一个可以通过查看心电图来预测心脏诊断的深度神经网络。而凭人力则很难通过任一数据集以做出这样的预测(除了第二个数据集,一个心脏病专家也许能做到)。

因此,我想选择一个涉及音频的项目来在人类难以做到的事情上进行预测。我想挑战极限,去做一些有野心的项目。因为我喜欢音乐,所以我想看看仅在歌曲样本上训练的模型是否可以预测曲目的流行程度。这是一个很常见的想法(博客 1、博客 2、博客 3),但这些项目都使用了 Spotify 提供的音频功能(例如跳舞性、乐器性等)。它们实际上并不使用音频样本,而我认为利用神经网络和原始歌曲样本的表现可能会更好。而因为你不需要依赖 Spotify 的指标,并且可以在歌曲发布之前进行此分析,这也显得模型更加有用。

流行程度是一个有难度的响应目标——上述博客文章把成功程度与更传统的(即非神经网络)技术混合起来。此外,神经网络通常用于人耳相对容易辨别的音频数据。但是一个人不能够听一首歌然后说“哦,这听起来很流行”。他们可能会说这听起来不错,但流行程度则有点难以量化。

KC Makes Music 是我表弟的艺名,他是 Spotify 上的Hip-Hop艺术家。我认为如果我利用我的数据科学技能能尝试帮助他在平台上取得听众数,这将是一次有趣的学习体验。他也取得了一些成功,截至 2021 年 12 月,他的每月听众数约为 2.45 万,但了解获得每月听众和追随者的方法往往是神秘的。Spotify 有一个非常强大的数据库,所以我知道有实际机会能帮助我的表弟增加他的听众群,从而扩大他作为艺术家的影响力。

在查看了这个项目的可行切入角度之后,我决定解决这个问题的其中一种方法是调查最近的 hip-hop 曲目并尝试建立一个模型来预测以前的未发行歌曲是否有可能在当前流行.我会随机收集各种歌曲(所有流行分数),获取他们的预览音频文件,将它们转换为频谱图,然后把它们输入神经网络。从而希望转化出一个模型,该模型可以捕捉流行歌曲中的共同特征,以便它判断一首新歌是否会流行。我的表弟有许多未发行的、已完成的曲目,而我们可以通过模型进行预测。如果模型足够准确,它可以找出哪些音轨会有好的表现。如果它们都表现得不好,那么可以跟我的表弟说明他的曲目需要更多的工作和改进,而我们可以利用迭代的方式重新测试它们。此外,我可以使用 LIME 之类的工具来找出模型输出中特别重要的结果,再让我的表弟接收到这些信息,让他能更专注于它们上。

对于这个项目,我决定将目标简化为流行/不流行的二进制标签,因为对于模型来说区分两个类会较确定某物的流行程度更容易一些。而对于我的商业目的,区分流行/不流行和确立流行程度之间没有太大区别。如果我得到了惊喜的结果,我总是可以在稍后将其转换成回归类型的模型,但就目前而言,简化标签有助我更容易地看到模型的有效性。此外,最重要的是模型可以将两个类分开。一个混合了流行歌曲和不流行歌曲的模型是没有用的。Type I错误(假阳性)意味着我的表弟发行了表现不好的歌曲,而Type II错误(假阴性)则意味着我的表弟在一首原来就很好的歌曲上付出了过多精力。不管怎样,我们得不到好的结果。考虑到这一点,准确度是一个有用的指标,但 ROC-AUC 是首选指标,因为它衡量的是两个类的分离程度。

数据采集

所有数据均来自 Spotify Web API。选择 Spotify 不仅是因为它是我们尝试进行优化的平台,还因为它是最全面的音乐数据库之一。使用 Spotify Web API 的 Python 接口 Spotipy,我收集了2019-2021年发布的“Hip-Hop”类型的随机歌曲的信息。随机音轨是根据此处概述的方法生成的。

有关我是怎样获取数据的更多详细信息,请参阅 GitHub。

最终,16168 首曲目被选择进行收录。数据中最关键的部分是约 30 秒 mp3 预览音频文件的 http 链接和流行度分数。分数的范围是 0-100,其中 100 代表最受欢迎。流行度是三峰的,在 0 处有一个巨大的峰值,在 ~28 处有一个小峰值,而在 ~45 处有一个大峰值。

数据集:完整性和代表性的检验

通过查看一些我认识的在过去 3 年中发行歌曲的代表性艺术家,有关数据的完整性和代表性的检验能很容易完成。我挑选了Drake、Kanye West 和 KC Makes Music(我的表弟)。有关代码的更多详细信息,请查看 Github。

Drake 有大约 140 首曲目。似乎是对的。

Kanye West 有 9 个。绝对不准确,因为在不久前他才发布了 Donda。

KC Makes Music 完全不见了!

这个数据集似乎并不完整。其中有很多歌曲有着非常多样化的艺术家和流行程度,但它缺少我期望出现的歌曲。例如,Kanye West 不久前发布了 Donda,但我在这里看不到这张专辑的任何内容。此外,这里根本没有我表弟的音乐的代表内容。

正如我所说,它仍然有一定数量的歌曲,所以对于现在来说可能已经足够好了。我会利用目前的数据集来进行项目,而如果我需要更多样本,我会在之后进行挖掘更多。

流行程度:对目标的考察

track_df['popularity'].hist(bins=100)

1c182e0286a61ff4bf6b8f9b203e43c5.png

Fig 1 流行程度(x轴)对照数量(y轴)

40 或更高似乎是一个不错的截断值,但我会在训练-测试-保持拆分后选择截断值,以将数据泄漏降至最低。

在文章稍后会看到39 是最终的截断数字,所以这个估计很接近。

另一个有趣的发现是没有 mp3 预览的歌曲更受欢迎,这令人十分意外!

track_df[track_df['preview_url'].isna()]\
['popularity'].hist(bins=100)

ba02554c6e4f0e036575a13055d89851.png

 Fig 2 流行程度(x轴)对照数量(y轴)

# before removal of nulls/duplicate links
track_df['popularity'].hist(bins=100)

1bf09c0393a4c365eab3819763f522eb.png

Fig 3 流行程度(x轴)对照数量(y轴)

# after removal of nulls/duplicate links
mp3s['popularity'].hist(bins=100)

160e1bbd93757808f52cc668eff7a7a6.png

Fig 4 流行程度(x轴)对照数量(y轴)

如你所见,带有预览 mp3 的歌曲,和所有收集到的没有重复的歌曲的分布大致相同。这是因为上述结论在没有 mp3 的歌曲上仍是正确的,因此删除没有预览 mp3 的歌曲并不会引入统计上的误差。

艺人:对平均流行程度的考察

一方面为了满足我的好奇心,而另一方面把流行度值放入下文中,我查看了数据中艺人平均歌曲流行度的分布。同样地,我使用了上面相同的 track_df_expart 来查看。

很多我认为非常受欢迎的艺人的平均流行度低于 70,而我从未听说过的许多艺术家的平均流行程度则非常高……

45a43acd311ebb13c90b5b262a50644e.png

 Fig 5 某部分有名气的艺人的平均流行程度

考虑到这一点,以上 40 的截断值估计似乎是一个用来描绘“流行”的可靠准则。同样,我将只查看训练数据,但由于样本量大,我认为它也会有相似的分布。

歌曲:对重复值的考察

我留意到有些歌曲有不同的 mp3 预览 http 链接,但实际上是同一首歌,只是在不同的专辑中。我会进一步看看具体情况。

# find duplicates based on track name and the duration
# lots of repeats -- 652 in the dataset
mp3s[mp3s.duplicated(subset=['track', 'duration_ms'], keep=False)]['track'].value_counts()
6 'N the Mornin'                                     6
3 Headed Goat (feat. Lil Baby & Polo G)              6
Zulu Screams (feat. Maleek Berry & Bibi Bourelly)    4
How TF (feat. 6LACK)                                 4
durag activity (with Travis Scott)                   4
                                                    ..
Zu Besuch                                            2
50 in Da Safe (feat. Pink Sweat$)                    2
The Announcement (Sex Drugs & Rock and Roll)         2
Shotta Flow                                          2
I'LL TAKE YOU ON (feat. Charlie Wilson)              2
Name: track, Length: 652, dtype: int64

如您所见,有许多重复值。让我们看一下在上部分的例子。

mp3s[mp3s['track'] == "3 Headed Goat (feat. Lil Baby & Polo G)"]

1c3fe41220151a86088de767a835469b.png

Fig 6 最多重复值的歌曲, 以表格呈现

哇,这首歌有2个单曲,2个专辑,2个豪华专辑,且都有不同的预览mp3链接(图中没有展示)。而且它们都有不同的流行程度得分。

尽管有来自不同专辑(单曲、专辑等)的重复歌曲,但它们通常有不同的流行程度,所以这仍然是有用的信息。故只要它们的流行程度不同,我就会保持这些重复值。最后我仅删除了 26 个条目:

mp3s[mp3s.duplicated(subset=['track','duration_ms','popularity'], 
                     keep=False)]['track'].value_counts()
6 'N the Mornin'                           6
9ja Hip Hop                                3
Face Off                                   2
8 Figures                                  2
80's - Instrumental - Remastered           2
3 Headed Goat (feat. Lil Baby & Polo G)    2
Studio 54                                  2
SAME THING                                 2
One Punch Wulf                             2
50/50 Love                                 2
96 Freestyle                               2
6itch remix - feat. Nitro                  2
6565                                       2
Zero Survivors                             2
Jazz Hands                                 2
Just Mellow - Norman Cook 7'' Remix        2
Aries (feat. Peter Hook and Georgia)       2
60%                                        2
Sex Cells                                  2
Seven Day Hustle                           2
Ring (feat. Young Thug)                    2
8 Missed Calls                             2
Name: track, dtype: int64

目标处理和 Librosa 处理

现在有了提取好的数据集,我就需要将我的目标处理成为二进制编码的变量,并利用Librosa把 mp3 预览链接转换成 Mel 频谱图,然后才能将两者都输入到神经网络中。

正如我之前提到的,流行程度会被简化成一个二进制标签,使模型更简单,因为流行程度的多少与是否受欢迎相比并没有更多的描述性质。

梅尔频谱图是一种广为人知的以图像格式来表示音频数据的工具。它将音频分解为频率(梅尔频率是一种更接近人类听觉的频率尺度)并显示频率随时间的分布。这样,模型可以侦测到有关于节拍、音色等等的样式。

# making train test holdout splits
# train = 75%, test = 15%, holdout = 10%


X = mp3s.drop(columns=['popularity'])
y = mp3s['popularity']


X_pretr, X_holdout, y_pretr, y_holdout = train_test_split(X, y, test_size=0.10, random_state=42)
X_train, X_test, y_train, y_test = train_test_split(X_pretr, y_pretr, test_size=15/90, random_state=42)
print(X_train.shape, X_test.shape, X_holdout.shape)
(12125, 9) (2426, 9) (1617, 9)

流行程度

39看起来是一个不错的截断值。

fig, ax = plt.subplots(figsize=(10,6))
y_train.hist(bins=95, ax=ax)
ax.grid(False)
ax.set(title='Popularity', xlabel='Score', ylabel='Song Count')

54c23d3f6f1be2e7a7554a72a1aa0b49.png

Fig 7 最终流行程度截断值(文字和截断值的记号在图表生成后才加上)

# defining popular as >= 39 and encoding (1 = popular)
y_train = y_train.map(lambda x: 1 if x >= 39 else 0)
y_train.value_counts(normalize=True)0    0.512
1    0.488
Name: popularity, dtype: float64y_test = y_test.map(lambda x: 1 if x >= 39 else 0)
y_test.value_counts(normalize=True)0    0.516076
1    0.483924
Name: popularity, dtype: float64y_holdout = y_holdout.map(lambda x: 1 if x >= 39 else 0)
y_holdout.value_counts(normalize=True)0    0.506494
1    0.493506
Name: popularity, dtype: float64

梅尔频谱图的处理

查看 github 以获取有关代码的更多详细信息,因为它的内容对于一篇博客文章来说实在是太多了。

一般来说,处理流程是这样的:

1. 从 http 链接取得 .mp3

2. 使用 pydub.AudioSegment 将 .mp3 转换为 .wav

3. 获

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值