web推荐系统知识介绍

利用数据提供某种建议是很常见的需求,它可以改善用户体验,好的推荐系统可以比较精准地为用户提供信息,增加购买率。

目前常见的推荐算法有以下三种:

1. 基于热度进行推荐
2. 基于用户的协同过滤方法
3. 基于物品的协同过滤方法

1. 基于热度进行推荐

优点是简单粗暴,利用目前访问量最大的一些数据做推荐,技术上可以结合redis一类的内存型NoSQL系统。但缺点也很明显,不够个性化,我们不清楚每个用户的兴趣和习惯,精准率也低。不过在面对一些新用户的时候,由于我们对他们一无所知,所以基于热度进行推荐可能是对于这种情况比较好的一种推荐方法。

2. 基于用户的协同过滤方法

这种方法一般会利用用户兴趣爱好,找到有类似兴趣爱好的人,然后根据这些人的爱好向你推荐你可能感兴趣的东西。

为此我们需要找到一种度量两个用户之间相似程度的方法,这可以用“余弦相似度”,从数学的角度理解就是两个向量夹角的余弦值(初中知识)

两个向量的夹角的余弦值

计算公式如下:

公式1-两个向量夹角余弦值计算

于是可知,cosθ ∈ [-1, 1]

直观上来说,它用来测量两个向量之间的“角度”,如果两个向量指向同一方向,那么余弦值相似度就是1,如果相反,则是-1,如果两个向量互相垂直,为0,这表示两个向量之间没有任何关系。我们可以把这部分概念应用到实际中,但是我们只考虑相关和不想关,即只取0和1两种值。0表示不相关,1表示相关,负相关我们暂时不去考虑,因为这比判定正相关要复杂很多,例如我们可以很明显地看出手办爱好者和动漫迷的关系是正相关(有可能他们都被打了动漫的标签),但是就很难去判断厨师和程序猿的关系,所以这种关系不明确的情况,一般就赋予0值。

刚才只考虑了两个向量的情况,现在来考虑更普遍的情形。我们先根据两个用户的爱好来计算他们之间的相似度,的在此之前需要知道Jaccard公式

公式2-Jaccard公式

N(u)表示用户u的爱好集合,N(v)表示用户v的爱好集合,Jaccard公式可以计算用户u和v的相似度。

还有一个公式计算余弦相似度

公式3-余弦相似度公式

|N(u)|和|N(v)|分表表示用户u和用户v爱好集合的大小。

(1)推荐相似用户

有了上面这两个公式,就可以着手计算出每两个用户之间的相似度了。

为此可以建立一个用户-爱好表,比如有A、B、C、D四个用户,以及a、b、c、d、e 5个爱好,这四个用户具有的爱好情况如下:

A | a b d
B | a c
C | b e
D | c d e

据此可以建立物品-用户倒排表了:

a | A B
b | A C
c | B D
d | A D
e | C D

然后建立一个用户-用户矩阵,矩阵的行和列都是用户,i行j列的数字表示这个元素对应行的用户和对应列的用户的喜欢相同物品的个数,需要注意的是,用户自身不和自己进行比较:

#   A   B   C   D

A   0   1   1   1

B   1   0   0   1

C   1   0   0   1

D   1   1   1   0

用户-用户矩阵

我们使用余弦相似度的公式来计算:

计算结果

根据这个相似度矩阵,就可以很直观地看出任意两个用户之间的相似度了。比如对于用户A来说,他与B、C、D三个用户的相似分别是

A<->B 0.4082482904638631

A<->C 0.4082482904638631

A<->D 0.3333333333333333

由此可见A和B、C两个用户比较相似度较高,和D用户相似度较低。

(2)推荐物品

对于物品,我们也可以利用相似性来推荐,刚才我们计算出了每两个用户之间的相似性,每个结果都是一个数值,这个数值的取值范围是[0, 1]。于是我们可以大胆地猜测,相似的人可能会喜欢相似的物品,虽然这不一定完全正确,但总能为我们提供一些有用的信息。

