当前位置:   article > 正文

基于协同过滤算法使用Tensorflow构建一套电影推荐系统_使用协同过滤建构简单的电影

使用协同过滤建构简单的电影

现在在互联网的时代,你会发现有很多智能的推荐系统,比如说商品的推荐,歌曲的推荐,电影的推荐。在推荐系统中,协同过滤算法是诞生最早的,也是很常用的推荐算法。有句古话:物以类聚,人以群分。推荐算法的思想就是找到和你相似的人,也就是口味相同的人,把他喜欢的物品或者电影歌曲推荐给你;或者是将你买过的或者喜欢的物品,电影或者歌曲推荐给你;还有就是可能将你搜索过的物品、电影或者歌曲推荐给你;还有可能就是综合上述的算法做推荐。
我们来看看协同过滤算法的流程吧:

1.收集用户的喜好

这个步骤其实就是大数据的收集处理,这些数据其实反映了用户的某些偏好,通过分析用户的行为评分,基于用户所有的数据来做出推荐。
首先我们拿到了用户的数据之后要对数据进行处理,处理的依据主要可以分为:

  • 将不同的行为分组:举个商城商品的栗子,有些用户是购买了商品,有些用户是重复浏览某类型的商品,有的用户是收藏了某商品,所以说现在淘宝、京东、亚马逊这类大型的电商都有类似的推荐,比如说购买此商品的用户还购买了某某商品,浏览了此商品的用户还浏览了某某商品。
  • 将用户的行为进行加权操作:对于不同的用户操作,当然对数据的结果有着不同的影响,我们可以想象一下,购买的操作和浏览的操作显然可以看出用户对于商品的喜爱程度不一样,那么在进行数据处理的时候,我们可以加大用户对于购买商品的权重,而对于假如购物车的商品和浏览的商品给予一个相对较低的权重。
    在做完以上的处理之后,我们还需要对数据进行一些预处理,主要的操作就是减噪和归一化。在我们获取的数据中,有许多数据是在非正常的情况下产生的,因此在我们得到的用户数据中,有许多的噪声,所以我们减噪的目的就是让数据更加纯净,让我们在后续的数据分析更加精确。还有一个处理就是归一化,因为我们获取的数据可能来源于不同的系统,所以数据的样式参差不齐,我们的加权操作就没办法进行,我们需要把数据统一标准,将数据归一化。
2.查找相似的用户或者商品

协同过滤(collaborative filtering)算法主要有两个分支,基于用户的协同过滤(user-based collaborative filtering)和基于商品的协同过滤(item-based collaborative filtering)。如何查找相似的用户和商品,这里就涉及到相似度的计算了,下面是几个计算相似度的方法:

  • 欧式距离:欧几里得距离(Euclidean Distance)在二维平面里可以表示为:
    d ( x , y ) = ( x 1 − x 2 ) 2 + ( y 1 − y 2 ) 2 d(x,y) = \sqrt {{{({x_1} - {x_2})}^2} + {{({y_1} - {y_2})}^2}} d(x,y)=(x1x2)2+(y1y2)2
    这个很容易理解,扩展到n维空间可以表示成:
    d ( x , y ) = ∑ ( x i − y i ) 2 d(x,y) = \sqrt {\sum {{{({x_i} - {y_i})}^2}} } d(x,y)=(xiyi)2
    如果用欧式距离表示相似度,则可以用以下公式表示:
    s i m ( x , y ) = 1 1 + d ( x , y ) sim(x,y) = \frac{1}{{1 + d(x,y)}} sim(x,y)=1+d(x,y)1
    由上述公式可以知道,欧式距离越小,相似度就越大。
  • 余弦相似:一般在文本处理中,可以将语料分词向量化之后进行两个向量的余弦计算,来判断两个文本的相似程度,当两个向量正交的时候,余弦值为0,表示两个文本的相似度低,余弦值越接近1表示两个文本的相似度越高,反之余弦值为越接近-1,表示两个文本越负相关。余弦相似度的计算公式如下:
    T ( x , y ) = x ⋅ y ∥ x ∥ 2 × ∥ y ∥ 2 = ∑ x i y i ∑ x i 2 ∑ y i 2 T(x,y) = \frac{{x \cdot y}}{{\parallel x{\parallel ^2} \times \parallel y{\parallel ^2}}} = \frac{{\sum {{x_i}{y_i}} }}{{\sqrt {\sum {{x_i}^2} } \sqrt {\sum {{y_i}^2} } }} T(x,y)=x2×y2xy=xi2 yi2 xiyi
    在上述式子中,||x||^2表示x的二范数,就是将向量的每个元素的平方和再开平方根。
  • 皮尔逊相关系数(Pearson Correlation Coefficient):皮尔逊相关系数是在机器学习中很常用的一个相关性指标,皮尔逊相关系数的公式是:
    C O R ( X , Y ) = ∑ 1 n ( X i − X ˉ ) ( Y i − Y ˉ ) ∑ 1 n ( X i − X ˉ ) 2 ∑ 1 n ( Y i − Y ˉ ) 2 COR(X,Y) = \frac{{\sum\limits_1^n {({X_i} - \bar X)({Y_i} - \bar Y)} }}{{\sqrt {\sum\limits_1^n {{{({X_i} - \bar X)}^2}\sum\limits_1^n {{{({Y_i} - \bar Y)}^2}} } } }} COR(X,Y)=1n(XiXˉ)21n(YiYˉ)2 1n(XiXˉ)(YiYˉ)
    由上述公式可以看出,皮尔逊相关系数其实就是协方差除以标准差得到的,取值范围是[-1,1],同理,皮尔逊相关系数越接近1就表示相关度越高,越接近-1,表示越负相关,越接近0,就表示相关度越低。
