8.1.推荐系统(协同过滤算法)

1)题目:

在本次练习中,你将实现协同过滤算法并将它运用在电影评分的数据集上,最后根据新用户的评分来给新用户推荐10部电影。这个电影评分数据集由1到5的等级组成。数据集有nu=943n_u=943nu​=943个用户和nm=1682n_m=1682nm​=1682部电影。在计算完协同过滤的代价函数以及梯度后,将使用牛顿共轭梯度法求得参数。
数据集中,Y是一个(1682, 943)的矩阵,存储了从1到5的评分,矩阵R为二值指标矩阵,其中如果用户j对电影i进行评级,则R(i,j)=1R(i, j)=1R(i,j)=1,否则R(i,j)=0R(i, j)=0R(i,j)=0。协同过滤的目的就是预测用户尚未评分的电影的评分,即R(i,j)=0R(i, j)=0R(i,j)=0的条目。这样我们就可以向用户推荐预测评分最高的电影。
X是电影的特征矩阵,Theta是用户的特征矩阵,X的第i行对应x(i)x^{(i)}x(i),表示第i部电影的特征向量(即描述第i部电影内容的特征量),Theta的第j行对应θ(j)\theta^{(j)}θ(j),表示第j个用户的特征向量(即第j个用户对不同类型电影的偏好)。这里x(i)x^{(i)}x(i)和θ(j)\theta^{(j)}θ(j)都是100维的向量,因此X的维数是(1682100),Theta的维数是(943100)。
数据集链接: https://pan.baidu.com/s/1cEgQIvehUcLxZ0WVhxcPuQ 提取码: xejn

