如何计算两个向量的余弦相似度?

37

如何找到向量之间的余弦相似度?

我需要找到相似度来衡量两行文本之间的相关性。

例如,我有两个句子:

用户界面系统

用户界面机器

...以及它们在tF-idf之后的各自向量,例如使用LSI进行归一化,分别为[1,0.5][0.5,1]

我如何测量这些向量之间的相似度?

7个回答

71

如果您想避免依赖第三方库来完成这样一个简单的任务,这里是一个纯Java实现:

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));
}

请注意,该函数假设这两个向量具有相同的长度。出于安全考虑,您可能希望明确检查它。


2
谢谢,我只是太懒做了。 :) - Enrichman

32

请看:http://en.wikipedia.org/wiki/Cosine_similarity

如果你有向量A和B。

相似性定义为:

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)

例子:

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

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

22
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;
  }
}

最近我在大学的信息检索课上做了一些tf-idf相关的事情。我使用了这个余弦相似度方法,使用了Jama: Java Matrix Package

完整的源代码请参见IR Math with Java:相似性度量,这是一个非常好的资源,涵盖了许多不同的相似性度量方法。


5

如果你想在Java中使用矩阵代码,我建议使用Colt库。如果你已经有了这个库,代码看起来应该像这样(未经测试或编译):

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))

上述代码也可以修改为使用其中一个Blas.dnrm2()方法或Algebra.DEFAULT.norm2()进行范数计算。完全相同的结果,更易读取取决于个人喜好。


2
当我一段时间前在进行文本挖掘时,我使用了提供了Java的广泛不同度量标准的SimMetrics库。如果需要更多的话,还可以看看R和CRAN
但是,根据维基百科中的描述编写代码是相当简单的任务,并且可能是一个不错的练习。

1
看起来你的SimMetrics链接已经失效,现在指向一个关于鞋子的垃圾博客。https://github.com/Simmetrics/simmetrics 看起来更好一些。 - Kaypro II

1

对于使用Map(dimension -> magnitude)稀疏表示向量的方法 这里提供一个scala版本(在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))
}

0
def cosineSimilarity(vectorA: Vector[Double], vectorB: Vector[Double]):Double={
    var dotProduct = 0.0
    var normA = 0.0
    var normB = 0.0
    var i = 0

    for(i <- vectorA.indices){
        dotProduct += vectorA(i) * vectorB(i)
        normA += Math.pow(vectorA(i), 2)
        normB += Math.pow(vectorB(i), 2)
    }

    dotProduct / (Math.sqrt(normA) * Math.sqrt(normB))
}

def main(args: Array[String]): Unit = {
    val vectorA = Array(1.0,2.0,3.0).toVector
    val vectorB = Array(4.0,5.0,6.0).toVector
    println(cosineSimilarity(vectorA, vectorA))
    println(cosineSimilarity(vectorA, vectorB))
}

Scala版本


这看起来非常像 Java。 - Yeikel

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接