自定义代码实现UserCF——基于用户的协同过滤算法

文章向导

  • 一、什么是基于用户协同过滤算法
  • 二、如何计算相似度
  • 三、算法思路
  • 四、代码实现
    • 1、准备数据
    • 2、编写将数据标准化的函数
    • 3、编写特征距离计算函数
    • 4、 S ( u , k ) S(u,k) S(u,k) 和用户u最相似的k个用户
    • 5、 N ( i ) N(i) N(i)对电影 i 有过评分的用户
    • 6、 r v i r_{vi} rvi 用户 v 对 电影i的评分
    • 7、将前面的函数组装起来就是UserCF的公式实现
    • 8、查看各个商品对用户的推荐度

一、什么是基于用户协同过滤算法

  • 我觉得概念讲高大上了,并没有什么用,反而还难以理解。我就用通俗的语言来描述一下这个算法的逻辑吧。这个算法的核心就是这样的:当前如果有一个用户 A 他正在等待被推荐,这时我们就会找出平时的行为和 A 相似的用户 B ,然后我们再讲用户 B 在意或者感兴趣的且 A 还没浏览过的商品推荐给 A ;这就是基于用户的协同过滤算法。

二、如何计算相似度

  • 在前面我们了解到了UserCF的算法逻辑了之后,可能你会发现概念中有一个很重要的点就是,计算与A相似的用户,我们用什么一个标准来衡量两个用户的相似程度了;其实已经引出概念了,我们就是需要用相似度来表示两个用户之间的相似程度,但是相似度该如何计算呢?
  • 先别急,我们先来想想如果要描述一个人该怎么描述,当然是用一些这个人的特征来描述,对吧。我们先来看个用户的特征表
用户 游戏 学习
A 7 7
B 6 8
C 1 10
D 9 1
  • 上面这个中的数字代表各个用户对学习和游戏的看法(也可以看做是评分)满分是10分,我们可以很容易的就把上面的数据加载到图中来:
    自定义代码实现UserCF——基于用户的协同过滤算法_第1张图片
    把图画出来了和你可以很容易的看出 AB 两点挨得最近,也就是 B 与 A 相较于其他的点与 A 的相似度最高,那么如何计算相似度呢?上面都已经说了是挨得比较近,挨得比较近指得是是很么,肯定就是距离了呗,那距离公式就多了,我们平时最常用的就是欧式距离 ( x 1 − x 2 ) 2 + ( y 1 − y 2 ) 2 \displaystyle \sqrt{(x_1-x_2)^2+(y_1-y_2)^2} (x1x2)2+(y1y2)2 ,这里我们评判用户的特征就只用两个方面,所以就是一个二维,但在实际场景当中,往往用于衡量一个用户有很多的特征俩描述,这时我们如果还想使用欧氏距离,就要把它推导至N维,也就是这样:
    D i s t a n c e ( X , Y ) = ∑ i = 0 n ( x 1 − y 2 ) 2 \displaystyle Distance(X,Y) = \sqrt{\sum_{i=0}^{n}{(x_1-y2)}^2} Distance(X,Y)=i=0n(x1y2)2
    至于如何公式的推导,想进一步了解的小伙伴可以看博主往期专门讲距离公式的博文,上面有具体的推导过程, 以及其他的一些常用的距离公式算法推导,跳转地址
  • 有了上面的概念,我们就知道,我们可以使用两个用户之间的 ‘特征距离‘ 来衡量用户的的相似度了,不过你先要明确一个概念就是,用户的特征距离值越大(也可以说两个用户离得越远)那么两个用户的相似度也就越小,意思就是距离是和相似度呈反比的。

