您的位置:首页 > 编程语言 > Python开发

python机器学习案例系列教程——匹配和推荐

2017-12-09 09:46 573 查看
全栈工程师开发手册 (作者:栾鹏)

python数据挖掘系列教程

主流的推荐系统算法大致分为两类:

基于用户行为数据的协同过滤算法

基于内容数据的过滤算法

大致而言,基于内容数据的算法适用于cold start,即用户和项目都比较少的时候,而基于用户行为数据的协同过滤算法在用户和项目较多,数据比较丰富的情况下有较高的准确率。

除此之外,还包括基于社会网络数据的推荐,基于语境(上下文)感知数据的推荐,基于心理学数据的推荐等等。

1. 基于用户行为数据的算法

1.1 基于用户的协同过滤算法(user-based CF)

一个用户喜欢和他具有相似喜好的用户喜欢的项目, 两个用户喜欢的项目交集越大, 这两个用户越相似。

两个用户兴趣相似度的计算可以有多种方法,常见的如 Pearson相关(皮尔逊相似性)和余弦相似度计算。

1.2 基于项目的协同过滤

基于项目的协同过滤推荐(item-based CF)基于这样的假设: 一个用户会喜欢与他之前喜欢的项目相似的项目。因此,基于项目的协同过滤推荐关键在于计算物品之间的相似度。

基于用户的协同过滤和基于项目的协同过滤统称为基于邻域的推荐 (nearest neighbor recommendation),也称作基于记忆的推荐算法(memory-based recommendation)。

基于邻域的推荐算法需要维护一个用户相似度矩阵或项目相似度矩阵, 因此对于项目的数目更新速度远远小于用户数目的增长速度的情况,宜采用基于项目的推荐算法, 如 Amazon 建立的推荐系统正是基于项目的协同过滤推荐算法, 还有移动应用产品的推荐。另外, 有研究表明, 基于项目的算法一般在性能上要优于基于用户的算法。

1.3 基于模型的协同过滤

基于模型的协同过滤推荐 (model-based CF) 是采用机器学习或数据挖掘等算法, 用训练数据来学习识别复杂模式, 从而得到学习模型, 然后基于学习模型在数据集上进行智能预测。

隐语义模型 (latent semantic CF models)/矩阵分解模型(matrix factorization)

