电影推荐系统python实现:基于矩阵分解的协同过滤算法

基于矩阵分解的协同过滤算法

在推荐系统中,我们经常看到如下图的表格,表格中的数字代表用户对某个物品的评分,0代表未评分。我们希望能够预测目标用户对物品的评分,进而根据评分高低,将分高的物品推荐给用户。

y物品1物品2物品3物品4物品5
用户155011
用户250411
用户310155
用户411040

基于矩阵分解的协同过滤算法正好能解决这个问题。

基于矩阵分解的协同过滤算法思想

基于矩阵分解的协同过滤算法通常都会构造如下图所示评分表y,这里我们以电影为例:

y电影1电影2电影3电影4电影5
用户155011
用户250411
用户310155
用户411040

我们认为,有很多因素会影响到用户给电影评分,如电影内容:感情戏,恐怖元素,动作成分,推理悬疑等等。假设我们现在想预测用户2对电影2的评分,用户2他很喜欢看动作片与推理悬疑,不喜欢看感情戏与恐怖的元素,而电影2只有少量的感情戏与恐怖元素,大部分都是动作与推理的剧情,则用户2对电影2评分可能很高,比如5分。

基于上面的设想,我们只要知道所有用户对电影内容各种因素喜欢程度与所有电影内容的成分,就能预测出所有用户对所有电影的评分了。
若只考虑两种因素则用户喜好表与电影内容表如下:

用户喜好表x

x因素1因素2
用户150
用户250
用户305
用户405

值越大代表用户越喜欢某种因素。

电影内容表:w:

w电影1电影2电影3电影4电影5
因素10.91.00.990.10
因素200.0101.00.9

值越大代表电影中某因素内容越多。

用户2对电影2评分为: 5 × 1.0 + 0 × 0.01 = 5.0 5\times1.0+0\times0.01=5.0 5×1.0+0×0.01=5.0

对于所有用户,我们可以将矩阵x与矩阵w相乘,得到所有用户对所有电影的预测评分如下表:

xw电影1电影2电影3电影4电影5
用户14.55.04.950.50
用户24.55.04.950.50
用户300.05054.5
用户400.05054.5

假设电影评分表y(为mn列的矩阵),我们考虑d种因素,则电影评分表可以分解为用户喜好表x(为md列的矩阵),与电影内容表w(为dn列的矩阵)。其中d为超参数,大小由我们自己定。

基于矩阵分解的协同过滤算法思想为:一个用户评分矩阵可以分解为一个用户喜好矩阵与内容矩阵,我们只要能找出正确的用户喜好矩阵参数与内容矩阵参数(即表内的值),就能对用户评分进行预测,再根据预测结果对用户进行推荐。

基于矩阵分解的协同过滤算法原理

将用户喜好矩阵与内容矩阵进行矩阵乘法就能得到用户对物品的预测结果,而我们的目的是预测结果与真实情况越接近越好。所以,我们将预测值与评分表中已评分部分的值构造平方差损失函数:

l o s s = 1 2 ∑ ( i , j ) ∈ r ( i , j ) = 1 ( ∑ l = 1 d x i l w l j − y i j ) 2 loss = \frac{1}{2}\sum\limits_{(i,j)\in r(i,j)=1}(\sum\limits_{l=1}^dx_{il}w_{lj}-y_{ij})^2 loss=21(i,j)r(i,j)=1(l=1dxilwljyij)2

其中:

i:第i个用户
j:第j个物品
d:第d种因素
x:用户喜好矩阵
w:内容矩阵
y:评分矩阵
r:评分记录矩阵,无评分记为0,有评分记为1。r(i,j)=1代表用户i对物品j进行过评分,r(i,j)=0代表用户i对物品j未进行过评分

损失函数python实现代码如下:

import numpy as np
loss = np.sum(np.multiply((y-np.dot(x,w))**2,record))

其中,record为评分记录矩阵。

我们的目的就是最小化平方差损失函数,通常机器学习都是使用梯度下降的方法来最小化损失函数得到正确的参数。

对每个参数求得偏导如下:

∂ l o s s ∂ x i k = ∑ j ∈ r ( i , j ) = 1 ( ∑ l = 1 d x i l w l j − y i j ) w k j \frac{\partial loss}{\partial x_{ik}} = \sum\limits_{j\in r(i,j)=1}(\sum\limits_{l=1}^dx_{il}w_{lj}-y_{ij})w_{kj} xikloss=jr(i,j)=1(l=1dxilwljyij)wkj

∂ l o s s ∂ w k j = ∑ i ∈ r ( i , j ) = 1 ( ∑ l = 1 d x i l w l j − y i j ) x i k \frac{\partial loss}{\partial w_{kj}} = \sum\limits_{i\in r(i,j)=1}(\sum\limits_{l=1}^dx_{il}w_{lj}-y_{ij})x_{ik} wkjloss=ir(i,j)=1(l=1dxilwljyij)xik

则梯度为:

Δ x = r . ( x w − y ) w T \Delta x = r.(xw-y)w^T Δx=r.(xwy)wT

