今天看啥  ›  专栏  ›  Kervin_Chan

基于Python和Tensorflow的电影推荐算法

Kervin_Chan  · 掘金  ·  · 2018-05-16 09:57

基于Python和Tensorflow的电影推荐算法

第一步:收集数据

数据链接:https://grouplens.org/datasets/movielens/

下载文件:ml-latest-small

import pandas as pd
import numpy as np
import tensorflow as tf
ratings_df = pd.read_csv('./ml-latest-small/ratings.csv')
#倒入ratings.csv文件
ratings_df.tail()
#tail命令用于输入文件中的尾部内容。tail命令默认在屏幕上显示指定文件的末尾5行。

结果:

userId movieId rating timestamp
99999 671 6268 2.5 1065579370
100000 671 6269 4.0 1065149201
100001 671 6365 4.0 1070940363
100002 671 6385 2.5 1070979663
100003 671 6565 3.5 1074784724

筛选movies_df中的特征

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

结果:

movieId title genres
9120 162672 Mohenjo Daro (2016) Adventure|Drama|Romance
9121 163056 Shin Godzilla (2016) Action|Adventure|Fantasy|Sci-Fi
9122 163949 The Beatles: Eight Days a Week - The Touring Y... Documentary
9123 164977 The Gay Desperado (1936) Comedy
9124 164979 Women of '69, Unboxed Documentary

将ratings_df中的movieId替换为行号

movies_df['movieRow'] = movies_df.index
#生成一列‘movieRow’,等于索引值index
movies_df.tail()

结果:

movieId title genres movieRow
9120 162672 Mohenjo Daro (2016) Adventure|Drama|Romance 9120
9121 163056 Shin Godzilla (2016) Action|Adventure|Fantasy|Sci-Fi 9121
9122 163949 The Beatles: Eight Days a Week - The Touring Y... Documentary 9122
9123 164977 The Gay Desperado (1936) Comedy 9123
9124 164979 Women of '69, Unboxed Documentary 9124
movies_df = movies_df[['movieRow','movieId','title']]
#筛选三列出来
movies_df.to_csv('./ml-latest-small/moviesProcessed.csv', index=False, header=True, encoding='utf-8')
#生成一个新的文件moviesProcessed.csv
movies_df.tail()

结果:

movieRow movieId title
9120 9120 162672 Mohenjo Daro (2016)
9121 9121 163056 Shin Godzilla (2016)
9122 9122 163949 The Beatles: Eight Days a Week - The Touring Y...
9123 9123 164977 The Gay Desperado (1936)
9124 9124 164979 Women of '69, Unboxed
ratings_df = pd.merge(ratings_df, movies_df, on='movieId')
#根据movieId,合并rating_df和movie_df
ratings_df.head()

结果:

userId movieId rating timestamp movieRow title
0 1 31 2.5 1260759144 30 Dangerous Minds (1995)
1 7 31 3.0 851868750 30 Dangerous Minds (1995)
2 31 31 4.0 1273541953 30 Dangerous Minds (1995)
3 32 31 4.0 834828440 30 Dangerous Minds (1995)
4 36 31 3.0 847057202 30 Dangerous Minds (1995)
ratings_df = ratings_df[['userId','movieRow','rating']]
#筛选出三列
ratings_df.to_csv('./ml-latest-small/ratingsProcessed.csv', index=False, header=True, encoding='utf-8')
#导出一个新的文件ratingsProcessed.csv
ratings_df.head()

结果:

userId movieRow rating
0 1 30 2.5
1 7 30 3.0
2 31 30 4.0
3 32 30 4.0
4 36 30 3.0

第二步:创建电影评分矩阵rating和评分纪录矩阵record

