繁体   English   中英

2个数字列表之间的余弦相似度

[英]Cosine Similarity between 2 Number Lists

我想计算两个列表之间的余弦相似度,例如列表 1 是dataSetI和列表 2 是dataSetII

假设dataSetI[3, 45, 7, 2]并且dataSetII[2, 54, 13, 15] 列表的长度总是相等的。 我想将余弦相似度报告为 0 到 1 之间的数字。

dataSetI = [3, 45, 7, 2]
dataSetII = [2, 54, 13, 15]

def cosine_similarity(list1, list2):
  # How to?
  pass

print(cosine_similarity(dataSetI, dataSetII))

你应该试试SciPy 它有一堆有用的科学例程,例如,“数值计算积分、求解微分方程、优化和稀疏矩阵的例程”。 它使用超快优化的 NumPy 进行数字运算。 请参阅此处进行安装。

请注意, spatial.distance.cosine 计算距离,而不是相似度。 因此,您必须从 1 中减去该值才能获得相似度

from scipy import spatial

dataSetI = [3, 45, 7, 2]
dataSetII = [2, 54, 13, 15]
result = 1 - spatial.distance.cosine(dataSetI, dataSetII)

仅基于numpy的另一个版本

from numpy import dot
from numpy.linalg import norm

cos_sim = dot(a, b)/(norm(a)*norm(b))

您可以使用sklearn.metrics.pairwise文档cosine_similarity函数

In [23]: from sklearn.metrics.pairwise import cosine_similarity

In [24]: cosine_similarity([[1, 0, -1]], [[-1,-1, 0]])
Out[24]: array([[-0.5]])

我认为性能在这里并不重要,但我无法抗拒。 zip() 函数完全重新复制两个向量(实际上更多的是矩阵转置)只是为了以“Pythonic”顺序获取数据。 确定具体实施的时间会很有趣:

import math
def cosine_similarity(v1,v2):
    "compute cosine similarity of v1 to v2: (v1 dot v2)/{||v1||*||v2||)"
    sumxx, sumxy, sumyy = 0, 0, 0
    for i in range(len(v1)):
        x = v1[i]; y = v2[i]
        sumxx += x*x
        sumyy += y*y
        sumxy += x*y
    return sumxy/math.sqrt(sumxx*sumyy)

v1,v2 = [3, 45, 7, 2], [2, 54, 13, 15]
print(v1, v2, cosine_similarity(v1,v2))

Output: [3, 45, 7, 2] [2, 54, 13, 15] 0.972284251712

这经历了一次提取一个元素的类似 C 的噪音,但不进行批量数组复制,并且在单个 for 循环中完成所有重要的事情,并使用单个平方根。

ETA:将打印调用更新为函数。 (原来是 Python 2.7,而不是 3.3。当前在 Python 2.7 下运行,带有from __future__ import print_function语句。)无论哪种方式,输出都是相同的。

3.0GHz Core 2 Duo 上的 CPYthon 2.7.3:

>>> timeit.timeit("cosine_similarity(v1,v2)",setup="from __main__ import cosine_similarity, v1, v2")
2.4261788514654654
>>> timeit.timeit("cosine_measure(v1,v2)",setup="from __main__ import cosine_measure, v1, v2")
8.794677709375264

因此,在这种情况下,非 Python 方式的速度大约快 3.6 倍。

不使用任何进口

数学.sqrt(x)

可以替换为

x** .5

不使用 numpy.dot() 您必须使用列表理解创建自己的点函数:

def dot(A,B): 
    return (sum(a*b for a,b in zip(A,B)))

然后它只是应用余弦相似度公式的简单问题:

def cosine_similarity(a,b):
    return dot(a,b) / ( (dot(a,a) **.5) * (dot(b,b) ** .5) )

我根据问题中的几个答案做了一个基准测试,下面的代码片段被认为是最好的选择:

def dot_product2(v1, v2):
    return sum(map(operator.mul, v1, v2))


def vector_cos5(v1, v2):
    prod = dot_product2(v1, v2)
    len1 = math.sqrt(dot_product2(v1, v1))
    len2 = math.sqrt(dot_product2(v2, v2))
    return prod / (len1 * len2)

结果让我惊讶的是,基于scipy的实现并不是最快的。 我分析并发现 scipy 中的余弦需要很长时间才能将向量从 python 列表转换为 numpy 数组。

在此处输入图像描述

import math
from itertools import izip

def dot_product(v1, v2):
    return sum(map(lambda x: x[0] * x[1], izip(v1, v2)))

def cosine_measure(v1, v2):
    prod = dot_product(v1, v2)
    len1 = math.sqrt(dot_product(v1, v1))
    len2 = math.sqrt(dot_product(v2, v2))
    return prod / (len1 * len2)

您可以在计算后对其进行四舍五入:

cosine = format(round(cosine_measure(v1, v2), 3))

如果你想要它真的很短,你可以使用这个单行:

from math import sqrt
from itertools import izip

def cosine_measure(v1, v2):
    return (lambda (x, y, z): x / sqrt(y * z))(reduce(lambda x, y: (x[0] + y[0] * y[1], x[1] + y[0]**2, x[2] + y[1]**2), izip(v1, v2), (0, 0, 0)))

Python代码计算:

  • 余弦距离
  • 余弦相似度
  • 角距离
  • 角度相似度

import math

from scipy import spatial


def calculate_cosine_distance(a, b):
    cosine_distance = float(spatial.distance.cosine(a, b))
    return cosine_distance


