时雨小径 May the Spirit be with you

通过豆瓣电台搭建自己的音乐仓库 IV - 推荐系统

好了, 总算是写到这个项目比较有趣的一部分了, 这次实现了一个基于标签的音乐推荐系统.

这个就比单纯的关键字要给力多了, 举个简单的例子, 首先我用的是这组数据进行调教推荐, 得到的是这三首, 可以看出主要偏重的是'日本'和'女声':
jf

接着我调整一下参数, 将'女声'一项的影响降低, 同时提升'原声'的数值, 则得到对应的另外三首:
jy

与关键字相比, 现在的'距离比较'在更多的维度上进行计算, 因此结果显得更为精确.

DEMO 在此

前段时间看到了一本书, 叫'A Programmer’s Guide to Data Mining', 跳过其中coding的部分, 一个下午就可以看完了, 可以说是迅速入门, 个人感觉挺实用的, 可以了解一下这个领域里的一些基本概念.
关于推荐系统, 很容易想到的就是豆瓣电台, 它可以通过用户的行为来为其推荐可能喜欢的歌曲. 在拥有大量的用户群的时候, 可以采用基于用户的协同过滤来进行推荐, 简单的一个例子就是比如有A喜欢{a,b,c}, B喜欢{a,c,d}, 那么可能向A推荐是一个合适的选择. 其中当然也用到了一些概率论的东西, 比如马尔可夫, 我很想尝试一下实现这个方法, 可惜我现在只有我一个人的豆瓣电台历史记录, 不能测试基于用户的协同过滤, 所以我只实现了Item-based的推荐算法.

由于原始数据的局限性, 豆瓣提供的标签比较杂乱(毕竟都是由用户添加的), 因此第一步是对标签的标准化处理, 对建立的14个音乐属性, 我建立了相应的14个文件, 其中存储的是一些常见的标签, 比如对于流行音乐, 豆瓣上常见的标签有:
txt
这个虽然比较人肉, 但是我觉得不管做什么总得有些基本的东西要做的比较朴素, 如果一开始就想着非常fancy的做法, 比如我一开始总是在考虑自动归类的事情, 可能一两个礼拜以后还没跨出第一步.

在有这个基本的标准标签库之后, 接着开始处理那些不常见的标签的问题, 我的思路是, 对于同一张专辑的标签, 他们之间应该是有一定的联系的, 比如说对于这张专辑桜の木になろう(初回限定盤Type-B)(DVD付), 它下面的标签是AKB48, J-Pop, 日本, JPOP, 桜の木になろう,2011, 日本音乐,日语, 虽然AKB48不在基本的标签库里, 然而因为与它同属一张专辑的其他标签很多都可以在属于'日本'的基本标签库里找到, 可以推测这个标签与日本是有一定的联系的.

顺着这个思路, 为每一个标签建立一个14维的矢量$V$, 分别对应14个类别的数值, 那么对于同一张专辑A下的标签集合$T$, 通过加入这张专辑对于其中每个标签的属性影响为 $\Delta = \frac{\sum\limits_{i=1}^NV_{t_i}}{\left| V \right|}$ , 在计算完所有的数据后, 需要对结果矢量进行normalize, 以成比例的形式保存结果, normalization的过程为, $V'_i=1000*\frac{V_i-Min}{Max-Min}$

再计算完标签的属性后, 有一件事情可以做, 就是可以调整基本标签库, 因为在这个时候, 已经可以对所有标签以各个维度进行排序, 从而将一些以前漏掉的常见标签加入到标准标签库里, 从而提供之后的算法效果.

接下来比较直接, 就是根据专辑中每个标签的属性计算出该专辑的属性, 可以简单的用求平均值的方法, 同样的也是建立14维的矢量. 再计算完每个专辑的属性之后, 就可以进行推荐算法的实现了. 推荐算法的基础是计算专辑向量与目标向量之间的相似度, 因为有着14维的矢量, 数据总体来说是比较稀疏的, 毕竟音乐不可能同时属于很多类别, 比较常见的情况是 其中几项很高, 而其他项目很低. 对于这种情况, 在开头提到的书中建议采用的是Cosine Similarity, 在测试过Minkowski Distance以及Cosine similarity的效果之后, 发现后者在这种情况下的确比前者好了非常多.

现在存在的问题是每次查询都需要向数据库取全部的数据, 效率非常的低, 接下来一段时间主要就解决查询效率上的问题了, 这个问题解决了再去考虑根据历史播放记录以及加星曲目对于推荐算法的优化.

=====更新======
后来想起来只要如果每次没有更换参数就可以利用之前的询问, 这样子的话只要缓存就可以了, 并且总体数据库也可以缓存在服务器上, 这样其实每次最多只是一个排序的复杂度, 试了一下, 已经达到了期望的效果.