余弦相似度及基于python的三种代码实现、与欧氏距离的区别

 

1.余弦相似度可用来计算两个向量的相似程度

对于如何计算两个向量的相似程度问题,可以把这它们想象成空间中的两条线段,都是从原点([0, 0, ...])出发,指向不同的方向。两条线段之间形成一个夹角,如果夹角为0度,意味着方向相同、线段重合;如果夹角为90度,意味着形成直角,方向完全不相似;如果夹角为180度,意味着方向正好相反。因此,我们可以通过夹角的大小,来判断向量的相似程度。夹角越小,就代表越相似。

 

以二维空间为例,上图的a和b是两个向量,我们要计算它们的夹角θ。余弦定理告诉我们,可以用下面的公式求得:

假定a向量是[x1, y1],b向量是[x2, y2],那么可以将余弦定理改写成下面的形式:

余弦的这种计算方法对n维向量也成立。假定A和B是两个n维向量,A是 [A1, A2, ..., An] ,B是 [B1, B2, ..., Bn] ,则A与B的夹角θ的余弦等于:

余弦值越接近1,就表明夹角越接近0度,也就是两个向量越相似,这就叫"余弦相似性"

余弦值的范围在[-1,1]之间,值越趋近于1,代表两个向量的方向越接近;越趋近于-1,他们的方向越相反;接近于0,表示两个向量近乎于正交。

特殊情况分析:

(1)夹角为0度 :此时向量A与向量B应该是最相似的,余弦相似度应该为1。按照公式(4),我们计算很容易计算出来cosθ=1。 

(2)夹角为90度 :此时余弦相似度为0。 

(3)夹角为180度 :此时余弦相似度为-1,两个向量的方向完全相反。

 

一般情况下,相似度都是归一化到[0,1]区间内,因此余弦相似度表示为 cosineSIM = 0.5cosθ + 0.5

  

2. 余弦相似度的python实现

 

import numpy as np


def bit_product_sum(x, y):
    return sum([item[0] * item[1] for item in zip(x, y)])


def cosine_similarity(x, y, norm=False):
    """ 计算两个向量x和y的余弦相似度 """
    assert len(x) == len(y), "len(x) != len(y)"
    zero_list = [0] * len(x)
    if x == zero_list or y == zero_list:
        return float(1) if x == y else float(0)

    # method 1
    res = np.array([[x[i] * y[i], x[i] * x[i], y[i] * y[i]] for i in range(len(x))])
    cos = sum(res[:, 0]) / (np.sqrt(sum(res[:, 1])) * np.sqrt(sum(res[:, 2])))

    # method 2
    # cos = bit_product_sum(x, y) / (np.sqrt(bit_product_sum(x, x)) * np.sqrt(bit_product_sum(y, y)))

    # method 3
    # dot_product, square_sum_x, square_sum_y = 0, 0, 0
    # for i in range(len(x)):
    #     dot_product += x[i] * y[i]
    #     square_sum_x += x[i] * x[i]
    #     square_sum_y += y[i] * y[i]
    # cos = dot_product / (np.sqrt(square_sum_x) * np.sqrt(square_sum_y))

    return 0.5 * cos + 0.5 if norm else cos  # 归一化到[0, 1]区间内


if __name__ == '__main__':
    print cosine_similarity([0, 0], [0, 0])  # 1.0
    print cosine_similarity([1, 1], [0, 0])  # 0.0
    print cosine_similarity([1, 1], [-1, -1])  # -1.0
    print cosine_similarity([1, 1], [2, 2])  # 1.0
    print cosine_similarity([3, 3], [4, 4])  # 1.0
    print cosine_similarity([1, 2, 2, 1, 1, 1, 0], [1, 2, 2, 1, 1, 2, 1])  # 0.938194187433

 

 

3. 余弦相识度 vs 欧氏距离

余弦距离使用两个向量夹角的余弦值作为衡量两个个体间差异的大小。相比欧氏距离,余弦距离更加注重两个向量在方向上的差异

借助三维坐标系来看下欧氏距离和余弦距离的区别:

从上图可以看出,欧氏距离衡量的是空间各点的绝对距离,跟各个点所在的位置坐标直接相关而余弦距离衡量的是空间向量的夹角,更加体现在方向上的差异,而不是位置

如果保持A点位置不变,B点朝原方向远离坐标轴原点,那么这个时候余弦距离是保持不变的(因为夹角没有发生变化),而A、B两点的距离显然在发生改变,这就是欧氏距离和余弦距离之间的不同之处。

欧氏距离和余弦距离各自有不同的计算方式和衡量特征,因此它们适用于不同的数据分析模型:

  • 欧氏距离能够体现个体数值特征的绝对差异,所以更多的用于需要从维度的数值大小中体现差异的分析,如使用用户行为指标分析用户价值的相似度或差异。
  • 余弦距离更多的是从方向上区分差异,而对绝对的数值不敏感,更多的用于使用用户对内容评分来区分兴趣的相似度和差异,同时修正了用户间可能存在的度量标准不统一的问题(因为余弦距离对绝对数值不敏感)

 

正因为余弦相似度在数值上的不敏感,会导致这样一种情况存在:

用户对内容评分,按5分制,X和Y两个用户对两个内容的评分分别为(1,2)和(4,5),使用余弦相似度得到的结果是0.98,两者极为相似。但从评分上看X似乎不喜欢2这个 内容,而Y则比较喜欢,余弦相似度对数值的不敏感导致了结果的误差,需要修正这种不合理性就出现了调整余弦相似度,即所有维度上的数值都减去一个均值,比如X和Y的评分均值都是3,那么调整后为(-2,-1)和(1,2),再用余弦相似度计算,得到-0.8,相似度为负值并且差异不小,但显然更加符合现实。 那么是否可以在(用户-商品-行为数值)矩阵的基础上使用调整余弦相似度计算呢?从算法原理分析,复杂度虽然增加了,但是应该比普通余弦夹角算法要强。

 

4. 在计算文本相似度上基本流程

  (1)使用TF-IDF算法,找出两篇文章的关键词;

  (2)每篇文章各取出若干个关键词(比如20个),合并成一个集合,计算每篇文章对于这个集合中的词的词频(为了避免文章长度的差异,可以使用相对词频);

  (3)生成两篇文章各自的词频向量;

  (4)计算两个向量的余弦相似度,值越大就表示越相似。

 

参考:

http://www.ruanyifeng.com/blog/2013/03/cosine_similarity.html

https://www.cnblogs.com/guoxueyuan/p/7779239.html

http://blog.csdn.net/zz_dd_yy/article/details/51926305

https://www.cnblogs.com/wxiaoli/p/6940702.html

http://blog.csdn.net/jediael_lu/article/details/77863419

posted @ 2018-03-01 16:17  焦距  阅读(53351)  评论(2编辑  收藏  举报