前言

推荐系统实践那本书基本上就更新到上一篇了,之后的内容会把各个算法拿来当专题进行讲解。在这一篇,我们将会介绍矩阵分解这一方法。一般来说,协同过滤算法(基于用户、基于物品)会有一个比较严重的问题,那就是头部效应。热门的物品容易跟大量的物品产生相似性,而尾部的物品由于特征向量系数很少产生与其他物品的相似性,也就很少被推荐。

矩阵分解算法

为了解决这个问题,矩阵分解算法在协同过滤算法中共现矩阵的基础上加入了隐向量的概念,也是为了增强模型处理稀疏矩阵的能力。物品和用户的隐向量是通过分解协同过滤的共现矩阵得到的。矩阵分解的主要方法有三种,特征值分解、奇异值分解以及梯度下降。特征值分解主要用于方阵,而用户-物品矩阵并不一定是方阵,所以不太适用。而奇异值分解通过保留对角矩阵较大元素的方式,对矩阵进行分解比较完美的解决了矩阵分解的问题,但是计算复杂度到达了\(O(mn^2)\)的级别,在显示业务场景当中显然是无法使用的。所以梯度下降成为了矩阵分解的主要方法。

梯度下降

有过深度学习基础的同学肯定对梯度下降不陌生,介绍梯度下降的博文也是数不胜数,这里给出一篇博文作为参考,不再赘述。这里梯度下降所需要优化的目标函数是

\[\min_{q^*,p^*}\sum_{u,i\in K}(r_{ui}-q_i^Tp_u)^2
\]

其中,\(r_ui\)是用户\(u\)对物品\(i\)的评分,用户向量为\(p_u\),物品向量为\(q_i\)。

比较有趣的一点是,由于不同用户的打分体系不一样,我们还需要消除用户和物品打分的品茶,通常的做法是加入用户和物品的偏差向量:

\[r_{ui}=\mu + b_i + b_u + q_i^T p_u
\]

其中,\(\mu\)是全局偏差常数,\(b_i\)是物品\(i\)的偏差系数,可以使用收到所有评分的均值,\(b_u\)是用户偏差系数,可以使用用户\(u\)给出所有评分的均值。

优缺点

优点:

  • 泛化能力强,一定程度解决了数据系数的问题。
  • 空间复杂度低。只需要存储用户和物品向量,空间复杂度降低到了\((m+n)k\)的级别。
  • 更好地扩展性。最终的输出是用户和物品的隐向量,所以可以很好地拼接到深度学习当中。

    缺点:
  • 没有考虑用户、物品、上下文的特征。
  • 缺乏用户历史行为时无法进行推荐。

实验

我们在动漫推荐数据集上来实现以下矩阵分解中梯度下降的算法,数据集已经给出来了。下面给出相对应的代码:

#library imports
import numpy as np
import pandas as pd
from collections import Counter
from sklearn.model_selection import train_test_split
from scipy import sparse lmbda = 0.0002 def encode_column(column):
""" Encodes a pandas column with continous IDs"""
keys = column.unique()
key_to_id = {key: idx for idx, key in enumerate(keys)}
return key_to_id, np.array([key_to_id[x] for x in column]), len(keys) def encode_df(anime_df):
"""Encodes rating data with continuous user and anime ids""" anime_ids, anime_df['anime_id'], num_anime = encode_column(
anime_df['anime_id'])
user_ids, anime_df['user_id'], num_users = encode_column(
anime_df['user_id'])
return anime_df, num_users, num_anime, user_ids, anime_ids def create_embeddings(n, K):
"""
Creates a random numpy matrix of shape n, K with uniform values in (0, 11/K)
n: number of items/users
K: number of factors in the embedding
"""
return 11 * np.random.random((n, K)) / K def create_sparse_matrix(df, rows, cols, column_name="rating"):
""" Returns a sparse utility matrix"""
return sparse.csc_matrix((df[column_name].values,
(df['user_id'].values, df['anime_id'].values)),
shape=(rows, cols)) def predict(df, emb_user, emb_anime):
""" This function computes df["prediction"] without doing (U*V^T). Computes df["prediction"] by using elementwise multiplication of the corresponding embeddings and then
sum to get the prediction u_i*v_j. This avoids creating the dense matrix U*V^T.
"""
df['prediction'] = np.sum(np.multiply(emb_anime[df['anime_id']],
emb_user[df['user_id']]),
axis=1)
return df def cost(df, emb_user, emb_anime):
""" Computes mean square error"""
Y = create_sparse_matrix(df, emb_user.shape[0], emb_anime.shape[0])
predicted = create_sparse_matrix(predict(df, emb_user,
emb_anime), emb_user.shape[0],
emb_anime.shape[0], 'prediction')
return np.sum((Y - predicted).power(2)) / df.shape[0] def gradient(df, emb_user, emb_anime):
""" Computes the gradient for user and anime embeddings"""
Y = create_sparse_matrix(df, emb_user.shape[0], emb_anime.shape[0])
predicted = create_sparse_matrix(predict(df, emb_user,
emb_anime), emb_user.shape[0],
emb_anime.shape[0], 'prediction')
delta = (Y - predicted)
grad_user = (-2 / df.shape[0]) * (delta * emb_anime) + 2 * lmbda * emb_user
grad_anime = (-2 / df.shape[0]) * (delta.T *
emb_user) + 2 * lmbda * emb_anime
return grad_user, grad_anime def gradient_descent(df,
emb_user,
emb_anime,
iterations=2000,
learning_rate=0.01,
df_val=None):
"""
Computes gradient descent with momentum (0.9) for given number of iterations.
emb_user: the trained user embedding
emb_anime: the trained anime embedding
"""
Y = create_sparse_matrix(df, emb_user.shape[0], emb_anime.shape[0])
beta = 0.9
grad_user, grad_anime = gradient(df, emb_user, emb_anime)
v_user = grad_user
v_anime = grad_anime
for i in range(iterations):
grad_user, grad_anime = gradient(df, emb_user, emb_anime)
v_user = beta * v_user + (1 - beta) * grad_user
v_anime = beta * v_anime + (1 - beta) * grad_anime
emb_user = emb_user - learning_rate * v_user
emb_anime = emb_anime - learning_rate * v_anime
if (not (i + 1) % 50):
print("\niteration", i + 1, ":")
print("train mse:", cost(df, emb_user, emb_anime))
if df_val is not None:
print("validation mse:", cost(df_val, emb_user, emb_anime))
return emb_user, emb_anime def encode_new_data(valid_df, user_ids, anime_ids):
""" Encodes valid_df with the same encoding as train_df.
"""
df_val_chosen = valid_df['anime_id'].isin(
anime_ids.keys()) & valid_df['user_id'].isin(user_ids.keys())
valid_df = valid_df[df_val_chosen]
valid_df['anime_id'] = np.array(
[anime_ids[x] for x in valid_df['anime_id']])
valid_df['user_id'] = np.array([user_ids[x] for x in valid_df['user_id']])
return valid_df anime_ratings_df = pd.read_csv("../dataset/anime/rating.csv")
print(anime_ratings_df.shape)
print(anime_ratings_df.head()) anime_ratings = anime_ratings_df.loc[
anime_ratings_df.rating != -1].reset_index()[[
'user_id', 'anime_id', 'rating'
]]
print(anime_ratings.shape)
anime_ratings.head() print(Counter(anime_ratings.rating)) # Average number of ratings per user
print(np.mean(anime_ratings.groupby(['user_id']).count()['anime_id'])) train_df, valid_df = train_test_split(anime_ratings, test_size=0.2) # resetting indices to avoid indexing errors in the future
train_df = train_df.reset_index()[['user_id', 'anime_id', 'rating']]
valid_df = valid_df.reset_index()[['user_id', 'anime_id', 'rating']] anime_df, num_users, num_anime, user_ids, anime_ids = encode_df(train_df)
print("Number of users :", num_users)
print("Number of anime :", num_anime)
anime_df.head() Y = create_sparse_matrix(anime_df, num_users, num_anime) # to view matrix
Y.todense() emb_user = create_embeddings(num_users, 3)
emb_anime = create_embeddings(num_anime, 3)
emb_user, emb_anime = gradient_descent(anime_df,
emb_user,
emb_anime,
iterations=800,
learning_rate=1) print("before encoding:", valid_df.shape)
valid_df = encode_new_data(valid_df, user_ids, anime_ids)
print("after encoding:", valid_df.shape) train_mse = cost(train_df, emb_user, emb_anime)
val_mse = cost(valid_df, emb_user, emb_anime)
print(train_mse, val_mse)

现在实验还在跑着,等出了结果就贴在这里。

参考

动漫推荐数据集

矩阵分解实现

