2 संख्या सूचियों के बीच कोसाइन समानता


119

मुझे दो सूचियों के बीच कॉस्मिक समानता की गणना करने की आवश्यकता है , चलो उदाहरण के लिए कहते हैं कि सूची 1 जो है और सूची 2 जो है । मैं कुछ भी उपयोग नहीं कर सकता जैसे कि संख्याबल या सांख्यिकी मॉड्यूल। मुझे सामान्य मॉड्यूल (गणित, आदि) और (संभव के रूप में कम से कम मॉड्यूल, खर्च किए गए समय को कम करने के लिए) का उपयोग करना चाहिए।dataSetIdataSetII

आइए कहना dataSetIहै [3, 45, 7, 2]और dataSetIIहै [2, 54, 13, 15]। सूचियों की लंबाई हमेशा बराबर होती है।

बेशक, ब्रह्मांडीय समानता 0 और 1 के बीच है , और इसकी खातिर, इसे तीसरे या चौथे दशमलव के साथ गोल किया जाएगा format(round(cosine, 3))

मदद करने के लिए अग्रिम धन्यवाद।


29
जिस तरह से एसओ ने इस होमवर्क सवाल से आत्मा को कुचल दिया, उसे एक अच्छा सामान्य संदर्भ बना दिया। ओपी कहते हैं " मैं खतना का उपयोग नहीं कर सकता , मुझे पैदल चलने वाले गणित के रास्ते पर चलना चाहिए", और शीर्ष उत्तर जाता है "आपको डराने की कोशिश करनी चाहिए, यह खस्ता का उपयोग करता है"। SO यांत्रिकी लोकप्रिय प्रश्न के लिए एक गोल्ड बैज प्रदान करते हैं।
निकेना रेक्लाविक्स

1
निकाना रेक्लाविक, यह एक उत्कृष्ट बिंदु है। मुझे वह समस्या अधिक से अधिक बार StackOverflow के साथ मिली है। और मेरे पास पहले के कुछ प्रश्नों के "डुप्लिकेट" के रूप में चिह्नित कई प्रश्न थे, क्योंकि मध्यस्थों ने यह समझने में समय नहीं लिया कि किस प्रश्न को अद्वितीय बनाया गया था।
LRK9 22

@NikanaReklawyks, यह बहुत अच्छा है। उसकी प्रोफ़ाइल देखें, यह SO के शीर्ष .01% योगदानकर्ताओं में से एक की कहानी बताता है, आप जानते हैं?
नाथन चैपल

जवाबों:


174

आपको साइपी की कोशिश करनी चाहिए । इसमें उदाहरण के लिए उपयोगी वैज्ञानिक दिनचर्या का एक समूह है, "अभिन्न कंप्यूटिंग के लिए दिनचर्या संख्यात्मक रूप से, अंतर समीकरणों को हल करने, अनुकूलन, और विरल मैट्रिसेस।" यह अपने नंबर की crunching के लिए सुपरफास्ट अनुकूलित 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)

122

numpyकेवल दूसरे संस्करण पर आधारित है

from numpy import dot
from numpy.linalg import norm

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

3
परिभाषा के रूप में बहुत स्पष्ट है, लेकिन शायद np.inner(a, b) / (norm(a) * norm(b))समझना बेहतर है। वैक्टर के लिए के dotरूप में एक ही परिणाम प्राप्त कर सकते हैं inner
बेल्टर

15
FYI करें यह समाधान मेरे सिस्टम पर उपयोग करने की तुलना में काफी तेज है scipy.spatial.distance.cosine
ओझा

@ZhengfangXin cosine समानता परिभाषा से -1 से 1 तक है
नॉट्लू

2
इससे भी कम:cos_sim = (a @ b.T) / (norm(a)*norm(b))
उदाहरण के लिए लर्निंग आँकड़े

यह दूसरों की तुलना में अब तक का सबसे तेज़ तरीका है।
जेसन यॉन

