2018年10月

第一步:收集和清洗数据
数据链接: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行。
复制代码结果:
| |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.csv文件
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

将movies_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 = 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

根据movieId,合并rating_df和movie_df
ratings_df = pd.merge(ratings_df, movies_df, on='movieId')
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 = 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)
复制代码
注:如果数据出现较多的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)
#用来显示标量信息
复制代码结果:

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)
https://juejin.im/post/5afbfe316fb9a07aa5427d73

sudo apt-get install openssl
sudo apt-get install libssl-dev
sudo apt-get install zlib1g-dev
sudo apt-get install librtmp-dev

安装librtmp
wget -S https://pypi.python.org/packages/48/a6/33b1a5864e22de3e59dd29fcbc2602462511c04a31057b16baec639d7d4f/python-librtmp-0.3.0.tar.gz#md5=f7afe8d463214072281998fa845539271

tar -zxvf python-librtmp-0.3.0.tar.gz1

cd python-librtmp-0.3.01

python setup.py install

解决MacOS升级后出现xcrun: error: invalid active developer path, missing xcrun的问题

解决方法,重装xcode command line:

xcode-select --install
如果没有解决问题,执行以下命令

sudo xcode-select -switch /

TensorFlow、SciKit-learn、Theano、Keras