推荐系统实践 0x0b 矩阵分解的更多相关文章

  1. SVD++:推荐系统的基于矩阵分解的协同过滤算法的提高

    1.背景知识 在讲SVD++之前,我还是想先回到基于物品相似的协同过滤算法.这个算法基本思想是找出一个用户有过正反馈的物品的相似的物品来给其作为推荐.其公式为:

  2. HAWQ + MADlib 玩转数据挖掘之(四)——低秩矩阵分解实现推荐算法

    一.潜在因子(Latent Factor)推荐算法 本算法整理自知乎上的回答@nick lee.应用领域:"网易云音乐歌单个性化推荐"."豆瓣电台音乐推荐"等. ...

  3. RS:关于协同过滤,矩阵分解,LFM隐语义模型三者的区别

    项亮老师在其所著的<推荐系统实战>中写道: 第2章 利用用户行为数据 2.2.2 用户活跃度和物品流行度的关系 [仅仅基于用户行为数据设计的推荐算法一般称为协同过滤算法.学术界对协同过滤算 ...

  4. 推荐系统之矩阵分解及C++实现

    1.引言 矩阵分解(Matrix Factorization, MF)是传统推荐系统最为经典的算法,思想来源于数学中的奇异值分解(SVD), 但是与SVD 还是有些不同,形式就可以看出SVD将原始的评 ...

  5. 【RS】Matrix Factorization Techniques for Recommender Systems - 推荐系统的矩阵分解技术

    [论文标题]Matrix Factorization Techniques for Recommender Systems(2009,Published by the IEEE Computer So ...

  6. 推荐系统之矩阵分解及其Python代码实现

    有如下R(5,4)的打分矩阵:(“-”表示用户没有打分) 其中打分矩阵R(n,m)是n行和m列,n表示user个数,m行表示item个数 那么,如何根据目前的矩阵R(5,4)如何对未打分的商品进行评分 ...

  7. 矩阵分解(Matrix Factorization)与推荐系统

    转自:http://www.tuicool.com/articles/RV3m6n 对于矩阵分解的梯度下降推导参考如下:

  8. 推荐系统(recommender systems):预测电影评分--构造推荐系统的一种方法:低秩矩阵分解(low rank matrix factorization)

    如上图中的predicted ratings矩阵可以分解成X与ΘT的乘积,这个叫做低秩矩阵分解. 我们先学习出product的特征参数向量,在实际应用中这些学习出来的参数向量可能比较难以理解,也很难可 ...

  9. 推荐系统之矩阵分解(MF)

    一.矩阵分解 1.案例 我们都熟知在一些软件中常常有评分系统,但并不是所有的用户user人都会对项目item进行评分,因此评分系统所收集到的用户评分信息必然是不完整的矩阵.那如何跟据这个不完整矩阵中已 ...

随机推荐

  1. POJ2432 Around the world

    题意描述 Around the world 在一个圆上有 \(n\) 点,其中有 \(m\) 条双向边连接它们,每条双向边连接两点总是沿着圆的最小弧连接. 求从 \(1\) 号点出发并回到 \(1\) ...

  2. .netcore中的依赖注入

    IOC.DI相关概念的理解 1.依赖:简单的讲就是"引用到".例如AccountController.cs引用到IAccountService.cs,那么AccountContro ...

  3. 关于Java中泛型、反射和注解的扫盲篇

    泛型 泛型概念   泛型是在JDK1.5之后引入的,旨在让我们写出更加通用化,更加灵活的代码.通用化的手段在于让数据类型变得参数化,定义泛型时,对应的数据类型是不确定的,泛型方法被调用时,会指定具体类 ...

  4. Python 列表的11个重要操作

    列表是python中内置的数据结构,它的表现形式为方括号中不同数据的集合,用逗号分隔开.列表可以用来存储相同数据类型或不同数据类型. 列表是可变的,这也是它如此常用的原因,然而在某些情况下,可变性需要 ...

  5. Ideas and Tricks Part II

    33.对于统计答案幂次的技巧 对于$x^k$,考虑其组合意义:将$k$个不同球放到$x$个不同的盒子里的方案数,直接维护不好维护,那么考虑枚举把这些球放到了哪些盒子里,最后乘上第二类斯特林数和对于的阶 ...

  6. layui table表格详解

    上次做table有些东西 忘记了 这次当作来个分析总结一下  跟大家共同学习 闲话不多说 直接上例子   代码: <form id="form1" runat="s ...

  7. TCP/IP协议与Socket

    1.计算机网络体系结构分层 OSI 参考模型注重"通信协议必要的功能是什么", TCP/IP 则更强调"在计算机上实现协议应该开发哪种程序". 2.TCP/IP ...

  8. 设计师建筑师太难了,既要学BIM、无人机,还要学GIS!

    我,一个平平无奇的城市规划专业(建筑专业.路桥专业)大学生,还有一年要毕业,很担心工作以后受到社会的毒打,遂问导师和学长,我要自学点什么技能和软件? 学长A:CAD,SketchUp,PS我都很熟练了 ...

  9. reids 入门

    1.reids 服务的安装有两种 1.1 exe文件安装,安装完成后,就直接在 "服务"列表中可以查看,并可以停止或启动 1.2 命令行安装:将文件解压至指定文件夹,CMD命令进入 ...

  10. npm的命令参数 --save-dev和 --save两者有什么区别?

    我们在安装npm包的时候经常会遇到 --save-dev 和 --save 这两个命令参数,两个命令都是往package.json文件里写入信息,两者有什么区别呢? 1. --save 会把依赖包名称 ...