贝叶斯信念网协同过滤模型(Bayesian belief nets CF models

聚类协同过滤模型 (clustering CF models)

概率因素模型(probabilistic factor models)

2. 基于内容数据的推荐

对一个给定的用户, 推荐与他之前喜欢的项目在内容上有相似性的其他项目。

这种推荐仅需要得到两类信息: 项目特征的描述和用户过去的喜好信息。

利用领域专家给项目打标签的方法 , 也即传统的分类系统(Taxonomy),

另一种是用户给项目打标签, 也即大众分类系统 (Folksolomy)。

3. 基于社会网络数据的推荐

在社会网络中, 与其他用户进行链接的用户, 表示愿意分享一定共同的兴趣爱好, 或者他们之间有相似的品味(同质性原理),

这里的相似性被期望来帮助改进推荐准确率和质量

基于社会网络数据的推荐(social network-based recommendation)早先大部分是基于领域的方法。首先探索打分者的社会网络, 聚集打分者的打分来计算预测打分; 然后找到打分者的邻居。除了简单的基于邻域的方法, 还有许多是基于模型的推荐方法,如采用矩阵分解方法、 图模型, 将用户的社会网络和用户物品的喜好关系建模到一张图中, 联合基于项目的推荐和基于信任(trust-based)的推荐, 然后利用随机游走算法给用户做推荐。采用异构数据源对用户兴趣进行建模, 从异构社会网络中学习相关性。

这里, 基于信任的推荐是将信任度引入推荐系统, 用信任度代替相似度。有许多方法可以计算用户的 top-N信任邻居。用一个信任矩阵 (如 Eigentrust)来计算 top-N 被信任的用户, 或者激活扩散模型(spreading activation model)通过他们接收到的能量来给结点排序。

4. 基于语境(上下文)感知数据的推荐(context aware-based recommendation)

语境信息类型包括时间、 信息、 外界物理环境 (如天气、温度等)、 设备类型、 周围人员、 活动状态、 目的/意图等。

还有些系统考虑了情绪、 计算平台、 网络条件、社会网络等更为广泛的语境。

(1)、通过语境驱动的查询和搜索推荐

典型的是使用语境信息来查询或搜索一个特 定的资源应答(如餐馆), 并给出最好的匹配资源推荐给用户(如离用户最近的当前营业着的餐馆)

(2)、通过语境喜好的启发和评估推荐

使用多维方法来整合语境信息到推荐系统, 这里传统的二维用户/项目范例被延伸来支持额外的语境维度, 如时间、 位置和公司。

5. 基于人口统计学数据的推荐

基于人口统计学数据的推荐 (demographic-based recommendation)是根据人口统计学数据对每个用户建立一个用户剖面(user profile), 系统根据用户的剖面图, 计算用户间相似度, 得到当前用户的最近邻集, 最后系统会把基于 “邻居” 用户群喜好的项目推荐给当前用户。

6. 基于心理学数据的推荐

人的心理特征和情感因素在用户做决策时非常重要。人类情感、 个性及其模型已经被广泛地在计算机上实施。基于心理学数据的推荐(psychology-based recommendation)方法有情感智能 (emotional intelligence)、 满意度 (satisfaction) 、 心理作用 (psychological effects)、 个性特质 (personalitybased)。

值得一提的是, Neflix竞赛获奖者之一就是学心理学的。

7. 基于大数据的推荐(big data-based recommendation)

目前, 大数据通常被认为是当前传统技术难以处理的, 这里的传统技术也包括传统推荐技术。

基于大数据的推荐(big data-based recommendation)有两个内涵: 一个是大数据使得传统推荐技术更加准确; 另一个是传统的技术已经不能满足需求, 大数据需要新的推荐技术。

大数据的来源多样, 对推荐系统而言,“大数据”包含系统可触及到的数据, 如用户行为数据、 社会网络数据、 人口统计学数据、 语境感知数据等。

大数据环境下的推荐是至少基于两种类型的数据而进行的推荐。研究表明, 基于大数据的各种混合推荐算法的推荐效果要优于单纯的基于一种数据的推荐, 如将基于社会网络的推荐和协同过滤推荐结合, 将基于内容的推荐和协同过滤方法结合, 跨领域推荐。

案例

在样本数据集中,数据存储一般以矩阵的形式,每一行代表一个对象,每一列代表一个特征属性。而文本介绍一种稀疏高维的特征数据集。例如在文本分类中,文章的特征用文章包含的单词表示,在客户购物中,客户的特征用所购买的物品表示。这种对象的特征维度往往都是很大的,并且是稀疏的。对于这种数据集进行计算,算法往往比较复杂。

一个普通的特征数据集如下表:

人        身高    年龄     体重
小明1     182     23      67
小明2     181     24      65
小明3     184     24      65
小明4     183     21      68
小明5     185     22      66


一个稀疏高维特征数据集如下:0表示没买,1表示买

客户     宝贝1   宝贝2    宝贝3   宝贝4   宝贝5   宝贝6  宝贝7    ...
客户1     1       0       0       0      0      1      0      ...
客户2     0       0       1       0      1      0      0      ...
客户3     0       1       0       0      0      0      1      ...
客户4     0       0       1       0      0      0      0      ...
客户5     1       0       0       0      1      0      0      ...
客户6     0       0       0       1      0      0      0      ...
客户7     0       0       0       0      1      0      0      ...
客户8     0       1       0       0      0      1      0      ...
客户9     0       0       0       0      1      0      0      ...


高维稀疏特征数据集是经常存在的,例如:

影评人给电影打分。 向影评人推荐相似影评人,向影评人推荐电影。

买家给宝贝评分。向淘宝买家推荐宝贝等。

读者停留在某类文章上的时间长度。向读者推荐类似文件的强度。

上网用户对某博客进行了点击。向用户推荐感性的博主。

有多的应用需要在工作中学会洞察。

对这种数据集进行聚类或者分类往往比较困难,而且尤其当我们关系的特征出现的数量很少还要主要注意进行样本均衡。这些读者可以在以后的学习中注意。

本文仅以电影推荐这个例子来讲述匹配与推荐的算法(协作性过滤算法)。

构造数据集

我们先使用如下简单的数据集。

注意:实际中特征数据集可能是稀疏的,因为本文重点是为了了解匹配和推荐机制。

# 偏好数据集(人-电影-评分)
prefs={
'name1': {'movie1': 2.5, 'movie2': 3.5,'movie3': 3.0, 'movie4': 3.5, 'movie5': 2.5, 'movie6': 3.0},
'name2': {'movie1': 3.0, 'movie2': 3.5,'movie3': 1.5, 'movie4': 5.0, 'movie6': 3.0,'movie5': 3.5},
'name3': {'movie1': 2.5, 'movie2': 3.0,'movie4': 3.5, 'movie6': 4.0},
'name4': {'movie2': 3.5, 'movie3': 3.0, 'movie6': 4.5, 'movie4': 4.0, 'movie5': 2.5},
'name5': {'movie1': 3.0, 'movie2': 4.0, 'movie3': 2.0, 'movie4': 3.0, 'movie6': 3.0,'movie5': 2.0},
'name6': {'movie1': 3.0, 'movie2': 4.0, 'movie6': 3.0, 'movie4': 5.0, 'movie5': 3.5},
'name7': {'movie2':4.5,'movie5':1.0,'movie4':4.0}
}


相似度计算方法

我们可以对行或对列进行相似度计算。这里先了解如何对计算两个行的相似程度。我们采用欧几里得距离和皮尔逊相似度。将计算的值映射的0-1范围上来代表相似程度,1代表完全相同,0代表完全不同。

注意:读者可以思考,如何计算高维稀疏特征数据集中两行之间的相似度

欧几里得距离计算

公式:|x| = √( x[1]2 + x[2]2 + … + x
2 ) 欧式距离百科

获取两个行(对象)的相同列,生成一个局部特征数据集。再根据这个局部特征数据集计算欧式距离(这是因为属性数据集太系数,直接计算相似度会计算出来的值特别小)。

from math import sqrt
# 计算两行之间的欧几里得距离,以此来代表相似度。prefs表示偏好数据集
def sim_distance(prefs,row1_name,row2_name):
# 首先计算是否有共同列(都看过的电影)
si={}
for item in prefs[row1_name]:
if item in prefs[row2_name]: si[item]=1

# 如果没有共同列,则两行之间相似度为0
if len(si)==0: return 0

# 根据共同列计算两行的欧几里得距离,并将距离映射到0-1上。0表示完全不相似,1表示完全相似
sum_of_squares=sum([pow(prefs[row1_name][item]-prefs[row2_name][item],2) for item in prefs[row1_name] if item in prefs[row2_name]])
return 1/(1+sum_of_squares)


皮尔逊相似度计算

假设有两个向量X、Y,那么两向量间的皮尔逊相关系数可通过以下公式计算:

公式一:



可以将公式进行多种演变,其中一种转换为下面的公式。

公式二:



其中E是数学期望,cov表示协方差,N表示变量取值的个数。

我们就根据公式二进行计算。

获取两个行(对象)的相同列,生成一个局部特征数据集。再根据这个局部特征数据集计算皮尔逊相似度(这是因为属性数据集太系数,直接计算相似度会计算出来的值特别小)。

虽然皮尔逊相似度的计算过程更复杂一些,但它在数据不是很规范的时候(例如:影评者对影片的评价总是相对于平均水平偏离很大时),会倾向于给出很更好的结果。

# 计算两行的皮尔逊相似度,以此来代表相似度。prefs表示数据集
def sim_pearson(prefs,row1_name,row2_name):
# 首先计算是否有共同列(都看过的电影)
si={}
for item in prefs[row1_name]:
if item in prefs[row2_name]: si[item]=1

# 如果没有共同列,两行之间相似度为0
if len(si)==0: return 0

# 得到列表元素个数
n=len(si)

# 对两行的共同列求和
sum1=sum([prefs[row1_name][it] for it in si])
sum2=sum([prefs[row2_name][it] for it in si])

# 对两行的共同列求平方和
sum1Sq=sum([pow(prefs[row1_name][it],2) for it in si])
sum2Sq=sum([pow(prefs[row2_name][it],2) for it in si])

# 对两行的共同列求乘积之和
pSum=sum([prefs[row1_name][it]*prefs[row2_name][it] for it in si])

# 计算皮尔逊评价值
num=pSum-(sum1*sum2/n)
den=sqrt((sum1Sq-pow(sum1,2)/n)*(sum2Sq-pow(sum2,2)/n))
if den==0: return 0

r=num/den

return r


匹配相似行

有了每两行之间的相似度计算方法,就可以计算每行的相似行了。在计算相似行时,对于两个特征属性的数量不同的行计算相似度时,只用计算公共特征属性即可。

# 匹配相似行
# 根据偏好数据集,返回与某个行最匹配的n行。person表示要匹配的行(人),similarity表示相似度计算函数
def topMatches(prefs,row_name,n=5,similarity=sim_pearson):
scores=[(similarity(prefs,row_name,other),other) for other in prefs if other!=row_name]
scores.sort()
scores.reverse()
num = n
if n>len(scores):num= len(scores)
return scores[0:num]


利用相似行估计列的值,并排名

有了相似行,某一行就可以根据相似行对各列的评值,来估计当前行各列存在的空白值。为了避免算法的不精准,所以相似行不止一个,每个相似行根据相似度确定权重。最后估值采用加权平均的方式。

对当前行各列存在的空白值进行估计以后,将估计的值进行排名推荐。

# 利用相似行,估计某行所有列存在的空白值,并排名(估计影片评分,并排名推荐)
# 利用所有其他行的各列取值的加权平均(相似度为权值),为某行各列提供估值
def getRecommendations(prefs,row_name,similarity=sim_pearson):
totals={}
simSums={}
for other in prefs:
# 不和自己做比较
if other==row_name: continue
sim=similarity(prefs,row_name,other)

# 忽略评价值为0或为负的情况
if sim<=0: continue
for item in prefs[other]:
# 只对自己还未有的列进行临时估值
if item not in prefs[row_name] or prefs[row_name][item]==0:
# 相似度*临时估值
totals.setdefault(item,0)
totals[item]+=prefs[other][item]*sim
# 相似度之和
simSums.setdefault(item,0)
simSums[item]+=sim

# 建立归一化列表
rankings=[(total/simSums[item],item) for item,total in totals.items()]

# 返回最终估值经过排序的列表
rankings.sort()
rankings.reverse()
return rankings


匹配相似列

对于数据来说他无法识别每行的含义,只是在做行间运算。如果我们把数据矩阵进行转置,再做行间运算。那就是进行的列匹配。

数据集转置

def transformPrefs(prefs):
result={}
for row_name in prefs:
for item in prefs[row_name]:
result.setdefault(item,{})

# 将行与列对调
result[item][row_name]=prefs[row_name][item]
return result


匹配相似列

# 匹配相似列,返回各列的匹配集合(因为各列的匹配可提前在用户登陆前完成),
# 根据转置后的偏好数据集,获取每列相似的n个其他列
def calculateSimilarItems(prefs,n=10):
# 建立字典,以给出与这些列最为相近的所有其他列
itemMatch={}
# 以列为中心对偏好矩阵实施转置处理
itemPrefs=transformPrefs(prefs)
c=0
for item in itemPrefs:
# 针对大数据集更新状态变量
c+=1
if c%100==0: print("%d / %d" % (c,len(itemPrefs)))
# 寻找最为相近的列
scores=topMatches(itemPrefs,item,n=n,similarity=sim_distance)
itemMatch[item]=scores

return itemMatch    #返回每列匹配的其他列


利用相似列,对某一行的各列空白处进行估值

与根据相似行对各列的空白值进行估计类似。

# 利用相似列,对某一行的各列进行估值,(估计影片评分,并排名推荐):根据偏好数据集和提前构造好的物品匹配库,向用户推荐物品
def getRecommendedItems(prefs,itemMatch,row_name):
onerow=prefs[row_name]  #获取当前行所拥有的列
scores={}
totalSim={}
# 循环遍历由当前行所拥有的列
for (item,rating) in onerow.items( ):

# 循环遍历与当前列相似的列
for (similarity,item2) in itemMatch[item]:

# 忽略行已经拥有的列
if item2 in onerow: continue
# 估值与相似度的加权之和
scores.setdefault(item2,0)
scores[item2]+=similarity*rating
# 全部相似度之和
totalSim.setdefault(item2,0)
totalSim[item2]+=similarity

# 将每个合计值除以加权和,求出平均值
rankings=[(score/totalSim[item],item) for item,score in scores.items( )]

# 按最高值到最低值的顺序,返回估值排行
rankings.sort( )
rankings.reverse( )
return rankings


运行试验

有了上面的算法,我们就可以来尝试效果了。

if __name__=="__main__":     #只有在执行当前模块时才会运行此函数
#利用相似人推荐相似物品
rankings = getRecommendations(prefs,'name7')
print(rankings)   #打印推荐排名
#利用相似物品推荐相似物品
itemMatch = calculateSimilarItems(prefs)  # 提前计算所有物品的相似物品
rankings = getRecommendedItems(prefs,itemMatch,'name7')
print(rankings)  #打印推荐排名


应该选用哪一种相似性度量方式

我们在此处已经介绍了两种不同的度量方式,但实际上,还有许多其他的衡量两个数组相似度的方式。例如jaccard系数和曼哈顿距离算法。使用哪种方式最优取决于具体的应用。如果你想看看哪种方式最优建议你都尝试一下。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