写点什么

歌曲推荐系统实践:Pandas、SciPy 和 D3.js

2015 年 5 月 07 日

时至今日,虽然海量数据、大数据、数据挖掘、个性化等名词术语已耳熟能详,仿佛谁人两两遇到都可以轻易写个挖掘系统出来,但情况真的是这样么? Flipboard 数据产品部门的工程师 Ben Frederickson 在与友人的讨论中就发现,写个推荐系统并没有那么轻而易举,为此他专门写了一篇博文来记录自己实现的整个过程,利用的工具是数据挖掘领域很热门的 Pandas SciPy 函数库,最后使用 D3.js 进行交互和可视化,相关的代码都放在了 GitHub 上。

具体来讲,一个推荐系统包括数据的获取和存储,相似度的计算以及最终结果的可视化,下面分别阐述。

数据获取

Ben 的推荐系统是针对 Last.fm 用户的,所用数据集是通过 Last.fm 的 API 获取的大约 36 万用户对歌手的喜爱程度。程度以用户对该歌手的播放次数为指标,数据集大小在 1 千 7 百万左右。想要在程序中使用这个数据集,ben 通过 Python 数据挖掘工具 Pandas 的 read_table 将 csv 格式的数据导入成为表格。

复制代码
data = pandas.read_table("usersha1-artmbid-artname-plays.tsv",
usecols=[0, 2, 3],
names=['user', 'artist', 'plays'])

将数据加载为表格以后,剩下的任务就是计算相似度了,ben 给出了三种相似度的计算方法,分别是简单的相似度计算,余弦相似度和来自信息学的相似度计算,并给出了各类方法最后的可视化比较。

简单相似度

简单相似度计算,顾名思义,是最简单的相似度计算方法,用来计算两个歌手的相似程度。这种计算方法,忽略歌手被用户播放的次数,只是简单计算两个歌手重叠的用户数目。

复制代码
def overlap(a, b):
return len(a.intersection(b))

这种计算方法的问题在于,那些流行的歌手的存在,会极大影响相似度的准确性。例如几乎每个用户都听过 Radiohead、Coldplay 和披头士,这使得简单相似度方法给出的答案里面,越是流行的歌手越相似。

为了解决这个问题,ben 引入了新的相似度定义, Jaccard 相似度,利用数据挖掘中常用的正则化(Normalize)手段,将简单相似度正则化,消除用户数目对歌手相似度的影响,具体计算方法如下:

复制代码
def jaccard(a, b):
intersection = float(len(a.intersection(b)))
return intersection / (len(a) + len(b) - intersection)

类似的正则化方法还有很多,比如 Dice 正则和 Ochiai 正则等,从一定程度上改善了相似度计算的准确性,但也带来了一点问题,即集合大小相近的歌手会更加相似,ben 觉得这样也并不合理,因此进一步提出了使用余弦相似度。

余弦相似度

上文中提到的简单相似度抛弃了用户对歌手播放次数这一重要信息,实际上它代表了用户对该歌手的喜爱程度,细想一下是非常有道理的,一个披头士的重度听众怎么能够跟听过寥寥几曲的听众一样呢?那么,利用上播放次数这一信息最直接的办法,就是余弦相似度方法,计算公式如下:

复制代码
def cosine(a, b):
return dot(a, b.T)[0, 0] / (norm2(a) * norm2(b))

通过上面公式,我们就可以将播放次数引入到相似度的计算中。公式中的 a 和 b 分别代表歌手的听众向量,通过下面的代码构造生成:

复制代码
# map each username to a unique numeric value
userids = defaultdict(lambda: len(userids))
data['userid'] = data['user'].map(userids.__getitem__)
# map each artist to a sparse vector of their users
artists = dict((artist, csr_matrix(
(group['plays'], (zeros(len(group)), group['userid'])),
shape=[1, len(userids)]))
for artist, group in data.groupby('artist'))

来自信息学的相似度

除了单纯利用播放次数以外,ben 还介绍了来自信息学的,确切来讲是来自搜索引擎中常用的自然语言处理技术,来计算歌手之间的相似度,即词频 - 逆文档频率(TF-IDF)作为向量的相似度计算方法。

