爱共享 爱生活 加油 2021

​百度网盘​

提取码:qhhv 

 

概述

个性化的推荐系统被应用于互联网生活中的方方面面,本篇文章中,将会利用电影评分数据,实现一个简单的电影个性化推荐系统,并实现推荐系统相关的协同过滤算法(Collaborative Filter Learning Algorithm)。这些电影评分数据集的评分范围为1-5,数据集中包含着943个用户(用儒猿技术窝Spring顶尖高手进阶:102讲带你实 战互联网教育系统项目_数据集表示)和1682部电影(用儒猿技术窝Spring顶尖高手进阶:102讲带你实 战互联网教育系统项目_数据集_02表示 )。

电影评分数据集的处理

这些电影评分数据集被保存在一个​​ex8_movies.mat​​的文件中,数据集中矩阵儒猿技术窝Spring顶尖高手进阶:102讲带你实 战互联网教育系统项目_推荐系统_03表示电影评分数据,用儒猿技术窝Spring顶尖高手进阶:102讲带你实 战互联网教育系统项目_数据_04表示,其值范围是​​(from 1 to 5)​​,其含义是第儒猿技术窝Spring顶尖高手进阶:102讲带你实 战互联网教育系统项目_协同过滤算法_05个用户对第儒猿技术窝Spring顶尖高手进阶:102讲带你实 战互联网教育系统项目_数据集_06个电影的评分数数据,其大小为儒猿技术窝Spring顶尖高手进阶:102讲带你实 战互联网教育系统项目_数据集_07,矩阵儒猿技术窝Spring顶尖高手进阶:102讲带你实 战互联网教育系统项目_数据_08是一个二进制值的标识矩阵,如果儒猿技术窝Spring顶尖高手进阶:102讲带你实 战互联网教育系统项目_数据集_09表示第儒猿技术窝Spring顶尖高手进阶:102讲带你实 战互联网教育系统项目_数据集_10个用户对第儒猿技术窝Spring顶尖高手进阶:102讲带你实 战互联网教育系统项目_损失函数_11部的电影进行了评价,若是为0,则表示没有该用户没有对此电影做出评价。协同过滤算法的适用目标是为用户还未做出评价的电影预测评分,也就是被儒猿技术窝Spring顶尖高手进阶:102讲带你实 战互联网教育系统项目_推荐系统_12所标识的数据,这样能够使得算法能够将预测评分最高电影推荐给用户。

为了了解数据集,可以先求出对第一个电影的评分的均值,并利用​​python​​的绘图功能绘出所有评分数据的颜色分布,用以可视化的显示相关数据。具体实现代码如下所示:



#导入相关库
import matplotlib.pyplot as plt
import numpy as np
import scipy.io as scio
import scipy.optimize as opt

# 加载数据
data = scio.loadmat('ex8_movies.mat')
Y = data['Y']
R = data['R']
#求出第一个电影的平均值
average = np.mean(Y[0,np.where(R[0]==1)])
#可视化显示
plt.figure()
plt.imshow(Y)
plt.colorbar()
plt.xlabel('Users')
plt.ylabel('Movies')


最后,以上代码数据结果如下图所示:

 




 


儒猿技术窝Spring顶尖高手进阶:102讲带你实 战互联网教育系统项目_推荐系统_13


 




 


儒猿技术窝Spring顶尖高手进阶:102讲带你实 战互联网教育系统项目_数据集_14


 


除了电影评分数据集中的矩阵儒猿技术窝Spring顶尖高手进阶:102讲带你实 战互联网教育系统项目_推荐系统_15和矩阵儒猿技术窝Spring顶尖高手进阶:102讲带你实 战互联网教育系统项目_数据_16外,对于特征矩阵儒猿技术窝Spring顶尖高手进阶:102讲带你实 战互联网教育系统项目_推荐系统_17和参数矩阵儒猿技术窝Spring顶尖高手进阶:102讲带你实 战互联网教育系统项目_数据集_18有如解释:

矩阵儒猿技术窝Spring顶尖高手进阶:102讲带你实 战互联网教育系统项目_数据集_19儒猿技术窝Spring顶尖高手进阶:102讲带你实 战互联网教育系统项目_数据集_20的形式如下图所示:




 


儒猿技术窝Spring顶尖高手进阶:102讲带你实 战互联网教育系统项目_数据_21


 


 