3.计算推荐

上面我们有说过协同过滤分成两种过滤方法,基于用户的和基于商品的。首先我们来说一下基于用户的协同过滤推荐方法:基于用户的推荐大体意思就是我们来找到一些相似性很高的用户,那么别人喜欢的商品也很有可能是你喜欢的商品,我们以此来作为依据推荐。还有一种就是基于商品的推荐,其实也很容易理解,类似于基于用户的协同过滤,这里就是寻找商品之间的关系,商品之间的相似度,关联度,以此来对用户做推荐,所以我们经常会在网购的时候发现一些推荐,比如购买了此商品的用户还购买了XXX,这就是基于商品协同过滤的推荐。


有了这些数学和理论的基础,我们就可以用TensorFlow来构建一套电影的推荐系统。

1.准备数据集

首先我们要准备数据集,数据集可以从电影数据集链接 来下载解压得到.csv文件。

2.数据的清洗

首先我们要导入Python的几个库:

import pandas as pd
import numpy as np
import tensorflow as tf
  • 1
  • 2
  • 3

然后是读取电影评分的ratings.csv文件

ratings_df = pd.read_csv('Desktop/ml-latest-small/ratings.csv')
  • 1

就可以查看这张电影评分表的内容了,我们来看一下这个表中最后几行的内容

ratings_df.tail()
  • 1

电影评分表的内容
同样,我们来读取电影库的.csv文件

movies_df = pd.read_csv('Desktop/ml-latest-small/movies.csv')
movies_df.tail()
  • 1
  • 2

我们来看看电影列表中的最后几行内容:
电影列表
增加一列名为movieRow的索引值,内容为行号:

movies_df['MovieRow'] = movies_df.index
movies_df.tail()
  • 1
  • 2

电影列表
相较于上一张图,我们可以看到最后多了一列索引值,和行号相等。

3.特征提取

我们清洗好了数据之后,就可以将数据进行特征提取,从而拿到我们需要的一些数据。

movies_df = movies_df[['MovieRow','movieId','title']]
movies_df.tail()
  • 1
  • 2

我们从电影列表中提取出"movieRow"、"movieId"和"title"三列数据,可以得到:特征提取后的电影列表
然后我们将这个列表作为处理好的数据储存起来方便后面用到:

movies_df.to_csv('Desktop/ml-latest-small/moviesProcessed.csv',index = False, header = True, encoding = 'utf-8')
  • 1

储存好了特征提取之后的数据之后,我们将评分列表和电影列表合并,并对其movieId这一列。

ratings_df = pd.merge(ratings_df, movies_df, on = 'movieId')
ratings_df.head()
  • 1
  • 2

这合并之后的列表
合并两张表之后,我们将合并好的表再来提取特征:

ratings_df = ratings_df[['userId','MovieRow','rating']]
ratings_df.head()
  • 1
  • 2

合并之后特征提取

4.创建电影评分矩阵rating和评分记录矩阵record

特征提取完成之后,首先我们来获取评分的用户和电影的数量:

userNo = ratings_df['userId'].max()+1
movieNo = ratings_df['MovieRow'].max()+1
  • 1
  • 2

接下来创建一个movieNo行,userNo列,内容是0的矩阵:

rating = np.zeros((movieNo,userNo))
  • 1

设置一个标签位flag,然后获取合并之后列表的列数,然后遍历rating矩阵每一个位置,将电影的评分填入矩阵中:

