【知识发现】隐语义模型LFM算法python实现(三)
http://blog.csdn.net/fjssharpsword/article/details/78257126
基于上篇再优化。
1、回顾LFM原理,可以更好地理解代码
对于一个给定的用户行为数据集(数据集包含的是所有的user, 所有的item,以及每个user有过行为的item列表),使用LFM对其建模后,可得到如下图所示的模型:(假设数据集中有3个user, 4个item, LFM建模的分类数为4)
R矩阵是user-item矩阵,矩阵值Rij表示的是user i 对item j的兴趣度。对于一个user来说,当计算出其对所有item的兴趣度后,就可以进行排序并作出推荐。
LFM算法从数据集中抽取出若干主题,作为user和item之间连接的桥梁,将R矩阵表示为P矩阵和Q矩阵相乘。其中P矩阵是user-class矩阵,矩阵值Pij表示的是user i对class j的兴趣度;Q矩阵式class-item矩阵,矩阵值Qij表示的是item j在class i中的权重,权重越高越能作为该类的代表。所以LFM根据如下公式来计算用户U对物品I的兴趣度:
使用LFM后, 不需要关心分类的角度,结果都是基于用户行为统计自动聚类的,全凭数据自己说了算:
l 不需要关心分类粒度的问题,通过设置LFM的最终分类数就可控制粒度,分类数越大,粒度约细
l 对于一个item,并不是明确的划分到某一类,而是计算其属于每一类的概率,是一种标准的软分类
l 对于一个user,我们可以得到他对于每一类的兴趣度,而不是只关心可见列表中的那几个类。
l 对于每一个class,我们可以得到类中每个item的权重,越能代表这个类的item,权重越高
现在讨论如何计算矩阵P和矩阵Q中参数值。一般做法就是最优化损失函数来求参数。在定义损失函数之前,先对数据集和兴趣度的取值做一说明。
数据集应该包含所有的user和他们有过行为的(也就是喜欢)的item。所有的这些item构成了一个item全集。对于每个user来说,我们把他有过行为的item称为正样本,规定兴趣度Rui=1,此外我们还需要从item全集中随机抽样,选取与正样本数量相当的样本作为负样本,规定兴趣度为Rui=0。因此,兴趣的取值范围为[0,1]。
采样之后原有的数据集得到扩充,得到一个新的user-item集K={(U,I)},其中如果(U,I)是正样本,则Rui=1,否则Rui=0。损失函数如下所示:
上式中的
是用来防止过拟合的正则化项,λ需要根据具体应用场景反复实验得到。损失函数的优化使用随机梯度下降算法:
通过求参数Puk和Qki的偏导确定最快的下降方向;
迭代计算不断优化参数(迭代次数事先人为设置),直到参数收敛。
其中,α是学习速率,α越大,迭代下降的越快。α和λ一样,也需要根据实际的应用场景反复实验得到。在ratings数据集上进行实验,取分类数F=100,α=0.02,λ=0.01。
综上所述,执行LFM需要:根据数据集初始化P和Q矩阵,确定4个参数:隐类数F、迭代次数N、学习速率α、正则化参数λ。
2、基于原理,代码构建如下:
# -*- coding: utf-8 -*-
'''
Created on 2017年10月16日@author: Administrator
'''
'''
实现隐语义模型,对隐式数据进行推荐
1.对正样本生成负样本-负样本数量相当于正样本-物品越热门,越有可能成为负样本
2.使用随机梯度下降法,更新参数
'''
import numpy as np
import pandas as pd
from math import exp
import time
import math
from sklearn import cross_validation
import random
import operatorclass LFM:'''初始化隐语义模型参数:*data 训练数据,要求为pandas的dataframe*F 隐特征的个数 *N 迭代次数 *alpha 随机梯度下降的学习速率 *lamda 正则化参数 *ratio 负样本/正样本比例 *topk 推荐的前k个物品'''def __init__(self,data,ratio,F=5,N=2,alpha=0.02,lamda=0.01,topk=10):self.data=data #样本集self.ratio =ratio #正负样例比率,对性能最大影响self.F = F#隐类数量,对性能有影响self.N = N#迭代次数,收敛的最佳迭代次数未知self.alpha =alpha#梯度下降步长self.lamda = lamda#正则化参数self.topk =topk #推荐top k项'''初始化物品池,物品池中物品出现的次数与其流行度成正比{item1:次数,item2:次数,...}''' def InitItemPool(self):itemPool=dict()groups = self.data.groupby([1])for item,group in groups:itemPool.setdefault(item,0)itemPool[item] =group.shape[0]itemPool=dict(sorted(itemPool.items(), key = lambda x:x[1], reverse = True))return itemPool'''获取每个用户对应的商品(用户购买过的商品)列表,如{用户1:[商品A,商品B,商品C],用户2:[商品D,商品E,商品F]...}''' def user_item(self):ui = dict()groups = self.data.groupby([0])for item,group in groups:ui[item]=set(group.ix[:,1])return ui'''初始化隐特征对应的参数numpy的array存储参数,使用dict存储每个用户(物品)对应的列'''def initParam(self):users=set(self.data.ix[:,0])items=set(self.data.ix[:,1])arrayp = np.random.rand(len(users), self.F) #构造p矩阵,[0,1]内随机值arrayq = np.random.rand(self.F, len(items)) #构造q矩阵,[0,1]内随机值P = pd.DataFrame(arrayp, columns=range(0, self.F), index=users)Q = pd.DataFrame(arrayq, columns=items, index=range(0,self.F))return P,Q'''self.Pdict=dict()self.Qdict=dict()for user in users:self.Pdict[user]=len(self.Pdict)for item in items:self.Qdict[item]=len(self.Qdict)self.P=np.random.rand(self.F,len(users))self.Q=np.random.rand(self.F,len(items))''''''生成负样本'''def RandSelectNegativeSamples(self,items):ret=dict()for item in items:#所有正样本评分为1ret[item]=1#负样本个数,四舍五入negtiveNum = int(round(len(items)*self.ratio))N = 0#while N<negtiveNum:#item = self.itemPool[random.randint(0, len(self.itemPool) - 1)]for item,count in self.itemPool.items():if N>negtiveNum: breakif item in items:#如果在用户已经喜欢的物品列表中,继续选continueN+=1#负样本评分为0ret[item]=0return retdef sigmod(self,x):# 单位阶跃函数,将兴趣度限定在[0,1]范围内y = 1.0/(1+exp(-x))return ydef lfmPredict(self,p, q, userID, itemID):#利用参数p,q预测目标用户对目标物品的兴趣度p = np.mat(p.ix[userID].values)q = np.mat(q[itemID].values).Tr = (p * q).sum()r = self.sigmod(r)return r'''使用随机梯度下降法,更新参数'''def stochasticGradientDecent(self,p,q):alpha=self.alphafor i in range(self.N):for user,items in self.ui.items():ret=self.RandSelectNegativeSamples(items)for item,rui in ret.items():eui = rui - self.lfmPredict(p,q, user, item) for f in range(0, self.F):#df[列][行]定位tmp= alpha * (eui * q[item][f] - self.lamda * p[f][user])q[item][f] += alpha * (eui * p[f][user] - self.lamda * q[item][f])p[f][user] +=tmp''' p=self.P[:,self.Pdict[user]]q=self.Q[:,self.Qdict[item]]eui=rui-sum(p*q)tmp=p+alpha*(eui*q-self.lamda*p)self.Q[:,self.Qdict[item]]+=alpha*(eui*p-self.lamda*q)self.P[:,self.Pdict[user]]=tmp ''' alpha*=0.9return p,qdef Train(self):self.itemPool=self.InitItemPool()#生成物品的热门度排行self.ui = self.user_item()#生成用户-物品p,q=self.initParam()#生成p,q矩阵 self.P,self.Q=self.stochasticGradientDecent(p,q) #随机梯度下降训练def Recommend(self,user):items=self.ui[user]predictList = [self.lfmPredict(self.P, self.Q, user, item) for item in items]series = pd.Series(predictList, index=items)series = series.sort_values(ascending=False)[:self.topk]return series'''#items=self.ui[user]p=self.P[:,self.Pdict[user]]rank = dict()for item,id in self.Qdict.items():#if item in items:# continueq=self.Q[:,id];rank[item]=sum(p*q)#return sorted(rank.items(),lambda x,y:operator.gt(x[0],y[0]),reverse=True)[0:self.topk-1];return sorted(rank.items(),key=operator.itemgetter(1),reverse=True)[0:self.topk-1];'''def recallAndPrecision(self,test):#召回率和准确率userID=set(test.ix[:,0])hit = 0recall = 0precision = 0for userid in userID:#trueItem = test[test.ix[:,0] == userid]#trueItem= trueItem.ix[:,1]trueItem=self.ui[userid]preitem=self.Recommend(userid)for item in list(preitem.index):if item in trueItem:hit += 1recall += len(trueItem)precision += len(preitem)return (hit / (recall * 1.0),hit / (precision * 1.0))def coverage(self,test):#覆盖率userID=set(test.ix[:,0])recommend_items = set()all_items = set()for userid in userID:#trueItem = test[test.ix[:,0] == userid]#trueItem= trueItem.ix[:,1]trueItem=self.ui[userid]for item in trueItem:all_items.add(item)preitem=self.Recommend(userid)for item in list(preitem.index):recommend_items.add(item)return len(recommend_items) / (len(all_items) * 1.0)def popularity(self,test):#流行度userID=set(test.ix[:,0])ret = 0n = 0for userid in userID:preitem=self.Recommend(userid)for item in list(preitem.index):ret += math.log(1+self.itemPool[item])n += 1return ret / (n * 1.0)if __name__ == "__main__": start = time.clock() #导入数据 data=pd.read_csv('D:\\dev\\workspace\\PyRecSys\\demo\\ratings.csv',nrows=10000,header=None)data=data.drop(0)data=data.ix[:,0:1]train,test=cross_validation.train_test_split(data,test_size=0.2)train = pd.DataFrame(train)test = pd.DataFrame(test)print ("%3s%20s%20s%20s%20s" % ('ratio',"recall",'precision','coverage','popularity'))for ratio in [1,2,3,5]:lfm = LFM(data,ratio)lfm.Train()#rank=lfm.Recommend('1')#print (rank)recall,precision = lfm.recallAndPrecision(test)coverage =lfm.coverage(test)popularity =lfm.popularity(test)print ("%3d%19.3f%%%19.3f%%%19.3f%%%20.3f" % (ratio,recall * 100,precision * 100,coverage * 100,popularity))end = time.clock() print('finish all in %s' % str(end - start))
执行后的指标:主要观察ratio比例对结果的影响
ratio recall precision coverage popularity1 7.001% 100.000% 12.976% 2.0032 7.001% 100.000% 9.663% 2.4123 7.001% 100.000% 7.869% 2.6275 7.001% 100.000% 5.356% 2.809
finish all in 702.2413190975064
【知识发现】隐语义模型LFM算法python实现(三)相关推荐
- 【知识发现】隐语义模型LFM算法python实现(一)
1.隐语义模型: 物品:表示为长度为k的向量q(每个分量都表示 物品具有某个特征的程度) 用户兴趣:表示为长度为k的向量p(每个分量都表示 用户对某个特征的喜好程度) 用户u对物品i的兴趣可以表示 ...
- 【知识发现】隐语义模型LFM算法python实现(二)
http://blog.csdn.net/fjssharpsword/article/details/78015956 基于该篇文章中的代码优化,主要是在生成负样例上提高执行速度,代码参考如下: # ...
- 推荐算法之隐语义模型(LFM)矩阵分解梯度下降算法实现
推荐算法之隐语义模型(LFM)矩阵分解梯度下降算法实现 基于协同过滤的推荐一般分为基于近邻的推荐和基于模型的推荐,其中,基于近邻是指预测时直接使用用户已有的偏好数据,通过近邻数据来预测新物品的偏好.而 ...
- 隐语义模型LFM(Latent Factor Model)
隐语义模型LFM(Latent Factor Model)是主题模型中的一种,跟其他主题模型一样,LFM也需要定义若干"主题",来表示个中隐含的关系,这些"主题" ...
- 隐语义模型( LFM )
基于模型的协同过滤思想 ●基本思想 -用户具有一定的特征,决定着他的偏好选择; -物品具有一定的特征,影响着用户需是否选 ...
- python实现lfm_【知识发现】隐语义模型(LFM,Latent Factor Model)推荐算法python实现
1.隐语义模型: 物品:表示为长度为k的向量q(每个分量都表示 物品具有某个特征的程度) 用户兴趣:表示为长度为k的向量p(每个分量都表示 用户对某个特征的喜好程度) 用户u对物品i的兴趣可以表示 ...
- python实现lfm_推荐系统之隐语义模型(LFM)
一 基本概念 LFM(latent factor model)隐语义模型,这也是在推荐系统中应用相当普遍的一种模型.那这种模型跟ItemCF或UserCF有什么不同呢?这里可以做一个对比: 对于Use ...
- 推荐系统(5)—隐语义模型(LFM)
https://www.toutiao.com/a6663676280782717454/ 2019-03-02 14:27:17 基本概念 LFM(latent factor model)隐语义模型 ...
- 无监督-主题模型(TM)/隐语义模型(LFM)(四):LDA(隐狄利克雷分布)【 数据(似然)(多项分布)+先验分布(狄雷分布)-> 后验分布(狄雷分布),后验分布作为下一轮的先验分布】【广泛使用】
一.LDA简介 1.概述01 LDA(Latent Dirichlet Allocation)模型是一种引入全概率模型的文本主题表示方法,其核心是:根据文本主题分布和主题词语分布的狄利克雷先验假设,结 ...
最新文章
- 继续上次WDS部署安装未在真机上面实现问题解答
- Python 学习日记5
- Ubuntu13.04 下源码安装Rapidsvn
- 搜索引擎solr和elasticsearch
- ajax发送post请求_按键精灵安卓版发送post和get请求
- linux之NTP服务
- 简单明了区分escape、encodeURI和encodeURIComponent
- python中xlsxwriter_python xlsxwriter使用方法汇总
- Leetcode 950. Reveal Cards In Increasing Order
- php类中双冒号和-的区别
- C语言课后习题(25)
- R中数据结构与数据的输入
- 对比AppScan Source和Fortify扫描AltoroJ的结果
- 水凝胶 静电纺丝_离子液体/水和静电纺丝条件对聚偏氟乙烯纳米纤维晶体结构的影响...
- 黑客入侵电脑网络四大步骤全面曝光
- 华为数通HCNA学习资料
- win10 外接键盘 win失效
- Java面向对象总结篇
- 网易服务器维护,网易:方便玩家 各大区服务器维护详细时间表列
- 手摸手,带你用vue撸后台 系列一(基础篇)