给定一个用户u,首先要从除了u的所有用户中选出K个和他最相似的用户,用集合S(u, K)表示。然后将S中用户喜欢的物品全部提取出来,并除去u喜欢的物品,得到一个物品列表,对于每个候选物品i,可以用如下公式计算用户u对它感兴趣的程度:

计算用户对物品感兴趣的程度

w(uv)表示用户u和用户v的相似度,r(vi)表示用户v对物品i的喜爱程度。

这里给定一个用户A,相似用户是B、C、D,候选物品为c、e,并取r(vi)都为1,那么根据上面的公式:

用户A对物品c的喜爱程度

用户A对物品e的喜爱程度

据此可以给出一个量化标准说明某用户对某物品的可能感兴趣程度。

(3)基于用户的协同过滤算法的一些问题

在二维向量的情况下,余弦相似度公式等价于:

二维向量情况下的余弦相似度公式

如果推广到更一般的形式:

n维向量情况下的余弦相似度公式

这里就有一个问题,在高维向量空间中,绝大多数向量之间都离得非常远,因此让他们之间的方向也悬殊很大,也就是说,当兴趣的个数增大时,即使是与给定用户“最相似的用户”,实际上很可能也没有什么相似之处。因此当兴趣数量很多的时候,基于用户的协同过滤方法不是很实用。

3. 基于物品的协同过滤方法

这种方法是先计算两个物品之间的相似度,然后将与用户当前物品相似的物品放到一起,并从中为用户推荐可能感兴趣的东西。

假设有ABCD四个用户和abcde五个物品,他们的关系如下(用户喜欢物品):

A | a b d
B | a c
C | b e
D | c d e

用户-物品矩阵

行-用户
列-物品

#  a  b  c  d  e

A  1  1  0  1  0

B  1  0  1  0  0

C  0  1  0  0  1

D  0  0  1  1  1

物品-用户矩阵(是用户-物品矩阵的转置)

行-物品
列-用户

#   A   B   C   D

a   1   1   0   0

b   1   0   1   0

c   0   1   0   1

d   1   0   0   1

e   0   0   1   1

如果两个用户的兴趣矩阵完全重合,那么他们的相似度为1,如果完全不重合,相似度为0。

接下来需要计算物品之间的相似度,我们根据余弦相似度公式可以考察两两物品间的相似度(注意物品对自己的相似度要置为0):

#   a    b    c    d    e

a   0   1/2  1/2  1/2   0

b  1/2   0    0   1/2  1/2

c  1/2   0    0   1/2  1/2

d  1/2  1/2  1/2   0   1/2

e   0   1/2  1/2  1/2   0

这个矩阵就是物品相似度矩阵。

一个实例

这个实例会考察:

1.使用基于用户的协同过滤算法来给指定用户推荐兴趣
2.使用基于物品的协同过滤算法来给指定用户推荐兴趣

我们有一个用户兴趣列表users_interests,users_interests中的每一个元素都代表某个用户的兴趣列表。

users_interests = [
    ["Hadoop", "Big Data", "HBase", "Java", "Spark", "Storm", "Cassandra"],
    ["NoSQL", "MongoDB", "Cassandra", "HBase", "Postgres"],
    ["Python", "scikit-learn", "scipy", "numpy", "statsmodels", "pandas"],
    ["R", "Python", "statistics", "regression", "probability"],
    ["machine learning", "regression", "decision trees", "libsvm"],
    ["Python", "R", "Java", "C++", "Haskell", "programming languages"],
    ["statistics", "probability", "mathematics", "theory"],
    ["machine learning", "scikit-learn", "Mahout", "neural networks"],
    ["neural networks", "deep learning", "Big Data", "artificial intelligence"],
    ["Hadoop", "Java", "MapReduce", "Big Data"],
    ["statistics", "R", "statsmodels"],
    ["C++", "deep learning", "artificial intelligence", "probability"],
    ["pandas", "R", "Python"],
    ["databases", "HBase", "Postgres", "MySQL", "MongoDB"],
    ["libsvm", "regression", "support vector machines"]
]

