基于SVD思想的简单推荐系统的实现
来源:互联网 发布:sql 带游标的存储过程 编辑:程序博客网 时间:2024/05/18 00:48
简介
通过本文,你可以了解
- Python的一些小tips
- SVD算法的基本思想以及实现
- 如何利用SVD来实现一个比较基础的推荐系统
实现环境
- Pycharm Community Edition
- Python3
- numpy
- pandas
- sklearn
数据来源
- MovieLens数据集
参考文献
《Advances in Collaborative Filtering》
环境的配置
pycharm & python3
配置方式参考上一篇BLOG。
numpy & pandas & sklearn
pandas直接用pip安装即可。
因为在windows环境下直接采用pip安装会出现错误,所以本文推荐在此下载网 1下载numpy+scpiy+sklearn的二进制文件到目录下采用
pip install ***.whl
安装即可。
核心思想
关于SVD的纯数学意义
在矩阵M的奇异值分解中
- V的列(columns)组成一套对
M 的正交”输入”或”分析”的基向量。这些向量是M∗M 的特征向量。 - U的列(columns)组成一套对
M 的正交”输出”的基向量。这些向量是MM∗ 的特征向量。 - Σ对角线上的元素是奇异值,可视为是在输入与输出间进行的标量的”膨胀控制”。这些是
MM∗ 及M∗M 的特征值的非负平方根,并与U和V的行向量相对应。
本算法所采用的思想
在基于SVD算法的推荐系统中,我们可以发现,对于一个打分矩阵M,可以将其分解成P,Q的两个矩阵的乘积,而我们可以分别认为P与Q两个矩阵为用户和电影对每一个因子的影响程度。
了解了以上的思想,我们发现,我们要做的就是根据一个不完整的评分矩阵(通常是如此,因为用户不会对所有的电影打分,因此才需要我们做出推荐)来得出
在论文《Advances in Collaborative Filtering》中,我们找到了一种通过学习来得到
rui=μ+bi+bu+qTipu
其中
*
*
*
*
*
由此我们发现,问题的关键在于如何获得
minb∗,q∗,p∗∑(u,i)∈K(rui−μ−bi−bu−qTipu)2+λ4(b2i+b2u+∥qi∥2)+∥pu∥2)
取到最小值。同样该方法无法直接实现,于是论文中采用了梯度向下的方法进行学习,通过如下四个公式优化
bu←bu+γ⋅(eui−λ4⋅bu)
bi←bi+γ⋅(eui−λ4⋅bi)
qi←qi+γ⋅(eui⋅pu−λ4⋅qi)
pu←pu+γ⋅(eui⋅qi−λ4⋅pu)
来得到
rui=μ+bi+bu+qTipu
中去,来对每一段电影进行打分,其中当
得到了具体的实现方法,我们需要一个合理的架构来实现它。通过多次迭代的方式来梯度下降学习到四个参数的值是一个比较好的思路,因此我们采用RMSE(均方根误差)来评价每次得到的结果如何。
到此我们已经基本阐述了一种简单的基于SVD的推荐系统,在接下来的部分我们主要介绍如何通过python来实现它。
代码实现
读取数据
通过研究数据集我们发现,数据集的结构为
用户ID::电影ID::打分::时间戳
这样的结构,我们采用pandas的read_csv方法将其读取进来。
file = '../DATA/Movie Lens/ratings.dat'xls_data = pd.read_csv(file, sep='::', header=None)
其中参数sep='::'
表示以’::’分割数据,header=None
则表示该数据没有头行,即第一行就是数据。
划分训练集和测试集
python的sklearn库提供了非常方便的方法来划分测试集和训练集
train_X, test_X = sklearn.cross_validation.train_test_split(xls_data, test_size=0.2)
其中参数test_size=0.2
表示测试集占总的比例为0.2
推荐系统的初始化
我们通过如下的方式来初始化推荐系统
def __init__(self,data,vector_length=20): #总共有多少用户,多少电影 self.user_num=6040 self.movie_num=3952 # 数据集 self.data=np.array(data) # 向量的长度 self.vector_length=vector_length # 电影的平均分 self.movie_average=np.mean(self.data[:,2]) # 损失函数的四个需要优化的参数 bi,bu,qi,pu self.Bi = {} self.Bu = {} self.Qi = {} self.Pu = {} # 电影-用户矩阵 self.movie_user_mat={} # 用户-电影矩阵 self.user_movie_mat={} # 遍历数据,初始化数据集 for i in range(self.data.shape[0]): user_id = self.data[i][0] movie_id = self.data[i][1] rating = self.data[i][2] #初始化两个矩阵的每一行并赋值 self.movie_user_mat.setdefault(movie_id,{}) self.user_movie_mat.setdefault(user_id,{}) self.movie_user_mat[movie_id][user_id]=rating self.user_movie_mat[user_id][movie_id]=rating #通过随机赋值的方式初始化四个参数 self.Bi.setdefault(movie_id,0) self.Bu.setdefault(user_id,0) t=np.random.random((self.vector_length,1))/10*(np.sqrt(self.vector_length)) self.Qi.setdefault(movie_id,t) t=np.random.random((self.vector_length,1))/10*(np.sqrt(self.vector_length)) self.Pu.setdefault(user_id,t)
以上代码的注释比较齐全就不在此赘述,只提一点,向量的长度就是因子数量
如何训练
训练的方法也比较简洁,贴出代码如下所示
def train_model(self,steps=20,alpha=0.15,beta=0.04): for step in range(steps): sum_Rmse=0 for i in range(self.data.shape[0]): user_id=self.data[i][0] movie_id=self.data[i][1] rating=self.data[i][2] # 计算均方根误差 temp_Rmse=rating-self.recommend(user_id,movie_id) sum_Rmse+=temp_Rmse**2 # 根据结果采用随机梯度下降进行优化 self.Bu[user_id]+=beta*(temp_Rmse-alpha*self.Bu[user_id]) self.Bi[movie_id]+=beta*(temp_Rmse-alpha*self.Bi[movie_id]) temp=self.Qi[movie_id] self.Qi[movie_id]+=beta*(temp_Rmse*self.Pu[user_id]-alpha*self.Qi[movie_id]) self.Pu[user_id]+=beta*(temp_Rmse*temp-alpha*self.Pu[user_id]) print('第'+str(step)+'步迭代的RMSE值为'+str(np.sqrt(sum_Rmse/self.data.shape[0]))) alpha=alpha*0.93
具体的打分函数recommend
为
self.Bi.setdefault(movie_id,0)self.Bu.setdefault(user_id,0)self.Qi.setdefault(movie_id,np.zeros((self.vector_length,1)))self.Pu.setdefault(user_id,np.zeros((self.vector_length,1)))if (self.Qi[movie_id]==None): self.Qi[movie_id]=np.zeros((self.vector_length,1))if (self.Pu[user_id]==None): self.Pu[user_id]=np.zeros((self.vector_length,1))score=self.movie_average+self.Bi[movie_id]+self.Bu[user_id]+np.sum(self.Qi[movie_id]*self.Pu[user_id])if score>5: return 5elif score<1: return 1return score
如何测试
测试的思想比较简单,通过读取测试数据进行打分再与真实分数进行比较并计算RMSE即可
test_data_rmse=0test_data=np.array(test_data)result_list=[]for i in range(test_data.shape[0]): my_rating=self.recommend(test_data[i][0],test_data[i][1]) result_list.append((test_data[i][0],test_data[i][1],my_rating)) test_data_rmse+=(my_rating-test_data[i][2])**2print('测试集的RMSE为'+str(np.sqrt(test_data_rmse/test_data.shape[0])))return result_list
如何推荐
推荐的思路为首先遍历整个数据寻找仍未打分的数据,然后对该用户所有未打分的电影进行打分并排序,将得分最高的五个返回即可。
#寻找已经评分的电影rated_num=[]for i in range(self.data.shape[0]): i_user_id=self.data[i][0] i_movie_id=self.data[i][1] if (i_user_id==user_id): rated_num.append(i_movie_id)#对未评分的电影打分rating_list=[]for i in range(self.movie_num): this_movie_id=i+1 if (this_movie_id not in rated_num): score=self.recommend(user_id,this_movie_id) rating_list.append((this_movie_id,score))# 对得分进行排序rating_list.sort(key=operator.itemgetter(1),reverse=True)# 选出得分最高的五部电影return rating_list[:5]
结语 & 碎碎念
关于SVD
在最初由于只考虑到了SVD的数学意义即可把任意一个矩阵分解成三个矩阵的乘积,结果在完成后发现由于评分矩阵中存在着大量的未打分矩阵所以无法从数学意义上进行分解而需要采用机器学习的方法学习得到两个因子矩阵。而正是为此翻阅论文也是花费了不少时间。
关于python
python的pip是真的坑啊,python的对齐也是真的坑啊!
关于机器学习
机器学习的公式推导全是数学上的计算,感觉自己脆弱的数学根基受到了极大地摧残。
结语
实验终于结束啦,耶!
- 基于SVD思想的简单推荐系统的实现
- Scikit-learn实现基于模型的推荐系统(SVD)
- 推荐算法:基于svd的算法:svd
- 基于协同过滤的SVD的推荐系统
- 基于奇异值分解(SVD)的推荐系统算法实现
- 基于模型融合的推荐系统实现(2):迭代式SVD分解
- 基于用户的deviantArt推荐系统(SVD因子分解)
- 用于推荐系统的SVD算法python实现
- 基于内容推荐的个性化新闻阅读实现(二):基于SVD的推荐算法
- 推荐算法:基于svd的算法:比较
- 通过SVD对推荐系统的优化
- 推荐系统的相关算法SVD
- svd及推荐算法的实现
- 基于baseline、svd和stochastic gradient descent的个性化推荐系统
- 基于baseline、svd和stochastic gradient descent的个性化推荐系统
- 基于baseline、svd和stochastic gradient descent的个性化推荐系统
- 推荐算法:基于svd的算法:基于领域
- 推荐系统中矩阵分解方法:svd,非对称svd和svd++的区别
- 计算机网络——各种时延的计算
- 路由生产算法
- Android.mk中添加目录下所有cpp文件
- Java:java.util包
- Scala基础—并发编程示例
- 基于SVD思想的简单推荐系统的实现
- apk签名文件
- ArchLinux下fcitx ctrl+space无法调出输入法
- 《高级软件工程》学习总结
- Maven安装与配置
- 关于混淆。。。
- 关于python爬取指定网页的图片
- Oracle那些事(10)-scott用户的简单使用
- 栈的顺序存储结构及实现