Deej-A.I.
[Robert Dargavel Smith](https://github.com/teticio/Deej-AI/blob/master/mailto:teticio@gmail.com - 高级机器学习硕士项目结题作品(MBIT学院,西班牙马德里)
更新:经过近5年时间,我终于重新训练了部署在https://deej-ai.online 的模型,使用了一百万个播放列表和一百万首歌曲。人们持续使用它证明了它的优秀表现,但是是时候加入2018年以来发行的新歌了!我在此仓库中新增了一个train
目录,其中的README
详细说明了如何获取数据集并从头开始训练你自己的模型。
更新:现在你可以在Hugging Face平台上更轻松地使用这个模型。
更新:在Spotify上应用于32万首歌曲后的结果可以在这里查看。网站的代码可在这里获取。
动机
市面上有许多自动DJ工具,它们能巧妙地匹配一首歌与另一首歌的节奏并混音。老实说,我一直觉得这种DJ相当无聊:他们在技术上越出色,听起来就越像一首永无止境的歌。在我看来,重要的不是你如何播放,而是你播放什么。多年来,我收集了许多稀有唱片,并在电台和俱乐部做过一些DJ工作。我几乎可以立即判断出我是否会喜欢一首歌,只需听几秒钟。或者,如果正在播放一首歌,通常能立即想到一首与之搭配的歌。我认为人工智能可以应用于这种"音乐直觉",作为一个基于简单聆听歌曲的音乐推荐系统(当然,还需要百科全书式的音乐知识)。
几年前,iPod有一个非常酷的功能叫Genius,它可以根据几首示例歌曲即时创建播放列表。苹果决定移除这个功能(尽管它在iTunes中仍然可用),可能是为了说服人们订阅他们的音乐流媒体服务。当然,Spotify现在提供了这种功能,但就我个人而言,我发现它的推荐充其量是我已经知道的音乐,最糟糕的情况下则显得相当商业化且缺乏创意。我有一个庞大的音乐库,我很怀念有一种简单的方式来说"继续播放类似这样的歌"(尤其是在我开车时),以及一些能帮助我发现新音乐的东西,即便是在我自己的收藏中。我花了一些时间寻找替代方案,但没有找到任何合适的。
实现细节
一种常见的方法是使用音乐流派来分类音乐,但我发现这太过简单化和局限。警察乐队的Roxanne是雷鬼、流行还是摇滚?那么不断演变的电子音乐的各种细分呢?我觉得有必要找到一种更高维度、更连续的音乐描述方法,而且不需要为每首歌曲贴标签(即采用无监督学习方法)。
我做的第一件事是抓取尽可能多的Spotify播放列表。(不幸的是,我在一个类似的比赛已经结束后才想到做这个项目,那个比赛提供了一百万首歌曲的访问权限。)我的想法是,通过播放列表对歌曲进行分组可以为单个歌曲提供一些背景或含义 - 例如,"80年代迪斯科音乐"或"我最喜欢的海滩歌曲"。人们倾向于制作由相似艺术家、相似情绪、风格、流派的歌曲组成的播放列表,或为特定目的(例如,在健身房锻炼)制作播放列表。不幸的是,Spotify API并不特别方便下载播放列表,所以我采用了一种相当粗糙的方法:我搜索了所有名称中包含字母'a'的播放列表,然后是'b',以此类推直到'Z'。通过这种方式,我成功抓取了24万个播放列表,包含400万首独特的歌曲。我故意排除了所有由Spotify策划的播放列表,因为这些播放列表特别商业化(我相信艺术家可以付费在其中露面)。
然后,我使用Word2Vec算法创建了这些歌曲的嵌入("[Track2Vec](https://github.com/teticio/Deej-AI/blob/master/notebooks/Track2Vec.ipynb"),将每首歌曲视为一个"词",每个播放列表视为一个"句子"。(相信我,我独立想到了这个想法,与[这些人](https://spandan-madan.github.io/Spotify/)不谋而合。)我发现100个维度是一个不错的大小。给定一首特定的歌曲,该模型能够令人信服地推荐Spotify上同一艺术家或相似艺术家的歌曲,或来自同一时期和流派的歌曲。由于独特歌曲的数量巨大,我将"词汇表"限制在至少出现在10个播放列表中的歌曲,最终得到了45万首歌曲。 Spotify API的一个好处是它为大多数歌曲提供了URL,允许你下载30秒的MP3样本。我下载了所有这些MP3,并将它们转换为Mel频谱图——一种紧凑的歌曲表示形式,据说能反映人耳对声音的响应。就像人类只听几秒钟就能想到相关音乐一样,我认为5秒的窗口就足以捕捉歌曲的要点。即使使用如此有限的表示,所有频谱图压缩后的大小也达到了4.5GB!
下一步是尝试使用从Spotify获得的信息来从频谱图中提取特征,以便有意义地将它们相互关联。我训练了一个卷积神经网络,尽可能精确地(以余弦相似度计)重现与给定频谱图(输入x)相对应的Track2Vec向量(输出y)。我尝试了一维(在时间轴上)和二维卷积网络,并将结果与基准模型进行了比较。基准模型试图在不实际听音乐的情况下得出最接近的Track2Vec向量。理论上,这导致了一首每个人都应该稍微喜欢(或讨厌)的歌曲(SBTRKT - Sanctuary),余弦相似度为0.52。在过拟合发生之前,我能够在验证数据上获得的最佳分数是0.70。使用300维嵌入时,验证分数更好,但基准分数也更高:我认为更重要的是要有更低的基准分数和两者之间更大的差异,反映出具有更多多样性和区分能力的潜在表示。当然,分数仍然很低,但期望频谱图能捕捉到人类基于文化和历史因素将歌曲归类在一起的相似性是不合理的。此外,有些歌曲在5秒窗口中的表示效果很差(例如,在皇后乐队的"Don't stop me now"中,这部分对应的是布莱恩·梅的吉他独奏...)。我尝试了自编码器和变分自编码器,希望迫使频谱图的内部潜在表示更加连续、解耦,从而更有意义。初步结果似乎表明,二维卷积网络更善于捕捉频谱图中包含的信息。我还考虑训练一个孪生网络来直接比较两个频谱图。我把这些想法留作未来可能的研究。
最后,对于一个MP3文件库,我将每个MP3映射到一系列Track2Vec向量,每个向量对应5秒的时间片段。大多数歌曲从头到尾变化很大,因此逐片段的推荐结果差异很大。就像我们可以应用Doc2Vec模型来比较相似文档一样,我为每个MP3计算了一个"MP3ToVec"向量,根据其TF-IDF(词频-逆文档频率)权重包含每个组成的Track2Vec向量。这种方案给予频繁出现且特定于某首歌曲的推荐更高的重要性。由于这是一个O(n²)算法,有必要将MP3库分成100个一批进行处理(否则我的8,000个MP3将需要10天才能处理完!)。我检查了这对计算出的向量的影响可以忽略不计。
结果
你可以在这个工作簿的末尾查看部分结果,并自行判断。它在识别古典音乐、口语、嘻哈和电子音乐方面表现尤为出色。事实上,我对它的效果如此惊讶,以至于开始思考这种效果有多少是由TF-IDF算法贡献的,又有多少是由神经网络贡献的。因此,我创建了另一个基准模型,使用随机初始化权重的神经网络将频谱图映射到向量。我发现这个基准模型擅长识别流派和结构相似的歌曲,但在不确定的情况下,会提出一些完全不适合的建议。相比之下,经过训练的神经网络似乎会选择能量、情绪或乐器配置相似的歌曲。在许多方面,这正是我所追求的:一种超越严格流派界限的创造性方法。通过调整参数(该参数决定了在TF-IDF算法中两个向量是否被视为相同),可以在流派(全局特征)和"感觉"(局部特征)之间找到一个良好的平衡。我还将结果与iTunes中Genius生成的播放列表进行了比较,虽然这很主观,但我觉得Genius更倾向于严格遵循流派,即使歌曲并不完全匹配,而且它提供的选择不太"有灵感"。也许需要进行一次众包的"可口可乐"测试来做最终判断。
考虑到数据、计算能力和时间的限制,我认为这些结果可以作为概念验证。
应用
除了最初的自动(广播而非俱乐部)DJ的想法外,还有几个其他有趣的应用。例如,由于向量映射是连续的,你可以轻松创建一个播放列表,在一首歌和另一首歌之间平滑地"连接点",经过任意多的中间点。比如,你可以从灵魂乐通过放克和鼓打贝斯过渡到电子乐。或者从摇滚到歌剧 :-)。
另一个简单的想法是使用麦克风听音乐,并实时提出一组下一首要播放的歌曲。与其比较整体的MP3ToVec,可能更适合只考虑每首歌的开头部分,这样音乐就可以更自然地从一首过渡到另一首。
亲自尝试
首先用以下命令安装所需的Python包:
pip install -r requirements.txt
然后下载模型权重到Python文件所在的目录,你就可以处理你的MP3(和M4A)音乐库了。只需运行以下命令并等待...
python MP3ToVec.py Pickles mp3tovec --scan c:/your_music_library
它会创建一个名为"Pickles"的目录,在其子目录"mp3tovecs"中创建一个名为"mp3tovec.p"的文件。完成后,你可以用以下命令尝试:
python Deej-A.I.py Pickles mp3tovec
然后在浏览器中访问http://localhost:8050。如果你添加参数--demo 5
,你就不必等到每首歌结束。只需加载一个你想用作播放列表基础的MP3或M4A文件;它不一定要来自你的音乐库。最后,还有几个控件可以调整(按目前的程序设置,这些调整只会在下一首歌开始时生效,如果当前已经在播放)。"Keep on"决定了在生成播放列表时要考虑的前几首歌的数量,"Drunk"指定了要加入多少随机性。另外,你还可以用以下命令创建一个你选择的音乐旅程的MP3混音:
python Join_the_dots.py Pickles\mp3tovecs\mp3tovec.p --input tracks.txt mix.mp3 9
其中"tracks.txt"是一个包含MP3或M4A文件列表的文本文件,这里的9是指你想在每两首歌之间生成的额外曲目数。
如果你对我用来训练神经网络的数据感兴趣,欢迎给我发电子邮件。
生成.m3u播放列表文件
你还可以选择生成一个带相对路径的.m3u
文件,以导出播放列表并在其他设备上播放。
播放列表文件使用相对路径。所以如果你指定--playlist
为/home/user/playlists/playlist_outfile.m3u
,其中一首歌位于/home/user/music/some_awesome_music.mp3
,那么生成的播放列表将把这首歌写成../music/some_awesome_music.mp3
。
运行命令:
python Deej-A.I.py Pickles mp3tovec --playlist playlist_outfile.m3u --inputsong startingsong.mp3
它还接受以下可选参数:
```bash
--nsongs # 播放列表中的歌曲数量
--noise # 播放列表中的噪音量(默认为0)
--lookback # 播放列表中的回溯量(默认为3)