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

두 목록 사이 의 코사인 유사성 을 계산해야합니다. 예를 들어 목록 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이었습니다. 현재는 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 배 더 빠릅니다.


답변

수입품을 사용하지 않고

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


답변

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

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 배열로 벡터를 캐스팅하는 데 많은 시간이 걸린다는 것을 알았습니다.

여기에 이미지 설명 입력


답변

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