这种相似度的发明,来自搜索引擎对检索结果排序的需求,即计算检索关键词与检索返回的文档之间的相似程度。具体来讲,如果某个词语在一个描述语句中出现的频率很高(TF 很高),而在其他描述语句中很少出现(IDF 很高),则认为该词语具有很好的区分文档的能力,其 TF-IDF 值就比较高,那么对应到歌曲推荐这个任务来讲,ben 将用户(听众)看作一个个的单词,来进一步考虑特定用户对相似度准确性的影响,可谓是三种方法中比较准确的一个了,ben 还在原文中用 D3.js 给出了几种相似度的效果对比分析。

总结

在专业术语充斥耳畔的今天,能够有耐心真正自己去尝试一些想当然的东西、算法甚至系统,是非常难能可贵的精神,而收获也是非常丰富的。Ben 以 Python 中常用的 Pandas 和 SciPy 等工具,展现了从头实现一个推荐系统的方法,正是这种精神的实践典范。


感谢崔康对本文的审校。

给InfoQ 中文站投稿或者参与内容翻译工作,请邮件至 editors@cn.infoq.com 。也欢迎大家通过新浪微博( @InfoQ @丁晓昀),微信(微信号: InfoQChina )关注我们,并与我们的编辑和其他读者朋友交流(欢迎加入 InfoQ 读者交流群)。

2015 年 5 月 07 日 08:093980
用户头像

发布了 268 篇内容, 共 103.2 次阅读, 收获喜欢 19 次。

关注

评论

发布
暂无评论
发现更多内容

LiteOS:SpinLock自旋锁及LockDep死锁检测

华为云开发者社区

死锁 LiteOS 自旋锁 SpinLock LockDep

我看JAVA 之 基本数据类型与封装类型

awen

Java 源码 基本数据类型

探索图神经网络的网络架构和训练方法

华为云开发者社区

神经网络 AI 图神经网络 网络架构 GNNs

深入解读华为云细粒度文本情感分析及其应用

华为云开发者社区

AI 华为云 情感分析 语言语义 文本情感分析

元宵佳节快乐哦

Nydia

优雅地停止Spring Boot应用

韩斌

(28DW-S8-Day7) 比特币原始文献略读

mtfelix

比特币 区块链 白皮书 28天写作 工作量证明

写给 Go 初学者的指北

Rayjun

go

我看JAVA 之 Class

awen

Java 源码 jdk class

第十二周 学习总结

简简单单

Nginx中常见header配置及修改

运维研习社

nginx Linux

记忆这件“小事”「Day 7」

道伟

心理学 28天写作

从萧何进入咸阳丞相府到数字化时代的决策

数列科技杨德华

28天写作

如何学习一项技能

不在调上

《经济学人》2021年2月27日刊精彩文章导读及资源下载

wbliu85

多年阅读《经济学人》是一种什么体验?

wbliu85

【管理笔记14】行销活动的意义及范围

俊毅

28天写作

简单脚本监控SSL证书,并到期提醒续签

运维研习社

开源镜像仓库Harbor的镜像安全

运维研习社

Docker 安全 Harbor CI/CD

万绿丛中一点红——雷斯多夫效应

Justin

心理学 交互设计 28天写作 游戏设计

前端170面试题+答案学习整理(良心制作)

魔王哪吒

程序员 面试 前端 28天写作 2月春节不断更

Linux入门篇 —— Linux 用户与组管理详解(system-config-users && 命令行)

若尘

Linux 命令行 用户

如何探索自己的职业价值观,让工作更有动力

一笑

28天写作

Linux常见IO分析工具

运维研习社

我看JAVA 之 Annotation

awen

Java 源码 jdk 注解 annotation

28天瞎写的第二百四十五天:怎么样开始练习冥想?

树上

冥想 28天写作 正念

智能客服+智能助手,这波“数字员工”的业务能力太强了!

华为云开发者社区

华为 AI 数字 智能客服 智能助手

我看JAVA 之 Thread & ThreadLocal

awen

Java 源码 多线程

我看JAVA 之 枚举类型

awen

Java 源码 jdk 枚举

第十二周 作业

简简单单

Elasticsearch 相关度评分

escray

elastic 七日更 28天写作 死磕Elasticsearch 60天通过Elastic认证考试 2月春节不断更

技术为帆,纵横四海- Lazada技术东南亚探索和成长之旅

技术为帆,纵横四海- Lazada技术东南亚探索和成长之旅

歌曲推荐系统实践:Pandas、SciPy和D3.js-InfoQ