Article sur la similitude des cosinus sur Wikipedia
Pouvez-vous montrer les vecteurs ici (dans une liste ou quelque chose), puis faire le calcul, et voyons comment cela fonctionne?
Je suis un débutant.
Article sur la similitude des cosinus sur Wikipedia
Pouvez-vous montrer les vecteurs ici (dans une liste ou quelque chose), puis faire le calcul, et voyons comment cela fonctionne?
Je suis un débutant.
Réponses:
Voici deux textes très courts à comparer:
Julie loves me more than Linda loves me
Jane likes me more than Julie loves me
Nous voulons savoir à quel point ces textes sont similaires, uniquement en termes de nombre de mots (et en ignorant l'ordre des mots). Nous commençons par faire une liste des mots des deux textes:
me Julie loves Linda than more likes Jane
Maintenant, nous comptons le nombre de fois que chacun de ces mots apparaît dans chaque texte:
me 2 2
Jane 0 1
Julie 1 1
Linda 1 0
likes 0 1
loves 2 1
more 1 1
than 1 1
Nous ne sommes cependant pas intéressés par les mots eux-mêmes. Nous ne nous intéressons qu'à ces deux vecteurs verticaux de dénombrement. Par exemple, il y a deux instances de «moi» dans chaque texte. Nous allons décider de la proximité de ces deux textes en calculant une fonction de ces deux vecteurs, à savoir le cosinus de l'angle entre eux.
Les deux vecteurs sont, encore une fois:
a: [2, 0, 1, 1, 0, 2, 1, 1]
b: [2, 1, 1, 0, 1, 1, 1, 1]
Le cosinus de l'angle entre eux est d'environ 0,822.
Ces vecteurs sont à 8 dimensions. Une vertu de l'utilisation de la similitude cosinus est clairement qu'elle convertit une question qui dépasse la capacité humaine à visualiser en une question qui peut l'être. Dans ce cas, vous pouvez considérer cela comme un angle d'environ 35 degrés, ce qui représente une certaine «distance» de zéro ou un accord parfait.
Je suppose que vous êtes plus intéressé à obtenir un aperçu du " pourquoi " la similitude cosinus fonctionne (pourquoi elle fournit une bonne indication de la similitude), plutôt que " comment " elle est calculée (les opérations spécifiques utilisées pour le calcul). Si votre intérêt est dans ce dernier, voir la référence indiquée par Daniel dans ce post, ainsi qu'une question SO connexe .
Pour expliquer à la fois le comment et plus encore le pourquoi, il est utile, dans un premier temps, de simplifier le problème et de ne travailler qu'en deux dimensions. Une fois que vous obtenez cela en 2D, il est plus facile de le penser en trois dimensions, et bien sûr plus difficile à imaginer dans de nombreuses autres dimensions, mais d'ici là, nous pouvons utiliser l'algèbre linéaire pour faire les calculs numériques et aussi pour nous aider à penser en termes de lignes / vecteurs / "plans" / "sphères" en n dimensions, même si nous ne pouvons pas les dessiner.
Donc, en deux dimensions : en ce qui concerne la similitude du texte, cela signifie que nous nous concentrerions sur deux termes distincts, disons les mots "Londres" et "Paris", et nous compterions combien de fois chacun de ces mots se trouve dans chacun des les deux documents que nous souhaitons comparer. Cela nous donne, pour chaque document, un point dans le plan xy. Par exemple, si Doc1 avait Paris une fois et Londres quatre fois, un point en (1,4) présenterait ce document (en ce qui concerne cette évaluation diminutive des documents). Ou, en termes de vecteurs, ce document Doc1 serait une flèche allant de l'origine au point (1,4). Avec cette image à l'esprit, réfléchissons à ce que signifie que deux documents soient similaires et comment cela se rapporte aux vecteurs.
DES DOCUMENTS TRÈS similaires (encore une fois en ce qui concerne cet ensemble limité de dimensions) auraient le même nombre de références à Paris, ET le même nombre de références à Londres, ou peut-être, ils pourraient avoir le même rapport de ces références. Un document, Doc2, avec 2 références à Paris et 8 références à Londres, serait également très similaire, mais avec peut-être un texte plus long ou en quelque sorte plus répétitif des noms des villes, mais dans la même proportion. Peut-être que les deux documents sont des guides sur Londres, ne faisant que des références passagères à Paris (et à quel point cette ville est cool ;-) Je plaisante !!!.
Maintenant, des documents moins similaires peuvent également inclure des références aux deux villes, mais dans des proportions différentes. Peut-être que Doc2 ne citerait Paris qu'une seule fois et Londres sept fois.
De retour à notre plan xy, si nous dessinons ces documents hypothétiques, nous voyons que lorsqu'ils sont TRÈS similaires, leurs vecteurs se chevauchent (bien que certains vecteurs puissent être plus longs), et comme ils commencent à avoir moins en commun, ces vecteurs commencent à diverger, pour avoir un angle plus large entre eux.
En mesurant l'angle entre les vecteurs, nous pouvons avoir une bonne idée de leur similitude , et pour rendre les choses encore plus faciles, en prenant le cosinus de cet angle, nous avons une belle valeur de 0 à 1 ou -1 à 1 qui indique cette similitude, en fonction de quoi et comment nous rendons compte. Plus l'angle est petit, plus la valeur du cosinus est grande (proche de 1) et plus la similitude est élevée.
A l'extrême, si Doc1 ne cite que Paris et Doc2 ne cite que Londres, les documents n'ont absolument rien en commun. Doc1 aurait son vecteur sur l'axe des x, Doc2 sur l'axe des y, l'angle 90 degrés, Cosinus 0. Dans ce cas, nous dirions que ces documents sont orthogonaux entre eux.
Ajout de dimensions :
avec cette sensation intuitive de similitude exprimée sous la forme d'un petit angle (ou d'un grand cosinus), nous pouvons maintenant imaginer des choses en 3 dimensions, par exemple en introduisant le mot "Amsterdam" dans le mélange, et visualiser assez bien comment un document à deux les références à chacun auraient un vecteur allant dans une direction particulière, et nous pouvons voir comment cette direction se comparerait à un document citant Paris et Londres trois fois chacun, mais pas Amsterdam, etc. Comme dit, nous pouvons essayer d'imaginer cette fantaisie espace pour 10 ou 100 villes. C'est difficile à dessiner, mais facile à conceptualiser.
Je terminerai en disant quelques mots sur la formule elle-même . Comme je l'ai dit, d'autres références fournissent de bonnes informations sur les calculs.
D'abord en deux dimensions. La formule du cosinus de l'angle entre deux vecteurs est dérivée de la différence trigonométrique (entre l'angle a et l'angle b):
cos(a - b) = (cos(a) * cos(b)) + (sin (a) * sin(b))
Cette formule ressemble beaucoup à la formule du produit scalaire:
Vect1 . Vect2 = (x1 * x2) + (y1 * y2)
où cos(a)
correspond à la x
valeur et sin(a)
la y
valeur, pour le premier vecteur, etc. Le seul problème est que x
, y
etc. ne sont pas exactement les cos
et les sin
valeurs, ces valeurs doivent être lues sur le cercle unité. C'est là que le dénominateur de la formule entre en jeu: en divisant par le produit de la longueur de ces vecteurs, les coordonnées x
et y
deviennent normalisées.
Voici mon implémentation 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));
}
}
}
Ce code Python est ma tentative rapide et sale d'implémenter l'algorithme:
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))
En utilisant l'exemple @Bill Bell, deux façons de procéder dans [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)))
ou en profitant des performances de la méthode crossprod () ...
e = crossprod(a, b) / (sqrt(crossprod(a, a)) * sqrt(crossprod(b, b)))
Il s'agit d'un Python
code simple qui implémente la similitude cosinus.
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));
}
}
Code JAVA simple pour calculer la similitude cosinus
/**
* 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));
}
Deux vecteurs A et B existent dans un espace 2D ou 3D, l'angle entre ces vecteurs est la similitude cos.
Si l'angle est supérieur (peut atteindre un maximum de 180 degrés), c'est Cos 180 = -1 et l'angle minimum est de 0 degré. cos 0 = 1 implique que les vecteurs sont alignés les uns aux autres et donc les vecteurs sont similaires.
cos 90 = 0 (ce qui est suffisant pour conclure que les vecteurs A et B ne sont pas du tout similaires et comme la distance ne peut pas être négative, les valeurs de cosinus se situeront de 0 à 1. Par conséquent, plus d'angle implique de réduire la similitude (la visualiser également logique)