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

第一步:收集和清洗数据

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

下载文件:ml-latest-small

 

import pandas as pd
import numpy as np
import tensorflow as tf

导入ratings.csv文件

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

结果:

userIdmovieIdratingtimestamp
9999967162682.51065579370
10000067162694.01065149201
10000167163654.01070940363
10000267163852.51070979663
10000367165653.51074784724

导入movies.csv文件

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

结果:

movieIdtitlegenres
9120162672Mohenjo Daro (2016)Adventure|Drama|Romance
9121163056Shin Godzilla (2016)Action|Adventure|Fantasy|Sci-Fi
9122163949The Beatles: Eight Days a Week - The Touring Y...Documentary
9123164977The Gay Desperado (1936)Comedy
9124164979Women of '69, UnboxedDocumentary

将movies_df中的movieId替换为行号

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

结果:

movieIdtitlegenresmovieRow
9120162672Mohenjo Daro (2016)Adventure|Drama|Romance9120
9121163056Shin Godzilla (2016)Action|Adventure|Fantasy|Sci-Fi9121
9122163949The Beatles: Eight Days a Week - The Touring Y...Documentary9122
9123164977The Gay Desperado (1936)Comedy9123
9124164979Women of '69, UnboxedDocumentary9124

筛选movies_df中的特征

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

结果:

movieRowmovieIdtitle
91209120162672Mohenjo Daro (2016)
91219121163056Shin Godzilla (2016)
91229122163949The Beatles: Eight Days a Week - The Touring Y...
91239123164977The Gay Desperado (1936)
91249124164979Women of '69, Unboxed

根据movieId,合并rating_df和movie_df

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

结果:

userIdmovieIdratingtimestampmovieRowtitle
01312.5126075914430Dangerous Minds (1995)
17313.085186875030Dangerous Minds (1995)
231314.0127354195330Dangerous Minds (1995)
332314.083482844030Dangerous Minds (1995)
436313.084705720230Dangerous Minds (1995)

筛选ratings_df中的特征

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

结果:

userIdmovieRowrating
01302.5
17303.0
231304.0
332304.0
436303.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)

注:如果数据出现较多的NaNN,对后面的运算影响较大

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时,按行。
# 6) 注意,reduction_indices与axis不能同时设置。

# tf.matmul(a,b),将矩阵 a 乘以矩阵 b,生成a * b
# tf.matmul(a,b)参数解释:
# 1) a:类型为 float16,float32,float64,int32,complex64,complex128 和 rank > 1的张量。
# 2) b:与 a 具有相同类型和 rank。
# 3) transpose_a:如果 True,a 在乘法之前转置。
# 4) transpose_b:如果 True,b 在乘法之前转置。
# 5) adjoint_a:如果 True,a 在乘法之前共轭和转置。
# 6) adjoint_b:如果 True,b 在乘法之前共轭和转置。
# 7) a_is_sparse:如果 True,a 被视为稀疏矩阵。
# 8) b_is_sparse:如果 True,b 被视为稀疏矩阵。
# 9) 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)

更多Python视频、源码、资料加群725638078免费获取
转载至:https://juejin.im/post/5afbfe316fb9a07aa5427d73
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

posted @ 2022-10-07 20:56  I'm_江河湖海  阅读(152)  评论(0编辑  收藏  举报