2009-02-06 25 views
27

Comment trouver la similarité entre les cosinus entre les vecteurs?Comment calculer la similarité en cosinus de deux vecteurs?

J'ai besoin de trouver la similarité pour mesurer la parenté entre deux lignes de texte.

Par exemple, j'ai deux phrases comme:

système

pour l'interface utilisateur

machine d'interface utilisateur

... et leurs vecteurs respectifs après tF-idf, suivie par la normalisation à l'aide LSI, par exemple [1,0.5] et [0.5,1].

Comment mesurer la concordance entre ces vecteurs?

Répondre

18
public class CosineSimilarity extends AbstractSimilarity { 

    @Override 
    protected double computeSimilarity(Matrix sourceDoc, Matrix targetDoc) { 
    double dotProduct = sourceDoc.arrayTimes(targetDoc).norm1(); 
    double eucledianDist = sourceDoc.normF() * targetDoc.normF(); 
    return dotProduct/eucledianDist; 
    } 
} 

J'ai récemment fait quelques trucs tf-idf pour mon unité de recherche d'information à l'université. J'ai utilisé cette méthode de similarité des cosinus qui utilise Jama: Java Matrix Package.

Pour le code source complet, voir IR Math with Java : Similarity Measures, très bonne ressource qui couvre quelques bonnes mesures de similarité.

+0

sonne parfaitement .. merci –

29

Regardez: http://en.wikipedia.org/wiki/Cosine_similarity.

Si vous avez des vecteurs A et B

La similarité est définie comme:

cosine(theta) = A . B/||A|| ||B|| 

For a vector A = (a1, a2), ||A|| is defined as sqrt(a1^2 + a2^2) 

For vector A = (a1, a2) and B = (b1, b2), A . B is defined as a1 b1 + a2 b2; 

So for vector A = (a1, a2) and B = (b1, b2), the cosine similarity is given as: 

    (a1 b1 + a2 b2)/sqrt(a1^2 + a2^2) sqrt(b1^2 + b2^2) 

Exemple:

A = (1, 0.5), B = (0.5, 1) 

cosine(theta) = (0.5 + 0.5)/sqrt(5/4) sqrt(5/4) = 4/5 
2

Quand je travaillais avec text mining il y a quelque temps, je utilisait la bibliothèque SimMetrics qui fournit une vaste gamme de mesures différentes en Java. Si vous avez besoin de plus, alors il y a toujours R and CRAN à regarder. Mais le coder à partir de la description dans Wikipedia est une tâche plutôt triviale, et peut être un bon exercice.

+1

Il semble que votre SimMetrics lien pourrie et pointe maintenant sur un blog de spam sur les chaussures . https://github.com/Simmetrics/simmetrics ressemble à un meilleur. –

5

Pour le code matriciel en Java, je recommande d'utiliser la bibliothèque Colt. Si vous avez cela, le code ressemble (non testé ou même compilé):

DoubleMatrix1D a = new DenseDoubleMatrix1D(new double[]{1,0.5}}); 
DoubleMatrix1D b = new DenseDoubleMatrix1D(new double[]{0.5,1}}); 
double cosineDistance = a.zDotProduct(b)/Math.sqrt(a.zDotProduct(a)*b.zDotProduct(b)) 

Le code ci-dessus peut également être modifié pour utiliser une des méthodes Blas.dnrm2() ou Algebra.DEFAULT.norm2() pour le calcul de la norme. Exactement le même résultat, qui est plus lisible dépend du goût.

38

Si vous voulez éviter d'utiliser des bibliothèques tierces pour une tâche simple, voici une implémentation Java simple:

public static double cosineSimilarity(double[] vectorA, double[] vectorB) { 
    double dotProduct = 0.0; 
    double normA = 0.0; 
    double normB = 0.0; 
    for (int i = 0; i < vectorA.length; i++) { 
     dotProduct += vectorA[i] * vectorB[i]; 
     normA += Math.pow(vectorA[i], 2); 
     normB += Math.pow(vectorB[i], 2); 
    } 
    return dotProduct/(Math.sqrt(normA) * Math.sqrt(normB)); 
} 

Notez que la fonction suppose que les deux vecteurs ont la même longueur. Vous pouvez vouloir vérifier explicitement pour la sécurité.

+2

Merci, j'étais juste trop paresseux pour le faire. :) – Enrichman

+0

Merci, vous avez facilité mon travail! – Techiee

0

Pour la représentation parcimonieuse des vecteurs en utilisant Map(dimension -> magnitude) Voici une version scala (Vous pouvez faire des choses similaires en Java 8)

def cosineSim(vec1:Map[Int,Int], 
       vec2:Map[Int,Int]): Double ={ 
    val dotProduct:Double = vec1.keySet.intersect(vec2.keySet).toList 
    .map(dim => vec1(dim) * vec2(dim)).sum 
    val norm1:Double = vec1.values.map(mag => mag * mag).sum 
    val norm2:Double = vec2.values.map(mag => mag * mag).sum 
    return dotProduct/(Math.sqrt(norm1) * Math.sqrt(norm2)) 
}