scala 简单实现余弦相似度
余弦相似度:也叫余弦距离,是用向量空间中两个向量夹角的余弦值作为衡量两个个体间差异的大小的度量。余弦值越接近1,就表明夹角越接近0度,也就是两个向量越相似。余弦相似度广泛应用于机器学习领域。
百度百科https://baike.baidu.com/item/%E4%BD%99%E5%BC%A6%E7%9B%B8%E4%BC%BC%E5%BA%A6/17509249
本文用scala简单实现了余弦相似算法
举个例子说明
以下是两句话,我们用余弦相识度算法计算两个句子的相似度
val str1 = "这句话和下面那句很像"
val str2 = "我就是下面那句话"
-
step1 分词(只做了简单的字符拆分)
str1 :这 句 话 和 下 面 那 句 很 像
str1 :我 就 是 下 面 那 句 话 -
step2 列出两句话出现的所有的词
这 句 话 和 下 面 那 很 像 我 就 是 -
step3 计算词频
str1 :这1, 句2, 话1, 和1, 下1, 面1, 那1, 很1, 像1
str2 :我1, 就1, 是1, 下 1,面1, 那1, 句1, 话1
转化为向量即为:
V1:(1,2,1,1,1,1,1,1,1,0,0,0)
V2:(0,1,1,0,1,1,1,0,0,1,1,1) -
计算两个向量的余弦
=0.612
以下是代码实现部分
/**
* 求向量的模
* @param vec
* @return
*/
def module(vec: Vector[Double]) = {
math.sqrt(vec.map(math.pow(_, 2)).sum)
}
/**
* 求两个向量的内积
* @param v1
* @param v2
* @return
*/
def innerProduct(v1: Vector[Double], v2: Vector[Double]) = {
val listBuffer = ListBuffer[Double]()
for (i <- 0 until v1.length; j <- 0 to v2.length; if i == j) {
if (i == j) listBuffer.append(v1(i) * v2(j))
}
listBuffer.sum
}
/**
* 求两个向量的余弦
* @param v1
* @param v2
* @return
*/
def cosvec(v1: Vector[Double], v2: Vector[Double]) = {
val cos = innerProduct(v1, v2) / (module(v1) * module(v2))
if (cos <= 1) cos else 1.0
}
/**
* 余弦相似度
* @param str1
* @param str2
* @return
*/
def textCosine(str1: String, str2: String) = {
val set = mutable.Set[Char]()
// 不进行分词
str1.foreach(set += _)
str2.foreach(set += _)
val ints1: Vector[Double] = set.toList.sorted.map(ch => {
str1.count(s => s == ch).toDouble
}).toVector
val ints2: Vector[Double] = set.toList.sorted.map(ch => {
str2.count(s => s == ch).toDouble
}).toVector
cosvec(ints1, ints2)
}
调用以上函数得出
val d = textCosine(str1, str2)
println("相似度:" + d)
计算结果得相似度:0.6123724356957945
与手算结果一致
余弦相似度缺点
对于文本 由于余弦相似算法没有考虑词的顺序信息会造成结果的误判,比如“牛奶”和“奶牛”的相似度为 100%;在做 文本处理的时候没有考虑语义、情感等重要信息。
因此,在做更复杂的数据处理的时候,都是多种算法结合使用,各取所长才能得出更好的结果。