2)知识点概括:

  • 推荐系统(recommender systems)
    nun_unu​表示用户数量
    nmn_mnm​表示电影数量
    r(i,j)=1r(i,j)=1r(i,j)=1表示用户j对电影i打分了
    y(i,j)y^{(i,j)}y(i,j)表示用户j对电影i给出的评分
    θ(j)\theta^{(j)}θ(j)表示用户j的参数向量(n维向量)
    x(i)x^{(i)}x(i)表示电影i的特征向量(n维向量)

  • 基于内容的推荐算法(content based recommendations)
    假设我们已经有了电影的特征向量x(i)x^{(i)}x(i)(即描述电影内容的特征量),还需要得到每个用户的参数向量θ(j)\theta^{(j)}θ(j)(即每个用户对不同类型电影的偏好),即对每个用户应用了一个不同的线性回归,然后就可以得到预测的评分为(θ(j))T(x(i))(\theta^{(j)})^T(x^{(i)})(θ(j))T(x(i))。
    优化的目标函数为:
    min⁡θ(1),⋯ ,θ(nu)12∑j=1nu∑i:r(i,j)=1((θ(j))T(x(i))−y(i,j))2+λ2∑j=1nu∑k=1n(θk(j))2\min_{\theta^{(1)},\cdots,\theta^{(n_u)}}{1\over2}\sum_{j=1}^{n_u}\sum_{i:r(i,j)=1}((\theta^{(j)})^T(x^{(i)})-y^{(i,j)})^2+{\lambda\over 2}\sum_{j=1}^{n_u}\sum_{k=1}^n(\theta_k^{(j)})^2minθ(1),⋯,θ(nu​)​21​∑j=1nu​​∑i:r(i,j)=1​((θ(j))T(x(i))−y(i,j))2+2λ​∑j=1nu​​∑k=1n​(θk(j)​)2
    再用梯度下降等方法求解。

  • 基于用户的推荐算法
    假设我们已经有了每个用户的参数向量θ(j)\theta^{(j)}θ(j)(即每个用户对不同类型电影的偏好),用这个来得到电影的特征向量x(i)x^{(i)}x(i)(即描述电影内容的特征量),同样对每个用户应用了一个不同的线性回归,然后就可以得到预测的评分为(θ(j))T(x(i))(\theta^{(j)})^T(x^{(i)})(θ(j))T(x(i))。
    优化的目标函数为:
    min⁡x(1),⋯ ,x(nm)12∑i=1nm∑j:r(i,j)=1((θ(j))T(x(i))−y(i,j))2+λ2∑i=1nm∑k=1n(xk(i))2\min_{x^{(1)},\cdots,x^{(n_m)}}{1\over2}\sum_{i=1}^{n_m}\sum_{j:r(i,j)=1}((\theta^{(j)})^T(x^{(i)})-y^{(i,j)})^2+{\lambda\over 2}\sum_{i=1}^{n_m}\sum_{k=1}^n(x_k^{(i)})^2minx(1),⋯,x(nm​)​21​∑i=1nm​​∑j:r(i,j)=1​((θ(j))T(x(i))−y(i,j))2+2λ​∑i=1nm​​∑k=1n​(xk(i)​)2

  • 协同过滤(collaborative filtering)
    用用户的参数量来学习电影的特征量,再用电影的特征量来学习用户的参数量,如此迭代下去,得到一个最优解。

    两个合起来优化目标就变成了
    J(x(1),⋯ ,x(nm),θ(1),⋯ ,θ(nu))=12∑(i,j):r(i,j)=1((θ(j))T(x(i))−y(i,j))2+λ2∑i=1nm∑k=1n(xk(i))2+λ2∑j=1nu∑k=1n(θk(j))2J({x^{(1)},\cdots,x^{(n_m)},\theta^{(1)},\cdots,\theta^{(n_u)}})={1\over2}\sum_{(i,j):r(i,j)=1}((\theta^{(j)})^T(x^{(i)})-y^{(i,j)})^2+{\lambda\over 2}\sum_{i=1}^{n_m}\sum_{k=1}^n(x_k^{(i)})^2+{\lambda\over 2}\sum_{j=1}^{n_u}\sum_{k=1}^n(\theta_k^{(j)})^2J(x(1),⋯,x(nm​),θ(1),⋯,θ(nu​))=21​∑(i,j):r(i,j)=1​((θ(j))T(x(i))−y(i,j))2+2λ​∑i=1nm​​∑k=1n​(xk(i)​)2+2λ​∑j=1nu​​∑k=1n​(θk(j)​)2

  • 计算步骤:
    1、随机初始化x(1),⋯ ,x(nm),θ(1),⋯ ,θ(nu){x^{(1)},\cdots,x^{(n_m)},\theta^{(1)},\cdots,\theta^{(n_u)}}x(1),⋯,x(nm​),θ(1),⋯,θ(nu​)
    2、最小化,使用梯度下降法或其他高等优化算法J(x(1),⋯ ,x(nm),θ(1),⋯ ,θ(nu))J({x^{(1)},\cdots,x^{(n_m)},\theta^{(1)},\cdots,\theta^{(n_u)}})J(x(1),⋯,x(nm​),θ(1),⋯,θ(nu​))
    3、对用户有参数Θ\ThetaΘ,电影有特征XXX,预测的评分矩阵即为XΘTX\Theta^TXΘT

  • 协同过滤的矩阵表示(也叫低秩矩阵分解low rank matrix factorization)

  • 如何寻找和电影iii相关的电影jjj呢?
    衡量两个电影的相关度,即两个电影的特征向量的距离∥x(i)−x(j)∥\|x^{(i)}-x^{(j)}\|∥x(i)−x(j)∥,如果想要找到5部与电影i最相似的电影,只需找到∥x(i)−x(j)∥\|x^{(i)}-x^{(j)}\|∥x(i)−x(j)∥中最小的五个即可。

  • 均值标准化,即Y−μY-\muY−μ可以避免出现因为某个用户对所有电影没有评分而导致该用户所有预测的评分都为零的情况。再在预测完之后加回这个均值μ\muμ。相当于对于没有评分的用户预测的评分为这个所有用户评分的均值。