#标志位
flag = 0
#获取合并表中的列数
ratings_df_length = np.shape(ratings_df)[0]
#遍历矩阵,将电影的评分填入表中
for index,row in ratings_df.iterrows():
    rating[int(row['MovieRow']), int(row['userId'])] = row['rating']
    flag += 1
    print('processed %d, %d left' %(flag,ratings_df_length-flag))
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

然后我们来获取一个电影是否被用户评分的列表,其中1代表该电影已经被用户评分,0是用户没有对该电影评分:

record = rating > 0
record = np.array(record, dtype = int)
record
  • 1
  • 2
  • 3

可以得到结果矩阵:

array([[0, 0, 0, ..., 0, 1, 1],
       [0, 0, 0, ..., 0, 0, 0],
       [0, 0, 0, ..., 0, 0, 0],
       ...,
       [0, 0, 0, ..., 0, 0, 0],
       [0, 0, 0, ..., 0, 0, 0],
       [0, 0, 0, ..., 0, 0, 0]])
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

同时我们也可以得到评分的矩阵:

array([[0., 0., 0., ..., 0., 4., 5.],
       [0., 0., 0., ..., 0., 0., 0.],
       [0., 0., 0., ..., 0., 0., 0.],
       ...,
       [0., 0., 0., ..., 0., 0., 0.],
       [0., 0., 0., ..., 0., 0., 0.],
       [0., 0., 0., ..., 0., 0., 0.]])
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
5.构建模型

构建模型之前,我们来定义一个标准化评分表的方法:

def normalizeRatings(rating, record):
    #获取电影的数量m和用户的数量n
    m,n = rating.shape
    #rating_mean-电影平均分   rating_norm-标准化后的电影得分
    rating_mean = np.zeros((m,1))
    rating_norm = np.zeros((m,n))
    for i in range(m):
        idx = record[i,:]!=0
        rating_mean[i] = np.mean(rating[i,idx])
        rating_norm[i,idx] -= rating_mean[i]
    return rating_norm, rating_mean
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

然后我们对我们数据进行标准化的时候,会出现以下的问题:


/usr/local/lib/python3.6/site-packages/numpy/core/fromnumeric.py:2957: RuntimeWarning: Mean of empty slice.
  out=out, **kwargs)
/usr/local/lib/python3.6/site-packages/numpy/core/_methods.py:80: RuntimeWarning: invalid value encountered in double_scalars
  ret = ret.dtype.type(ret / rcount)
  • 1
  • 2
  • 3
  • 4
  • 5

这是因为在处理数据的时候,大量出现nan引起的,所以我们将nan的地方转成数字0:

rating_norm = np.nan_to_num(rating_norm)
  • 1

可以得到标准化后的矩阵:

array([[ 0.        ,  0.        ,  0.        , ...,  0.        ,
        -3.87246964, -3.87246964],
       [ 0.        ,  0.        ,  0.        , ...,  0.        ,
         0.        ,  0.        ],
       [ 0.        ,  0.        ,  0.        , ...,  0.        ,
         0.        ,  0.        ],
       ...,
       [ 0.        ,  0.        ,  0.        , ...,  0.        ,
         0.        ,  0.        ],
       [ 0.        ,  0.        ,  0.        , ...,  0.        ,
         0.        ,  0.        ],
       [ 0.        ,  0.        ,  0.        , ...,  0.        ,
         0.        ,  0.        ]])
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

同理我们计算电影平均的评分:

rating_mean = np.nan_to_num(rating_mean)
  • 1
array([[3.87246964],
       [3.40186916],
       [3.16101695],
       ...,
       [3.        ],
       [0.        ],
       [5.        ]])
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

接下来就是用TensorFlow来构建模型:

num_features = 10
X_parameters = tf.Variable(tf.random_normal([movieNo, num_features],stddev = 0.35))
Theta_parameters = tf.Variable(tf.random_normal([userNo, num_features],stddev = 0.35))
  • 1
  • 2
  • 3