73

आप 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]])

21
बस एक अनुस्मारक जो इनपुट डेटा के रूप में एक आयाम सरणियों को पास कर रहा है वह स्केलेर संस्करण 0.17 में पदावनत है, और 0.19 में ValueError बढ़ाएगा।
चोंग तांग

4
स्केलेर के साथ ऐसा करने का सही तरीका क्या है, जिससे यह अपक्षय चेतावनी दी गई है?
इलियट

2
@ ईलियट 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 तुम्हारा मतलब है? लेकिन उस परिणाम का क्या मतलब है कि यह वापस आ गया है? इसकी एक नई 2d सरणी, एक कोज्या समानता नहीं है।
इस्बिस्टर

10
इसे एक और ब्रैकेट के साथ संलग्न करेंcosine_similarity([[1, 0, -1]], [[-1,-1, 0]])
आयुष

34

मुझे नहीं लगता कि यहां प्रदर्शन का महत्व ज्यादा है, लेकिन मैं विरोध नहीं कर सकता। जिप () फ़ंक्शन पूरी तरह से दोनों वैक्टर (मैट्रिक्स ट्रांसजेंड का अधिक, वास्तव में) को केवल "पायथोनिक" क्रम में डेटा प्राप्त करने के लिए फिर से खोल देता है। यह नट-और-बोल्ट कार्यान्वयन के समय के लिए दिलचस्प होगा:

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

तत्वों को एक बार में निकालने के सी-लाइक शोर के माध्यम से जाता है, लेकिन कोई भी थोक सरणी की नकल नहीं करता है और लूप के लिए एक ही बार में सब कुछ महत्वपूर्ण हो जाता है, और एक एकल वर्गमूल का उपयोग करता है।

ETA: एक फ़ंक्शन होने के लिए अद्यतित प्रिंट कॉल। (मूल पायथन 2.7 था, 3.3 नहीं। एक from __future__ import print_functionबयान के साथ पायथन 2.7 के तहत वर्तमान रन ।) आउटपुट एक ही है, किसी भी तरह से।

सीपीयूथॉन 2.7.3 3.0GHz कोर 2 डुओ पर:

>>> 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 गुना तेज है।


2
cosine_measureइस मामले में क्या है ?
MERose

1
@MERose: cosine_measureऔर cosine_similarityएक ही गणना के अलग-अलग कार्यान्वयन हैं। "यूनिट वैक्टर" और डॉट उत्पाद लेने के लिए दोनों इनपुट सरणियों को स्केल करने के बराबर।
माइक हॉस्की

3
मैंने उसी का अनुमान लगाया होगा। लेकिन यह मददगार नहीं है। आप दो एल्गोरिदम की समय तुलना प्रस्तुत करते हैं लेकिन उनमें से केवल एक को प्रस्तुत करते हैं।
मेरोस

@ मेरी ओह, क्षमा करें। cosine_measureपहले pkacprzak द्वारा पोस्ट किया गया कोड है। यह कोड "अन्य" सभी मानक-पायथन समाधान का एक विकल्प था।
माइक हॉस्की

धन्यवाद, यह बहुत अच्छा है क्योंकि यह किसी भी पुस्तकालय का उपयोग नहीं कर रहा है और इसके पीछे के गणित को समझने के लिए स्पष्ट है
grepit

18

किसी भी आयात का उपयोग किए बिना

math.sqrt (एक्स)

से बदला जा सकता है

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

15

मैंने प्रश्न में कई उत्तरों के आधार पर एक बेंचमार्क किया और निम्नलिखित स्निपेट को सबसे अच्छा विकल्प माना जाता है:

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सबसे तेज़ एक नहीं है। मैंने प्रोफाइल किया और पाया कि डरपोक में कोसाइन को वेक्टर की अजगर सूची से लेकर सुपीरियर एरे तक कास्ट करने में बहुत समय लगता है।