3)大致步骤:

  • 数据导入及可视化。计算第一部电影《玩具总动员》的平均得分,然后可视化评分矩阵。
  • 协同过滤的代价函数。先导入训练好的参数(电影的特征和用户的特征),然后截取一小部分数据集用于验证写好的代价函数,注意这里传入代价函数的参数包含两部分(电影的参数和用户的参数),而且是一个向量形式,因为后面要使用fmin_ncg函数,和之前在神经网络的处理类似,需要展开和reshape,最后得到代价函数的值为22.22。
  • 梯度函数与梯度检查。根据公式求出梯度,注意这里的参数是x和theta两个矩阵展开,因此梯度展开后也应该是对每个参数求导,所以最后出来的维数应该和参数一样;关于梯度检验给出的参考代码里又重新随机化一个很小的测试数据,我这里直接用之前取的小样本数据来检查梯度了,效果一样,免得麻烦了,梯度检查的具体方法和之前神经网络时的梯度检查原理一样,也是用斜率来逼近,最后求出来两个梯度的差不超过1e-9。
  • 正则化的代价函数和梯度函数。修改前面的两个函数,加上正则化的部分,取lmbda=1.5可以得到修改后的代价函数为31.34,同样进行梯度检验。
  • 增加新用户评分。首先读入电影列表,注意这里有一些法文电影名称,运行的时候会出现编码错误,对于这个问题还不是很熟悉,所以就修改了源文件把法文写为英文再进行读入;然后再增加自己的评分列,打印出评分以及对应的电影。
  • 训练模型。在原来的评分矩阵的基础上加上我的评分,再进行标准化,这里注意对未评分的电影不需要进行标准化,然后再随机初始化参数,用牛顿共轭梯度算法训练得到参数。
  • 最后得到预测的评分矩阵,并基于我的评分给我推荐10部电影。

4)关于Python:

  • plt.colorbar( )函数在加颜色渐变条。
  • 当两个矩阵形状相同时,即维数一样,可以用A[np.where(B的条件)]来筛选满足B的条件的位置的A的值,这样计算比较方便。
  • strip( ) 用于移除字符串头尾指定的字符(默认为空格或换行符)或字符序列。
  • join( )返回通过指定字符连接序列中元素后生成的新字符串,例如str = “-”;seq = (“a”, “b”, “c”); print str.join( seq )为a-b-c,’ ‘.join([‘a’,‘b’]) 会得到a b
  • [::-1]用在数组后可以让数组倒序。

5)代码与结果:


import numpy as np
import matplotlib.pyplot as plt
import scipy.io as scio
import scipy.optimize as opt'''============================part1 数据导入及可视化========================='''
data = scio.loadmat('ex8_movies.mat')
y = data['Y']
r = data['R']#第一部电影《玩具总动员》的平均得分
print('Average rating for movie 1 (Toy Story): %f / 5\n\n'%np.mean(y[0, np.where(r[0, :]==1)]))#可视化评分矩阵
#MATLAB中imagesc(A)将矩阵A中的元素数值按大小转化为不同颜色,并在坐标轴对应位置处以这种颜色染色
plt.figure(figsize=(5,9))
plt.imshow(y)
plt.colorbar() #加颜色条
plt.ylabel('Movies')
plt.xlabel('Users')'''============================part2 协同过滤的代价函数=============================='''
#导入训练好的参数
parameters = scio.loadmat('ex8_movieParams.mat')
x = parameters['X'] #电影的特征矩阵
theta = parameters['Theta'] #用户的特征矩阵
num_users = parameters['num_users']
num_movies = parameters['num_movies']
num_features = parameters['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]'''代价函数'''
#这里的params包含x和theta两个矩阵,它是一维的,因为要使用fmin_ncg函数的原因需要展开
def cofiCostFunc(params, y, r, num_users, num_movies, num_features, lmd=0):#reshape得到x和theta矩阵x = np.reshape(params[0:num_movies*num_features], (num_movies,num_features))theta = np.reshape(params[num_movies*num_features:], (num_users,num_features))#代价函数J = np.sum(np.square((x@theta.T-y))[np.where(r==1)])/2 reg = (lmd/2) * (np.sum(x*x) + np.sum(theta*theta))return J+reg#展开参数
params = np.r_[X.flatten(), Theta.flatten()]
#测试代价函数是否正确
J = cofiCostFunc(params, Y, R, num_users, num_movies, num_features, lmd=0)
print('Cost at loaded parameters: %f \n(this value should be about 22.22)\n'% J)'''============================part3 梯度函数与梯度检验=============================='''
'''梯度函数'''
def cofiGradFunc(params, y, r, num_users, num_movies, num_features, lmd=0):#reshape得到x和theta矩阵x = np.reshape(params[0:num_movies*num_features], (num_movies,num_features))theta = np.reshape(params[num_movies*num_features:], (num_users,num_features))#梯度函数x_grad = ((x@theta.T-y)*r)@theta + lmd*x #shape和x一样,为(num_movies,num_features)theta_grad = ((x@theta.T-y)*r).T@x + lmd*theta #shape和theta一样,为(num_users,num_features)grad = np.r_[x_grad.flatten(), theta_grad.flatten()]return gradcofiGradFunc(params, Y, R, num_users, num_movies, num_features, lmd=0).shape #(27,)'''梯度检查'''
#数值梯度
def computeNumericalGradient(J, params):numgrad = np.zeros(params.shape)perturb = np.zeros(params.shape)e =1e-4for p in range(len(params)):perturb[p] = eloss1 = J(params - perturb)loss2 = J(params + perturb)numgrad[p] = (loss2 - loss1) / (2*e)perturb[p] = 0return numgrad#重写一下代价函数,以免重复写其他参数(Y, R, num_users, num_movies, num_features, lmd=0)
def cost_func(t):return cofiCostFunc(t, Y, R, num_users, num_movies, num_features, lmd=0)#计算数值梯度与理论梯度之间的差值
numgrad = computeNumericalGradient(cost_func, params)
grad = cofiGradFunc(params, Y, R, num_users, num_movies, num_features, lmd=0)
diff = np.linalg.norm(numgrad-grad) / np.linalg.norm(numgrad+grad)
print('If your cost function implementation is correct, then \n' 'the relative difference will be small (less than 1e-9). \n' '\nRelative Difference: %g\n'% diff)'''============================part4 正则化的代价函数和梯度函数========================'''
#修改前面的两个函数,加上正则化的部分
J = cofiCostFunc(params, Y, R, num_users, num_movies, num_features, lmd=1.5)
print('Cost at loaded parameters (lambda = 1.5): %f \n(this value should be about 31.34)\n'% J)#正则化之后的梯度检验
def cost_func(t):return cofiCostFunc(t, Y, R, num_users, num_movies, num_features, lmd=1.5)
#计算数值梯度与理论梯度之间的差值
numgrad = computeNumericalGradient(cost_func, params)
grad = cofiGradFunc(params, Y, R, num_users, num_movies, num_features, lmd=1.5)
diff = np.linalg.norm(numgrad-grad) / np.linalg.norm(numgrad+grad)
print('If your cost function implementation is correct, then \n' 'the relative difference will be small (less than 1e-9). \n' '\nRelative Difference: %g\n'% diff)'''============================part5 新用户增加评分=============================='''
#电影名称的文件里有一些电影好像是法文电影,所以编码上有些问题,会出现UnicodeDecodeError,所以进行以下步骤
#检查文件中哪些字符的编码转不过来
f = open('movie_ids.txt',"rb")#二进制格式读文件
lines = f.readlines()
for line in lines:try:line.decode('utf8') #解码except:#打印编码有问题的行print(str(line))
f.close#修改那些好像是法文的字符,文件另存为movie_ids_mod.txt,再进行电影列表读入
t = open('movie_ids_mod.txt')
movie_list = []
for line in t:#先用strip去掉开头结尾的空格,然后用split对空格切片,选取从编号之后的所有字符串,再用jion空格连接成一个字符串movie_list.append(' '.join(line.strip().split(' ')[1:]))
t.closelen(movie_list) #得到了长度为1682的电影列表#初始化我的评分
my_ratings = np.zeros((1682, 1))#添加电影评分,注意这里的索引比作业中少1,从0开始的
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('\n\nNew user ratings:\n')
for i in range(len(my_ratings)):if my_ratings[i] > 0:print('Rated %d for %s\n' %(my_ratings[i], movie_list[i]))'''============================part6 训练协同过滤算法=============================='''
#y和r是part1中的矩阵,在此基础上加上my_ratings构成新的评分矩阵
Y = np.c_[my_ratings, y] #(1682, 944)
R = np.c_[(my_ratings != 0), r] #(1682, 944)'''标准化'''
def normalizeRatings(y, r):#为了下面可以直接矩阵相减,将(1682,)reshape成(1682,1)mu = (np.sum(y, axis=1)/np.sum(r, axis=1)).reshape((len(y),1)) y_norm = (y - mu)*r #未评分的依然为0return y_norm, mu#标准化
Ynorm, Ymean = normalizeRatings(Y, R)
num_users = Y.shape[1]
num_movies = Y.shape[0]
num_features = 10#随机初始化参数
X = np.random.randn(num_movies, num_features)
Theta = np.random.randn(num_users, num_features)
initial_parameters = np.r_[X.flatten(), Theta.flatten()]#训练模型
lmd = 10
#params = opt.fmin_ncg(f=cofiCostFunc, fprime=cofiGradFunc, x0=initial_parameters, args=(Y, R, num_users, num_movies, num_features, lmd), maxiter=100)
#上面的方法不知道为什么特别慢,跑不出来,但其实应该是一样的啊,不解~
res = opt.minimize(fun=cofiCostFunc,x0=initial_parameters,args=(Y, R, num_users, num_movies, num_features, lmd),method='TNC',jac=cofiGradFunc,options={'maxiter': 100})#得到模型参数
params = res.x
X = np.reshape(params[0:num_movies*num_features], (num_movies,num_features))
Theta = np.reshape(params[num_movies*num_features:], (num_users,num_features))'''============================part7 预测评分并推荐=============================='''
#预测的评分矩阵
p = X @ Theta.T
#我的预测评分
my_predictions = (p[:,0].reshape(len(p),1) + Ymean).flatten() #为了矩阵相加,后面展开是为了打印方便#为我推荐的10部电影
ix = np.argsort(my_predictions)[::-1] #逆序,由大到小得到索引
print('\nTop recommendations for you:\n')
for i in range(10):j = ix[i]print('Predicting rating %.1f for movie %s\n' %(my_predictions[j], movie_list[j]))#我原来的评分
print('\nOriginal ratings provided:\n')
for i in range(len(my_ratings)):if my_ratings[i] > 0:print('Rated %d for %s\n' %(my_ratings[i], movie_list[i]))

第一部电影《玩具总动员》的平均得分

评分矩阵可视化结果

不带正则项的代价函数验证结果

不带正则化的梯度函数检查结果

正则化之后的代价函数和梯度检验

增加自己的评分列

最后的推荐结果,为我推荐的10部电影以及预测的评分(注意这里可能因为初始化的随机性导致推荐的结果和评分不一样)

吴恩达|机器学习作业8.1.推荐系统(协同过滤算法)相关推荐

  1. 机器学习编程作业ex8(matlab/octave实现)-吴恩达coursera 异常检测与推荐系统/协同过滤

    程序打包网盘地址提取码1111 一.(Week 9)内容回顾 非监督学习问题的两种应用:异常检测与推荐系统 1.1 Anomaly Detection 异常检测 1.Density Estimatio ...

  2. 吴恩达机器学习作业ex2-python实现

    系列文章目录 吴恩达机器学习作业ex1-python实现 吴恩达机器学习作业ex2-python实现 吴恩达机器学习作业ex3-python实现 作业说明及数据集 链接:https://pan.bai ...

  3. k均值算法python实现(吴恩达机器学习作业)

    k均值算法python实现(吴恩达机器学习作业) 题目要求 数据集 读取mat文件 K-means 实现 结果 问题 题目要求 采用K均值算法对样本进行聚类. 编写K均值算法源代码,对ex7data2 ...

  4. 第一章-机器学习简介 深度之眼_吴恩达机器学习作业训练营

    目录 专栏简介: 一,机器学习简介 1.1 机器学习定义 1.1 机器学习的重要性 1.2 应用领域 二.监督学习 三.无监督学习 四.总结 专栏简介: 本栏主要内容为吴恩达机器学习公开课的学习笔记, ...

  5. 吴恩达机器学习作业7 - K-means和PCA主成分分析(Python实现)

    吴恩达机器学习作业7 - K-means和PCA主成分分析(Python实现) Introduction 在本实验中,将实现K-means聚类算法,并将其应用于图像压缩.在第二部分实验中,将使用主成分 ...

  6. 吴恩达机器学习作业Python实现(八):异常检测和推荐系统

    吴恩达机器学习系列作业目录 1 Anomaly detection 这部分,您将实现一个异常检测算法来检测服务器计算机中的异常行为.他的特征是测量每个服务器的响应速度(mb/s)和延迟(ms).当你的 ...

  7. 吴恩达机器学习作业Python实现(七):K-means和PCA主成分分析

    吴恩达机器学习系列作业目录 1 K-means Clustering 在这个练习中,您将实现K-means算法并将其用于图像压缩.通过减少图像中出现的颜色的数量,只剩下那些在图像中最常见的颜色. 1. ...

  8. 吴恩达机器学习作业Python实现(六):SVM支持向量机

    吴恩达机器学习系列作业目录 1 Support Vector Machines 1.1 Example Dataset 1 %matplotlib inline import numpy as np ...

  9. 吴恩达机器学习作业Python实现(五):偏差和方差

    吴恩达机器学习系列作业目录 在本练习中,您将实现正则化的线性回归和多项式回归,并使用它来研究具有不同偏差-方差属性的模型 1 Regularized Linear Regression 正则线性回归 ...

最新文章

  1. NS2网络模拟(2)-丢包率
  2. linux sshd cpu 过高 问题解决
  3. 自己做站点(二) 20块钱搞定一个企业站:域名amp;空间申请
  4. android studio failed to open zip file,Android Studio出现Failed to open zip file问题的解决方法...
  5. python中的rstrip函数_Python strip() lstrip() rstrip() 函数 去除空格
  6. limit实现原理 mysql_值得一生典藏:MySQL的事务实现原理
  7. 职业生涯中12个最致命的想法
  8. UltraEdit 25注册机及免费破解注册教程(附带工具)
  9. php实现mpg自动提取音轨,视频如何导出音轨 怎么将双音轨mpg提取其中一个音轨保存...
  10. C/C++:个人收支系统实现
  11. spark学习之执行计划explain
  12. 苹果手机还原后无法激活
  13. 集合之比较接口器+Map家族的HashMap+LinkedHashMap+Hashtable+ConcurrentHashMap
  14. JavaScript的三种写法
  15. windows10 设定和取消定时关机
  16. go语言比java高级在哪里
  17. 测序技术回顾与第三代测序技术展望
  18. 2024南京大学计算机考研信息汇总
  19. 【AD22】设置原理图纸张大小
  20. JS中script标签defer和async属性的区别

热门文章

  1. 02:同行列对角线的格子
  2. 如何让putty像secureCRT一样支持多标签 - 趁我还年轻 - 博客频道 - CSDN.NET - Opera
  3. 仿QQ聊天室【方案】
  4. java里面object和string的相互转换
  5. Python-序列-str list tuple
  6. kotlin项目开发基础之gradle初识
  7. vs2017 EFCore 迁移数据库命令
  8. 数据结构 顺序串笔记
  9. 推断一个图片是不是伪造的方法
  10. 2016/06/11