我们可以根据基于内容推荐算法的损失函数公式:
J ( θ ) = 1 2 ∑ j = 1 u ∑ i , r ( i , j ) = 1 ( ( θ ( j ) ) T x i − y ( i , j ) ) 2 + λ 2 ∑ j = 1 u ∑ k = 1 n ( θ k ( j ) ) 2 J(\theta ) = \frac{1}{2}\sum\limits_{j = 1}^u {\sum\limits_{i,r(i,j) = 1} {{{({{({\theta ^{(j)}})}^T}{x^i} - {y^{(i,j)}})}^2} + \frac{\lambda }{2}\sum\limits_{j = 1}^u {\sum\limits_{k = 1}^n {{{(\theta _k^{(j)})}^2}} } } } J(θ)=21j=1ui,r(i,j)=1((θ(j))Txiy(i,j))2+2λj=1uk=1n(θk(j))2
在这个公式中,r(x,y)是评分记录表,u是用户数量,θ(j)是j用户的喜好,y(i,j)是i用户对j电影的评分,xi表示电影的内容,n是特征数量,最后一部分是正则化项。我们后续的目的就是来最小化这个损失函数,接下来就是优化J(θ),使其最小化。

loss = 1/2 * tf.reduce_sum(((tf.matmul(X_parameters, Theta_parameters, transpose_b=True) - rating_norm) * record) ** 2) + 1/2 * (tf.reduce_sum(X_parameters ** 2) + tf.reduce_sum(Theta_parameters ** 2)) 
optimizer = tf.train.AdamOptimizer(1e-4)
train = optimizer.minimize(loss)
  • 1
  • 2
  • 3

最后的步骤就是训练模型了:

tf.summary.scalar('loss', loss)
  • 1

结果:

<tf.Tensor 'loss:0' shape=() dtype=string>
  • 1

summaryMerged = tf.summary.merge_all()
#merge_all 可以将所有summary全部保存到磁盘,以便tensorboard显示。
filename = './movie_tensorborad'
writer = tf.summary.FileWriter(filename)
#指定一个文件用来保存图。
sess = tf.Session()
#定义一个session
init = tf.global_variables_initializer()
sess.run(init)
#运行session
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

接下来就是递归5000次,直到收敛:

for i in range(5000):
    _, movie_summary = sess.run([train, summaryMerged])
    writer.add_summary(movie_summary, i)
  • 1
  • 2
  • 3

模型训练完成之后,我们再来评估一下我们训练的模型:

Current_X_parameters, Current_Theta_parameters = sess.run([X_parameters, Theta_parameters])
# Current_X_parameters为用户内容矩阵,Current_Theta_parameters用户喜好矩阵
predicts = np.dot(Current_X_parameters,Current_Theta_parameters.T) + rating_mean
errors = np.sqrt(np.sum((predicts - rating)**2))
# sqrt(arr) ,计算各元素的平方根
errors
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

我得到的error为4045.411568958254。
最后我们可以尝试输出我们想要的电影推荐了:

userId = input('您要向哪位用户进行推荐呢?请输入用户编号:(smaller than 672)')
sortedResult = predicts[:, int(userId)].argsort()[::-1]
idx = 0
print('为该用户推荐的评分最高的20部电影是:'.center(80,'='))
for i in sortedResult:
    print('score: %.3f, movie name: %s' % (predicts[i, int(userId)], movies_df.iloc[i]['title']))
    idx += 1
    if idx == 20:break
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

我们可以输入用户的ID,然后就可以得到如下的电影推荐输出:

您要向哪位用户进行推荐呢?请输入用户编号:(smaller than 672)666
==============================为该用户推荐的评分最高的20部电影是:===============================
score: 5.421, movie name: Caveman (1981)
score: 5.116, movie name: Daniel Tosh: Completely Serious (2007)
score: 4.867, movie name: Ben X (2007)
score: 4.737, movie name: Pirate Movie, The (1982)
score: 4.721, movie name: G.O.R.A. (2004)
score: 4.692, movie name: Ben-hur (2016)
score: 4.674, movie name: Ricky Gervais Live 3: Fame (2007)
score: 4.655, movie name: Hands in the Air (2010)
score: 4.634, movie name: Robin Williams: Weapons of Self Destruction (2009)
score: 4.625, movie name: Ricky Gervais Live: Animals (2003)
score: 4.604, movie name: Frozen Planet (2011)
score: 4.595, movie name: The Last Days of Emma Blank (2009)
score: 4.556, movie name: And God Created Woman (Et Dieu... créa la femme) (1956)
score: 4.508, movie name: Absolute Giganten (1999)
score: 4.445, movie name: Paris, France (1993)
score: 4.429, movie name: Cutting Edge: The Magic of Movie Editing, The (2004)
score: 4.418, movie name: I'm No Angel (1933)
score: 4.390, movie name: Survive and Advance (2013)
score: 4.358, movie name: Alien Escape (1995)
score: 4.357, movie name: Pervert's Guide to Cinema, The (2006)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/weixin_40725706/article/detail/198626
推荐阅读
相关标签
  

闽ICP备14008679号