2 Numara Listesi Arasındaki Kosinüs Benzerliği


119

İki liste arasındaki kosinüs benzerliğini hesaplamam gerekiyor , örneğin diyelim ki liste 1 olan liste 2 . Numpy veya istatistik modülü gibi bir şey kullanamıyorum . Ortak modüller (matematik vb.) (Ve harcanan zamanı azaltmak için mümkün olan en az modül) kullanmalıyım.dataSetIdataSetII

Diyelim ki dataSetIöyle [3, 45, 7, 2]ve dataSetIIöyle [2, 54, 13, 15]. Listelerin uzunluğu her zaman eşittir.

Elbette, kosinüs benzerliği 0 ile 1 arasındadır ve bunun uğruna üçüncü veya dördüncü ondalık sayıya yuvarlanacaktır format(round(cosine, 3)).

Yardımınız için şimdiden çok teşekkür ederim.


29
Güzel bir genel referans sorusu yapmak için bu ev ödevi sorusundan ruhu ezme şeklini seviyorum. OP " Uyuşuk kullanamam , yaya matematiği yoluna gitmeliyim " der ve en üstteki cevap "scipy'yi denemelisin, uyuşuk kullanır". SO mekaniği popüler soruya altın bir rozet verir.
Nikana Reklawyks

1
Nikana Reklawyks, bu mükemmel bir nokta. StackOverflow'da bu sorunu giderek daha sık yaşadım. Ve daha önceki bazı soruların "kopyaları" olarak işaretlenmiş birkaç sorum var, çünkü moderatörler sorumu benzersiz kılan şeyi anlamak için zaman ayırmadılar.
LRK9

@NikanaReklawyks, bu harika. Profiline bakın, SO'nun en çok katkıda bulunan% 0,01'inden birinin hikayesini anlatıyor, biliyor musunuz?
Nathan Chappell

Yanıtlar:


175

SciPy'yi denemelisin . Örneğin, "integralleri sayısal olarak hesaplama, diferansiyel denklemleri çözme, optimizasyon ve seyrek matrisler için yordamlar" gibi birçok yararlı bilimsel rutine sahiptir. Sayı hesaplaması için süper hızlı optimize edilmiş NumPy'yi kullanır. Kurulum için buraya bakın .

Spatial.distance.cosine'nin benzerliği değil mesafeyi hesapladığını unutmayın . Yani, benzerliği elde etmek için değeri 1'den çıkarmanız gerekir .

from scipy import spatial

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

125

numpysadece başka bir sürüm

from numpy import dot
from numpy.linalg import norm

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

3
Tanım olarak çok açık, ancak np.inner(a, b) / (norm(a) * norm(b))anlaşılması daha iyi olabilir. vektörlerle dotaynı sonucu alabilir inner.
Belter

15
Bilginize, bu çözüm benim sistemimde kullanmaktan önemli ölçüde daha hızlı scipy.spatial.distance.cosine.
Ozzah

@ZhengfangXin kosinüs benzerliği tanım olarak -1 ile 1 arasında değişiyor
dontloo

2
Daha da kısa:cos_sim = (a @ b.T) / (norm(a)*norm(b))
göre öğrenme istatistikleri

Bu, diğerlerine kıyasla en hızlı yaklaşımdır.
Jason Youn

73

cosine_similarityİşlev formu belgelerini kullanabilirsinizsklearn.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
Girdi verileri olarak bir boyut dizisinin geçirilmesinin sklearn sürüm 0.17'de kaldırıldığını ve 0.19'da ValueError'ı yükselteceğini hatırlatmak isteriz.
Chong Tang

4
Bu kullanımdan kaldırma uyarısı verildiğinde bunu sklearn ile yapmanın doğru yolu nedir?
Elliott

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 sanırım demek istiyorsun? Peki bu sonuç ne anlama geliyor? Bu yeni bir 2d dizisi, kosinüs benzerliği değil.
Isbister

10
Bir parantez daha ekleyincosine_similarity([[1, 0, -1]], [[-1,-1, 0]])
Ayush

34

Burada performansın pek önemli olduğunu sanmıyorum ama karşı koyamıyorum. Zip () işlevi, verileri "Pythonic" sırasına göre elde etmek için her iki vektörü de (aslında bir matris devrikinden daha fazlası) tamamen yeniden kopyalar. Somun ve cıvata uygulamasını zamanlamak ilginç olurdu:

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

Bu, öğeleri birer birer ayıklamanın C benzeri gürültüsünden geçer, ancak toplu dizi kopyalaması yapmaz ve önemli her şeyi tek bir for döngüsünde yapar ve tek bir karekök kullanır.

ETA: Yazdırma çağrısı bir işlev olacak şekilde güncellendi. (Orijinal Python 2.7 idi, 3.3 değil. Şu anki Python 2.7 altında bir from __future__ import print_functionifade ile çalışıyor.) Çıktı her iki şekilde de aynı.

3.0GHz Core 2 Duo üzerinde 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

Bu durumda, pirtonik olmayan yol yaklaşık 3.6 kat daha hızlıdır.


2
cosine_measureBu durumda ne var ?
MERose

1
@MERose: cosine_measureve cosine_similarityaynı hesaplamanın farklı uygulamalarıdır. Her iki giriş dizisini de "birim vektörler" e ölçeklendirmeye ve iç çarpımı almaya eşdeğerdir.
Mike Housky 21

3
Ben de aynısını tahmin ederdim. Ama yardımcı olmuyor. İki algoritmanın zaman karşılaştırmalarını sunuyorsunuz, ancak bunlardan yalnızca birini sunuyorsunuz.
MERose

@MERose Oh, üzgünüm. cosine_measurepkacprzak tarafından daha önce yayınlanan koddur. Bu kod, "diğer" tüm standart Python çözümüne bir alternatifti.
Mike Housky

teşekkür ederim, bu harika çünkü herhangi bir kitaplık kullanmıyor ve arkasındaki matematiği anlamak açık
grepit

18

herhangi bir ithalat kullanmadan

Math.sqrt (x)

ile değiştirilebilir

x ** .5

numpy.dot () kullanmadan, liste anlamayı kullanarak kendi nokta işlevinizi oluşturmanız gerekir:

def dot(A,B): 
    return (sum(a*b for a,b in zip(A,B)))

ve sonra kosinüs benzerlik formülünü uygulamak basit bir mesele:

def cosine_similarity(a,b):
    return dot(a,b) / ( (dot(a,a) **.5) * (dot(b,b) ** .5) )

15

Sorudaki birkaç yanıtı temel alarak bir kıyaslama yaptım ve aşağıdaki ön bilginin en iyi seçim olduğuna inanılıyor:

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)

Sonuç, temel alınan uygulamanın scipyen hızlısı olmaması beni şaşırttı . Profilimi çıkardım ve scipy'deki kosinüsün bir vektörü python listesinden numpy dizisine çevirmenin çok zaman aldığını gördüm.

görüntü açıklamasını buraya girin


bunun en hızlısı olduğundan nasıl bu kadar eminsiniz?
Jeru Luke

@JeruLuke Karşılaştırma sonucumun bağlantısını cevabın en başına yapıştırdım: 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)

Hesapladıktan sonra yuvarlayabilirsiniz:

cosine = format(round(cosine_measure(v1, v2), 3))

Gerçekten kısa olmasını istiyorsanız, bu tek astarı kullanabilirsiniz:

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

Bu kodu denedim ve işe yaramadı. V1 [2,3,2,5]ve v2 olarak denedim [3,2,2,0]. Sanki 1.0tamamen aynıymış gibi geri döner . Neyin yanlış olduğuyla ilgili bir fikriniz var mı?
Rob Alsod

Düzeltme burada çalıştı. İyi iş! Daha çirkin ama daha hızlı bir yaklaşım için aşağıya bakın.
Mike Housky

Benzerliğin iki vektör için değil de bir matris içinde hesaplanması gerekiyorsa, bu kodu uyarlamak nasıl mümkün olabilir? İkinci vektör yerine bir matris ve transpoze matrisi aldığımı düşündüm, biraz işe yaramıyor gibi görünüyor.
öğrenci

daha basit hale getirmek için np.dot (x, yT) kullanabilirsiniz
user702846

3

Bunu Python'da basit bir işlev kullanarak yapabilirsiniz:

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
Bu, kosinüsün bir metin uygulamasıdır. Sayısal girdi için yanlış çıktı verecektir.
alvas

Neden "kesişim = set (vec1.keys ()) & set (vec2.keys ())" satırında set kullandığınızı açıklayabilir misiniz?
Ghos3t

Ayrıca işleviniz haritalar bekliyor gibi görünüyor, ancak ona tam sayılar listesi gönderiyorsunuz.
Ghos3t

3

Numpy kullanarak bir sayı listesini birden çok listeyle (matris) karşılaştırın:

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

Kosinüs benzerliğini hesaplamak için bu basit işlevi kullanabilirsiniz:

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
neden tekerleği yeniden icat ettin?
Jeru Luke

@JeruLuke belki "bağımsız" bir cevap verebilir, ek içe aktarma (lar) gerektirmeyen cevaplar (ve belki listeden numpy.array'e veya bunun gibi bir şeye dönüşümler)
Marco Ottina

0

Halihazırda PyTorch kullanıyorsanız , CosineSimilarity uygulamasına devam etmelisiniz .

İki olduğunu varsayalım nboyutlu numpy.ndarrays, v1ve v2onların şekiller hem yani (n,). Kosinüs benzerliğini şu şekilde elde edersiniz:

import torch
import torch.nn as nn

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

Yoksa iki olduğunu varsayın numpy.ndarrays w1ve w2kimin şekilleri hem, (m, n). Aşağıdakiler size kosinüs benzerliklerinin bir listesini verir, her biri bir satır w1ve içindeki karşılık gelen satır arasındaki kosinüs benzerliğidir w2:

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

-1

NumPy'yi kullanamayacağınız durumlar için tüm cevaplar harika. Yapabiliyorsanız, işte başka bir yaklaşım:

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)

Ayrıca EPSILON = 1e-07bölünmeyi sağlamayı da aklınızda bulundurun .

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.