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

综合编程 2018-05-16 阅读原文

数据链接: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行。
userIdmovieIdratingtimestamp
9999967162682.51065579370
10000067162694.01065149201
10000167163654.01070940363
10000267163852.51070979663
10000367165653.51074784724

筛选movies_df中的特征

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

将ratings_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[['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
ratings_df = pd.merge(ratings_df, movies_df, on='movieId')
#根据movieId,合并rating_df和movie_df
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[['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)
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)
#用来显示标量信息
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)

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

稀土掘金

责编内容by:稀土掘金阅读原文】。感谢您的支持!

您可能感兴趣的

VSCode的Python扩展下程序运行的几种方式与环境变量管理... 在VSCode中编写Python程序时,由于有些地方要使用环境变量,但是发现设置的环境变量有时不起作用,花了点时间研究了一下,过程不表,直接说结论。 首先,环境变量的设置,Python扩展中有三种方式: 直接设置系统环境...
Taking eBPF (bcc-tools) for a quick spin in RHEL 7... I was made aware that eBPF was available now in RHEL via the below tweet from Brendan Gregg. As our main distro is RHEL ...
Sorting and grouping several dictionaries into pyt... I have an unknown number of dictionaries each identified by a specific code.All of the values are create dynami...
造轮子寻找UAF漏洞 前言 轮子的核心是使用 Frida 来实现的。 菜鸡的心得,大神骂的时候温柔点,谢谢^_^ 由于本人没有做过漏洞挖掘(目前苦逼码农),也没有研究过漏洞原理,就只是听说过大概有两种内存漏洞,一种...
Traveling salesman portrait in Python Last week, Antonio S. Chinchón made an interesting post showing how to create a traveling salesman portrait in R...