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

来源:互联网 发布:linux dns 添加mx 编辑:程序博客网 时间:2024/06/07 05:13

全栈工程师开发手册 (作者:栾鹏)

python数据挖掘系列教程

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

本文我们通过为用户推荐物品的方式学习协作性过滤算法

只要数据集符合“主语-宾语-取值”,也就是说,每行代表一个主语对不通过宾语的取值,每一列代表不同主语对同一宾语的取值。取值甚至可以只有0、1固定值都可以使用这种算法。进行行列的匹配、推荐。

例如:
影评人(主语)给电影(宾语)打分(取值)。 向影评人推荐相似影评人,向影评人推荐电影。
买家(主语)给宝贝(宾语)评分(取值)。向淘宝买家推荐宝贝等。
读者(主语)停留在某类文章(宾语)上的时间长度(取值)。向读者推荐类似文件的强度。
上网用户(主语)对某博主(宾语)进行了点击(取值0、1)。向用户推荐感性的博主。
有多的应用需要在工作中学会洞察。

构造数据集

首先需要构造数据集,下载地址
https://grouplens.org/datasets/movielens/

这里我们先不使用网页中复杂的数据集,我们先使用如下简单的数据集。至于如何读取下载的数据集成下面的字典格式,相信大家肯定会了。不会的可以参考csv文件读写http://blog.csdn.net/luanpeng825485697/article/details/78358997

# 偏好数据集(人-电影-评分)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代表完全不同。

欧几里得距离计算

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)

皮尔逊相似度计算

# 计算两行的皮尔逊相似度,以此来代表相似度。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)  #打印推荐排名

完整的代码

#根据偏好数据集,匹配行和列,进而实现推荐行和推荐列# 偏好数据集(人-电影-评分)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}}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)# 计算两行的皮尔逊相似度,以此来代表相似度。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 rankingsif __name__=="__main__":     #只有在执行当前模块时才会运行此函数    #利用相似人推荐相似物品    rankings = getRecommendations(prefs,'name7')    print(rankings)   #打印推荐排名    #利用相似物品推荐相似物品    itemMatch = calculateSimilarItems(prefs)  # 提前计算所有物品的相似物品    rankings = getRecommendedItems(prefs,itemMatch,'name7')    print(rankings)  #打印推荐排名
阅读全文
1 0
原创粉丝点击