web-dev-qa-db-ja.com

2つの数値リスト間のコサイン類似性

コサイン類似度の間で2つのリストを計算する必要があります。たとえば、リスト1はdataSetIであり、リスト2はdataSetIIです。 numpyや統計モジュールなどは使用できません。共通のモジュール(数学など)を使用する必要があります(使用する時間を減らすために、可能な限り最小のモジュールを使用します)。

dataSetI[3, 45, 7, 2]であり、dataSetII[2, 54, 13, 15]であるとします。リストの長さはalways等しいです。

もちろん、コサインの類似性は0と1の間であり、そのため、format(round(cosine, 3))で3番目または4番目の小数に丸められます。

助けてくれてありがとう。

88
Rob Alsod

SciPy を試してください。例えば、「積分を数値的に計算し、微分方程式を解き、最適化し、スパース行列にするためのルーチン」など、多数の有用な科学的ルーチンがあります。数値演算のために、超高速に最適化されたNumPyを使用します。インストールについては here をご覧ください。

Spatial.distance.cosineは、類似度ではなく、distanceを計算することに注意してください。そのため、1から値を減算して、similarityを取得する必要があります。

from scipy import spatial

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

numpyのみに基づく別のバージョン

from numpy import dot
from numpy.linalg import norm

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

cosine_similarity関数フォームsklearn.metrics.pairwiseを使用できます docs

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

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

ここではパフォーマンスはそれほど重要ではないと思いますが、抵抗することはできません。 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

これは、要素を1つずつ抽出する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

したがって、この場合、unpythonicの方法は約3.6倍高速です。

30
Mike Housky

質問のいくつかの回答に基づいて ベンチマーク を実行しましたが、次のスニペットが最良の選択であると考えられます。

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配列にベクトルをキャストするのに時間がかかることがわかりました。

enter image description here

12
McKelvin
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)))
10
pkacprzak

インポートを使用せずに

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

Numpyを使用して、数値の1つのリストを複数のリスト(マトリックス)と比較します。

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]
2
sten

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

この単純な関数を使用して、コサイン類似度を計算できます。

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
Isira