儒猿技术窝Spring顶尖高手进阶:102讲带你实 战互联网教育系统项目_推荐系统_22矩阵的第儒猿技术窝Spring顶尖高手进阶:102讲带你实 战互联网教育系统项目_数据_23行对应着第儒猿技术窝Spring顶尖高手进阶:102讲带你实 战互联网教育系统项目_数据_24部电影的特征向量儒猿技术窝Spring顶尖高手进阶:102讲带你实 战互联网教育系统项目_数据集_25,而参数矩阵的第儒猿技术窝Spring顶尖高手进阶:102讲带你实 战互联网教育系统项目_推荐系统_26行对应着第儒猿技术窝Spring顶尖高手进阶:102讲带你实 战互联网教育系统项目_数据集_27个用户的参数向量儒猿技术窝Spring顶尖高手进阶:102讲带你实 战互联网教育系统项目_数据集_28儒猿技术窝Spring顶尖高手进阶:102讲带你实 战互联网教育系统项目_数据集_29儒猿技术窝Spring顶尖高手进阶:102讲带你实 战互联网教育系统项目_推荐系统_30都是儒猿技术窝Spring顶尖高手进阶:102讲带你实 战互联网教育系统项目_数据集_31维向量,在本此算法实现中,令儒猿技术窝Spring顶尖高手进阶:102讲带你实 战互联网教育系统项目_数据集_32,即也就是矩阵儒猿技术窝Spring顶尖高手进阶:102讲带你实 战互联网教育系统项目_数据集_33的大小是儒猿技术窝Spring顶尖高手进阶:102讲带你实 战互联网教育系统项目_损失函数_34,而矩阵儒猿技术窝Spring顶尖高手进阶:102讲带你实 战互联网教育系统项目_数据集_35的大小是儒猿技术窝Spring顶尖高手进阶:102讲带你实 战互联网教育系统项目_推荐系统_36.

协同过滤算法的实现

  • 算法介绍
    电影推荐系统的协同过滤算法需要考虑儒猿技术窝Spring顶尖高手进阶:102讲带你实 战互联网教育系统项目_推荐系统_37维参数向量儒猿技术窝Spring顶尖高手进阶:102讲带你实 战互联网教育系统项目_推荐系统_38和向量儒猿技术窝Spring顶尖高手进阶:102讲带你实 战互联网教育系统项目_数据集_39,此模型预测的第儒猿技术窝Spring顶尖高手进阶:102讲带你实 战互联网教育系统项目_推荐系统_40个用户对第儒猿技术窝Spring顶尖高手进阶:102讲带你实 战互联网教育系统项目_数据集_41个电影的评分可以用儒猿技术窝Spring顶尖高手进阶:102讲带你实 战互联网教育系统项目_推荐系统_42表示,给定一些用户对一些电影的评分数据集,可以通过协同过滤算法得到一系列参数向量儒猿技术窝Spring顶尖高手进阶:102讲带你实 战互联网教育系统项目_推荐系统_43儒猿技术窝Spring顶尖高手进阶:102讲带你实 战互联网教育系统项目_协同过滤算法_44能够对模型实现最好的拟合(最小化平方误差)。
  • 算法描述
    尽管与单变量线性回归的实现方式很相似,但还是略有不同,单变量线性回归的实现方式只需要通过梯度下降最小化损失函数的到一个参数向量儒猿技术窝Spring顶尖高手进阶:102讲带你实 战互联网教育系统项目_协同过滤算法_45,而协同过滤算法需要得到两个参数,其实现方法可以简单总结为​​控制变量法​​,即给定参数矩阵儒猿技术窝Spring顶尖高手进阶:102讲带你实 战互联网教育系统项目_协同过滤算法_46,求出参数矩阵儒猿技术窝Spring顶尖高手进阶:102讲带你实 战互联网教育系统项目_损失函数_47,再将参数矩阵儒猿技术窝Spring顶尖高手进阶:102讲带你实 战互联网教育系统项目_数据_48保持不变,求出参数矩阵儒猿技术窝Spring顶尖高手进阶:102讲带你实 战互联网教育系统项目_协同过滤算法_49, 其实现方式如下公式所示,需要注意的是,为了防止过拟合,也要加入正则项。
    给定参数矩阵儒猿技术窝Spring顶尖高手进阶:102讲带你实 战互联网教育系统项目_损失函数_50,预估参数矩阵儒猿技术窝Spring顶尖高手进阶:102讲带你实 战互联网教育系统项目_损失函数_51

儒猿技术窝Spring顶尖高手进阶:102讲带你实 战互联网教育系统项目_数据_52

给定参数矩阵儒猿技术窝Spring顶尖高手进阶:102讲带你实 战互联网教育系统项目_损失函数_53,预估参数矩阵参数阵儒猿技术窝Spring顶尖高手进阶:102讲带你实 战互联网教育系统项目_损失函数_54:

