协同过滤推荐算法-音乐推荐

Music Recommendation based on Collaborative Filtering

具体代码参见 CollaborativeFiltering_MusicRecommendation

1. 任务目标与系统背景

1.1 协同过滤系统(Collaborative filtering systems)

计算用户或者项之间的相似性来推荐项,与某用户相似的用户所喜欢的项会推荐给该用户。

1.2 效用矩阵(Utility Matrix)

  • 两类元素:用户(user)、项(item);
  • 矩阵中每个“用户-项”所对应的值代表当前用户对此项的喜好程度;
  • 通常该矩阵是稀疏的;
  • 推荐系统的目标是预测矩阵的空白元素;
  • 实践中,只需给出每一行中某些评级可能较高的元素即可。

1.3 任务目标

  • 基于UV分解,建立协同过滤模型(矩阵分解的代码要自己编写);
  • 在user_artist_data中,预留20%的数据,作为验证集;
  • 计算模型对验证集进行预测的结果的RMSE。

2. 数据集简介

  • user_artist_data.txt:2420万条用户播放艺术家歌曲次数
  • artist_data.txt: 160+万个艺术家的ID和名字
  • artist_alias.txt: 拼写错误的艺术家ID(变体)到该艺术家的规范ID的映射关系(19万条记录)

3. 算法介绍

基于模型的协同过滤算法:

矩阵因子分解将项和用户都转化成相同的潜在空间,即用户偏好矩阵分解成一个用户-潜在因子矩阵乘以一个潜在因子-项矩阵,代表用户和项之间的潜相互作用。

\left[\begin{array}{lllll}{5} & {2} & {4} & {4} & {3} \\ {3} & {1} & {2} & {4} & {1} \\ {2} & {?} & {3} & {1} & {4} \\ {2} & {5} & {4} & {3} & {5} \\ {4} & {4} & {5} & {4} & {?}\end{array}\right]=\left[\begin{array}{ll}{u_{11}} & {u_{12}} \\ {u_{21}} & {u_{22}} \\ {u_{31}} & {u_{32}} \\ {u_{41}} & {u_{42}} \\ {u_{51}} & {u_{52}}\end{array}\right] \times\left[\begin{array}{ccccc}{v_{11}} & {v_{12}} & {v_{13}} & {v_{14}} & {v_{15}} \\ {v_{21}} & {v_{22}} & {v_{23}} & {v_{24}} & {v_{25}} \end{array}\right]

如上例,即将5*5矩阵分解为两个5*2矩阵和2*5矩阵,k=2即为潜在因子数。

评价指标:
均方根误差:Root-mean-square error (RMSE)
\sqrt{ \frac1N \sum_{xi}(r_{xi} - \hat{r_{xi}} )^2 }

则矩阵因子分解算法的目标为最小化RMSE,或最小化
\sum{e^2} = \sum_{xi}(r_{xi} - \hat{r_{xi}} )^2

3.1 UV 分解(增量式计算)

将原始评分矩阵M_{m*n}分解为U_{m*d}、V_{d*n},两个矩阵。即有:
M_{m*n} \approx U_{m*d}*V_{d*n} = P_{m*n}
其中d为潜在因子,为超参数,取值通常与项的种类数有关。

求解UV

u_{rs}=x进行变化,使得𝑀和𝑈𝑉间𝑅𝑀𝑆𝐸最小:
p_{rj} = \sum^{d}_{k=1} u_{rk}v_{kj}
该元素对误差平方和贡献为:
(m_{rj}-p_{rj})^2 = ( m_{rj} - \sum_{k\neq s}u_{rk}v_{kj} - xv_{sj} )^2
对所有非空𝑚_{𝑟𝑗}在𝑗上求和:
\sum_{j} ( m_{rj} - \sum_{k\neq s}u_{rk}v_{kj} - xv_{sj} )^2
对上式求导并令其等于0,整理得:
x = \frac{ \sum_{j} v_{sj}( m_{rj} - \sum_{k\neq s}u_{rk}v_{kj}) } { \sum_{j} v_{sj}^2 }

类似的,对𝑣_{𝑟𝑠}=𝑦进行改变,则使RMSE最小的𝑦值:
y=\frac{\sum_{i} u_{i r}\left(m_{i s}-\sum_{k \neq r} u_{i k} v_{k s}\right)}{\sum_{i} u_{i r}^{2}}

显然,对U、V对遍历修改复杂度很高。
当分别先后计算U、V矩阵时,单个矩阵内当不同行(列)间当计算互不影响,故可以考虑并行化加速计算。

3.2 梯度下降法(Gradient descent)

寻找 P_{m*k}、Q_{k*n} 满足:
M_{m*n} \approx P_{m*k}*Q_{k*n} = {\hat{M}}_{m*n}
与3.1具体求解方式不同。

定义损失函数:
e_{i j}^{2}=\left(r_{i j}-\hat{r}_{i j}\right)^{2}=\left(r_{i j}-\sum_{k=1}^{K} p_{i k} q_{k j}\right)^{2}

为了防止过拟合,加入(L2)正则化项:
E_{i, j}^{2}=\left(r_{i, j}-\sum_{k=1}^{K} p_{i, k} q_{k, j}\right)^{2}+\frac{\beta}{2} \sum_{k=1}^{K}\left(p_{i, k}^{2}+q_{k, j}^{2}\right)

