Qualcuno può dare un esempio di somiglianza del coseno, in un modo molto semplice e grafico?


201

Cosine Articolo di somiglianza su Wikipedia

Puoi mostrare i vettori qui (in un elenco o qualcosa del genere) e quindi fare i calcoli e farci vedere come funziona?

Sono un principiante.


1
Prova a prendere una copia di Geometry and Definition di Widdows ( press.uchicago.edu/presssite/… ), l'ho letto un po 'di tempo fa e avrei voluto averlo avuto un certo numero di anni fa, un ottimo testo introduttivo.
Nathan Howell,

Risposte:


463

Ecco due testi molto brevi da confrontare:

  1. Julie loves me more than Linda loves me

  2. Jane likes me more than Julie loves me

Vogliamo sapere quanto sono simili questi testi, puramente in termini di conteggio delle parole (e ignorando l'ordine delle parole). Iniziamo facendo un elenco delle parole di entrambi i testi:

me Julie loves Linda than more likes Jane

Ora contiamo il numero di volte in cui ciascuna di queste parole appare in ciascun testo:

   me   2   2
 Jane   0   1
Julie   1   1
Linda   1   0
likes   0   1
loves   2   1
 more   1   1
 than   1   1

Tuttavia, non ci interessano le parole stesse. Siamo interessati solo a quei due vettori verticali di conteggi. Ad esempio, ci sono due istanze di "me" in ciascun testo. Decideremo quanto sono vicini questi due testi calcolando una funzione di quei due vettori, vale a dire il coseno dell'angolo tra di loro.

I due vettori sono, di nuovo:

a: [2, 0, 1, 1, 0, 2, 1, 1]

b: [2, 1, 1, 0, 1, 1, 1, 1]

Il coseno dell'angolo tra loro è di circa 0,822.

Questi vettori sono a 8 dimensioni. Una virtù dell'utilizzo della somiglianza del coseno è chiaramente che converte una domanda che va oltre la capacità umana di visualizzare in una che può essere. In questo caso puoi pensare a questo come ad un angolo di circa 35 gradi che è una certa "distanza" da zero o accordo perfetto.


12
Questo e 'esattamente quello che stavo cercando. Esattamente. È considerata la forma più semplice di "modello dello spazio vettoriale"?
TIMEX,

2
Sono davvero felice che ti sia stato utile, Alex. Ci scusiamo per il ritardo nella risposta. Non visito StackOverflow da un po '. In realtà questo è un esempio di "spazio interno del prodotto". C'è una discussione di base su Wikipedia.
Bill Bell,

1
C'è un modo per normalizzare per la lunghezza del documento?
sinθ

1
È necessario utilizzare la normalizzazione della lunghezza e prima di ciò, provare a utilizzare la ponderazione della frequenza di registro su tutti i vettori di termini. Se hai già a che fare con vettori normalizzati, allora è il prodotto punto di AB
Ali Gajani,

4
Esempio più dettagliato con l'uso della normalizzazione della lunghezza e TF-IDF: site.uottawa.ca/~diana/csi4107/cosine_tf_idf_example.pdf
Mike B.

121

Immagino che tu sia più interessato a capire meglio " perché " la somiglianza del coseno funziona (perché fornisce una buona indicazione di somiglianza), piuttosto che " come " viene calcolata (le operazioni specifiche utilizzate per il calcolo). Se il tuo interesse è per quest'ultimo, vedi il riferimento indicato da Daniel in questo post, così come una relativa domanda SO .

Per spiegare sia il come sia ancora di più il perché, all'inizio è utile semplificare il problema e lavorare solo in due dimensioni. Una volta ottenuto questo in 2D, è più facile pensarlo in tre dimensioni, e ovviamente più difficile da immaginare in molte più dimensioni, ma per allora possiamo usare l'algebra lineare per fare i calcoli numerici e anche per aiutarci a pensare in termini di linee / vettori / "piani" / "sfere" in n dimensioni, anche se non possiamo disegnarle.

Quindi, in due dimensioni : per quanto riguarda la somiglianza del testo questo significa che ci concentreremo su due termini distinti, diciamo le parole "Londra" e "Parigi", e conteremmo quante volte ciascuna di queste parole si trova in ciascuna delle i due documenti che desideriamo confrontare. Questo ci dà, per ogni documento, un punto nel piano xy. Ad esempio, se Doc1 avesse Parigi una volta e Londra quattro volte, un punto in (1,4) avrebbe presentato questo documento (per quanto riguarda questa valutazione ridotta dei documenti). Oppure, parlando in termini di vettori, questo documento Doc1 sarebbe una freccia che va dall'origine al punto (1,4). Con questa immagine in mente, pensiamo a cosa significa che due documenti sono simili e come questo si collega ai vettori.

Documenti MOLTO simili (sempre per quanto riguarda questo insieme limitato di dimensioni) avrebbero lo stesso numero di riferimenti a Parigi, E lo stesso numero di riferimenti a Londra, o forse, potrebbero avere lo stesso rapporto di questi riferimenti. Un documento, Doc2, con 2 riferimenti a Parigi e 8 riferimenti a Londra, sarebbe anche molto simile, solo con un testo forse più lungo o in qualche modo più ripetitivo dei nomi delle città, ma nella stessa proporzione. Forse entrambi i documenti sono guide su Londra, facendo solo riferimenti passanti a Parigi (e quanto sia scortese quella città ;-) Sto solo scherzando !!!.

Ora, documenti meno simili possono anche includere riferimenti ad entrambe le città, ma in proporzioni diverse. Forse Doc2 citerebbe Parigi solo una volta e Londra sette volte.

Tornando al nostro piano xy, se disegniamo questi documenti ipotetici, vediamo che quando sono MOLTO simili, i loro vettori si sovrappongono (anche se alcuni vettori potrebbero essere più lunghi) e quando iniziano ad avere meno in comune, questi vettori iniziano a divergere, per avere un angolo più ampio tra di loro.

Misurando l'angolo tra i vettori, possiamo avere una buona idea della loro somiglianza e per rendere le cose ancora più facili, prendendo il Coseno di questo angolo, abbiamo un bel valore da 0 a 1 o da -1 a 1 che è indicativo di questa somiglianza, a seconda di cosa e come rappresentiamo. Più piccolo è l'angolo, maggiore è (più vicino a 1) il valore del coseno e maggiore è la somiglianza.

All'estremo, se Doc1 cita solo Parigi e Doc2 cita solo Londra, i documenti non hanno assolutamente nulla in comune. Doc1 avrebbe il suo vettore sull'asse x, Doc2 sull'asse y, l'angolo 90 gradi, il coseno 0. In questo caso diremmo che questi documenti sono ortogonali tra loro.

Aggiunta di dimensioni :
con questa intuitiva sensazione di somiglianza espressa come un piccolo angolo (o grande coseno), ora possiamo immaginare cose in 3 dimensioni, dire portando la parola "Amsterdam" nel mix e visualizzare abbastanza bene come un documento con due i riferimenti a ciascuno avrebbero un vettore che va in una direzione particolare, e possiamo vedere come questa direzione potrebbe essere paragonata a un documento che cita tre volte Parigi e Londra, ma non Amsterdam, ecc. Come detto, possiamo provare a immaginare questa fantasia spazio per 10 o 100 città. È difficile da disegnare, ma facile da concettualizzare.

Concluderò semplicemente pronunciando alcune parole sulla formula stessa . Come ho già detto, altri riferimenti forniscono buone informazioni sui calcoli.

Primo in due dimensioni. La formula per il Coseno dell'angolo tra due vettori deriva dalla differenza trigonometrica (tra l'angolo a e l'angolo b):

cos(a - b) = (cos(a) * cos(b)) + (sin (a) * sin(b))

Questa formula è molto simile alla formula del prodotto dot:

Vect1 . Vect2 =  (x1 * x2) + (y1 * y2)

dove cos(a)corrisponde al xvalore e sin(a)il yvalore, per il primo vettore, ecc L'unico problema è che x, yecc non sono esattamente gli cose sinvalori, questi valori devono essere letti sul cerchio unitario. È qui che entra in gioco il denominatore della formula: dividendo per il prodotto della lunghezza di questi vettori, le coordinate xe ydiventano normalizzate.


25

Ecco la mia implementazione in C #.

using System;

namespace CosineSimilarity
{
    class Program
    {
        static void Main()
        {
            int[] vecA = {1, 2, 3, 4, 5};
            int[] vecB = {6, 7, 7, 9, 10};

            var cosSimilarity = CalculateCosineSimilarity(vecA, vecB);

            Console.WriteLine(cosSimilarity);
            Console.Read();
        }

        private static double CalculateCosineSimilarity(int[] vecA, int[] vecB)
        {
            var dotProduct = DotProduct(vecA, vecB);
            var magnitudeOfA = Magnitude(vecA);
            var magnitudeOfB = Magnitude(vecB);

            return dotProduct/(magnitudeOfA*magnitudeOfB);
        }

        private static double DotProduct(int[] vecA, int[] vecB)
        {
            // I'm not validating inputs here for simplicity.            
            double dotProduct = 0;
            for (var i = 0; i < vecA.Length; i++)
            {
                dotProduct += (vecA[i] * vecB[i]);
            }

            return dotProduct;
        }

        // Magnitude of the vector is the square root of the dot product of the vector with itself.
        private static double Magnitude(int[] vector)
        {
            return Math.Sqrt(DotProduct(vector, vector));
        }
    }
}

questo è fantastico grazie ho adorato il modo in cui hai spiegato Magnitude =)
liminal18

