有人可以用一个非常简单的graphics方式举一个余弦相似的例子吗?

余弦相似性文章在维基百科

你可以在这里(列表或其他)显示载体,然后做math,让我们看看它是如何工作的?

我是初学者。

这里有两个很短的文字比较:

  1. Julie loves me more than Linda loves me

  2. Jane likes me more than Julie loves me

我们想知道这些文本是如何相似的,纯粹是根据字数(而忽略字序)。 我们首先列出两个文本中的单词:

 me Julie loves Linda than more likes Jane 

现在我们计算每个文本中出现的每个单词的次数:

  me 2 2 Jane 0 1 Julie 1 1 Linda 1 0 likes 0 1 loves 2 1 more 1 1 than 1 1 

尽pipe我们对这个词本身并不感兴趣。 我们只对这两个垂直向量计数感兴趣。 例如,每个文本中都有两个“我”的例子。 我们将通过计算这两个向量的一个函数,即它们之间的夹angular的余弦来决定这两个文本是多么接近。

这两个向量又是:

 a: [2, 1, 0, 2, 0, 1, 1, 1] b: [2, 1, 1, 1, 1, 0, 1, 1] 

它们之间angular度的余弦约为0.822。

这些向量是8维的。 使用余弦相似性的优点显然在于它将超出人类能力的问题转化为可能的问题。 在这种情况下,你可以把这个angular度看作是从零开始的一个“距离”或完全一致的约35度的angular度。

我猜测你更有兴趣了解余弦相似性的原因(为什么它提供了相似性的一个很好的指示),而不是“ 如何 ”计算(用于计算的具体操作)。 如果您对后者感兴趣,请参阅Daniel在本文中提到的参考资料以及相关的SO问题 。

为了解释为什么,甚至更多地解释为什么,首先简化问题并且仅在两个方面工作是有用的。 一旦你得到了这个二维数据,从三个维度来考虑它就更容易了,当然更难以想象更多的维度,但是到那时我们可以使用线性代数来进行数值计算,并且帮助我们从根本上思考线条/vector/“平面”/“球体”的n维,即使我们不能画这些。

所以… 在两个维度上 :关于文本的相似性,这意味着我们将把重点放在两个不同的术语上,比如说“伦敦”和“巴黎”,并且我们会计算出这些单词在我们希望比较两个文件中的每一个。 这给了我们每个文件在xy平面上的一个点,例如,如果Doc1有一次巴黎,伦敦有四次,那么(1,4)的一点就会提出这个文件(关于这个文件的微小评估) 。 或者,从vector的angular度来讲,这个Doc1文档将是一个从原点到点(1,4)的箭头。 考虑到这个图像,让我们思考两个文档的相似性以及这与vector的关系。

非常相似的文件(再次涉及这个有限维度集合)将具有与巴黎相同数量的引用,以及与伦敦相同数量的引用,或者可以具有相同比例的这些引用文件Doc2,其中2个参考巴黎和8个参考伦敦,也是非常相似,只是可能是一个更长的文本或更重复的城市的名字,但在相同的比例:也许这两个文件是关于伦敦的指南,只有制造传递给巴黎的参考(以及那个城市是多么的不起眼;-)只是在开玩笑!!!)。 现在较less的类似文件也可能包括对两个城市的引用,但是在不同的比例中,也许Doc2只会引用巴黎一次和伦敦7次。

回到我们的xy平面,如果我们绘制这些假设的文档,我们看到,当它们非常相似时,它们的vector重叠(尽pipe一些vector可能更长),并且当它们开始具有较less的共同时,这些vector开始发散,他们之间有更大的angular度。

巴姆! 通过测量vector之间的angular度,我们可以很好地了解它们之间的相似性 ,为了使事情更容易,通过取这个angular度的余弦 ,我们有一个很好的0到1(或-1到1,取决于什么以及我们如何考虑)表示这种相似性的价值。 angular度越小,余弦值越大(接近1),相似度越大。

在极端情况下,如果Doc1只引用了巴黎,而Doc2引用了伦敦,那么这些文件完全没有什么共同之处。 Doc1将在x轴上具有其vector,在y轴上具有Doc2,angular度为90度,余弦为0.(顺便说一下,当我们说两件事彼此正交时,就是这个意思

添加尺寸
有了这种直观的相似性expression为一个小angular度(或大余弦),我们现在可以想象在三个维度的事情,比如在混合中引入“阿姆斯特丹”一词。 并且很好地想象一下,如果一个文件有两个参考文献,每个文献都会有一个特定方向的向量,我们可以看到这个方向将如何与一个引用巴黎和伦敦的文献相提并论,而不是阿姆斯特丹等。我们可以试着想象一下这个10或100个城市的空间,很难画,但是容易理解。

我只会对公式本身几句话 。 如上述其他参考文献提供了关于计算的良好信息。

再次在2个维度。 两个向量之间夹angular的余弦公式是从三angular差(angular度a和angular度b之间)

 cos(a - b) = (cos(a) * cos(b)) + (sin (a) * sin(b)) 

这个公式看起来很像点积公式:

 Vect1 . Vect2 = (x1 * x2) + (y1 * y2) 

其中,cos(a)与x值相匹配,sin(a)与第一个向量的y值相匹配。 等等。唯一的问题是,x,y等不完全是cos和sin值,因为这些值需要在单位圆上读取。 这就是公式的分母:通过除以这些向量的长度的乘积,x和y坐标被归一化。

这是我在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)); } } } 

这个Python代码是我实现这个algorithm的快速和肮脏的尝试:

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

为了简单,我正在减lessvectora和b:

 Let : a : [1, 1, 0] b : [1, 0, 1] 

然后余弦相似度(Theta):

  (Theta) = (1*1 + 1*0 + 0*1)/sqrt((1^2 + 1^2))* sqrt((1^2 + 1^2)) = 1/2 = 0.5 

那么cos0.5的倒数是60度。

用@Bill Bell的例子,有两种方法可以在[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))) 

或利用crossprod()方法的性能…

 e = crossprod(a, b) / (sqrt(crossprod(a, a)) * sqrt(crossprod(b, b))) 

这是一个简单的实现余弦相似性的Python代码。

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

简单的JAVA代码来计算余弦相似度

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