集体智慧编程之用户相似度

Publish: September 10, 2018 Category: 数据分析 No Comments

寻找相近的用户

我们有如下数据,列出了每个人对每部电影的喜好评分,数字越大表示越喜欢。要做推荐算法首先要做的是寻找相似爱好的用户,然后根据这些相似用户中最喜欢的一些电影推荐给该用户。

数据说明

首先第一步就是寻找相近用户,这里有两种相似用户寻找方式。

项目Lisa RoseGene SeymourMichael PhillipsClaudia PuigMick LaSalleJack MatthewsToby
Lady in the Water2.53.02.5 3.03.0
Snakes on a Plane3.53.53.03.54.04.04.5
Just My Luck3.01.5 3.02.0
Superman Returns3.55.03.54.05.05.04.0
You, Me and Dupree2.53.5 2.53.53.51.0
The Night Listener3.03.04.04.53.03.0

为方便处理用Python的字典方式描述如下:

critics = {'Lisa Rose': {'Lady in the Water': 2.5, 'Snakes on a Plane': 3.5,
                         'Just My Luck': 3.0, 'Superman Returns': 3.5, 'You, Me and Dupree': 2.5,
                         'The Night Listener': 3.0},
           'Gene Seymour': {'Lady in the Water': 3.0, 'Snakes on a Plane': 3.5,
                            'Just My Luck': 1.5, 'Superman Returns': 5.0, 'The Night Listener': 3.0,
                            'You, Me and Dupree': 3.5},
           'Michael Phillips': {'Lady in the Water': 2.5, 'Snakes on a Plane': 3.0,
                                'Superman Returns': 3.5, 'The Night Listener': 4.0},
           'Claudia Puig': {'Snakes on a Plane': 3.5, 'Just My Luck': 3.0,
                            'The Night Listener': 4.5, 'Superman Returns': 4.0,
                            'You, Me and Dupree': 2.5},
           'Mick LaSalle': {'Lady in the Water': 3.0, 'Snakes on a Plane': 4.0,
                            'Just My Luck': 2.0, 'Superman Returns': 3.0, 'The Night Listener': 3.0,
                            'You, Me and Dupree': 2.0},
           'Jack Matthews': {'Lady in the Water': 3.0, 'Snakes on a Plane': 4.0,
                             'The Night Listener': 3.0, 'Superman Returns': 5.0, 'You, Me and Dupree': 3.5},
           'Toby': {'Snakes on a Plane': 4.5, 'You, Me and Dupree': 1.0, 'Superman Returns': 4.0}}

欧几里得距离

  • 算法

简单理解就是两点之间的距离,用我们高中数学知识的公式表示方法如下

欧几里得距离

其中(x1, x2) (y1, y2)表示两点的坐标。

  • 实现

具体的算法实现

from math import sqrt
def sim_distance(prefs, person1, person2):
    si = {}
    # 没有交集数据直接返回
    for item in prefs[person1]:
        if item in prefs[person2]:
            si[item] = 1

    if len(si) == 0:  return 0

    sum_of_squares = sum(
        [pow(prefs[person1][item] - prefs[person2][item], 2) for item in prefs[person1] if item in prefs[person2]])

    return 1 / (1 + sqrt(sum_of_squares))

d1 = sim_distance(critics, 'Lisa Rose', 'Gene Seymour')
print (d1)

结果

0.294298055086

皮尔逊相关度

相对欧几里得距离,皮尔逊相关度略微复杂,但是对数据不是很规范,有偏离总体评价数据,的时候会有更好的效果。简单来说就是寻找两组数据与某一直线的拟合的度量。

为了更加形象的说明这个过程,使用matplotlib用散点图标注在图表中。matplotlib是python数据可视化的类库,还对数据分析学习有很大帮助,还不了解的同学建议学习下。

Gene Seymour 和 Mick LaSalle的散点图
image

Lisa Rose 和 Jack Matthews的散点图
image

