code

두 숫자 목록 간의 코사인 유사성

codestyles 2020. 8. 29. 11:05
반응형

두 숫자 목록 간의 코사인 유사성


두 목록 사이 코사인 유사성 을 계산해야합니다. 예를 들어 목록 1은 이고 목록 2는 입니다. numpy 또는 통계 모듈 과 같은 것을 사용할 수 없습니다 . 나는 공통 모듈 (수학 등)을 사용해야한다 (그리고 소비되는 시간을 줄이기 위해 가능한 한 최소한의 모듈).dataSetIdataSetII

하자 말은 dataSetI있다 [3, 45, 7, 2]하고 dataSetII있다 [2, 54, 13, 15]. 목록의 길이는 항상 동일합니다.

물론 코사인 유사성은 0과 1 사이 이며 ,이를 위해를 사용하여 세 번째 또는 네 번째 십진수로 반올림됩니다 format(round(cosine, 3)).

도와 주셔서 미리 감사드립니다.


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

cosine_similarity함수 양식 문서를 사용할 수 있습니다.sklearn.metrics.pairwise

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 : 인쇄 호출을 함수로 업데이트했습니다. (원본은 3.3이 아니라 Python 2.7이었습니다. 현재는 from __future__ import print_function명령문을 사용하여 Python 2.7에서 실행됩니다 .) 출력은 어느 쪽이든 동일합니다.

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

따라서이 경우 비 파이썬 방식은 약 3.6 배 더 빠릅니다.


I did a benchmark based on several answers in the question and the following snippet is believed to be the best choice:

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)

The result makes me surprised that the implementation based on scipy is not the fastest one. I profiled and find that cosine in scipy takes a lot of time to cast a vector from python list to numpy array.

enter image description here


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)

You can round it after computing:

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

If you want it really short, you can use this one-liner:

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

without using any imports

math.sqrt(x)

can be replaced with

x** .5

without using numpy.dot() you have to create your own dot function using list comprehension:

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

and then its just a simple matter of applying the cosine similarity formula:

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

You can do this in Python using simple function:

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)

Using numpy compare one list of numbers to multiple lists(matrix):

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]

You can use this simple function to calculate the cosine 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])))

If you happen to be using PyTorch already, you should go with their CosineSimilarity implementation.

Suppose you have two n-dimensional numpy.ndarrays, v1 and v2, i.e. their shapes are both (n,). Here's how you get their cosine similarity:

import torch
import torch.nn as nn

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

Or suppose you have two numpy.ndarrays w1 and w2, whose shapes are both (m, n). The following gets you a list of cosine similarities, each being the cosine similarity between a row in w1 and the corresponding row in w2:

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

참고URL : https://stackoverflow.com/questions/18424228/cosine-similarity-between-2-number-lists

반응형