使用基于用户的协同过滤算法来给指定用户推荐兴趣

1.余弦相似度函数:

def cosine_similarity(v, w):
    return dot(v, w) / math.sqrt(dot(v, v) * dot(w, w))

2.对兴趣排重,获得兴趣列表:

unique_interests = sorted(list({ interest
                             for user_interests in users_interests
                             for interest in user_interests }))

这会生成一个唯一兴趣列表:

[
    'Big Data',
    'C++',
    'Cassandra',
    'HBase',
    'Hadoop',
    'Haskell',
    'Java',
    'Mahout',
    'MapReduce',
    'MongoDB',
    'MySQL',
    'NoSQL',
    'Postgres',
    'Python',
    'R',
    'Spark',
    'Storm',
    'artificial intelligence',
    'databases',
    'decision trees',
    'deep learning',
    'libsvm',
    'machine learning',
    'mathematics',
    'neural networks',
    'numpy',
    'pandas',
    'probability',
    'programming languages',
    'regression',
    'scikit-learn',
    'scipy',
    'statistics',
    'statsmodels',
    'support vector machines',
    'theory'
]

3.创建用户-兴趣向量,向量长度是unique_interests的长度:

def make_user_interest_vector(user_interests):
    return [1 if interest in user_interests else 0 for interest in unique_interests]

4.生成用户-兴趣矩阵:

user_interest_matrix = map(make_user_interest_vector, users_interests)

得到:

[
    [1, 0, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
    [0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
    [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 1, 0, 0],
    [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0],
    [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0],
    [0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0],
    [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1],
    [0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0],
    [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
    [1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
    [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0],
    [0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0],
    [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0],
    [0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
    [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0]
]

5.生成兴趣相似性矩阵:

user_similarities = [[cosine_similarity(interest_vector_i, interest_vector_j)
                            for interest_vector_j in user_interest_matrix]
                                for interest_vector_i in user_interest_matrix]

得到:

[[1.0, 0.3380617018914066, 0.0, 0.0, 0.0, 0.1543033499620919, 0.0, 0.0, 0.1889822365046136, 0.5669467095138409, 0.0, 0.0, 0.0, 0.1690308509457033, 0.0], [0.3380617018914066, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.6, 0.0], [0.0, 0.0, 1.0, 0.18257418583505536, 0.0, 0.16666666666666666, 0.0, 0.20412414523193154, 0.0, 0.0, 0.23570226039551587, 0.0, 0.47140452079103173, 0.0, 0.0], [0.0, 0.0, 0.18257418583505536, 1.0, 0.22360679774997896, 0.3651483716701107, 0.4472135954999579, 0.0, 0.0, 0.0, 0.5163977794943222, 0.22360679774997896, 0.5163977794943222, 0.0, 0.2581988897471611], [0.0, 0.0, 0.0, 0.22360679774997896, 1.0, 0.0, 0.0, 0.25, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.5773502691896258], [0.1543033499620919, 0.0, 0.16666666666666666, 0.3651483716701107, 0.0, 1.0, 0.0, 0.0, 0.0, 0.20412414523193154, 0.23570226039551587, 0.20412414523193154, 0.47140452079103173, 0.0, 0.0], [0.0, 0.0, 0.0, 0.4472135954999579, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.2886751345948129, 0.25, 0.0, 0.0, 0.0], [0.0, 0.0, 0.20412414523193154, 0.0, 0.25, 0.0, 0.0, 1.0, 0.25, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], [0.1889822365046136, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.25, 1.0, 0.25, 0.0, 0.5, 0.0, 0.0, 0.0], [0.5669467095138409, 0.0, 0.0, 0.0, 0.0, 0.20412414523193154, 0.0, 0.0, 0.25, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.23570226039551587, 0.5163977794943222, 0.0, 0.23570226039551587, 0.2886751345948129, 0.0, 0.0, 0.0, 1.0, 0.0, 0.3333333333333333, 0.0, 0.0], [0.0, 0.0, 0.0, 0.22360679774997896, 0.0, 0.20412414523193154, 0.25, 0.0, 0.5, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.47140452079103173, 0.5163977794943222, 0.0, 0.47140452079103173, 0.0, 0.0, 0.0, 0.0, 0.3333333333333333, 0.0, 1.0, 0.0, 0.0], [0.1690308509457033, 0.6, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0], [0.0, 0.0, 0.0, 0.2581988897471611, 0.5773502691896258, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0]]

6.获取和某个用户最相似的用户:

def most_similar_users_to(user_id):
    pairs = [(other_user_id, similarity)                  # find other
         for other_user_id, similarity in                 # users with
            enumerate(user_similarities[user_id])         # nonzero
         if user_id != other_user_id and similarity > 0]  # similarity
    return sorted(pairs,                                   # sort them
              key=lambda (_, similarity): similarity,     # most similar
              reverse=True)                               # first

考察和第0个用户相似的用户:

print "User based similarity"
print "most similar to 0"
print most_similar_users_to(0)

得到:

User based similarity
most similar to 0
[
    (9, 0.5669467095138409),
    (1, 0.3380617018914066),
    (8, 0.1889822365046136),
    (13, 0.1690308509457033),
    (5, 0.1543033499620919)
]

7.使用基于用户的协同算法为某个用户推荐兴趣:

def user_based_suggestions(user_id, include_current_interests=False):
    # sum up the similarities
    suggestions = defaultdict(float)
    for other_user_id, similarity in most_similar_users_to(user_id):
        for interest in users_interests[other_user_id]:
          suggestions[interest] += similarity

    # convert them to a sorted list
    suggestions = sorted(suggestions.items(),
                     key=lambda (_, weight): weight,
                     reverse=True)

    # and (maybe) exclude already-interests
    if include_current_interests:
        return suggestions
    else:
        return [(suggestion, weight)
            for suggestion, weight in suggestions
            if suggestion not in users_interests[user_id]]

为第0位用户推荐兴趣:

print "Suggestions for 0"
print user_based_suggestions(0)

得到:

[
    ('MapReduce', 0.5669467095138409),
    ('MongoDB', 0.50709255283711),
    ('Postgres', 0.50709255283711),
    ('NoSQL', 0.3380617018914066),
    ('neural networks', 0.1889822365046136),
    ('deep learning', 0.1889822365046136),
    ('artificial intelligence', 0.1889822365046136),
    ('databases', 0.1690308509457033),
    ('MySQL', 0.1690308509457033),
    ('programming languages', 0.1543033499620919),
    ('Python', 0.1543033499620919),
    ('Haskell', 0.1543033499620919),
    ('C++', 0.1543033499620919),
    ('R', 0.1543033499620919)
]

第0位用户的兴趣如下:

["Hadoop", "Big Data", "HBase", "Java", "Spark", "Storm", "Cassandra"]

发现他对大数据和数据库系统比较感兴趣,再看推荐兴趣的前三名:

('MapReduce', 0.5669467095138409)
('MongoDB', 0.50709255283711)
('Postgres', 0.50709255283711)

直观上还是比较合适的一个推荐。

使用基于物品的协同过滤算法来给指定用户推荐兴趣

1.生成兴趣-用户矩阵:

interest_user_matrix = [[user_interest_vector[j]
                     for user_interest_vector in user_interest_matrix]
                    for j, _ in enumerate(unique_interests)]

得到:

[
    [1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0],
    [0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0],
    [1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
    [1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0],
    [1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0],
    [0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0],
    [1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0],
    [0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0],
    [0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0],
    [0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0],
    [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0],
    [0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
    [0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0],
    [0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0],
    [0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0],
    [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
    [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
    [0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0],
    [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0],
    [0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
    [0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0],
    [0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
    [0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0],
    [0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0],
    [0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0],
    [0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
    [0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0],
    [0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0],
    [0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0],
    [0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
    [0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0],
    [0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
    [0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0],
    [0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0],
    [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
    [0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0]
]

2.获得和某个兴趣最相似的几个兴趣:

interest_similarities = [[cosine_similarity(user_vector_i, user_vector_j)
                      for user_vector_j in interest_user_matrix]
                     for user_vector_i in interest_user_matrix]

得到(数据很多,仅仅是列出来,不用去细看了):

[[1.0, 0.0, 0.4082482904638631, 0.3333333333333333, 0.8164965809277261, 0.0, 0.6666666666666666, 0.0, 0.5773502691896258, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.5773502691896258, 0.5773502691896258, 0.4082482904638631, 0.0, 0.0, 0.4082482904638631, 0.0, 0.0, 0.0, 0.4082482904638631, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], [0.0, 1.0, 0.0, 0.0, 0.0, 0.7071067811865475, 0.4082482904638631, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.35355339059327373, 0.35355339059327373, 0.0, 0.0, 0.5, 0.0, 0.0, 0.5, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.4082482904638631, 0.7071067811865475, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], [0.4082482904638631, 0.0, 1.0, 0.8164965809277261, 0.5, 0.0, 0.4082482904638631, 0.0, 0.0, 0.5, 0.0, 0.7071067811865475, 0.5, 0.0, 0.0, 0.7071067811865475, 0.7071067811865475, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], [0.3333333333333333, 0.0, 0.8164965809277261, 1.0, 0.4082482904638631, 0.0, 0.3333333333333333, 0.0, 0.0, 0.8164965809277261, 0.5773502691896258, 0.5773502691896258, 0.8164965809277261, 0.0, 0.0, 0.5773502691896258, 0.5773502691896258, 0.0, 0.5773502691896258, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], [0.8164965809277261, 0.0, 0.5, 0.4082482904638631, 1.0, 0.0, 0.8164965809277261, 0.0, 0.7071067811865475, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.7071067811865475, 0.7071067811865475, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], [0.0, 0.7071067811865475, 0.0, 0.0, 0.0, 1.0, 0.5773502691896258, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.5, 0.5, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], [0.6666666666666666, 0.4082482904638631, 0.4082482904638631, 0.3333333333333333, 0.8164965809277261, 0.5773502691896258, 1.0, 0.0, 0.5773502691896258, 0.0, 0.0, 0.0, 0.0, 0.2886751345948129, 0.2886751345948129, 0.5773502691896258, 0.5773502691896258, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.5773502691896258, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.7071067811865475, 0.0, 0.7071067811865475, 0.0, 0.0, 0.0, 0.0, 0.0, 0.7071067811865475, 0.0, 0.0, 0.0, 0.0, 0.0], [0.5773502691896258, 0.0, 0.0, 0.0, 0.7071067811865475, 0.0, 0.5773502691896258, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.5, 0.8164965809277261, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.7071067811865475, 0.7071067811865475, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.7071067811865475, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.5773502691896258, 0.0, 0.0, 0.0, 0.0, 0.0, 0.7071067811865475, 1.0, 0.0, 0.7071067811865475, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.7071067811865475, 0.5773502691896258, 0.0, 0.0, 0.0, 0.0, 0.0, 0.7071067811865475, 0.0, 1.0, 0.7071067811865475, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.5, 0.8164965809277261, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.7071067811865475, 0.7071067811865475, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.7071067811865475, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], [0.0, 0.35355339059327373, 0.0, 0.0, 0.0, 0.5, 0.2886751345948129, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.75, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.5, 0.7071067811865475, 0.2886751345948129, 0.5, 0.2886751345948129, 0.35355339059327373, 0.5, 0.2886751345948129, 0.35355339059327373, 0.0, 0.0], [0.0, 0.35355339059327373, 0.0, 0.0, 0.0, 0.5, 0.2886751345948129, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.75, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.35355339059327373, 0.2886751345948129, 0.5, 0.2886751345948129, 0.0, 0.0, 0.5773502691896258, 0.35355339059327373, 0.0, 0.0], [0.5773502691896258, 0.0, 0.7071067811865475, 0.5773502691896258, 0.7071067811865475, 0.0, 0.5773502691896258, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], [0.5773502691896258, 0.0, 0.7071067811865475, 0.5773502691896258, 0.7071067811865475, 0.0, 0.5773502691896258, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], [0.4082482904638631, 0.5, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.5, 0.0, 0.0, 0.4082482904638631, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.5773502691896258, 0.0, 0.0, 0.0, 0.0, 0.0, 0.7071067811865475, 1.0, 0.0, 0.7071067811865475, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.7071067811865475, 0.7071067811865475, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.5773502691896258, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], [0.4082482904638631, 0.5, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.5, 0.0, 0.0, 0.4082482904638631, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.7071067811865475, 0.0, 1.0, 0.5, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.8164965809277261, 0.0, 0.0, 0.0, 0.0, 0.7071067811865475, 0.0], [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.7071067811865475, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.7071067811865475, 0.0, 0.5, 1.0, 0.0, 0.5, 0.0, 0.0, 0.0, 0.0, 0.4082482904638631, 0.5, 0.0, 0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.5773502691896258, 0.0, 0.0, 0.0, 0.0, 0.5773502691896258, 0.0, 0.0, 1.0], [0.4082482904638631, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.7071067811865475, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.5, 0.0, 0.0, 0.5, 0.0, 0.5, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.5, 0.0, 0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.5, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.7071067811865475, 0.0, 0.0, 0.0, 0.7071067811865475, 1.0, 0.0, 0.7071067811865475, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.7071067811865475, 0.35355339059327373, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.7071067811865475, 1.0, 0.0, 0.0, 0.0, 0.5, 0.7071067811865475, 0.0, 0.5, 0.0, 0.0], [0.0, 0.4082482904638631, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.2886751345948129, 0.2886751345948129, 0.0, 0.0, 0.4082482904638631, 0.0, 0.0, 0.4082482904638631, 0.0, 0.0, 0.5773502691896258, 0.0, 0.0, 0.0, 1.0, 0.0, 0.3333333333333333, 0.0, 0.0, 0.6666666666666666, 0.0, 0.0, 0.5773502691896258], [0.0, 0.7071067811865475, 0.0, 0.0, 0.0, 1.0, 0.5773502691896258, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.5, 0.5, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.2886751345948129, 0.2886751345948129, 0.0, 0.0, 0.0, 0.0, 0.5773502691896258, 0.0, 0.8164965809277261, 0.4082482904638631, 0.0, 0.0, 0.0, 0.0, 0.3333333333333333, 0.0, 1.0, 0.0, 0.0, 0.3333333333333333, 0.0, 0.5773502691896258, 0.0], [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.7071067811865475, 0.0, 0.0, 0.0, 0.0, 0.0, 0.35355339059327373, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.5, 0.0, 0.5, 0.7071067811865475, 0.5, 0.0, 0.0, 0.0, 1.0, 0.7071067811865475, 0.0, 0.5, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.5, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.7071067811865475, 0.0, 0.0, 0.0, 0.7071067811865475, 1.0, 0.0, 0.7071067811865475, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.2886751345948129, 0.5773502691896258, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.5773502691896258, 0.0, 0.0, 0.0, 0.6666666666666666, 0.0, 0.3333333333333333, 0.0, 0.0, 1.0, 0.4082482904638631, 0.0, 0.5773502691896258], [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.35355339059327373, 0.35355339059327373, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.7071067811865475, 0.5, 0.0, 0.0, 0.0, 0.5, 0.7071067811865475, 0.4082482904638631, 1.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.7071067811865475, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.5773502691896258, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0], [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.5773502691896258, 0.0, 0.0, 0.0, 0.0, 0.5773502691896258, 0.0, 0.0, 1.0]]

3.获得和某个兴趣最相似的几个兴趣:

def most_similar_interests_to(interest_id):
    similarities = interest_similarities[interest_id]
    pairs = [(unique_interests[other_interest_id], similarity)
         for other_interest_id, similarity in enumerate(similarities)
         if interest_id != other_interest_id and similarity > 0]
    return sorted(pairs,
              key=lambda (_, similarity): similarity,
              reverse=True)

我们考察和unique_interests[0]最相似的几个兴趣:

print "Item based similarity"
print "most similar to", unique_interests[0]
print most_similar_interests_to(0)

得到:

Item based similarity
most similar to Big Data
[
    ('Hadoop', 0.8164965809277261),
    ('Java', 0.6666666666666666),
    ('MapReduce', 0.5773502691896258),
    ('Spark', 0.5773502691896258),
    ('Storm', 0.5773502691896258),
    ('Cassandra', 0.4082482904638631),
    ('artificial intelligence', 0.4082482904638631),
    ('deep learning', 0.4082482904638631),
    ('neural networks', 0.4082482904638631),
    ('HBase', 0.3333333333333333)
]

发现和“Big Data”最相似的前三个兴趣分别是:

Hadoop
Java
MapReduce

相关性还是比较强的。

4.使用基于物品的协同算法为用户推荐兴趣:

def item_based_suggestions(user_id, include_current_interests=False):
    suggestions = defaultdict(float)
    user_interest_vector = user_interest_matrix[user_id]
    for interest_id, is_interested in enumerate(user_interest_vector):
        if is_interested == 1:
            similar_interests = most_similar_interests_to(interest_id)
            for interest, similarity in similar_interests:
                 suggestions[interest] += similarity

    suggestions = sorted(suggestions.items(),
                     key=lambda (_, similarity): similarity,
                     reverse=True)

    if include_current_interests:
        return suggestions
    else:
        return [(suggestion, weight)
                    for suggestion, weight in suggestions
                        if suggestion not in users_interests[user_id]]

我们为第0位用户推荐兴趣:

print "suggestions for user 0"
print item_based_suggestions(0)

得到:

suggestions for user 0
[
    ('MapReduce', 1.861807319565799),
    ('Postgres', 1.3164965809277263),
    ('MongoDB', 1.3164965809277263),     
    ('NoSQL', 1.2844570503761732),
    ('programming languages', 0.5773502691896258),
    ('MySQL', 0.5773502691896258),         
    ('Haskell', 0.5773502691896258),
    ('databases', 0.5773502691896258),
    ('neural networks', 0.4082482904638631),
    ('deep learning', 0.4082482904638631),
    ('C++', 0.4082482904638631),
    ('artificial intelligence', 0.4082482904638631),
    ('Python', 0.2886751345948129),
    ('R', 0.2886751345948129)
]

这个推荐也是合理的。

基于用户还是基于物品?

在针对大数据集生成推荐列表时,基于物品的过滤要比基于用户的过滤更快,效果也更好,但是基于物品的过滤算法更复杂一点,而且要维护一个物品相似度表,不过好在物品之间的相似度基本不怎么变化,所以很多电商都采用基于物品的协同过滤算法。

参考资料:

[1] Joel Grus 数据科学入门 [M].人民邮电出版社,2016.
[2] Create, Chen. 基于用户的协同过滤推荐算法原理和实现[EB/OL].http://www.cnblogs.com/technology/p/4467895.html.