马马虎虎能看出来后面的相似高于前者,通过这条拟合的曲线大致上能判断出两者的相似度。

图一生成脚本

from matplotlib.pylab import plt
import pandas as pd
fig = plt.figure()

critics = pd.Series(critics)
# plt.scatter(pd.Series(critics['Gene Seymour']).sort_index(), pd.Series(critics['Mick LaSalle']).sort_index())
plt.show()

图二生成脚本

from matplotlib.pylab import plt
import pandas as pd
fig = plt.figure()

critics = pd.Series(critics)

lisaData =  pd.Series(critics['Lisa Rose']).sort_index()
jackData =  pd.Series(critics['Jack Matthews']).sort_index()
# 计算交集
lisaData = lisaData.loc[lisaData.index.intersection(jackData.index)]
jackData = jackData.loc[lisaData.index.intersection(lisaData.index)]
plt.scatter(lisaData, jackData)

plt.show()
  • 算法

就我们的数据集,其基本算法思路如下。

  1. 找出两位评论者都评价过的商品
  2. 计算两者评分总和与平方和,求得评分的乘积之和
  3. 计算皮尔逊系数

公式描述:

皮尔逊相似度

其中 sum1和sum2偏好和, sum1Sq和sum2Sq平方和, pSum乘积和。

  • 实现
def sim_person(prefs, p1, p2):
    si={}
    for item in prefs[p1]:
        if item in prefs[p2]:
            si[item] = 1
    n = len(si)
    if n == 0: return 1

    # 所有偏好求和
    sum1 = sum([prefs[p1][it] for it in si])
    sum2 = sum([prefs[p2][it] for it in si])

    # 求平方和
    sum1Sq = sum([pow(prefs[p1][it], 2) for it in si])
    sum2Sq = sum([pow(prefs[p2][it], 2) for it in si])

    # 求乘积和
    pSum = sum([prefs[p1][it]*prefs[p2][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

print sim_person(critics, 'Lisa Rose', 'Gene Seymour')

结果

0.396059017191

最终用户行为相似度评分

通过上面的两个函数,生成最终的相似度排名和评分

def topMatches(prefs, person, n=5, similarity=sim_person):
    scores = [(sim_person(prefs, person, other), other) for other in prefs if other != person]

    scores.sort()
    scores.reverse()
    return scores[0:n]

print topMatches(critics, 'Toby', n=3)

返回结果

[(0.9912407071619299, 'Lisa Rose'), (0.9244734516419049, 'Mick LaSalle'), (0.8934051474415647, 'Claudia Puig')]

扩展应用,基于相似度的物品推荐

按照一般思路,物品推荐中我们基于上面得到的用户相似度排名,查找与自己喜好相近的人,然后从他所喜欢的影片中找出一部自己未看过的电影。但是这样存在两个问题:首先有些评论者未看过的电影恰恰是用户喜欢的,其次评论者可能有特殊的喜好偏离钟爱某部电影。

为了解决上面的问题,我们引入加权評價值来为影片打分。具体是用权重(相似度)乘以评分然后计算平均值。以影片《Night》为例:

其中Sim表示相似度,Score表示评分

代码实现

def getRecommendations(prefs, person, similarity=sim_person):
    totals = {}
    simSums = {}
    for other in prefs:
        if other == person: continue
        sim = similarity(prefs, person, other)

        if sim <= 0 : continue
        for item in prefs[other]:
            if item not in prefs[person] or prefs[person][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

print getRecommendations(critics, 'Toby')

输出结果

[(3.3477895267131013, 'The Night Listener'), (2.8325499182641614, 'Lady in the Water'), (2.5309807037655645, 'Just My Luck')]

总结

通过上面的两种简单的方法,得到了用户相似度的排名数据,有了该数据就可以对用户进行推荐了。后面经会继续学习相关的物品推荐内容。

Tags: 集体智慧编程, 用户相似度

Related Posts:
  • [尚无相关文章]

Leave a Comment