三、算法思路

  • 1、首先我们就需要将用户的一些特征数据转换成数值来衡量,因为毕竟计算机还是比较擅长于数值的处理。
    例如现在有一份观影数据表,表的含义就是各个用户的观影情况,以及对此电影的打分。

    用户名 观影名称 评分
    A 老炮儿 3.5
    A 唐人街探案 1.0
    B 老炮儿 2.5
    B 唐人街探案 3.5
    B 星球大战 3.0
    B 寻龙诀 3.5
    B 神探夏洛克 2.5
    B 小门神 3.0
    C 老炮儿 3.0
    C 唐人街探案 3.5
    C 星球大战 1.5
    C 寻龙诀 5.0
    C 神探夏洛克 3.0
    C 小门神 3.5
    D 老炮儿 2.5
    D 唐人街探案 3.5
    D 寻龙诀 3.5
    D 神探夏洛克 4.0
    E 老炮儿 3.5
    E 唐人街探案 2.0
    E 星球大战 4.5
    E 神探夏洛克 3.5
    E 小门神 2.0
    F 老炮儿 3.0
    F 唐人街探案 4.0
    F 星球大战 2.0
    F 寻龙诀 3.0
    F 神探夏洛克 3.0
    F 小门神 2.0
    G 老炮儿 4.5
    G 唐人街探案 1.5
    G 星球大战 3.0
    G 寻龙诀 5.0
    G 神探夏洛克 3.5

    上面的表有一个很关键的指标就是评分,可以通过这个值的大小来看出用户对该电影的喜爱度(也可以说是感兴趣程度),我们都知道描述一个用户的特征是多维度的,而且作为分析需要,我们描述各个用户都应该从相同的维度来描述。根据上面的思路,再根据表的结构,我们可以将上面的表转换成下面的格式:

    * 神探夏洛克 小门神 唐人街探案 寻龙诀 老炮儿 星球大战
    A 0 0 1.0 0 3.5 0
    B 2.5 3.0 3.5 3.5 2.5 3.0
    C 3.0 3.5 3.5 5.0 3.0 1.5
    D 4.0 0 3.5 3.5 2.5 0
    E 3.5 2.0 2.0 0 3.5 4.5
    F 3.0 2.0 4.0 3.0 3.0 2.0
    G 3.5 0 1.5 5.0 4.5 3.0

    转换成上面这个表的思路其实就是现将所有(不重复的电影名列出来)然后如果该用户没看过该电影,我们就将该用户对该电影的评分置为 0,通过这样的转换我们就将用户的特征由不规则的文本类型,转换成了标准的数值特征,例如:用户 A 的特征向量就为 V A = < 0 , 0 , 1.0 , 0 , 3.5 , 0 > V_A=<0,0,1.0,0,3.5,0> VA=<0,0,1.0,0,3.5,0>

  • 2、现在有了特征矩阵,我们就可以来计算用户之间的距离了,为了后续计算的方便,我们不妨将所有用户之间的距离都计算一遍,也就是计算一个距离矩阵:

    * A B C D E F G
    A ∞ \infty 6.61 7.42 5.96 6.12 5.94 6.89
    B 6.61 ∞ \infty 2.29 4.5 4.44 1.73 4.5
    C 7.42 2.29 ∞ \infty 4.24 6.24 2.6 4.58
    D 5.96 4.5 4.24 ∞ \infty 6.32 3.12 4.42
    E 6.12 4.44 6.24 6.32 ∞ \infty 4.44 5.7
    F 5.94 1.73 2.6 3.12 4.44 ∞ \infty 4.21
    G 6.89 4.5 4.58 4.42 5.7 4.21 ∞ \infty

    我们随便找两个用户来作为示例,例如要计算AB两个用户之间的距离,那么我们读前一个表可以知道 V A = < 0 , 0 , 1.0 , 0 , 3.5 , 0 > , V B = < 2.5 , 3.0 , 3.5 , 3.5 , 2.5 , 3.0 > V_A=<0,0,1.0,0,3.5,0>,V_B=<2.5,3.0,3.5,3.5,2.5,3.0> VA=<0,0,1.0,0,3.5,0>,VB=<2.5,3.0,3.5,3.5,2.5,3.0>,那么AB之间的距离可以表示为
    D A B = ( 0 − 2.5 ) 2 + ( 0 − 3.0 ) 2 + ( 1 − 3.5 ) 2 + ( 0 − 3.5 ) 2 + ( 3.5 − 2.5 ) 2 + ( 0 − 3.0 ) 2 = 6.61 \displaystyle D_{AB}=\sqrt{(0-2.5)^2+(0-3.0)^2+(1-3.5)^2+(0-3.5)^2+(3.5-2.5)^2+(0-3.0)^2}=6.61 DAB=(02.5)2+(03.0)2+(13.5)2+(03.5)2+(3.52.5)2+(03.0)2 =6.61
    因为用户和自己求距离必然会是0,但是为了后面我们要选取相似的用户时不选到自己,那么我们把用户和自己求距离这里置为无限大即可。

  • 下只能在我们引入UserCF算法公式:
    p ( u , i ) = ∑ v ∈ S ( u , k ) ∩ N ( i ) w u v r v i \displaystyle p(u,i)=\sum_{v\in S(u,k)\cap N(i)}{w_{uv}r_{vi}} p(u,i)=vS(u,k)N(i)wuvrvi
    先来解释一下公式中的参数含义:
    (1) S ( u , k ) S(u,k) S(u,k) 就是计算和用户 u 最相似的 k 个用户
    (2) N ( i ) N(i) N(i)表示对电影 i 有过评分记录的用户集合
    (3) w u v 表 示 用 户 u 和 用 户 v 之 间 的 相 似 度 w_{uv}表示用户u和用户v之间的相似度 wuvuv
    (4) r v i r_{vi} rvi表示用户 v 对电影 i 喜好度,也就是用户 v 对 电影i的评分
    然后通过这个公式计算出来值就是推荐指数(为了方便,我们这里就用距离代表用户之间的相似度,所以最后求出来的推荐度应该是越小,该物品就越值得推荐)

四、代码实现

1、准备数据

import numpy as np