È fantastico, ma cosa succede se stiamo lavorando con file o stringhe.
Talha,

21

Per semplicità sto riducendo il vettore aeb:

Let :
    a : [1, 1, 0]
    b : [1, 0, 1]

Quindi somiglianza del coseno (Theta):

 (Theta) = (1*1 + 1*0 + 0*1)/sqrt((1^2 + 1^2))* sqrt((1^2 + 1^2)) = 1/2 = 0.5

quindi l'inverso di cos 0,5 è di 60 gradi.


18

Questo codice Python è il mio tentativo rapido e sporco di implementare l'algoritmo:

import math
from collections import Counter

def build_vector(iterable1, iterable2):
    counter1 = Counter(iterable1)
    counter2 = Counter(iterable2)
    all_items = set(counter1.keys()).union(set(counter2.keys()))
    vector1 = [counter1[k] for k in all_items]
    vector2 = [counter2[k] for k in all_items]
    return vector1, vector2

def cosim(v1, v2):
    dot_product = sum(n1 * n2 for n1, n2 in zip(v1, v2) )
    magnitude1 = math.sqrt(sum(n ** 2 for n in v1))
    magnitude2 = math.sqrt(sum(n ** 2 for n in v2))
    return dot_product / (magnitude1 * magnitude2)


l1 = "Julie loves me more than Linda loves me".split()
l2 = "Jane likes me more than Julie loves me or".split()


v1, v2 = build_vector(l1, l2)
print(cosim(v1, v2))

Puoi spiegare perché hai usato set nella riga "all_items = set (counter1.keys ()). Union (set (counter2.keys ()))".
Ghos3t

@ Ghos3t, ovvero ottenere l'elenco di parole distinte da entrambi i documenti
Jobs,

7

Utilizzando l'esempio di @Bill Bell, due modi per farlo in [R]

a = c(2,1,0,2,0,1,1,1)

b = c(2,1,1,1,1,0,1,1)

d = (a %*% b) / (sqrt(sum(a^2)) * sqrt(sum(b^2)))

o sfruttando le prestazioni del metodo crossprod () ...

e = crossprod(a, b) / (sqrt(crossprod(a, a)) * sqrt(crossprod(b, b)))

5

Questo è un semplice Pythoncodice che implementa la somiglianza del coseno.

from scipy import linalg, mat, dot
import numpy as np

In [12]: matrix = mat( [[2, 1, 0, 2, 0, 1, 1, 1],[2, 1, 1, 1, 1, 0, 1, 1]] )

In [13]: matrix
Out[13]: 
matrix([[2, 1, 0, 2, 0, 1, 1, 1],
        [2, 1, 1, 1, 1, 0, 1, 1]])
In [14]: dot(matrix[0],matrix[1].T)/np.linalg.norm(matrix[0])/np.linalg.norm(matrix[1])
Out[14]: matrix([[ 0.82158384]])

3
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

/**
 * 
* @author Xiao Ma
* mail : 409791952@qq.com
*
*/
  public class SimilarityUtil {

public static double consineTextSimilarity(String[] left, String[] right) {
    Map<String, Integer> leftWordCountMap = new HashMap<String, Integer>();
    Map<String, Integer> rightWordCountMap = new HashMap<String, Integer>();
    Set<String> uniqueSet = new HashSet<String>();
    Integer temp = null;
    for (String leftWord : left) {
        temp = leftWordCountMap.get(leftWord);
        if (temp == null) {
            leftWordCountMap.put(leftWord, 1);
            uniqueSet.add(leftWord);
        } else {
            leftWordCountMap.put(leftWord, temp + 1);
        }
    }
    for (String rightWord : right) {
        temp = rightWordCountMap.get(rightWord);
        if (temp == null) {
            rightWordCountMap.put(rightWord, 1);
            uniqueSet.add(rightWord);
        } else {
            rightWordCountMap.put(rightWord, temp + 1);
        }
    }
    int[] leftVector = new int[uniqueSet.size()];
    int[] rightVector = new int[uniqueSet.size()];
    int index = 0;
    Integer tempCount = 0;
    for (String uniqueWord : uniqueSet) {
        tempCount = leftWordCountMap.get(uniqueWord);
        leftVector[index] = tempCount == null ? 0 : tempCount;
        tempCount = rightWordCountMap.get(uniqueWord);
        rightVector[index] = tempCount == null ? 0 : tempCount;
        index++;
    }
    return consineVectorSimilarity(leftVector, rightVector);
}

/**
 * The resulting similarity ranges from −1 meaning exactly opposite, to 1
 * meaning exactly the same, with 0 usually indicating independence, and
 * in-between values indicating intermediate similarity or dissimilarity.
 * 
 * For text matching, the attribute vectors A and B are usually the term
 * frequency vectors of the documents. The cosine similarity can be seen as
 * a method of normalizing document length during comparison.
 * 
 * In the case of information retrieval, the cosine similarity of two
 * documents will range from 0 to 1, since the term frequencies (tf-idf
 * weights) cannot be negative. The angle between two term frequency vectors
 * cannot be greater than 90°.
 * 
 * @param leftVector
 * @param rightVector
 * @return
 */
private static double consineVectorSimilarity(int[] leftVector,
        int[] rightVector) {
    if (leftVector.length != rightVector.length)
        return 1;
    double dotProduct = 0;
    double leftNorm = 0;
    double rightNorm = 0;
    for (int i = 0; i < leftVector.length; i++) {
        dotProduct += leftVector[i] * rightVector[i];
        leftNorm += leftVector[i] * leftVector[i];
        rightNorm += rightVector[i] * rightVector[i];
    }

    double result = dotProduct
            / (Math.sqrt(leftNorm) * Math.sqrt(rightNorm));
    return result;
}

public static void main(String[] args) {
    String left[] = { "Julie", "loves", "me", "more", "than", "Linda",
            "loves", "me" };
    String right[] = { "Jane", "likes", "me", "more", "than", "Julie",
            "loves", "me" };
    System.out.println(consineTextSimilarity(left,right));
}
}

3

Semplice codice JAVA per calcolare la somiglianza del coseno

/**
   * Method to calculate cosine similarity of vectors
   * 1 - exactly similar (angle between them is 0)
   * 0 - orthogonal vectors (angle between them is 90)
   * @param vector1 - vector in the form [a1, a2, a3, ..... an]
   * @param vector2 - vector in the form [b1, b2, b3, ..... bn]
   * @return - the cosine similarity of vectors (ranges from 0 to 1)
   */
  private double cosineSimilarity(List<Double> vector1, List<Double> vector2) {

    double dotProduct = 0.0;
    double normA = 0.0;
    double normB = 0.0;
    for (int i = 0; i < vector1.size(); i++) {
      dotProduct += vector1.get(i) * vector2.get(i);
      normA += Math.pow(vector1.get(i), 2);
      normB += Math.pow(vector2.get(i), 2);
    }
    return dotProduct / (Math.sqrt(normA) * Math.sqrt(normB));
  }

1
Non è un "modo semplice e grafico", ma solo un codice. Anche se altri hanno commesso lo stesso errore: /
Skrylar,

-1

Esistono due vettori A e B in uno spazio 2D o 3D, l'angolo tra questi vettori è così simile.

Se l'angolo è maggiore (può raggiungere un massimo di 180 gradi), ovvero Cos 180 = -1 e l'angolo minimo è 0 gradi. cos 0 = 1 implica che i vettori sono allineati tra loro e quindi i vettori sono simili.

cos 90 = 0 (che è sufficiente per concludere che i vettori A e B non sono affatto simili e poiché la distanza non può essere negativa, i valori del coseno si troveranno da 0 a 1. Quindi, più angolo implica implica ridurre la somiglianza (visualizzandolo anche ha senso)

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.