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


119

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

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

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

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


29
나는이 숙제 질문에서 영혼을 분쇄하여 훌륭한 일반 참고 자료로 만드는 방식을 좋아합니다. OP는 " 나는 numpy를 사용할 수 없습니다 . 보행자 수학 길을 가야합니다. "라고 말하고 , 최상위 답변은 "scipy를 시도해야합니다. numpy를 사용합니다"라고 말합니다. SO 역학은 인기있는 질문에 금 배지를 부여합니다.
Nikana Reklawyks

1
Nikana Reklawyks, 그것은 훌륭한 포인트입니다. 나는 StackOverflow에서 점점 더 자주 그 문제를 겪었습니다. 그리고 중재자가 내 질문을 독특하게 만드는 이유를 이해하는 데 시간을 할애하지 않았기 때문에 이전 질문의 "중복"으로 표시된 몇 가지 질문이 있습니다.
LRK9 2016

@NikanaReklawyks, 대단합니다. 그의 프로필을 보면 SO의 최고 .01 % 기여자 중 한 사람의 이야기를 들려줍니다.
Nathan Chappell

답변:


174

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)

122

numpy만 기반으로 다른 버전

from numpy import dot
from numpy.linalg import norm

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

3
정의는 매우 명확하지만 np.inner(a, b) / (norm(a) * norm(b))이해하는 것이 더 낫습니다. 벡터 dot와 동일한 결과를 얻을 수 있습니다 inner.
Belter

15
참고로이 솔루션은를 사용하는 것보다 내 시스템에서 훨씬 더 빠릅니다 scipy.spatial.distance.cosine.
Ozzah

정의 -1 1 코사인 유사도 범위를 @ZhengfangXin
dontloo

2
더 짧게 :cos_sim = (a @ b.T) / (norm(a)*norm(b))
예제 별 학습 통계

이것은 다른 것에 비해 훨씬 빠른 접근 방식입니다.
Jason Youn

73

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

21
하나의 차원 배열을 입력 데이터로 전달하는 것은 sklearn 버전 0.17에서 더 이상 사용되지 않으며 0.19에서 ValueError를 발생시킵니다.
Chong Tang

4
이 지원 중단 경고가 주어지면 sklearn으로 이것을 수행하는 올바른 방법은 무엇입니까?
Elliott 2016

2
@Elliott one_dimension_array.reshape (-1,1)
bobo32

2
@ bobo32 cosine_similarity (np.array ([1, 0, -1]). reshape (-1,0), np.array ([-1, -1, 0]). reshape (-1,0)) I 무슨 뜻인가요? 그러나 그 결과가 반환된다는 것은 무엇을 의미합니까? 코사인 유사성이 아닌 새로운 2D 배열입니다.
Isbister

10
하나 더 괄호로 묶으십시오cosine_similarity([[1, 0, -1]], [[-1,-1, 0]])
Ayush

34

나는 여기서 성능이 그다지 중요하다고 생각하지 않지만 저항 할 수는 없습니다. 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이었습니다. 현재는 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

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


2
무엇 cosine_measure이 경우에?
MERose

1
@MERose : cosine_measure그리고 cosine_similarity단순히 동일한 계산의 다른 구현이다. 두 입력 배열을 "단위 벡터"로 스케일링하고 내적을 취하는 것과 같습니다.
Mike Housky

3
나는 똑같이 짐작했을 것이다. 그러나 그것은 도움이되지 않습니다. 두 알고리즘의 시간 비교를 제시하지만 그중 하나만 제시합니다.
MERose

@MERose 오, 죄송합니다. cosine_measurepkacprzak이 이전에 게시 한 코드입니다. 이 코드는 "다른"모든 표준 Python 솔루션의 대안이었습니다.
마이크 Housky

감사합니다. 라이브러리를 사용하지 않고 그 뒤에있는 수학을 이해하는 것이 분명하기 때문에
훌륭

18

수입품을 사용하지 않고

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

15

나는 한 벤치 마크를 여러 질문에 답하고 다음 코드는 최선의 선택이 될 것으로 생각됩니다에 따라 :

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의 코사인이 파이썬 목록에서 numpy 배열로 벡터를 캐스팅하는 데 많은 시간이 걸린다는 것을 알았습니다.

여기에 이미지 설명 입력


이것이 가장 빠르다는 것을 어떻게 확신합니까?
Jeru Luke

@JeruLuke 답변의 맨 처음에 벤치 마크 결과 링크를 붙여 넣었습니다. gist.github.com/mckelvin/…
McKelvin

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

이 코드를 사용해 보았지만 작동하지 않는 것 같습니다. 나는 v1 인 [2,3,2,5], v2 인 시도했습니다 [3,2,2,0]. 1.0마치 똑같은 것처럼 반환됩니다 . 무엇이 잘못되었는지 아십니까?
Rob Alsod 2013-08-24

수정은 여기서 작동했습니다. 좋은 작업! 추악하지만 더 빠른 접근 방식은 아래를 참조하십시오.
Mike Housky 2013-08-25

유사성이 두 벡터가 아닌 행렬 내에서 계산되어야하는 경우이 코드를 어떻게 적용 할 수 있습니까? 두 번째 벡터 대신 행렬과 전치 행렬을 사용한다고 생각했는데 작동하지 않는 것 같습니다.
학생

np.dot (x, yT)를 사용하여 더 간단하게 만들 수 있습니다.
user702846

3

간단한 함수를 사용하여 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)

3
이것은 코사인의 텍스트 구현입니다. 숫자 입력에 대해 잘못된 출력을 제공합니다.
alvas

"intersection = set (vec1.keys ()) & set (vec2.keys ())"줄에서 set을 사용한 이유를 설명해 주시겠습니까?
Ghos3t

또한 함수가 맵을 예상하는 것처럼 보이지만 정수 목록을 보내고 있습니다.
Ghos3t

3

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]

1

이 간단한 함수를 사용하여 코사인 유사성을 계산할 수 있습니다.

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

1
왜 바퀴를 재발 명합니까?
Jeru Luke

@JeruLuke 어쩌면 "독립형"대답, 추가 수입 (들)을 필요로하지 않는 사람 (및 목록에서 numpy.array 또는 그런 일을 어쩌면 변환)을 얻었다
마르코 Ottina

1

이미 PyTorch를 사용 하고 있다면 CosineSimilarity 구현을 사용해야합니다. .

2 n차원 numpy.ndarrays가 v1있고 v2, 즉 모양이 모두 라고 가정합니다 (n,). 코사인 유사성을 얻는 방법은 다음과 같습니다.

import torch
import torch.nn as nn

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

아니면이 개 있다고 가정 numpy.ndarrayw1w2그 모양을 모두 있습니다 (m, n). 다음은 코사인 유사성 목록을 가져옵니다. 각각은의 행 w1과의 해당 행 사이의 코사인 유사성 입니다 w2.

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

-1

모든 답변은 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사단을 확보 할 것을 염두에 두십시오 .

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.