Шаг 1. Соберите и очистите данные
Канал передачи данных:группа Lens.org/datasets/mo…
Скачать файл: 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 |
Импорт файла фильмов.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 |
Замените movieId в movie_df номером строки
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 |
Функции фильтра в movie_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 |
Объединить rating_df и movie_df на основе movieId
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 |
Шаг 2: Создайте рейтинг матрицы рейтинга фильма и запись матрицы рейтинга.
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]])
Шаг 3: Постройте модель
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对一个损失变量基本上做两件事
# 它计算相对于模型参数的损失梯度。
# 然后应用计算出的梯度来更新变量。
Шаг 4: Обучите модель
# 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=./ в терминал
Шаг 5: Оцените модель
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
Шаг 6. Создайте полноценную систему рекомендаций фильмов
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)