Δ w = x T [ ( x w − y ) . r ] \Delta w = x^T[(xw-y).r] Δw=xT[(xwy).r]

其中:

.表示点乘法,无则表示矩阵相乘
上标T表示矩阵转置

梯度python代码如下:

x_grads = np.dot(np.multiply(record,np.dot(x,w)-y),w.T)
w_grads = np.dot(x.T,np.multiply(record,np.dot(x,w)-y))

然后再进行梯度下降:

#梯度下降,更新参数
for i in range(n_iter):
	x_grads = np.dot(np.multiply(record,np.dot(x,w)-y),w.T)
	w_grads = np.dot(x.T,np.multiply(record,np.dot(x,w)-y))
    x = alpha*x - lr*x_grads
    w = alpha*w - lr*w_grads

其中:

n_iter:训练轮数
lr:学习率
alpha:权重衰减系数,用来防止过拟合

基于矩阵分解的协同过滤算法流程

1.随机初始矩阵值
2.构造损失函数,求得矩阵参数梯度
3.进行梯度下降,更新矩阵参数值
4.喜好矩阵与内容矩阵相乘得到预测评分
5.根据预测评分进行推荐

电影推荐系统实现

电影评分数据

本次使用电影评分数据为672个用户对9123部电影的评分记录,部分数据如下:

userIdmovieRowrating
1302.5
7303
31304
32304

其中:

userId:用户编号
movieRow:电影编号
rating:评分值

如:

  • 第一行数据表示用户1对电影30评分为2.5分。
  • 第二行数据表示用户7对电影30评分为3分。

然后,我们还有电影编号与电影名字对应的数据如下:

movieRowtitle
0Toy Story (1995)
1Jumanji (1995)
2Grumpier Old Men (1995)
3Waiting to Exhale (1995)

其中:

movieRow:电影编号
title:电影名称

构造用户-电影评分矩阵

大家已经知道,要使用基于矩阵分解的协同过滤算法,首先得有用户与电影评分的矩阵,而我们实际中的数据并不是以这样的形式保存,所以在使用算法前要先构造出用户-电影评分矩阵,python实现代码如下:

import numpy as np
#获取用户数与电影数
userNo = max(ratings_df['userId'])+1
movieNo = max(ratings_df['movieRow'])+1

#创建电影评分表
rating = np.zeros((userNo,movieNo))
for index,row in ratings_df.iterrows():
    rating[int(row['userId']),int(row['movieRow'])]=row['rating']

构造出表格后,我们就能对用户进行电影推荐了。

python代码

# -*- coding: utf-8 -*-

import warnings
warnings.filterwarnings("ignore")

import pandas as pd
import numpy as np


#获取数据
ratings_df = pd.read_csv('real_ratings.csv')
movies_df = pd.read_csv('movies.csv')

userNo = max(ratings_df['userId'])+1
movieNo = max(ratings_df['movieRow'])+1


#创建电影评分表
rating = np.zeros((userNo,movieNo))

for index,row in ratings_df.iterrows():
    rating[int(row['userId']),int(row['movieRow'])]=row['rating']

def recommend(userID,lr,alpha,d,n_iter,data):
    '''
    userID(int):推荐用户ID
    lr(float):学习率
    alpha(float):权重衰减系数
    d(int):矩阵分解因子(即元素个数)
    n_iter(int):训练轮数
    data(ndarray):用户-电影评分矩阵
    ''' 
    #获取用户数与电影数
    m,n = data.shape 
    #初始化参数  
    x = np.random.uniform(0,1,(m,d))
    w = np.random.uniform(0,1,(d,n))
    #创建评分记录表,无评分记为0,有评分记为1
    record = np.array(data>0,dtype=int)
    #梯度下降,更新参数           
    for i in range(n_iter):
        x_grads = np.dot(np.multiply(record,np.dot(x,w)-data),w.T)
        w_grads = np.dot(x.T,np.multiply(record,np.dot(x,w)-data))
        x = alpha*x - lr*x_grads
        w = alpha*w - lr*w_grads
    #预测
    predict = np.dot(x,w)
    #将用户未看过的电影分值从低到高进行排列
    for i in range(n):
        if record[userID-1][i] == 1 :
            predict[userID-1][i] = 0 
    recommend = np.argsort(predict[userID-1])
    a = recommend[-1]
    b = recommend[-2]
    c = recommend[-3]
    d = recommend[-4]
    e = recommend[-5]
    print('为用户%d推荐的电影为:\n1:%s\n2:%s\n3:%s\n4:%s\n5:%s。'\
          %(userID,movies_df['title'][a],movies_df['title'][b],movies_df['title'][c],movies_df['title'][d],movies_df['title'][e]))   

recommend(666,1e-4,0.999,20,100,rating) 
>>>
为用户666推荐的电影为:
1:Aquamarine (2006)
2:It's a Boy Girl Thing (2006)
3:Kill the Messenger (2014)
4:Onion Field, The (1979)
5:Wind Rises, The (Kaze tachinu) (2013)

[数据下载:提取码:p8u0]( 链接 :https://pan.baidu.com/s/1R87yrW7bcXSEo6ztXxhqpA )

评论 12
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值