推荐系统实践 0x0b 矩阵分解
前言
推荐系统实践那本书基本上就更新到上一篇了,之后的内容会把各个算法拿来当专题进行讲解。在这一篇,我们将会介绍矩阵分解这一方法。一般来说,协同过滤算法(基于用户、基于物品)会有一个比较严重的问题,那就是头部效应。热门的物品容易跟大量的物品产生相似性,而尾部的物品由于特征向量系数很少产生与其他物品的相似性,也就很少被推荐。
矩阵分解算法
为了解决这个问题,矩阵分解算法在协同过滤算法中共现矩阵的基础上加入了隐向量的概念,也是为了增强模型处理稀疏矩阵的能力。物品和用户的隐向量是通过分解协同过滤的共现矩阵得到的。矩阵分解的主要方法有三种,特征值分解、奇异值分解以及梯度下降。特征值分解主要用于方阵,而用户-物品矩阵并不一定是方阵,所以不太适用。而奇异值分解通过保留对角矩阵较大元素的方式,对矩阵进行分解比较完美的解决了矩阵分解的问题,但是计算复杂度到达了\(O(mn^2)\)的级别,在显示业务场景当中显然是无法使用的。所以梯度下降成为了矩阵分解的主要方法。
梯度下降
有过深度学习基础的同学肯定对梯度下降不陌生,介绍梯度下降的博文也是数不胜数,这里给出一篇博文作为参考,不再赘述。这里梯度下降所需要优化的目标函数是
\]
其中,\(r_ui\)是用户\(u\)对物品\(i\)的评分,用户向量为\(p_u\),物品向量为\(q_i\)。
比较有趣的一点是,由于不同用户的打分体系不一样,我们还需要消除用户和物品打分的品茶,通常的做法是加入用户和物品的偏差向量:
\]
其中,\(\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 矩阵分解的更多相关文章
- SVD++:推荐系统的基于矩阵分解的协同过滤算法的提高
1.背景知识 在讲SVD++之前,我还是想先回到基于物品相似的协同过滤算法.这个算法基本思想是找出一个用户有过正反馈的物品的相似的物品来给其作为推荐.其公式为:
- HAWQ + MADlib 玩转数据挖掘之(四)——低秩矩阵分解实现推荐算法
一.潜在因子(Latent Factor)推荐算法 本算法整理自知乎上的回答@nick lee.应用领域:"网易云音乐歌单个性化推荐"."豆瓣电台音乐推荐"等. ...
- RS:关于协同过滤,矩阵分解,LFM隐语义模型三者的区别
项亮老师在其所著的<推荐系统实战>中写道: 第2章 利用用户行为数据 2.2.2 用户活跃度和物品流行度的关系 [仅仅基于用户行为数据设计的推荐算法一般称为协同过滤算法.学术界对协同过滤算 ...
- 推荐系统之矩阵分解及C++实现
1.引言 矩阵分解(Matrix Factorization, MF)是传统推荐系统最为经典的算法,思想来源于数学中的奇异值分解(SVD), 但是与SVD 还是有些不同,形式就可以看出SVD将原始的评 ...
- 【RS】Matrix Factorization Techniques for Recommender Systems - 推荐系统的矩阵分解技术
[论文标题]Matrix Factorization Techniques for Recommender Systems(2009,Published by the IEEE Computer So ...
- 推荐系统之矩阵分解及其Python代码实现
有如下R(5,4)的打分矩阵:(“-”表示用户没有打分) 其中打分矩阵R(n,m)是n行和m列,n表示user个数,m行表示item个数 那么,如何根据目前的矩阵R(5,4)如何对未打分的商品进行评分 ...
- 矩阵分解(Matrix Factorization)与推荐系统
转自:http://www.tuicool.com/articles/RV3m6n 对于矩阵分解的梯度下降推导参考如下:
- 推荐系统(recommender systems):预测电影评分--构造推荐系统的一种方法:低秩矩阵分解(low rank matrix factorization)
如上图中的predicted ratings矩阵可以分解成X与ΘT的乘积,这个叫做低秩矩阵分解. 我们先学习出product的特征参数向量,在实际应用中这些学习出来的参数向量可能比较难以理解,也很难可 ...
- 推荐系统之矩阵分解(MF)
一.矩阵分解 1.案例 我们都熟知在一些软件中常常有评分系统,但并不是所有的用户user人都会对项目item进行评分,因此评分系统所收集到的用户评分信息必然是不完整的矩阵.那如何跟据这个不完整矩阵中已 ...
随机推荐
- Java基础知识(三)
一.hashCode 与 equals (重要) 面试官可能会问你:"你重写过 hashcode 和 equals 么,为什么重写equals时必须重写hashCode方法?" 1 ...
- MIT 6.S081 Lab5 Copy-On-Write Fork
前言 最近绝大多数的空闲时间都拿来锤15-445了,很久没动6.S081.前几天回头看了一下一个月前锤完的Lazy Allocation,自己写的代码几乎都不认识了.......看来总结之类的东西最好 ...
- SVG--D3--血缘关系树
最近的工作与可视化有关,有展示血缘关系树的需求 ,类似于这样: 碰巧搜到 D3(用于可视化的js库,作者吕之华),瞬间无法自拔,它的树状图功能基于SVG.js ,暴露的可操作入口也简洁恰当,能帮助你快 ...
- 【SpringBoot】03.SpringBoot整合Servlet的两种方式
SpringBoot整合Servlet的两种方式: 1. 通过注解扫描完成Servlet组件注册 新建Servlet类继承HttpServlet 重写超类doGet方法 在该类使用注解@WebServ ...
- 巧用IDM工具 快捷下载ASTER GDEM v3高程数据
ASTER GDEM v3是NASA推出的30米高清DEM,覆盖了几乎全部的地球陆地.那么,在NASA官网怎么下载ASTER GDEM v3的地形高程数据呢? 首先,你需要注册一个nasa的账号 注册 ...
- 判断浏览器,还在用userAgent吗,你out了
以下内容摘自http://www.cnblogs.com/rubylouvre/archive/2009/10/14/1583362.html //2010 4 16日更新 ie678 = !+&qu ...
- Azure Data Factory(五)Blob Storage 密钥管理问题
一,引言 之前讲解的ADF 集成Azure DevOps 实现CI/CD,在 Releases Pipeline 阶段,我们是将两个 Blob Storage 的链接字符串复制.粘贴到 "O ...
- 源码分析:CountDownLatch 之倒计时门栓
简介 CountDownLatch 是JDK1.5 开始提供的一种同步辅助工具,它允许一个或多个线程一直等待,直到其他线程执行的操作完成为止.在初始化的时候给定 CountDownLatch 一个计数 ...
- 在 macOS 中使用 Podman
原文链接:https://fuckcloudnative.io/posts/use-podman-in-macos/ Podman 是一个无守护程序与 Docker 命令兼容的下一代 Linux 容器 ...
- Windows SMBv3 CVE-2020-0796 漏洞分析和l漏洞复现
0x00 漏洞描述 漏洞公告显示,SMB 3.1.1协议中处理压缩消息时,对其中数据没有经过安全检查,直接使用会引发内存破坏漏洞,可能被攻击者利用远程执行任意代码.攻击者利用该漏洞无须权限即可实现远 ...