यहां छवि विवरण दर्ज करें


आप कैसे सुनिश्चित हैं कि यह सबसे तेज है?
जेरु ल्यूक

@JeruLuke मैंने अपने बेंचमार्क परिणाम के लिंक को उत्तर की शुरुआत में ही चिपका
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)

आप कंप्यूटिंग के बाद इसे गोल कर सकते हैं:

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

मैंने इस कोड को आज़माया है, और यह काम नहीं करता है। मैंने इसे v1 होने [2,3,2,5]और v2 होने के साथ आज़माया [3,2,2,0]। यह 1.0वैसा ही लौटता है , जैसे वे बिलकुल एक जैसे हों। कुछ पता है क्या गड़बड़ है?
रोब अल्सोड

फिक्स ने यहां काम किया। अच्छी नौकरी! बदसूरत लेकिन तेज दृष्टिकोण के लिए नीचे देखें।
माइक हस्की

इस कोड को अनुकूलित करना कैसे संभव है यदि समानता को एक मैट्रिक्स के भीतर गणना करना है और दो वैक्टर के लिए नहीं? मुझे लगा कि मैं दूसरी वेक्टर के बजाय एक मैट्रिक्स और ट्रांसपोज़्ड मैट्रिक्स लेता हूं, बिट यह काम नहीं करता है।
छात्र

आप इसे सरल बनाने के लिए np.dot (x, yT) का उपयोग कर सकते हैं
user702846

3

आप सरल कार्य का उपयोग करके पायथन में यह कर सकते हैं:

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
यह कोसाइन का एक पाठ कार्यान्वयन है। यह संख्यात्मक इनपुट के लिए गलत आउटपुट देगा।
अलवास

क्या आप समझा सकते हैं कि आपने "चौराहे = सेट (vec1.keys) () और सेट (vec2.keys ())" लाइन में सेट का उपयोग क्यों किया।
घोस

इसके अलावा आपका कार्य नक्शे की अपेक्षा करता है लेकिन आप इसे पूर्णांकों की सूची भेज रहे हैं।
घोस

3

संख्याओं की एक सूची का उपयोग करके कई सूचियों (मैट्रिक्स) की संख्या की तुलना करें:

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

आप इस सरल फ़ंक्शन का उपयोग कॉस्मिक समानता की गणना करने के लिए कर सकते हैं:

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
पहिया को क्यों मजबूत करें?
जेरु ल्यूक

@JerLLuke शायद एक "स्टैंड अलोन" उत्तर देने के लिए, जिन्हें अतिरिक्त आयात (नों) की आवश्यकता नहीं है (और शायद सूची से सुन्न के लिए रूपांतरण या ऐसा कुछ)
मार्को ओटिना

1

यदि आप पहले से ही PyTorch का उपयोग कर रहे हैं , तो आपको उनके CosineSimilarity कार्यान्वयन के साथ जाना चाहिए ।

मान लीजिए कि आपके पास दो nआयामी numpy.ndarrayएस हैं, v1और v2, उनके आकार दोनों हैं (n,)। यहां बताया गया है कि आपको उनकी कोसाइनिटी ​​कैसे मिलती है:

import torch
import torch.nn as nn

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

या मान लें कि आपके पास दो numpy.ndarrays हैं w1और w2, जिनकी आकृतियाँ दोनों हैं (m, n)। निम्नलिखित आपको कोसिन समानता की एक सूची मिलती है, जिनमें से प्रत्येक में एक पंक्ति w1और इसी पंक्ति के बीच कोसाइन समानता होती है w2:

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

-1

सभी उत्तर उन स्थितियों के लिए बहुत अच्छे हैं जहाँ आप NumPy का उपयोग नहीं कर सकते हैं। यदि आप कर सकते हैं, तो यहां एक और तरीका है:

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)

EPSILON = 1e-07विभाजन को सुरक्षित करने के बारे में भी ध्यान रखें ।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.