余弦相似度
向量的余弦公式是
c o s = a b ∣ a ∣ ∗ ∣ b ∣ cos=\frac{ab}{|a|*|b|} cos=∣a∣∗∣b∣ab
其中a,b是向量,余弦也叫余弦函数,是三角函数的一种,且余弦定理亦称第二余弦定理,是关于三角形边角关系的重要定理之一。
ab:向量的内积
|a| |b|:表示向量的模
假设向量a:{x1,x2,x3,…,xn},向量b:{y1,y2,y3,…,yn}
则
a b = ( x 1 ∗ y 1 + x 2 ∗ y 2 + x 3 ∗ y 3 + . . . + x n ∗ y n ) ab=(x1*y1 + x2*y2 + x3*y3 + ... + xn*yn) ab=(x1∗y1+x2∗y2+x3∗y3+...+xn∗yn)
∣ a ∣ = x 1 ∗ x 1 + x 2 ∗ x 2 + x 3 ∗ x 3 + . . . + x n ∗ x n |a|=\sqrt{x1*x1 + x2*x2 + x3*x3 + ... + xn*xn} ∣a∣=x1∗x1+x2∗x2+x3∗x3+...+xn∗xn
∣ b ∣ = y 1 ∗ y 1 + y 2 ∗ y 2 + y 3 ∗ y 3 + . . . + y n ∗ y n |b|=\sqrt{y1*y1 + y2*y2 + y3*y3 + ... + yn*yn} ∣b∣=y1∗y1+y2∗y2+y3∗y3+...+yn∗yn
所以:
c o s = ( x 1 ∗ y 1 + x 2 ∗ y 2 + x 3 ∗ y 3 + . . . + x n ∗ y n ) x 1 ∗ x 1 + x 2 ∗ x 2 + x 3 ∗ x 3 + . . . + x n ∗ x n ∗ y 1 ∗ y 1 + y 2 ∗ y 2 + y 3 ∗ y 3 + . . . + y n ∗ y n cos = \frac{(x1*y1 + x2*y2 + x3*y3 + ... + xn*yn)}{\sqrt{x1*x1 + x2*x2 + x3*x3 + ... + xn*xn} * \sqrt{y1*y1 + y2*y2 + y3*y3 + ... + yn*yn}} cos=x1∗x1+x2∗x2+x3∗x3+...+xn∗xn∗y1∗y1+y2∗y2+y3∗y3+...+yn∗yn(x1∗y1+x2∗y2+x3∗y3+...+xn∗yn)
一个向量空间中两个向量夹角间的余弦值作为衡量两个个体之间差异的大小,余弦值接近1,夹角趋于0,表明两个向量越相似,余弦值接近于0,夹角趋于90度,表明两个向量越不相似。
使用余弦相似度计算两段文本的相似度
思路:1、分词;2、列出所有词;3、分词计算词频;4、词频向量化;5、套用余弦函数计量两个句子的相似度。
假设:
句子A:这只皮靴号码大了。那只号码合适。
句子B:这只皮靴号码不小,那只更合适。
1、分词:
使用ansj分词对上面两个句子分词后,分别得到两个列表:
listA=[这, 只, 皮靴, 号码, 大, 了, 那, 只, 号码, 合适]
listB=[这, 只, 皮靴, 号码, 不, 小, 那, 只, 更, 合适]
2、列出所有词
将listA和listB放在一个set中,得到:
set=[号码, 合适, 那, 更, 了, 大, 皮靴, 这, 只, 不, 小]
3、分词计算词频
词频表示分词在分词列表中出现的次数,分别计算listA和listB中每个分词在列表中的词频
freqMapA={这=1, 只=2, 皮靴=1, 号码=2, 大=1, 了=1, 那=1, 合适=1}
freqMapB={这=1, 只=2, 皮靴=1, 号码=1, 不=1, 小=1, 那=1, 更=1, 合适=1}
4、词频向量化
分别计算freqMapA和freqMapB在set中出现的次数,如果没有出现则为0,得到结果:
set= [号码, 合适, 那, 更, 了, 大, 皮靴, 这, 只, 不, 小]
freqListA=[2, 1, 1, 0, 1, 1, 1, 1, 2, 0, 0]
freqListB=[1, 1, 1, 1, 0, 0, 1, 1, 2, 1, 1]
5、套用余弦函数计算相似度
c o s = ( 2 ∗ 1 + 1 ∗ 1 + 1 ∗ 1 + 0 ∗ 1 + 1 ∗ 0 + 1 ∗ 0 + 1 ∗ 1 + 1 ∗ 1 + 2 ∗ 2 + 0 ∗ 1 + 0 ∗ 1 ) 2 ∗ 2 + 1 ∗ 1 + 1 ∗ 1 + 0 ∗ 0 + 1 ∗ 1 + 1 ∗ 1 + 1 ∗ 1 + 1 ∗ 1 + 2 ∗ 2 + 0 ∗ 0 + 0 ∗ 0 ∗ 1 ∗ 1 + 1 ∗ 1 + 1 ∗ 1 + 1 ∗ 1 + 0 ∗ 0 + 0 ∗ 0 + 1 ∗ 1 + 1 ∗ 1 + 2 ∗ 2 + 1 ∗ 1 + 1 ∗ 1 = 10 12.94 = 0.77 cos = \frac{(2*1 + 1*1 + 1*1 + 0*1 + 1*0 + 1*0 + 1*1 + 1* 1 + 2* 2 + 0*1 + 0*1)}{\sqrt{2*2 + 1*1 + 1*1 + 0*0 + 1*1 + 1*1 + 1*1 + 1*1 + 2*2 + 0*0 + 0*0}*\sqrt{1*1 + 1*1 + 1*1+1*1 + 0*0 + 0*0 + 1*1 + 1*1 + 2*2 + 1*1 + 1*1}}=\frac{10}{12.94} = 0.77 cos=2∗2+1∗1+1∗1+0∗0+1∗1+1∗1+1∗1+1∗1+2∗2+0∗0+0∗0∗1∗1+1∗1+1∗1+1∗1+0∗0+0∗0+1∗1+1∗1+2∗2+1∗1+1∗1(2∗1+1∗1+1∗1+0∗1+1∗0+1∗0+1∗1+1∗1+2∗2+0∗1+0∗1)=12.9410=0.77
实现代码:
public class CosineSimilarity {
public static double similar(String s1, String s2) {
//去除特殊字符
s1 = s1.replaceAll("\\p{P}", "");
s2 = s2.replaceAll("\\p{P}", "");
List<Term> termList1 = NlpAnalysis.parse(s1).getTerms();
List<String> wordList1 = termList1.stream().map(t -> t.getRealName()).collect(Collectors.toList());
List<Term> termList2 = NlpAnalysis.parse(s2).getTerms();
List<String> wordList2 = termList2.stream().map(t -> t.getRealName()).collect(Collectors.toList());
Set<String> totalSet = new HashSet<>();
totalSet.addAll(wordList1);
totalSet.addAll(wordList2);
Map<String, Integer> wordMap1 = new LinkedHashMap<>();
Map<String, Integer> wordMap2 = new LinkedHashMap<>();
for (String w : wordList1) {
if (wordMap1.containsKey(w)) {
int count = wordMap1.get(w);
count++;
wordMap1.put(w, count);
} else {
wordMap1.put(w, 1);
}
}
for (String w : wordList2) {
if (wordMap2.containsKey(w)) {
int count = wordMap2.get(w);
count++;
wordMap2.put(w, count);
} else {
wordMap2.put(w, 1);
}
}
List<Integer> freqList1 = new ArrayList<>();
List<Integer> freqList2 = new ArrayList<>();
for (String w : totalSet) {
if (wordMap1.containsKey(w)) {
freqList1.add(wordMap1.get(w));
} else {
freqList1.add(0);
}
if (wordMap2.containsKey(w)) {
freqList2.add(wordMap2.get(w));
} else {
freqList2.add(0);
}
}
int aa = 0;
int bb = 0;
int ab = 0;
for (int i = 0; i < freqList1.size(); i++) {
aa += freqList1.get(i) * freqList1.get(i);
bb += freqList2.get(i) * freqList2.get(i);
ab += freqList1.get(i) * freqList2.get(i);
}
double result = ab / (Math.sqrt(aa) * Math.sqrt(bb));
return result;
}
public static void main(String[] args) {
String s1 = "这只皮靴号码大了。那只号码合适";
String s2 = "这只皮靴号码不小,那只更合适";
double d = similar(s1, s2);
System.out.println(d);
}
}