movies_data_dict = {
    'A': {'老炮儿':3.5,'唐人街探案': 1.0},
    'B': {'老炮儿':2.5,'唐人街探案': 3.5,'星球大战': 3.0, '寻龙诀': 3.5, '神探夏洛克': 2.5, '小门神': 3.0},
    'C': {'老炮儿':3.0,'唐人街探案': 3.5,'星球大战': 1.5, '寻龙诀': 5.0, '神探夏洛克': 3.0, '小门神': 3.5},
    'D': {'老炮儿':2.5,'唐人街探案': 3.5,'寻龙诀': 3.5, '神探夏洛克': 4.0},
    'E': {'老炮儿':3.5,'唐人街探案': 2.0,'星球大战': 4.5, '神探夏洛克': 3.5,'小门神': 2.0},
    'F': {'老炮儿':3.0,'唐人街探案': 4.0,'星球大战': 2.0, '寻龙诀': 3.0,'神探夏洛克': 3.0, '小门神': 2.0},
    'G': {'老炮儿':4.5,'唐人街探案': 1.5,'星球大战': 3.0, '寻龙诀': 5.0,'神探夏洛克': 3.5}
    }

2、编写将数据标准化的函数

# 将整个数据集转换成矩阵形式,如果用户没有对该电影进行评分则该位置置为 0
def MoveiesScaler(data):
    movie_index = {}
    i = 0
    for _,ratings in data.items():
        for movie,score in ratings.items():
            if movie_index.get(movie) is None:
                movie_index[movie] = i
                i += 1
    result_data = {}
    for user,ratings in data.items():
        user_ratings = np.zeros(len(movie_index))
        for movie,score in ratings.items():
            user_ratings[movie_index[movie]] = score
        result_data[user] = user_ratings
    return movie_index,result_data

3、编写特征距离计算函数

# 计算两个用户的欧式距离
def EuclideanDistance(feature1,feature2):
    return np.sqrt(np.sum(np.power(feature1-feature2,2)))

# 计算所有用户俩俩之间的距离,也就是距离矩阵,用户对自己求距离时将距离置为无穷大方便后续处理
def DistanceMatrix(user_dict):
    result_matrix = np.zeros((len(user_dict),len(user_dict)))
    user_index = dict(zip(user_dict.keys(),range(len(user_dict))))
    features = list(user_dict.values())
    for r in range(len(features)):
        for c in range(r,len(features)):
            if c == r:
                result_matrix[r][c] = np.Inf
            else:
                ed = EuclideanDistance(features[r],features[c])
                result_matrix[r][c] = ed
                result_matrix[c][r] = ed
    return user_index,result_matrix

4、 S ( u , k ) S(u,k) S(u,k) 和用户u最相似的k个用户

def similarity_max_k(similarity_matrix,user_index,u,k):
    similarity_list = similarity_matrix[user_index[u]]
    user_sim_map = dict(zip(similarity_list.tolist(),user_index.keys()))
    least_k_keys = sorted(user_sim_map)[:k]
    least_k = set({user_sim_map[k] for k in least_k_keys})
    return least_k

5、 N ( i ) N(i) N(i)对电影 i 有过评分的用户

def user_with_item(data_sets,i):
    user_set = set()
    for user,ratings in data_sets.items():
        if(ratings[i] != 0):
            user_set.add(user)
    return user_set

6、 r v i r_{vi} rvi 用户 v 对 电影i的评分

def interest_degree(data_sets,v,i):
    return data_sets[v][i]

7、将前面的函数组装起来就是UserCF的公式实现

def recommended_score(datasets,u,k,i):
    user_index,similarity_matrix = DistanceMatrix(data_sets)
    similarity_k_set = similarity_max_k(similarity_matrix,user_index,u,k)
    user_item_set = user_with_item(data_sets,i)
    iter_set = similarity_k_set & user_item_set
    
    result = 0
    
    for v in iter_set:
        v_i = user_index[v]
        u_i = user_index[u]
        w = similarity_matrix[v_i][u_i]
        result += (w*interest_degree(data_sets,v,i))
    return result

8、查看各个商品对用户的推荐度

for i in range(6):
    print(recommended_score(movies_data_dict,"A",2,i))

out:

32.70698224032311
44.60234092774856
11.874342087037917
38.6651698842296
41.64426370618284
11.874342087037917

我们可以寻找出推荐度最小的(因为推荐度公式那儿我们已经换成了距离来计算,所以要越小越好)那几个商品,且用户A还没有看过的,那么我们就可以把这些商品推荐给他。
补充:我们可以使用不同的距离公式来计算两个用户之间的特征距离,比较好的就是 Jaccard(杰卡德距离),余弦距离您可以尝试使用不同的距离计算公式来尝试,然后调整您推荐算法的准确率。

你可能感兴趣的:(推荐算法,数据挖掘之机器学习算法推导,推荐算法,UserCF,机器学习,python)