使用梯度下降法获得修正的p和q分量:

求解损失函数的负梯度:
\begin{array}{l}{\frac{\partial}{\partial p_{i, k}} E_{i, j}^{2}=-2\left(r_{i, j}-\sum_{k=1}^{K} p_{i, k} q_{k, j}\right) q_{k, j}+\beta p_{i, k}=-2 e_{i, j} q_{k, j}+\beta p_{i, k}} \\ {\frac{\partial}{\partial q_{k, j}} E_{i, j}^{2}=-2\left(r_{i, j}-\sum_{k=1}^{K} p_{i, k} q_{k, j}\right) p_{i, k}+\beta q_{k, j}=-2 e_{i, j} p_{i, k}+\beta q_{k, j}}\end{array}

根据负梯度的方向更新变量:
\begin{array}{l}{p_{i, k}^{\prime}=p_{i, k}-\alpha\left(\frac{\partial}{\partial p_{i, k}} e_{i, j}^{2}+\beta p_{i, k}\right)=p_{i, k}+\alpha\left(2 e_{i, j} q_{k, j}-\beta p_{i, k}\right)} \\ {q_{k, j}^{\prime}=q_{k, j}-\alpha\left(\frac{\partial}{\partial q_{k, j}} e_{i, j}^{2}+\beta q_{k, j}\right)=q_{k, j}+\alpha\left(2 e_{i, j} p_{i, k}-\beta q_{k, j}\right)}\end{array}
其中p_{i, k}^{\prime},q_{k, j}^{\prime}即为更新后对值。

迭代直到算法最终收敛。

3.3 *ALS 算法

4. 算法实现与过程优化

本项目分别实现了UV分解算法梯度下降法

4.0 *数据预处理

  1. 首先统一解析别名artist_alias中的对应关系,对训练数据进行预处理。

  2. 考虑到训练集对应评分矩阵为10+w*100+w量级的巨大稀疏矩阵,故按照(坐标,评分)的形式key-value对存储为Map,以节约存储空间。(实验表明,按矩阵存储可能引起内存爆炸💥)

  3. 评分标准化。数据集中的score即为用户对特定歌曲对播放次数。
    考虑到用户听音乐时可能发生的长时间单曲循环,或由于忘记关闭导致对大量播放对矩阵产生对影响;同时避免归一化降低音乐发烧友对音乐的评价影响力,算法中对用户听歌次数对数化作为用户评分。

4.1 UV分解增量计算算法实现

以计算矩阵U为例,根据公式,在计算u_{rj}时,会重复使用分母\sum_{j} v_{sj}^2,为了减少重复计算,可先计算出对不同s(s in K)。

down_sum_vsj = [ sum( [ (v[s,j])**2 for j in range(n) ] ) for s in range(K) ]

此任务为计算密集型任务,为了提高计算效率,考虑多进程并行化计算(由于GIL限制,不推荐python多线程并发)。

if __name__ == "__main__":
    import multiprocessing
    pal = 5
    pool = multiprocessing.Pool(processes = pal)

    # cal U
    for r in range(0,m,pal):
        pool.map(cal_U, range(r, min(r+pal,m)) )
        print( f"processing {r}~{r+10} in U completed.", end="\r" )

上例代码即为 5个进程同时并发计算,每次计算5行的代码事例,其中cal_U函数的功能为计算一行数据。

4.2 梯度下降算法实现

根据3.2中迭代更新公式,对矩阵中有效位置进行遍历,计算与有效为相关的P、Q矩阵相关向量,核心迭代公式为:

p[i,:] = p[i,:] + a*(eij*q[:,j] - b*p[i,:])
q[:,j] = q[:,j] + a*(eij*p[i,:] - b*q[:,j])

对模型学习率动态调整测试,由下图可见10轮迭代后收敛。


代价-累计学习率

5. 模型评价

4.1 UV分解增量计算法

由于算法计算复杂度过高,在有限时间内未完成训练。

4.2 梯度下降法

对测试集每一项,通过P、Q对应向量内积求得评分 s ,再求 pre=2^s ,则pre即为预测听歌次数,对应于原始数据集。根据 3 中RMSE的计算,求得对20%测试集的RMSE为:
RMSE = 75.39

推荐结果

以第100000位用户为例,该用户最喜欢的艺术家为:

sample = { key[1]:value for key, value in test.items() if user_id_index[key[0]] == 100000 }

sample
{'1004226': 1, '1151014': 1, '1278': 13, '4267': 28, '5833': 8}

即id为4267的艺术家,根据artist_data,该艺术家为'Green Day':
绿日(Green Day),美国朋克乐队,2013年,获得第20届MTV欧洲音乐奖最佳摇滚艺人。2015年,入驻第30届美国摇滚名人堂。 -- 百度百科
为知名摇滚乐队。

对第100000位用户的推荐结果为56275号艺术家'Kettcar':
Kettcar,德国摇滚乐队。 -- 酷狗音乐

可见,该模型在一定程度上成功为用户推荐了喜欢的摇滚领域的艺术家。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,222评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,455评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,720评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,568评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,696评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,879评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,028评论 3 409
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,773评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,220评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,550评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,697评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,360评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,002评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,782评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,010评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,433评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,587评论 2 350

推荐阅读更多精彩内容