Kesamaan Cosine antara 2 Daftar Nomor


119

Saya perlu menghitung kesamaan kosinus antara dua daftar , misalkan daftar 1 yang mana dataSetIdan daftar 2 yang mana dataSetII. Saya tidak dapat menggunakan apa pun seperti numpy atau modul statistik. Saya harus menggunakan modul umum (matematika, dll) (dan modul sesedikit mungkin, untuk mengurangi waktu yang dihabiskan).

Katakanlah dataSetIadalah [3, 45, 7, 2]dan dataSetIIadalah [2, 54, 13, 15]. Panjang daftar selalu sama.

Tentu saja, kemiripan kosinusnya adalah antara 0 dan 1 , dan demi itu, akan dibulatkan ke desimal ketiga atau keempat dengan format(round(cosine, 3)).

Terima kasih banyak sebelumnya atas bantuannya.


29
Saya suka cara SO menghancurkan jiwa dari pertanyaan pekerjaan rumah ini untuk menjadikannya referensi umum yang bagus. OP mengatakan " Saya tidak bisa menggunakan numpy , saya harus mengikuti cara matematika pejalan kaki", dan jawaban teratas berbunyi "Anda harus mencoba scipy, itu menggunakan numpy". Mekanik SO memberikan lencana emas untuk pertanyaan populer.
Nikana Reklawyks

1
Nikana Reklawyks, itu adalah poin yang sangat bagus. Saya semakin sering mengalami masalah itu dengan StackOverflow. Dan saya memiliki beberapa pertanyaan yang ditandai sebagai "duplikat" dari beberapa pertanyaan sebelumnya, karena moderator tidak meluangkan waktu untuk memahami apa yang membuat pertanyaan saya unik.
LRK9

@NikanaReklawyks, ini bagus. Lihat profilnya, itu menceritakan kisah salah satu kontributor SO top .01%, Anda tahu?
Nathan Chappell

Jawaban:


175

Anda harus mencoba SciPy . Ia memiliki sekumpulan rutinitas ilmiah yang berguna misalnya, "rutinitas untuk menghitung integral secara numerik, menyelesaikan persamaan diferensial, pengoptimalan, dan matriks renggang." Ia menggunakan NumPy yang dioptimalkan super cepat untuk pengolah nomornya. Lihat disini untuk menginstal.

Perhatikan bahwa spatial.distance.cosine menghitung jarak , dan bukan kesamaannya. Jadi, Anda harus mengurangi nilainya dari 1 untuk mendapatkan kesamaan .

from scipy import spatial

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

125

versi lain numpyhanya berdasarkan

from numpy import dot
from numpy.linalg import norm

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

3
Sangat jelas sebagai definisinya, tapi mungkin np.inner(a, b) / (norm(a) * norm(b))lebih baik untuk dipahami. dotbisa mendapatkan hasil yang sama seperti innervektor.
Belter

15
FYI solusi ini secara signifikan lebih cepat di sistem saya daripada menggunakan scipy.spatial.distance.cosine.
Ozzah

@ZhengfangXin kesamaan kosinus berkisar dari -1 hingga 1 menurut definisi
dontloo

2
Bahkan lebih pendek:cos_sim = (a @ b.T) / (norm(a)*norm(b))
Belajar statistik dengan contoh

Sejauh ini, ini adalah pendekatan tercepat dibandingkan yang lain.
Jason Youn

73

Anda dapat menggunakan dokumencosine_similarity formulir fungsisklearn.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
Sekadar mengingatkan bahwa Meneruskan array satu dimensi sebagai data input tidak digunakan lagi di sklearn versi 0.17, dan akan memunculkan ValueError di 0.19.
Chong Tang

4
Apa cara yang benar untuk melakukan ini dengan sklearn mengingat peringatan penghentian ini?
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 tebak maksudmu? Tapi apa artinya hasil itu kembali? Ini adalah array 2d baru, bukan kesamaan kosinus.
Isbister

10
Lampirkan dengan satu braket lagicosine_similarity([[1, 0, -1]], [[-1,-1, 0]])
Ayush

34

Saya kira kinerja tidak terlalu penting di sini, tetapi saya tidak bisa menolak. Fungsi zip () sepenuhnya menyalin kedua vektor (sebenarnya lebih merupakan transpose matriks) hanya untuk mendapatkan data dalam urutan "Pythonic". Akan menarik untuk mengatur waktu implementasi mur-dan-baut:

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

Itu melewati noise seperti C dari mengekstraksi elemen satu per satu, tetapi tidak melakukan penyalinan array massal dan menyelesaikan semua yang penting dalam satu perulangan for, dan menggunakan satu akar kuadrat.

ETA: Panggilan cetak yang diperbarui menjadi sebuah fungsi. (Yang asli adalah Python 2.7, bukan 3.3. Arus berjalan di bawah Python 2.7 dengan from __future__ import print_functionpernyataan.) Outputnya sama.

CPYthon 2.7.3 pada 3.0GHz Core 2 Duo:

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

Jadi, cara unpythonic sekitar 3,6 kali lebih cepat dalam kasus ini.


2
Ada apa cosine_measuredalam kasus ini?
MERose

1
@MERose: cosine_measuredan cosine_similaritymerupakan implementasi yang berbeda dari kalkulasi yang sama. Setara dengan penskalaan kedua larik masukan menjadi "vektor satuan" dan mengambil perkalian titik.
Mike Housky

3
Saya akan menebak hal yang sama. Tapi itu tidak membantu. Anda menyajikan perbandingan waktu dari dua algoritme tetapi hanya menampilkan salah satunya.
MERose

@MERose Oh, maaf. cosine_measureadalah kode yang diposting sebelumnya oleh pkacprzak. Kode ini adalah alternatif dari solusi semua-standar-Python "lainnya".
Mike Housky

terima kasih, ini bagus karena tidak menggunakan perpustakaan apa pun dan jelas untuk memahami matematika di baliknya
grepit

18

tanpa menggunakan impor apapun

math.sqrt (x)

bisa diganti dengan

x ** .5

tanpa menggunakan numpy.dot () Anda harus membuat fungsi titik Anda sendiri menggunakan pemahaman daftar:

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

dan kemudian ini hanya masalah sederhana menerapkan rumus kesamaan kosinus:

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

15

Saya melakukan benchmark berdasarkan beberapa jawaban dalam pertanyaan tersebut dan cuplikan berikut diyakini menjadi pilihan terbaik:

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)

Hasilnya membuat saya terkejut bahwa implementasi berdasarkan scipybukan yang tercepat. Saya membuat profil dan menemukan bahwa cosinus di scipy membutuhkan banyak waktu untuk melemparkan vektor dari daftar python ke array numpy.

masukkan deskripsi gambar di sini


Bagaimana Anda yakin ini yang tercepat?
Jeru Luke

@JeruLuke Saya telah menempelkan tautan hasil benchmark saya di awal jawaban: 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)

Anda dapat membulatkannya setelah menghitung:

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

Jika Anda ingin sangat pendek, Anda dapat menggunakan satu baris ini:

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

Saya mencoba kode ini, dan sepertinya tidak berhasil. Saya mencobanya dengan makhluk v1 [2,3,2,5], dan makhluk v2 [3,2,2,0]. Ia kembali dengan 1.0, seolah-olah mereka persis sama. Apakah ada yang salah?
Rob Alsod

Perbaikan berhasil di sini. Pekerjaan yang baik! Lihat di bawah untuk pendekatan yang lebih jelek tetapi lebih cepat.
Mike Housky

Bagaimana mungkin untuk mengadaptasi kode ini jika kesamaan harus dihitung dalam matriks dan bukan untuk dua vektor? Saya pikir saya mengambil matriks dan matriks yang dialihkan alih-alih vektor kedua, bit sepertinya tidak berfungsi.
pelajar

Anda dapat menggunakan np.dot (x, yT) untuk membuatnya lebih sederhana
user702846

3

Anda dapat melakukan ini dengan Python menggunakan fungsi sederhana:

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
Ini adalah implementasi teks cosinus. Ini akan memberikan keluaran yang salah untuk masukan numerik.
alva

Bisakah Anda menjelaskan mengapa Anda menggunakan set di baris "intersection = set (vec1.keys ()) & set (vec2.keys ())".
Ghos3t

Juga fungsi Anda tampaknya mengharapkan peta tetapi Anda mengirimkannya daftar bilangan bulat.
Ghos3t

3

Menggunakan numpy membandingkan satu daftar angka dengan beberapa daftar (matriks):

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

Anda dapat menggunakan fungsi sederhana ini untuk menghitung kesamaan kosinus:

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
mengapa menemukan kembali roda?
Jeru Luke

@JeruLuke mungkin memberikan jawaban "berdiri sendiri", yang tidak memerlukan impor tambahan (dan mungkin konversi dari daftar ke numpy.array atau semacamnya)
Marco Ottina

0

Jika Anda kebetulan sudah menggunakan PyTorch , Anda harus menggunakan implementasi CosineSimilarity mereka .

Misalkan Anda memiliki dua ndimensi numpy.ndarrays, v1dan v2, yaitu, bentuk keduanya adalah keduanya (n,). Inilah cara Anda mendapatkan kesamaan kosinusnya:

import torch
import torch.nn as nn

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

Atau misalkan Anda memiliki dua numpy.ndarrays w1dan w2, yang bentuknya keduanya (m, n). Berikut ini adalah daftar persamaan kosinus, masing-masing merupakan kesamaan kosinus antara baris dalam w1dan baris terkait dalam w2:

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

-1

Semua jawaban bagus untuk situasi di mana Anda tidak dapat menggunakan NumPy. Jika Anda bisa, berikut ini pendekatan lain:

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)

Ingat juga tentang EPSILON = 1e-07mengamankan divisi.

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.