儒猿技术窝Spring顶尖高手进阶:102讲带你实 战互联网教育系统项目_协同过滤算法_55

综合以上,对两个参数矩阵同时进行最小化的公式如下所示:

儒猿技术窝Spring顶尖高手进阶:102讲带你实 战互联网教育系统项目_损失函数_56

综合以上,对协同过滤算法的实现步骤可以有以下总结:

  1. 初始化儒猿技术窝Spring顶尖高手进阶:102讲带你实 战互联网教育系统项目_协同过滤算法_57儒猿技术窝Spring顶尖高手进阶:102讲带你实 战互联网教育系统项目_协同过滤算法_58,并令其取得较小的随机值;
  2. 利用梯度下降算法最小化损失函数,并得到每一个参数向量,如下所示:
  • 算法实现
    1.在算法实现之前,需要对数据进行预处理操作,具体如下所示:



#加载训练前的数据
data = scio.loadmat('ex8_movieParams.mat')
X = data['X']
theta = data['Theta']
num_users = data['num_users']
num_movies = data['num_movies']
num_features = data['num_features']

#减少数据规模,能够使得数据算法运行的更快
num_users = 4
num_movies = 5
num_features = 3
X = X[0:num_movies, 0:num_features]
theta = theta[0:num_users, 0:num_features]
Y = Y[0:num_movies, 0:num_users]
R = R[0:num_movies, 0:num_users]


  1. 协同过滤损失函数的计算可如以下代码所示:



def cofi_cost_function(params, Y, R, num_users, num_movies, num_features, lmd):
X = params[0:num_movies * num_features].reshape((num_movies, num_features))
theta = params[num_movies * num_features:].reshape((num_users, num_features))
cost = 0
X_grad = np.zeros(X.shape)
theta_grad = np.zeros(theta.shape)

hypothesis = (np.dot(X, theta.T) - Y) * R

cost = (1/2)*np.sum(hypothesis**2) + (lmd/2)*np.sum(theta**2) + (lmd/2)*np.sum(X**2)

X_grad = np.dot(hypothesis, theta) + lmd * X
theta_grad = np.dot(hypothesis.T, X) + lmd * theta

#将两个梯度向量连接成为一维向量
grad = np.concatenate((X_grad.flatten(), theta_grad.flatten()))

return cost, grad
#对损失函数进行输出并评估模型性能
cost, grad = cofi_cost_function(np.concatenate((X.flatten(), theta.flatten())), Y, R, num_users, num_movies, num_features, 0)
print(cost)


注意:利用协同过滤算法得到的模型的梯度值之后,为了消除误差,需要对梯度值进行进一步检测,这个过程称之为​​梯度检测​​。具体的计算公式如下所示:
儒猿技术窝Spring顶尖高手进阶:102讲带你实 战互联网教育系统项目_推荐系统_59

梯度检测的代码实现如下所示:



def compute_numerial_gradient(cost_func, theta):
numgrad = np.zeros(theta.size)
perturb = np.zeros(theta.size)

e = 1e-4

for p in range(theta.size):
perturb[p] = e
loss1, grad1 = cost_func(theta - perturb)
loss2, grad2 = cost_func(theta + perturb)

numgrad[p] = (loss2 - loss1) / (2 * e)
perturb[p] = 0

return numgrad


随机设置一些输入数据运行协同过滤算法和加载数据集运行协同过滤算法得到的梯度值如果比较接近,则说明梯度误差较小,该算法能得到最小的损失函数值并且能够收敛,其具体代码实现和运行结果如下所示:



def check_cost_function(lmd):

# 创建随机的样本
x_t = np.random.rand(4, 3) #4x3随机矩阵
theta_t = np.random.rand(5, 3) #5x3随机矩阵

Y = np.dot(x_t, theta_t.T) # 4x5
Y[np.random.rand(Y.shape[0], Y.shape[1]) > 0.5] = 0 #Y矩阵的随机位置的值大于0.5,则设为0
R = np.zeros(Y.shape)
R[Y != 0] = 1 #设置R矩阵部分值为1,认为某用户对某电影做出评价

# 运行梯度检测
x = np.random.randn(x_t.shape[0], x_t.shape[1])
theta = np.random.randn(theta_t.shape[0], theta_t.shape[1])
num_users = Y.shape[1] #5
num_movies = Y.shape[0] #4
num_features = theta_t.shape[1] #3

#随机组的损失函数值和梯度计算
def cost_func(p):
return cofi_cost_function(p, Y, R, num_users, num_movies, num_features, lmd)

