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.
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.
Risposte:
Ecco due testi molto brevi da confrontare:
Julie loves me more than Linda loves me
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.
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 x
valore e sin(a)
il y
valore, per il primo vettore, ecc L'unico problema è che x
, y
ecc non sono esattamente gli cos
e sin
valori, 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 x
e y
diventano normalizzate.
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 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))
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)))
Questo è un semplice Python
codice 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]])
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));
}
}
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));
}
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)