userNo = ratings_df['userId'].max() + 1
#userNo的最大值
movieNo = ratings_df['movieRow'].max() + 1
#movieNo的最大值
rating = np.zeros((movieNo,userNo))
#创建一个值都是0的数据
flag = 0
ratings_df_length = np.shape(ratings_df)[0]
#查看矩阵ratings_df的第一维度是多少
for index,row in ratings_df.iterrows():
    #interrows(),对表格ratings_df进行遍历
    rating[int(row['movieRow']),int(row['userId'])] = row['rating']
    #将ratings_df表里的'movieRow'和'userId'列,填上row的‘评分’
    flag += 1
record = rating > 0
record
record = np.array(record, dtype = int)
#更改数据类型,0表示用户没有对电影评分,1表示用户已经对电影评分
record

结果:

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]])

第三步:构建模型

def normalizeRatings(rating, record):
    m, n =rating.shape
    #m代表电影数量,n代表用户数量
    rating_mean = np.zeros((m,1))
    #每部电影的平均得分
    rating_norm = np.zeros((m,n))
    #处理过的评分
    for i in range(m):
        idx = record[i,:] !=0
        #每部电影的评分,[i,:]表示每一行的所有列
        rating_mean[i] = np.mean(rating[i,idx])
        #第i行,评过份idx的用户的平均得分;
        #np.mean() 对所有元素求均值
        rating_norm[i,idx] -= rating_mean[i]
        #rating_norm = 原始得分-平均得分
    return rating_norm, rating_mean
rating_norm, rating_mean = normalizeRatings(rating, record)
/root/anaconda2/envs/python3/lib/python3.6/site-packages/numpy/core/fromnumeric.py:2957: RuntimeWarning: Mean of empty slice.
  out=out, **kwargs)
/root/anaconda2/envs/python3/lib/python3.6/site-packages/numpy/core/_methods.py:80: RuntimeWarning: invalid value encountered in double_scalars
  ret = ret.dtype.type(ret / rcount)
rating_norm =np.nan_to_num(rating_norm)
#对值为NaNN进行处理,改成数值0
rating_norm

结果:

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.        ]])
rating_mean =np.nan_to_num(rating_mean)
#对值为NaNN进行处理,改成数值0
rating_mean
array([[3.87246964],
       [3.40186916],
       [3.16101695],
       ...,
       [3.        ],
       [0.        ],
       [5.        ]])
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))
#tf.Variables()初始化变量
#tf.random_normal()函数用于从服从指定正太分布的数值中取出指定个数的值,mean: 正态分布的均值。stddev: 正态分布的标准差。dtype: 输出的类型
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))
#基于内容的推荐算法模型

函数解释:

reduce_sum() 就是求和,reduce_sum( input_tensor, axis=None, keep_dims=False, name=None, reduction_indices=None)

reduce_sum() 参数解释:

 1)input_tensor:输入的张量。
 2)axis:沿着哪个维度求和。对于二维的input_tensor张量,0表示按列求和,1表示按行求和,[0, 1]表示先按列求和再按行求和。
 3)keep_dims:默认值为Flase,表示默认要降维。若设为True,则不降维。
 4)name:名字。
 5)reduction_indices:默认值是None,即把input_tensor降到 0维,也就是一个数。对于2维input_tensor,reduction_indices=0时,按列;reduction_indices=1时,按行。
 注意,reduction_indices与axis不能同时设置。

tf.matmul(a,b),将矩阵 a 乘以矩阵 b,生成a * b

tf.matmul(a,b)参数解释:

 a:类型为 float16,float32,float64,int32,complex64,complex128 和 rank > 1的张量。
 b:与 a 具有相同类型和 rank。
 transpose_a:如果 True,a 在乘法之前转置。
 transpose_b:如果 True,b 在乘法之前转置。
 adjoint_a:如果 True,a 在乘法之前共轭和转置。
 adjoint_b:如果 True,b 在乘法之前共轭和转置。
 a_is_sparse:如果 True,a 被视为稀疏矩阵。
 b_is_sparse:如果 True,b 被视为稀疏矩阵。
 name:操作名称(可选)