numgrad = compute_numerial_gradient(cost_func, np.concatenate((x.flatten(), theta.flatten())))
#数据集的损失函数值和梯度计算
cost, grad = cofi_cost_function(np.concatenate((x.flatten(), theta.flatten())), Y, R, num_users, num_movies, num_features, lmd)

print(np.c_[numgrad, grad])


diff = np.linalg.norm(numgrad - grad) / np.linalg.norm(numgrad + grad)
print('the relative difference will be small (less than 1e-9).\n'
'Relative Difference: {:0.3e}'.format(diff))


设置儒猿技术窝Spring顶尖高手进阶:102讲带你实 战互联网教育系统项目_推荐系统_60,运行以上代码得到的部分结果如下图所示,两者差值不大于儒猿技术窝Spring顶尖高手进阶:102讲带你实 战互联网教育系统项目_损失函数_61.




 


儒猿技术窝Spring顶尖高手进阶:102讲带你实 战互联网教育系统项目_数据_62


 


 

电影推荐系统的实现

有了以上关于协同过滤模型的定义和实现,现在可以用以上算法实现一个电影推荐系统了,为了便于理解算法的运行过程,可以自己随机设置一些电影的评分数据和用户数据,具体代码实现如下所示:

  1. 加载电影评分数据
    电影评分数据的的以如下图所示的txt格式存在,逐行读取代码的格式如下所示:



     
    儒猿技术窝Spring顶尖高手进阶:102讲带你实 战互联网教育系统项目_推荐系统_63
     



def load_movie_list():
movie_list = []
with open("movie_ids.txt",encoding='utf-8') as f:
lines = f.readlines()
for line in lines:
idx, *movie_name = line.split(' ')
movie_list.append(' '.join(movie_name).rstrip())

return movie_list

# 重新设置一些随机评分数据
movie_list = load_movie_list()
my_ratings = np.zeros(len(movie_list))
my_ratings[0] = 4
my_ratings[97] = 2
my_ratings[6] = 3
my_ratings[11] = 5
my_ratings[53] = 4
my_ratings[63] = 5
my_ratings[65] = 3
my_ratings[68] = 5
my_ratings[182] = 4
my_ratings[225] = 5
my_ratings[354] = 5
print('New user ratings:\n')
for i in range(my_ratings.size):
if my_ratings[i] > 0:
print('Rated {} for {}'.format(my_ratings[i], movie_list[i]))


以上代码运行结果如下所示:

 




 


儒猿技术窝Spring顶尖高手进阶:102讲带你实 战互联网教育系统项目_推荐系统_64


 


  1. 实现电影推荐
    实现电影推荐之前,需要对电影评分数据做标准化处理。需要注意的是,为了减少算法运行时间,标准化处理数据之前,只考虑对此电影做出评价的用户的数据,而不考虑未对此电影做出评价的用户(即也就是儒猿技术窝Spring顶尖高手进阶:102讲带你实 战互联网教育系统项目_推荐系统_65)的数据,这样可以减少数据规模,节省算法运行时间。具体实现代码如下所示:



data = scio.loadmat('ex8_movies.mat')
Y = data['Y']
R = data['R']

# Y 是一个1682x943的矩阵, 包含943个用户对1682个电影的评分哦数据(1-5)

#
# Y 是一个1682x943的矩阵, 当 R[i,j] = 1 认为用户j对电影i做出了评价
# 将自己的评分数据添加到数据集中
Y = np.c_[my_ratings, Y]
R = np.c_[(my_ratings != 0), R]

#标准化数据
Ynorm, Ymean = normalize_ratings(Y, R)

#特征向量
num_users = Y.shape[1] #944
num_movies = Y.shape[0] #1682
num_features = 10

# 设置初始参数
X = np.random.randn(num_movies, num_features)
theta = np.random.randn(num_users, num_features)

initial_params = np.concatenate([X.flatten(), theta.flatten()])

lmd = 10


def cost_func(p):
return cofi_cost_function(p, Ynorm, R, num_users, num_movies, num_features, lmd)[0]


def grad_func(p):
return cofi_cost_function(p, Ynorm, R, num_users, num_movies, num_features, lmd)[1]

theta, *unused = opt.fmin_cg(cost_func, fprime=grad_func, x0=initial_params, maxiter=100, disp=False, full_output=True)


X = theta[0:num_movies * num_features].reshape((num_movies, num_features))
theta = theta[num_movies * num_features:].reshape((num_users, num_features))

print('Recommender system learning completed')
print(theta)

input('Program paused. Press ENTER to continue')


运行结果如下图所示:

 




 


儒猿技术窝Spring顶尖高手进阶:102讲带你实 战互联网教育系统项目_数据_66


 


最后,利用得到的参数,实现电影推荐,其结果如下所示: