text - distancia - ¿Puede alguien dar un ejemplo de similitud del coseno, de una manera muy simple y gráfica?
distancia coseno (9)
Aquí está mi implementación en 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));
}
}
}
Artículo sobre la similitud coseno en Wikipedia
¿Puedes mostrar los vectores aquí (en una lista o algo así) y luego hacer los cálculos, y veamos cómo funciona?
Soy un principiante.
Aquí hay dos textos muy cortos para comparar:
Julie loves me more than Linda loves me
Jane likes me more than Julie loves me
Queremos saber cuán similares son estos textos, puramente en términos de recuentos de palabras (e ignorar el orden de las palabras). Comenzamos haciendo una lista de las palabras de ambos textos:
me Julie loves Linda than more likes Jane
Ahora contamos la cantidad de veces que cada una de estas palabras aparece en cada texto:
me 2 2
Jane 0 1
Julie 1 1
Linda 1 0
likes 0 1
loves 2 1
more 1 1
than 1 1
Aunque no estamos interesados en las palabras en sí mismas. Estamos interesados solo en esos dos vectores verticales de conteos. Por ejemplo, hay dos instancias de ''mí'' en cada texto. Vamos a decidir cuán cerca se encuentran estos dos textos al calcular una función de esos dos vectores, a saber, el coseno del ángulo entre ellos.
Los dos vectores son, de nuevo:
a: [2, 0, 1, 1, 0, 2, 1, 1]
b: [2, 1, 1, 0, 1, 1, 1, 1]
El coseno del ángulo entre ellos es aproximadamente 0.822.
Estos vectores son de 8 dimensiones. Una virtud del uso de la similitud del coseno es claramente que convierte una pregunta que está más allá de la capacidad humana de visualizar en una que puede ser. En este caso, puede pensar en esto como el ángulo de unos 35 grados, que es una "distancia" de cero o un acuerdo perfecto.
Código JAVA simple para calcular la similitud 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));
}
Este código de Python es mi intento rápido y sucio de implementar el 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))
Este es un código de Python
simple que implementa similitud de 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]])
Para simplificar, estoy reduciendo el vector a y b:
Let :
a : [1, 1, 0]
b : [1, 0, 1]
Entonces similitud del coseno (Theta):
(Theta) = (1*1 + 1*0 + 0*1)/sqrt((1^2 + 1^2))* sqrt((1^2 + 1^2)) = 1/2 = 0.5
entonces el inverso de cos 0.5 es 60 grados.
Supongo que está más interesado en obtener una idea del " por qué " la similitud del coseno funciona (por qué proporciona una buena indicación de similitud), en lugar de " cómo " se calcula (las operaciones específicas utilizadas para el cálculo). Si su interés está en este último, consulte la referencia indicada por Daniel en esta publicación, así como una pregunta SO relacionada .
Para explicar tanto el cómo y aún más el por qué, es útil, al principio, simplificar el problema y trabajar solo en dos dimensiones. Una vez que obtienes esto en 2D, es más fácil pensarlo en 3 dimensiones y, por supuesto, es más difícil de imaginar en muchas más dimensiones, pero para entonces podemos utilizar el álgebra lineal para hacer los cálculos numéricos y también para ayudarnos a pensar en términos de líneas / vectores / "planos" / "esferas" en n dimensiones, aunque no podamos dibujarlas.
Entonces ... en dos dimensiones : con respecto a la similitud del texto, esto significa que nos enfocaremos en dos términos distintos, por ejemplo, las palabras "Londres" y "París", y contaremos cuántas veces cada palabra se encuentra en cada uno de los dos documentos que deseamos comparar Esto nos da, para cada documento, un punto en el plano xy, por ejemplo, si Doc1 tuvo París una vez y Londres cuatro veces, un punto en (1,4) presentaría este documento (con respecto a esta evaluación diminuta de documentos) . O, hablando en términos de vectores, este documento Doc1 sería una flecha que iría del origen al punto (1,4). Con esta imagen en mente, pensemos en lo que significa ser similar para dos documentos y cómo esto se relaciona con los vectores.
Documentos MUY similares (de nuevo con respecto a este conjunto limitado de dimensiones) tendrían el mismo número de referencias a París, Y el mismo número de referencias a Londres, o tal vez, podrían tener la misma proporción de estas referencias (por ejemplo, El Documento Doc2, con 2 referencias a París y 8 Refs a Londres, también sería muy similar, tal vez un texto más largo o de alguna manera más repetitivo de los nombres de las ciudades, pero en la misma proporción: tal vez ambos documentos son guías sobre Londres, solo pasando referencias a París (y qué poco atractivo es esa ciudad ;-) ¡¡Bromeando !!!). Ahora, documentos menos similares, también pueden incluir referencias a ambas ciudades, pero en diferentes proporciones, Tal vez Doc2 solo citaría Paris Once y Londres 7 veces.
Volviendo a nuestro plano xy, si dibujamos estos documentos hipotéticos, vemos que cuando son MUY similares, sus vectores se superponen (aunque algunos vectores pueden ser más largos), y como comienzan a tener menos en común, estos vectores comienzan a divergir, a tener un mayor ángulo entre ellos.
Bam! midiendo el ángulo entre los vectores, podemos tener una buena idea de su similitud y, para hacer las cosas aún más fáciles, tomando el coseno de este ángulo, tenemos un buen 0 a 1 (o -1 a 1, dependiendo de y cómo contabilizamos) valor que es indicativo de esta similitud. Cuanto menor sea el ángulo, mayor será (más cerca de 1) el valor del coseno, y también mayor será la similitud.
En el extremo, si Doc1 solo cita a Paris y Doc2 solo cita a Londres, los documentos no tienen absolutamente nada en común. Doc1 tendría su vector en el eje x, Doc2 en el eje y, el ángulo 90 grados, Coseno 0. (Por cierto, eso es lo que queremos decir cuando decimos que dos cosas son ortogonales entre sí )
Agregar dimensiones :
Con esta sensación intuitiva de similitud expresada como un ángulo pequeño (o coseno grande), ahora podemos imaginar cosas en 3 dimensiones, por ejemplo, incluir la palabra "Amsterdam" en la mezcla. Y visualizar, bastante bien, cómo un documento con dos referencias de cada uno tendría un vector yendo en una dirección particular y podemos ver cómo esta dirección se compararía con un documento que cita París y Londres 3 veces cada uno, pero no Amsterdam, etc. Como dije podemos intentar e imaginar el espacio lujoso para 10 o 100 ciudades, difícil de dibujar, pero fácil de conceptualizar.
Voy a concluir con solo decir algunas palabras sobre la fórmula en sí . Como se dijo, otras referencias proporcionan buena información sobre los cálculos.
Otra vez primero en 2 dimensiones. La fórmula para el coseno del ángulo entre dos vectores se deriva de la diferencia trigonométrica (entre el ángulo ay el ángulo b)
cos(a - b) = (cos(a) * cos(b)) + (sin (a) * sin(b))
Esta fórmula es muy similar a la fórmula del producto punto:
Vect1 . Vect2 = (x1 * x2) + (y1 * y2)
Donde cos (a) coincide con el valor xy sin (a) el valor y, para el primer vector. etc. El único problema es que x, y etc. no son exactamente los valores de cos y sin, ya que estos valores deben leerse en el círculo unitario. Ahí es donde comienza el denominador de la fórmula: al dividir por el producto de la longitud de estos vectores, las coordenadas xey se normalizan.
Usando el ejemplo de @Bill Bell, dos formas de hacer esto en [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 aprovechando el rendimiento del método crossprod () ...
e = crossprod(a, b) / (sqrt(crossprod(a, a)) * sqrt(crossprod(b, b)))
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
/**
*
* @author Xiao Ma
* mail : [email protected]
*
*/
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));
}
}