余弦相似度及基于python的余弦相似度实现

Python 专栏收录该内容
2 篇文章 0 订阅

一.余弦相似度简介

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

对n维向量A,B,假设A= [A1, A2, ..., An] ,B= [B1, B2, ..., Bn] ,则A与B的夹角θ的余弦等于:\cos\Theta =\frac{A\cdot B}{|A|\cdot |B|}=\tfrac{\sum_{n}^{i=1}(A_{i}\times B_{i})}{\sqrt{\sum_{n}^{i=1}(A_{i})^{2}}\times \sqrt{\sum_{n}^{i=1}(B_{i})^{2}}}

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

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

二.余弦相似度与欧式距离的区别

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

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

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

1.欧氏距离能够体现个体数值特征的绝对差异,所以更多的用于需要从维度的数值大小中体现差异的分析,如使用用户行为指标分析用户价值的相似度或差异。

2.余弦距离更多的是从方向上区分差异,而对绝对的数值不敏感,更多的用于使用用户对内容评分来区分兴趣的相似度和差异,同时修正了用户间可能存在的度量标准不统一的问题(因为余弦距离对绝对数值不敏感)。

2.1调整余弦相似度:

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

用户对内容评分,按5分制,X和Y两个用户对两个内容的评分分别为(1,2)和(4,5),使用余弦相似度得到的结果是0.98,两者极为相似。但从评分上看X似乎不喜欢2这个 内容,而Y则比较喜欢,余弦相似度对数值的不敏感导致了结果的误差,需要修正这种不合理性就出现了调整余弦相似度,即所有维度上的数值都减去一个均值,比如X和Y的评分均值都是3,那么调整后为(-2,-1)和(1,2),再用余弦相似度计算,得到-0.8,相似度为负值并且差异不小,但显然更加符合现实。

三.余弦相似度的python实现

方法一:

def cosine_similarity(x, y, dim=256):
    xx = 0.0
    yy = 0.0
    xy = 0.0
    for i in range(dim):
        xx += x[i] * x[i]
        yy += y[i] * y[i]
        xy += x[i] * y[i] 
    xx_sqrt = xx ** 0.5
    yy_sqrt = yy ** 0.5
    cos = xy/(xx_sqrt*yy_sqrt)*0.5+0.5
    return cos

方法二:

import numpy as np

def cosine_similarity(x,y):
    num = x.dot(y.T)
    denom = np.linalg.norm(x) * np.linalg.norm(y)
    return num / denom

方法三:

def cosine_similarity(x, y, norm=False):
    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)

    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])))

    return 0.5 * cos + 0.5 if norm else cos 
  • 9
    点赞
  • 4
    评论
  • 56
    收藏
  • 一键三连
    一键三连
  • 扫一扫,分享海报

©️2021 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值