コサイン類似度の間で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番目の小数に丸められます。
助けてくれてありがとう。
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)
numpy
のみに基づく別のバージョン
from numpy import dot
from numpy.linalg import norm
cos_sim = dot(a, b)/(norm(a)*norm(b))
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]])
ここではパフォーマンスはそれほど重要ではないと思いますが、抵抗することはできません。 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倍高速です。
質問のいくつかの回答に基づいて ベンチマーク を実行しましたが、次のスニペットが最良の選択であると考えられます。
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配列にベクトルをキャストするのに時間がかかることがわかりました。
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)))
インポートを使用せずに
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) )
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]
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)
この単純な関数を使用して、コサイン類似度を計算できます。
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])))