基于神经网络和协同过滤的电影推荐实验报告
一、实验目的
神经网络和协同过滤的简单实现。 运行程序并显示推荐结果。尽可能地减小推荐结果的错误率。
二、实验内容和原理
1.深度学习推荐系统基础
演化图谱
2.NerualCF
2.1 结构框架
1. Input Layer 将用户和物品识别的稀疏向量二值化,其中: 物品(i): 1表示用户u与物品(i)发生过交互 用户(u): 识别用户
2. Embedding layer 是一个全连接层,将稀疏表示投影到稠密向量。得到的用户/物品嵌入是潜在的用户/物品向量。
3. Neural CF layers使用多层神经架构将潜在向量映射到预测得分。
4. The final output layer通过最小化逐点损失/成对损失来返回预测得分。
2.2 特点
特点
- 新模型:基于两种嵌入:用户向量和项目向量,特征可以相互交叉,并且很容易添加新层,从而根据需要增加或减少模型的复杂度。
缺点
三、实验步骤
1. 安装库
1.1 Tensorflow安装
Python版本3.6.5
用以下代码安装tensotflow。
pip install tensorflow==2.0.0 --user -i http://pypi.douban.com/simple --trusted-host pypi.douban.com
1.2 其他库
import pandas as pd
import numpy as np
import tensorflow as tf
2 开始实验
2.1 数据读取和处理
数据读取
- 更改数据地址目录
- 构建电影评分矩阵和用户评分矩阵
# 加载文件和查看文件
ratings_df = pd.read_csv('ml/ratings.csv')
ratings_df.tail()
movies_df = pd.read_csv('ml/movies.csv')
movies_df.tail()
# 添加行号信息:因为movies表中的movieID远大于行号,如果使用movieID的最大值来构建评分表,那么评分表是一个非常大的稀疏矩阵,浪费内存,所以使用行号标志电影
movies_df['movieRow'] = movies_df.index
movies_df.tail()
'''
创建两个矩阵
1. 创建电影评分矩阵rating:用于记录每个用户对每个电影的评分
2. 创建用户是否评分矩阵record:如果评分是1,否则为0
'''
# 首先筛选movies_df中的特征
movies_df = movies_df[['movieRow', 'movieId', 'title']]
# 保存该处理后的数据
movies_df.to_csv('moviesProcessed.csv', index=False, header=True, encoding='utf-8')
movies_df.tail()
# 将ratings_df中的movieId替换为行号
ratings_df = pd.merge(ratings_df, movies_df, on='movieId')
ratings_df.head()
# 筛选需要用到的特征
ratings_df = ratings_df[['movieRow', 'userId', 'rating']]
ratings_df.to_csv('ratingsProcessed.csv', index=False, header=True, encoding='utf-8')
ratings_df.head()
# 1. 创建电影评分矩阵rating:用于记录每个用户对每个电影的评分
#****************************************************************************************
# 获取用户最大编号:作为rating矩阵中的列
userNo = ratings_df['userId'].max()+1
# 获取电影最大编号:作为rating矩阵中的行
movieNo = ratings_df['movieRow'].max()+1
# 创建rating矩阵
# 全部初始化为0
rating = np.zeros((movieNo, userNo))
# 创建电影评分表:添入rating矩阵
flag = 0 # 记录处理进度
ratings_df_length = np.shape(ratings_df)[0] # ratings_df的样本个数
# 将ratings_df中的数据填写到rating中
for index, row in ratings_df.iterrows():
rating[int(row['movieRow']), int(row['userId'])] = row['rating'] # 将row中的评分rating,填入rating中的电影编号和用户编号
flag += 1 # 处理完一行
print('processed %d, %d left' % (flag, ratings_df_length-flag)) #
# 2. 创建用户是否评分矩阵record:如果已经评分是1,否则为0
#****************************************************************************************
# 在电影评分表中,为0代表未评分
record = rating > 0
# 因为record中是布尔值组成的矩阵,将其转化为0和1
record = np.array(record, dtype=int)
2.2 模型构建
# 对评分取值范围进行缩放
# 定义函数:接受两个参数:电影评分表,评分记录表
def normalizeRating (rating, record):
m, n = rating.shape # m电影数,n用户数
# 每个电影每个用户的评分平均值
rating_mean = np.zeros((m, 1)) # 所有电影平均评分初始化为0
rating_norm = np.zeros((m, n)) # 保存处理之后的数据
for i in range(m): # 将原始评分减去平均评分,将结果和平均评分返回
idx = record[i, :] != 0 # 已评分的电影对应的用户下标
rating_mean[i] = np.mean(rating[i, idx]) # 记录这些评分的平均值,第i部电影
rating_norm[i, idx] -= rating_mean[i] # 原始评分减去评分的平均值
return rating_norm, rating_mean
rating_norm, rating_mean = normalizeRating(rating, record) # 结果提示有全0数据,需处理
rating_norm = np.nan_to_num(rating_norm) # 将nan数据转换为0
rating_mean = np.nan_to_num(rating_mean) # 将nan数据转换为0
num_features = 10 # 假设有10种类型的电影
# 初始化电影内容矩阵X,产生的每个参数都是随机数且正态分布
X_parameters = tf.Variable(tf.random.normal([movieNo, num_features], stddev=0.35))
# 初始化用户喜好矩阵theta,产生的每个参数都是随机数且正态分布
Theta_parameters = tf.Variable(tf.random.normal([userNo, num_features], stddev=0.35))
定义损失函数和参数矩阵
此处我将学习率调整为10^-3,得到结果误差降低
# 定义代价函数loss function:tf.reduce_sum求和,tf.matmul相乘,transpose_b=True转置b项,
#填空:参照实验手册中的公式补充loss function
#****************************************************************************************
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))
# 后面部分是正则化项,lambda为1,可以调整lambda来观察模型性能变化
tf.compat.v1.disable_eager_execution()
# 创建adam优化器和优化目标
optimizer = tf.compat.v1.train.AdamOptimizer(10**-3) # 学习速率10^-3
train = optimizer.minimize(loss) # 目标:最小化代价函数
# train = tf.compat.v1.train.GradientDescentOptimizer(0.0001).minimize(loss)
2.3 模型训练
# 由于loss值是标量,所以要用summary中的scalar
tf.compat.v1.summary.scalar('loss', loss)
# 将所有summary信息汇总
summaryMerged = tf.compat.v1.summary.merge_all()
# 创建tensorflow会话
sess = tf.compat.v1.Session()
init = tf.compat.v1.global_variables_initializer()
sess.run(init)
# 训练模型,训练次数5000
for i in range(5000):
movie_summary = sess.run([train, summaryMerged]) # 记录每次迭代的loss的变化,每次train训练的结果保存到_中
# #writer.add_summary(movie_summary, i) # 训练后保存数据,代价值随着迭代次数i的变化情况
2.4 模型评估
# 测试不同的num_features的值,通过比较误差,判断哪个num_features的值最合适
# 使用前面得到的参数,填满电影评分表
# 获取当前X和theta
Current_X_parameters, Current_Theta_parameters = sess.run([X_parameters, Theta_parameters])
# 将电影内容矩阵和用户喜好矩阵相乘,再加上每一行的均值,得到一个完整的电影评分表
# dot用于矩阵之间的乘法操作
predicts = np.dot(Current_X_parameters, Current_Theta_parameters.T) + rating_mean
# 计算预测值与真实值之间的算数平方根作为预测误差
#填空错误计算公式,结果越小越好
#****************************************************************************************
errors = np.sqrt(np.sum((predicts - rating)**2))
errors
2.5 推荐
# 获取用户ID,并保存
user_id = input('您要向哪位用户进行推荐?请输入用户编号:')
# 获取对该用户电影评分的列表
# 预测出的用户对电影的评分,并从大到小排序
sortedResult = predicts[:, int(user_id)].argsort()[::-1]
# 向该用户推荐评分最高的20部电影
idx = 0 # 保存已经推荐了多少部电影
print('为该用户推荐的评分最高的20部电影是'.center(80, '='))
# 开始推荐
#填空输出推荐内容
#****************************************************************************************
for i in sortedResult:
print('评分:%.2f, 电影名:%s' % (predicts[i, int(user_id)], movies_df.iloc[i]['title']))
idx += 1 # 已经推荐的电影
if idx == 20: break
四、结果展示
可以看到推荐结果的错误率从之前的4000附近降低到2000附近,效果更好。
推荐的结果也相较于之前更有针对性。