optimizer = tf.train.AdamOptimizer(1e-4)
# https://blog.csdn.net/lenbow/article/details/52218551
train = optimizer.minimize(loss)
# Optimizer.minimize对一个损失变量基本上做两件事
# 它计算相对于模型参数的损失梯度。
# 然后应用计算出的梯度来更新变量。

第四步:训练模型

# tf.summary的用法 https://www.cnblogs.com/lyc-seu/p/8647792.html
tf.summary.scalar('loss',loss)
#用来显示标量信息
<tf.Tensor 'loss_1:0' shape=() dtype=string>
summaryMerged = tf.summary.merge_all()
#merge_all 可以将所有summary全部保存到磁盘,以便tensorboard显示。
filename = './movie_tensorborad'
writer = tf.summary.FileWriter(filename)
#指定一个文件用来保存图。
sess = tf.Session()
#https://www.cnblogs.com/wuzhitj/p/6648610.html
init = tf.global_variables_initializer()
sess.run(init)
#运行
for i in range(5000):
    _, movie_summary = sess.run([train, summaryMerged])
    # 把训练的结果summaryMerged存在movie里
    writer.add_summary(movie_summary, i)
    # 把训练的结果保存下来

查看训练结果

在终端输入 tensorboard --logir=./

第五步:评估模型

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
# dot函数是np中的矩阵乘法,np.dot(x,y) 等价于 x.dot(y)
errors = np.sqrt(np.sum((predicts - rating)**2))
# sqrt(arr) ,计算各元素的平方根
errors

结果:

4037.9002717628305

第六步:构建完整的电影推荐系统

user_id = input('您要想哪位用户进行推荐?请输入用户编号:')
sortedResult = predicts[:, int(user_id)].argsort()[::-1]
# argsort()函数返回的是数组值从小到大的索引值; argsort()[::-1] 返回的是数组值从大到小的索引值
idx = 0
print('为该用户推荐的评分最高的20部电影是:'.center(80,'='))
# center() 返回一个原字符串居中,并使用空格填充至长度 width 的新字符串。默认填充字符为空格。
for i in sortedResult:
    print('评分: %.2f, 电影名: %s' % (predicts[i,int(user_id)],movies_df.iloc[i]['title']))
    # .iloc的用法:https://www.cnblogs.com/harvey888/p/6006200.html
    idx += 1
    if idx == 20:break

结果:

您要想哪位用户进行推荐?请输入用户编号:123
==============================为该用户推荐的评分最高的20部电影是:===============================
评分: 5.03, 电影名: Fireworks Wednesday (Chaharshanbe-soori) (2006)
评分: 4.88, 电影名: Woman on the Beach (Haebyeonui yeoin) (2006)
评分: 4.73, 电影名: Mummy's Ghost, The (1944)
评分: 4.66, 电影名: Maborosi (Maboroshi no hikari) (1995)
评分: 4.63, 电影名: Boiling Point (1993)
评分: 4.60, 电影名: Mala Noche (1985)
评分: 4.49, 电影名: All-Star Superman (2011)
评分: 4.47, 电影名: Bill Hicks: Relentless (1992)
评分: 4.45, 电影名: Something Borrowed (2011)
评分: 4.37, 电影名: Box of Moon Light (1996)
评分: 4.37, 电影名: Kwaidan (Kaidan) (1964)
评分: 4.35, 电影名: Sacrifice, The (Offret - Sacraficatio) (1986)
评分: 4.29, 电影名: Hotel de Love (1996)
评分: 4.27, 电影名: Aria (1987)
评分: 4.23, 电影名: Querelle (1982)
评分: 4.22, 电影名: Rocky VI (1986) 
评分: 4.21, 电影名: Little Lord Fauntleroy (1936)
评分: 4.19, 电影名: Hardcore (1979)
评分: 4.16, 电影名: Three of Hearts (1993)
评分: 4.15, 电影名: White Stripes Under Great White Northern Lights, The (2009)

本文章内容来自慕课网,仅供学习,如有侵权,联系删除




原文地址:访问原文地址
快照地址: 访问文章快照