def calculate_cosine_similarity(a, b):
    cosine_similarity = 1 - calculate_cosine_distance(a, b)
    return cosine_similarity


def calculate_angular_distance(a, b):
    cosine_similarity = calculate_cosine_similarity(a, b)
    angular_distance = math.acos(cosine_similarity) / math.pi
    return angular_distance


def calculate_angular_similarity(a, b):
    angular_similarity = 1 - calculate_angular_distance(a, b)
    return angular_similarity

您可以使用这个简单的函数来计算余弦相似度:

def cosine_similarity(a, b):
  return sum([i*j for i,j in zip(a, b)])/(math.sqrt(sum([i*i for i in a]))* math.sqrt(sum([i*i for i in b])))

您可以使用简单的函数在 Python 中执行此操作:

def get_cosine(text1, text2):
  vec1 = text1
  vec2 = text2
  intersection = set(vec1.keys()) & set(vec2.keys())
  numerator = sum([vec1[x] * vec2[x] for x in intersection])
  sum1 = sum([vec1[x]**2 for x in vec1.keys()])
  sum2 = sum([vec2[x]**2 for x in vec2.keys()])
  denominator = math.sqrt(sum1) * math.sqrt(sum2)
  if not denominator:
     return 0.0
  else:
     return round(float(numerator) / denominator, 3)
dataSet1 = [3, 45, 7, 2]
dataSet2 = [2, 54, 13, 15]
get_cosine(dataSet1, dataSet2)

使用 numpy 将一个数字列表与多个列表(矩阵)进行比较:

def cosine_similarity(vector,matrix):
   return ( np.sum(vector*matrix,axis=1) / ( np.sqrt(np.sum(matrix**2,axis=1)) * np.sqrt(np.sum(vector**2)) ) )[::-1]

如果你碰巧已经在使用PyTorch ,你应该使用他们的CosineSimilarity implementation

假设您有两个nnumpy.ndarrayv1v2 ,即它们的形状都是(n,) 以下是如何获得它们的余弦相似度:

import torch
import torch.nn as nn

cos = nn.CosineSimilarity()
cos(torch.tensor([v1]), torch.tensor([v2])).item()

或者假设您有两个numpy.ndarray s w1w2 ,它们的形状都是(m, n) 以下为您提供余弦相似度列表,每个都是w2 w1的相应行之间的余弦相似度:

cos(torch.tensor(w1), torch.tensor(w2)).tolist()

另一个版本,如果您有一个包含向量列表和查询向量的场景,并且您想计算查询向量与列表中所有向量的余弦相似度,您可以按照以下方式一次性完成:

>>> import numpy as np

>>> A      # list of vectors, shape -> m x n
array([[ 3, 45,  7,  2],
       [ 1, 23,  3,  4]])

>>> B      # query vector, shape -> 1 x n
array([ 2, 54, 13, 15])

>>> similarity_scores = A.dot(B)/ (np.linalg.norm(A, axis=1) * np.linalg.norm(B))

>>> similarity_scores
array([0.97228425, 0.99026919])

我们可以用简单的数学方程轻松计算余弦相似度。 Cosine_similarity = 1-(向量的点积/(向量的范数积))。 我们可以定义两个函数,分别用于计算点积和范数。

def dprod(a,b):
    sum=0
    for i in range(len(a)):
        sum+=a[i]*b[i]
    return sum

def norm(a):

    norm=0
    for i in range(len(a)):
    norm+=a[i]**2
    return norm**0.5

    cosine_a_b = 1-(dprod(a,b)/(norm(a)*norm(b)))

这是一个也适用于矩阵的实现。 它的行为与 sklearn 余弦相似度完全一样:

def cosine_similarity(a, b):    
    return np.divide(
        np.dot(a, b.T),
        np.linalg.norm(
            a,
            axis=1,
            keepdims=True
        ) 
        @ 
        np.linalg.norm(
            b,
            axis=1,
            keepdims=True
        ).T
    )

您可以使用SciPy (最简单的方法):

from scipy import spatial

dataSetI = [3, 45, 7, 2]
dataSetII = [2, 54, 13, 15]
print(1 - spatial.distance.cosine(dataSetI, dataSetII))

请注意, spatial.distance.cosine()为您提供了一个相异度(距离)值,因此要获得相似度,您需要从 1 中减去该值。

获得解决方案的另一种方法是自己编写 function ,甚至考虑使用不同长度的列表的可能性:

def cosineSimilarity(v1, v2):
  scalarProduct = moduloV1 = moduloV2 = 0

  if len(v1) > len(v2):
    v2.extend(0 for _ in range(len(v1) - len(v2)))
  else:
    v2.extend(0 for _ in range(len(v2) - len(v1)))

  for i in range(len(v1)):
    scalarProduct += v1[i] * v2[i]
    moduloV1 += v1[i] * v1[i]
    moduloV2 += v2[i] * v2[i]

  return round(scalarProduct/(math.sqrt(moduloV1) * math.sqrt(moduloV2)), 3)

dataSetI = [3, 45, 7, 2]
dataSetII = [2, 54, 13, 15]
print(cosineSimilarity(dataSetI, dataSetII))

对于无法使用 NumPy 的情况,所有答案都非常有用。 如果可以的话,这是另一种方法:

def cosine(x, y):
    dot_products = np.dot(x, y.T)
    norm_products = np.linalg.norm(x) * np.linalg.norm(y)
    return dot_products / (norm_products + EPSILON)

还要记住EPSILON = 1e-07以确保该部门的安全。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM