[python] 두 숫자 목록 간의 코사인 유사성
두 목록 사이 의 코사인 유사성 을 계산해야합니다. 예를 들어 목록 1은 이고 목록 2는 . numpy 또는 통계 모듈 과 같은 것을 사용할 수 없습니다 . 나는 공통 모듈 (수학 등)을 사용해야한다 (그리고 소비되는 시간을 줄이기 위해 가능한 한 최소한의 모듈).dataSetI
dataSetII
하자 말은 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)))