动手写简单的音乐推荐系统

article/2025/11/1 9:18:15

简单音乐推荐系统的设计与实现

本文提供两种简单的传统音乐推荐系统(next-songs 方向)的思路与实现。(数学原理和机器学习方法从略)

下文仅给出思路以及关键代码,完整代码实现见: https://github.com/cdfmlr/murecom-intro

1. 基于音频特征

分析音频特征,做基于内容的推荐(Content-Based Filtering,CBF)。

1.1 设计思路

一个喜欢巴赫的人可能也喜欢肖邦,所以一种自然的想法是,我们可以把音频送给机器进行学习,试图让它分别不同种类、风格的音乐。给定一首歌,送入训练好的模型,推荐出风格上最相近的其他歌曲。

song-classification.ipynb 实现了这种模型的训练。

加拿大维多利亚大学的 genres 数据集(http://opihi.cs.uvic.ca/sound/genres.tar.gz),提供了良好标注的不同种类音乐片段。

$ ls genres
blues     country   hiphop    metal     reggae
classical disco     jazz      pop       rock

我们把这些片段利用 librosa 库转化为梅尔频谱图(mel-spectrogram)。

数据集中 hiphop 风格片段的平均频谱图)

(上图为数据集中 Hip-Hop 风格片段的平均频谱图)

把频谱送入一个一维卷积池化堆叠 + 全连接分类头的神经网络,训练,得到的模型即一个音乐风格检测器。

def cnn_model(input_shape):inputs = Input(input_shape)x = inputs# 一维卷积池化levels = 64for level in range(3):x = Conv1D(levels, 3, activation='relu')(x)x = BatchNormalization()(x)x = MaxPooling1D(pool_size=2, strides=2)(x)levels *= 2# x -> shape(128)x = GlobalMaxPooling1D()(x)# 计算类型标签的全连接网络for fc in range(2):x = Dense(256, activation='relu')(x)x = Dropout(0.5)(x)labels = Dense(10, activation='softmax')(x)model = Model(inputs=[inputs], outputs=[labels])# optimizer and compile modelsgd = SGD(learning_rate=0.0003, momentum=0.9, decay=1e-5, nesterov=True)model.compile(optimizer=sgd, loss='categorical_crossentropy', metrics=['accuracy'])return modelmodel = cnn_model((128, 128))

训练得出的模型 song_classify.h5 可以很好的分类特征明显的音乐类型(例如古典乐),但对界限相对模糊的乐种(如摇滚乐)分类效果欠佳。

分类结果的混淆矩阵

(分类结果的混淆矩阵)

利用这个模型,在 index-local-mp3s.ipynb 中实现了相似音乐的推荐。

具体的做法是,手动做了一个简单的数据集,选取一些个人常听的音乐,转化为同样品质的 mp3 文件。

本地音乐数据集

(选取的音乐种类)

遍历处理这些文件,提取梅尔频谱图。

def process_mp3(path):signal, sr = librosa.load(path,res_type="kaiser_fast",offset=30,duration=30)melspec = librosa.feature.melspectrogram(signal, sr=sr).T[:1280, ]if len(melspec) != 1280:return Nonereturn {'path': path,'melspecs': np.asarray(np.split(melspec, 10))}# 对每个 MP3 的所有频谱进行索引
songs = [process_mp3(path) for path in tqdm(mp3s)]
songs = [song for song in songs if song]# 可以把他们连在一起,方便一批完成
inputs = []
for song in songs:inputs.extend(song['melspecs'])

接下来将预处理好的数据集送入训练好的模型。

由于我们只需要提取音频特征,而并不需要做分类,所以把模型最后基层的全连接分类头去掉,只留下前面的卷积特征提取层。输入音频频谱,输出一个 256 维的向量作为音乐的“特征向量”。

cnn_model = load_model('song_classify.h5')
vectorize_model = Model(inputs=cnn_model.input,outputs=cnn_model.layers[-4].output)
vectors = vectorize_model.predict(inputs)

建立一个无监督的最邻近模型,计算这些特征向量的相似度,也就其代表的 mp3 歌曲的相似度。

nbrs = NearestNeighbors(n_neighbors=10, algorithm='ball_tree'
).fit(vectors)def most_similar_songs(song_idx):distances, indices = nbrs.kneighbors(vectors[song_idx * 10: song_idx * 10 + 10])c = Counter()for row in indices:for idx in row[1:]:c[idx // 10] += 1return c.most_common()def print_similar_songs(song_idx, start=1, end=6):print("指定歌曲:", song_name(song_idx))for idx, score in most_similar_songs(song_idx)[start:end]:print(f"[相似度{score}] {song_name(idx)}")

最后,给定一首歌,就可以从最邻近模型中找到最接近的几首歌。

genres_result

(推荐结果示例)

模型最终表现还行吧。只是和分类的结果类似,不善于处理摇滚乐。

1.2 模型优缺点

这是我比较喜欢的一种方式,从音乐本身的特征出发,不基于以往用户数据,没有曲目列表限制。借助训练好的分类器网络,可以对任意没见过的音频进行推荐。

但是,需要处理完整的音频。频谱分析的过程比较消耗算力。并且只能推荐本地拥有的曲目在另一方面也可以看作一种限制。

这种模型可以用于离线的设备端音乐推荐。

1.3 改进空间

  1. 用于训练分类器的 genres 数据集虽然质量极高,但数据量不太大。考虑用更多数据,或许能得到更好的模型;
  2. 分类器网络的结构也比较粗糙,可以考虑进一步研究调整。例如考虑使用预训练的 NLP 模型进行迁移学习,或许能更加敏锐;
  3. 考虑构建多输入的模型(或者使用多个模型),加上一些其他方面的数据,比如歌曲的元数据(歌名、艺人、专辑、时常等)、以及歌词等不容易从频谱中得出的方面。

2. 基于现有播放列表数据

基于以往的、其他用户的数据,做协同过滤(Collaborative Filtering ,CF) 。

这种思路其实更常见。获取一系列的人建好的播放列表。通过某种方法建立其中曲目的距离关系。给定歌曲,推荐距离最近的。

2.1 获取数据

spotify-playlist.ipynb 中,利用 Spotify 的 API,随机获取一些播放列表,及其中曲目(只是获取元数据,不下载音频)。

但由于这种方法需要获取大量数据(需要数十万歌曲),而网络、数据库环境都有限制,Python 实现不甚稳定,难以完成工作,所以在 spotify/ 子目录中,使用 Golang 重写了这个实现,提供更加鲁棒的数据获取服务,将获取的数据存放在一个 SQLite 数据库中。

spotifyplaylist

(获取的播放列表及曲目数据)

这里目前获取了数 GiB 数据,包含 17 万个播放列表中,来自 80 万个艺人的近 500 万首歌曲。

sqlite> select count(*) from playlists;
177889
sqlite> select count(*) from artists;
801357
sqlite> select count(*) from tracks;
4995249

下面实现了两种思路来利用这些数据:

2.2 Word2vec

train-a-music-recommender.ipynb 中,将歌曲作为单词、将歌曲最成的播放列表作为句子:

sentences = [["track_1_id", "track_2_id", ...], # playlist_1[...], # playlist_2...
]

以此为语料,建立 Word2vec 模型。

model = gensim.models.Word2Vec(sentences=PlaylistTracksIter(DB), min_count=4)

训练完成后,给定曲目,可获取到最接近的推荐。

def suggest_songs(song_id):similar = dict(model.wv.most_similar([song_id]))song_ids = ', '.join(("'%s'" % x) for x in similar.keys())c = conn.cursor()c.execute("SELECT * FROM tracks WHERE id in (%s)" % song_ids)res = sorted((rec + (similar[rec[4]], find_artists(rec[4])) for rec in c.fetchall()),key=itemgetter(-1),reverse=True)return suggest_songs_result([*res])def suggest_from(song_name: str):s = find_song(song_name, limit=1)return s + suggest_songs(s[0]["id"])

这个模型也可用,但效果不算特别理想。

word2vec_result

(Word2Vec 模型推荐实例)

2.3 Surprise KNNBaseline

surprise.ipynb 中,将歌曲作为 item,将播放列表作为 user,播放列表包含某歌曲即看作 user 给 item 打了一分(rating=1)。

surprise_data

将这样处理好的数据集交给 Surprise 进行基本的协同过滤

from surprise import KNNBaseline
from surprise import Reader, Dataset# custom dataset
reader = Reader(rating_scale=(0, 1))
train_data = Dataset.load_from_df(pt_train[['userID', 'itemID', 'rating']],reader)
trainset = train_data.build_full_trainset()# compute  similarities between items
sim_options = {'user_based': False
}# 算法、训练
algo = KNNBaseline(sim_options=sim_options)
algo.fit(trainset)

同样得到 KNN 的模型,给定歌曲,从模型中获取最邻近的推荐。

def find_sim(track_id, k=5):sim = algo.get_neighbors(iid=algo.trainset.to_inner_iid(track_id), k=k)track_ids = [track_id] + list(map(algo.trainset.to_raw_iid, sim))tracks = []c = conn.cursor()for tid in track_ids:c.execute(f"SELECT * FROM tracks WHERE id = '{tid}'")tk = c.fetchall()[0]tracks.append(tk + (find_artists(tid),))c.close()return sim_result(tracks)

这个做出来效果不错。

surprise_result

(Surprise 模型推荐结果,Shout Baby 是输入的歌曲,下面 5 首是推荐出的,二刺螈狂喜。)

2.4 模型优缺点

这种思路是传统的过往用户数据分析,是推荐系统比较常规的实现方式,方案较为成熟。基于海量数据,可以达到比较好的推荐效果。

但是,大数据的处理速度可能较慢,并且存储器开销不是终端可以承受的。同时,对于用户,基于数据的邻近推荐会容易造成信息茧房问题,并不健康。

这种方案可以用于云端的音乐推荐。

2.5 改进空间

  1. 算法:目前实现的是最基本的基准算法,可以考虑尝试其他的算法。
  2. 数据:对于这种模型更多的数据几乎一定会带来更好的结果。
  3. 考虑抓取网易云音乐的数据,可能更优质:本土化音乐,评论、热度、播放列表标签分类。可以用更综合的模型进行推荐

参考文献

[1] Douwe Osinga. Deep Learning Cookbook[M]. O’Reilly, 2018: 210-227.

[2] Nicolas Hug. Surprise: A Python library for recommender systems[J]. Journal of Open Source Software, 2020, 5(52): 2174.


http://chatgpt.dhexx.cn/article/uJuCq0Wf.shtml

相关文章

基于hadoop大数据的音乐推荐系统

1 简介 今天向大家介绍一个帮助往届学生完成的毕业设计项目,基于hadoop大数据的音乐推荐系统。 1.4 Hadoop优势(4高) 1.5 Hadoop 组成(面试重点) 1.5.1 HDFS 架构概述 Hadoop Distributed File System ,简…

python音乐推荐系统_音乐推荐系统

音乐频道推荐业务,支持各个产品业务和策略。这里我先使用CB+CF+LR实现推荐部分,下面具体展开: 一、推荐系统流程图 CB,CF算法在召回阶段使用,推荐出来的item是粗排的,利用LR算法,可以将CB,CF召回来的item进行精排,然后选择分数最高,给用户推荐出来。后续我们可以采用…

(附源码)计算机毕业设计SSM音乐推荐系统

项目运行 环境配置: Jdk1.8 Tomcat7.0 Mysql HBuilderX(Webstorm也行) Eclispe(IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持)。 项目技术: SSM mybatis Maven Vue 等等组成,B/S模式 M…

音乐推荐系统设计

文章目录 问题描述如何解决系统设计评测指标推荐系统的用户画像运动音乐场景用户行为分析睡眠音乐场景用户行为分析治愈音乐场景用户行为分析其他音乐场景用户行为分析 问题描述 移动网络和数字多媒体技术的飞速发展促进了数字音乐产业的共享与广泛传播.对用户而言…

音乐推荐系统(协同过滤和SVD)

python音乐推荐系统 首先对音乐数据集进行数据清洗和特征提取,基于矩阵分解方式来进行音乐推荐。 直接上源码和数据集文件 链接:https://pan.baidu.com/s/1Q45-pTRgXcQ4H-Z2WsRbVQ?pwd55z0 提取码:55z0音乐数据处理 读取音乐数据集&#x…

基于深度学习的音乐推荐系统简述

本文简要介绍我做的基于深度学习的音乐推荐系统。主要从需求分析与设计实现的角度来进行介绍。 (一)需求分析 基于深度学习的音乐推荐系统旨在以个性化音乐推荐模型为基础,使用B/S架构的形式实现。个性化推荐模型使用了 随机梯度下降&#x…

基于大数据的音乐推荐系统的设计与实现

基于大数据的音乐推荐系统是为了给听众推荐符合内心喜好的个性化系统。系统提供的功能有,音乐管理:管理员可以添加删除音乐,音乐查找:用户可以在系统中自行查找想要听的歌曲,音乐推荐:系统在收集了用户的行…

context-aware recommendation

智能手机的普及让大家随时随地都可接入互联网,而这样的随时随地的应用场景,也让传统推荐技术需要充分考虑,利用这些信息提升推荐的准确性,同时从另外一方面考虑, 这种符合LBS的推荐, 因为有了这些信息后&am…

accept函数_使用函数式接口

像上几章提到的,函数式接口定义且只定义了一个抽象方法。函数式接口很有用,因为抽象方法的签名可以描述Lambda表达式的签名。函数式接口的抽象方法的签名成为函数描述符。所以为了应用不同的Lambda表达式,你需要一套能够描述常见函数描述符的…

accept函数(TCP)

accept函数&#xff08;TCP&#xff09; #include <sys/types.h> /* See NOTES */ #include <sys/socket.h> int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);功能&#xff1a;阻塞等待客户端的连接请求 sockfd&#xff1a;文件描述符&#…

Context context = getApplicationContext()

使用getApplicationContext 取得的是当前app所使用的application&#xff0c;这在AndroidManifest中唯一指定。意味着&#xff0c;在当前app的任意位置使用这个函数得到的是同一个Context 1、Context概念 其实一直想写一篇关于Context的文章&#xff0c;但是又怕技术不如而误人…

AutoCompleteTextView

自动完成的提供建议的文本 使用方法 //arrays.xml <?xml version"1.0" encoding"utf-8"?> <resources><array name"city_name"><item>Chengdu</item><item>Beijing</item><item>Tianjin<…

http状态码、accept、Content-Type

一、http状态码 &#xff08;1&#xff09;http状态码 1XX&#xff1a;信息&#xff0c;服务器收到请求&#xff0c;需要请求者继续执行操作 2XX&#xff1a;成功&#xff0c;操作被成功接收并处理 3XX&#xff1a;重定向&#xff0c;需要进一步的操作以完成请求 4XX&#xf…

Gated-Attention Readers for Text Comprehension

Gated-Attention Readers for Text Comprehension 文本理解中的门控attention阅读器 code Abstract 本文研究的是完形填空问题式MRC&#xff0c;作者提出的门控注意力阅读器集中了多跳结构和一种新的注意力计算机制&#xff08;基于query嵌入和RNN文档阅读器中间状态之间的…

Html中Input的accept属性

Accept属性规定通过在文件上传提交的服务接受的文件类型 但是accept属性只能适用在Html input类型为文件类型&#xff0c;也就是说其他类型的input accept属性就不适用 我写的这个过滤文件属性是针对于后缀名为.xls的文件&#xff0c;所有的文件只要不是.xls后缀就不显示出来…

ApplicationContext

如果说BeanFactory是Spring的心脏&#xff0c;那么Application就是完整的身躯。ApplicationContext就是由BeanFactory派生出来的。 1、ApplicationContext ApplicationContext的主要实现类是ClassPathXmlApplicationContext和FileSystemXmlApplicationContext,前者默认从类路径…

ActionContext

1、ActionContext翻译成中文就是Action的上下文&#xff08;为什么说是上下文&#xff0c;是应为他的生命周期长&#xff0c;和我们的项目的生命周期是相同的&#xff0c;我们很多公共的东西都放在里面&#xff0c;方便存取 &#xff09;&#xff0c;ActionContext是struts2的上…

readonly option is set (add ! to override)错误的解决

在mac电脑或linux系统中经常操作修改某个文件后保存退出出现readonly option is set (add ! to override)。如图&#xff1a; 正常情况下按 A进入编辑模式修改文件后按ESC退出编辑模式&#xff0c;:wq命令保存退出&#xff0c;但经常会遇到以上显示&#xff0c;文件权限只读或者…

ByteBuffer的allocate和allocateDirect

在Java中当我们要对数据进行更底层的操作时&#xff0c;通常是操作数据的字节&#xff08;byte&#xff09;形式&#xff0c;这时常常会用到ByteBuffer这样一个类。ByteBuffer提供了两种静态实例方式&#xff1a; Java代码 public static ByteBuffer allocate(int capacity…

直接内存 直接内存的释放和回收

直接内存 特点 不属于Java虚拟机管理&#xff0c;属于系统内存&#xff1b;属于操作系统&#xff0c;常见于NIO操作时&#xff0c;比如ByteBuffer【】用于数据缓冲区分配回收成本较高&#xff0c;但读写性能高&#xff1b;不受JVM内存回收管理 文件读写过程 java不具备磁盘…