文本相似度计算——HanLP分词+余弦相似度算法

article/2025/9/11 9:19:02

一、余弦相似度简介

余弦相似度(又称为余弦相似性):是通过测量两个向量的夹角的余弦值来度量它们之间的相似性。余弦值接近1,夹角趋于0,表明两个向量越相似;余弦值接近于0,夹角趋于90度,表明两个向量越不相似。

                

那么如何来计算余弦相似性呢?

余弦定理是三角形中三边长度与一个角的余弦值(cos)的数学式。

余弦定理的表达式如下:

勾股定理则是余弦定理的特殊情况,当角为直角时,即:cos\gamma = 0时,公式简化为c^{2}=a^{2}+b^{2}

根据余弦定理表达式,余弦的计算公式如下:

而a,b,c 是三个边的长度。假定a向量是[x1, y1],b向量是[x2, y2],那么根据向量求长度公式(向量的长度又叫向量的模,使用双竖线来包裹向量表示向量的长度)

\vec{\left | a \right |}=\sqrt{x_{1}^{2}+y_{1}^{2}},即a^{2}=x_{1}^{2}+y_{1}^{2};同理,\vec{\left | b \right |}=\sqrt{x_{2}^{2}+y_{2}^{2}},即b^{2}=x_{2}^{2}+y_{2}^{2} ,

c=\sqrt{(x_{1}-x_{2})^{2}+(y_{1}-y_{2})^{2}}

 将此时的a,b,c带入余弦公式,即可推导出

cos\theta =\frac{a\cdot b}{\left \| a \right \| *\left \| b \right \|} ,其中分子为向量a与向量b的点乘,分母为二者各自的L2相乘,即将所有维度值的平方相加后开方。 

二、文本相似度计算思路

句子A:我想养一头奶牛,这样就可以每天喝新鲜的牛奶。

句子B:我想每天喝新鲜的牛奶,所以打算养一头奶牛。

1. 分词

       A分词结果:[我, 想, 养, 一头, 奶牛, 这样, 就, 可以, 每天, 喝, 新鲜, 的, 牛奶]

       B分词结果:[我, 想, 每天, 喝, 新鲜, 的, 牛奶, 所以, 打算, 养, 一头, 奶牛]

2. 取并集(将句子A、B分词后的结果取并集)

        [我, 想, 养, 一头, 奶牛, 这样, 就, 可以, 每天, 喝, 新鲜, 的, 牛奶, 所以, 打算]

3. 写出词频向量

根据句子A、B的分词结果去计算词频向量,其中词频向量的长度为第二步中的并集,而每一位代表单词的出现次数。

        句子A:(1,1,1,1,1,1,1,1,1,1,1,1,1,0,0)

        句子B:(1,1,1,1,1,0,0,0,1,1,1,1,1,1,1)

到这里,问题就变成了如何计算这两个向量的相似程度。

4. 计算余弦相似度

将词频向量代入余弦相似度公式:

cos(\theta )=\frac{1\times 1+1\times 1+1\times 1+1\times 1+1\times 1+1\times 0+1\times 0+1\times 0+1\times 1+1\times 1+1\times 1+1\times 1+1\times 1+0\times 1+0\times 1}{\sqrt{1^{2}+1^{2}+1^{2}+1^{2}+1^{2}+1^{2}+1^{2}+1^{2}+1^{2}+1^{2}+1^{2}+1^{2}+1^{2}+0^{2}+0^{2}}\times \sqrt{1^{2}+1^{2}+1^{2}+1^{2}+1^{2}+0^{2}+0^{2}+0^{2}+1^{2}+1^{2}+1^{2}+1^{2}+1^{2}+1^{2}+1^{2}}}

 =\frac{10}{\sqrt{13}\times \sqrt{12}}\approx 0.80064

计算结果中夹角的余弦值为0.80064,非常接近于1,所以,上面的句子A和句子B是基本相似的。

三、代码实现

使用HanLP需要先导入maven依赖:

        <!-- https://mvnrepository.com/artifact/com.hankcs/hanlp --><dependency><groupId>com.hankcs</groupId><artifactId>hanlp</artifactId><version>portable-1.7.2</version></dependency>

Java代码如下:

package com.scb.dss.udf;import com.hankcs.hanlp.HanLP;import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;public class CosineSimilarity {/*** 使用余弦相似度算法计算文本相似性** @param sentence1* @param sentence2* @return*/public static double getSimilarity(String sentence1, String sentence2) {System.out.println("Step1. 分词");List<String> sent1Words = getSplitWords(sentence1);System.out.println(sentence1 + "\n分词结果:" + sent1Words);List<String> sent2Words = getSplitWords(sentence2);System.out.println(sentence2 + "\n分词结果:" + sent2Words);System.out.println("Step2. 取并集");List<String> allWords = mergeList(sent1Words, sent2Words);System.out.println(allWords);int[] statistic1 = statistic(allWords, sent1Words);int[] statistic2 = statistic(allWords, sent2Words);// 向量A与向量B的点乘double dividend = 0;// 向量A所有维度值的平方相加double divisor1 = 0;// 向量B所有维度值的平方相加double divisor2 = 0;// 余弦相似度 算法for (int i = 0; i < statistic1.length; i++) {dividend += statistic1[i] * statistic2[i];divisor1 += Math.pow(statistic1[i], 2);divisor2 += Math.pow(statistic2[i], 2);}System.out.println("Step3. 计算词频向量");for(int i : statistic1) {System.out.print(i+",");}System.out.println();for(int i : statistic2) {System.out.print(i+",");}System.out.println();// 向量A与向量B的点乘 / (向量A所有维度值的平方相加后开方 * 向量B所有维度值的平方相加后开方)return dividend / (Math.sqrt(divisor1) * Math.sqrt(divisor2));}// 3. 计算词频private static int[] statistic(List<String> allWords, List<String> sentWords) {int[] result = new int[allWords.size()];for (int i = 0; i < allWords.size(); i++) {// 返回指定集合中指定对象出现的次数result[i] = Collections.frequency(sentWords, allWords.get(i));}return result;}// 2. 取并集private static List<String> mergeList(List<String> list1, List<String> list2) {List<String> result = new ArrayList<>();result.addAll(list1);result.addAll(list2);return result.stream().distinct().collect(Collectors.toList());}// 1. 分词private static List<String> getSplitWords(String sentence) {// 标点符号会被单独分为一个Term,去除之return HanLP.segment(sentence).stream().map(a -> a.word).filter(s -> !"`~!@#$^&*()=|{}':;',\\[\\].<>/?~!@#¥……&*()——|{}【】‘;:”“'。,、? ".contains(s)).collect(Collectors.toList());}
}

测试代码:

package com.scb.dss.udf;import org.junit.Test;public class CosineSimilarityTest {@Testpublic void testGetSimilarity() throws Exception {String text3 = "我想养一头奶牛,这样就可以每天喝新鲜的牛奶。";String text4 = "我想每天喝新鲜的牛奶,所以打算养一头奶牛。";System.out.println("文本相似度为:"+CosineSimilarity.getSimilarity(text3, text4));}
}

 

四、参考

相似度计算方法(三) 余弦相似度_潘永青的博客-CSDN博客_余弦相似度

余弦相似度Cosine Similarity相关计算公式 - 蝈蝈俊 - 博客园


http://chatgpt.dhexx.cn/article/VGg94rZP.shtml

相关文章

文本相似度算法Jaccard相似度(杰卡德相似度)java实现

文本相似度算法 杰卡德相似度&#xff0c;指的是文本A与文本B中交集的字数除以并集的字数&#xff0c;公式非常简单&#xff1a; java代码 import java.util.HashSet; import java.util.Scanner; import java.util.Set;public class StrJaccard {public static void main(Str…

python中文相似度_基于TF-IDF、余弦相似度算法实现文本相似度算法的Python应用

基于TF-IDF算法、余弦相似度算法实现相似文本推荐——文本相似度算法,主要应用于文本聚类、相似文本推荐等场景。 设计说明 使用jieba切词,设置自定义字典 使用TF-IDF算法,找出文章的关键词; 每篇文章各取出若干个关键词(比如20个),合并成一个集合,计算每篇文章对于…

NLP文本相似度算法LCS

目录 一、什么是LCS子序列最长公共子序列 二、LCS的应用场景三、LCS的查找方法1. 动态规划法计算LCS的长度和两字符串的相似度2. 回溯算法查找LCS 四、代码实现 一、什么是LCS 子序列 子序列:一个序列S任意删除若干个字符得到的新序列T&#xff0c;则T叫做S的子序列 最长公共…

文本相似度算法对比分析,判断内容相似的算法有

有哪些算法用于比较两个字符串的相似程度 终于知道怎么判断字符串相似度了 一直不理解&#xff0c;为什么要计算两个字符串的相似度呢rfid。什么叫做两个字符串的相似度。经常看别人的博客&#xff0c;碰到比较牛的人&#xff0c;然后就翻了翻&#xff0c;终于找到了比较全面…

文本相似度算法对比分析,短文本相似度主流算法

如何检查多个word文档内容的相似度 工具/材料&#xff1a;电脑、WORD。第一步&#xff0c;打开电脑进入桌面&#xff0c;打开软件进界面。第二步&#xff0c;打开软件进入后&#xff0c;打开相应的文档。第三步&#xff0c;找到上方菜单栏的审阅点击。第四步&#xff0c;点击后…

文本相似度算法总结

文本匹配算法主要用于搜索引擎&#xff0c;问答系统等&#xff0c;是为了找到与目标文本最相关的文本。例如信息检索可以归结成查询项和文档的匹配&#xff0c;问答系统可以归结为问题和候选答案的匹配&#xff0c;对话系统可以归结为对话和回复的匹配。 一、传统模型 基于字面…

计算文本相似度

&#xfeff;&#xfeff; 计算文本相似度 推荐 2 收藏 简单讲解 上一章有提到过[基于关键词的空间向量模型]的算法&#xff0c;将用户的喜好以文档描述并转换成向量模型&#xff0c;对商品也是这么处理&#xff0c;然后再通过计算商品文档和用户偏好文档的余弦相似度。 文本相…

计算文本相似度的常用算法

文章目录 1. 余弦相似度2. TF-IDF模型2.1 词频TF的计算方法2.2 反文档频率IDF的计算方法2.3 TF-IDF的计算方法 3. 基于语义相似度的计算 —— DSSM4. LSI/LSA模型5. LDA模型6. 编辑距离计算7. 杰卡德系数计算8. Word2Vec计算9. BM25 NLP、数据挖掘领域中&#xff0c;文本分析是…

LCS、LIS

LCS是最长公共子序列的表示&#xff1a; 题目链接 代码也十分简单就不敲了。 LIS是最长上升子序列的英文缩写。 (动态规划) O(n2)O(n2) 状态表示&#xff1a;f[i]表示从第一个数字开始算&#xff0c;以w[i]结尾的最大的上升序列。(以w[i]结尾的所有上升序列中属性为最大值的…

LCMV相关的算法

写这个程序的时候&#xff0c;我们遵循如下的设计过程&#xff1a; 第一&#xff1a;设计LCMV&#xff1b; 第二&#xff1a;降秩LCMV&#xff1b; 第三&#xff1a;自适应降秩LCMV&#xff1b; 下面我首先给你捋一下整个算法的大致思路&#xff1a; 输入信号&#xff1a; …

SCC算法求强连通分量简单讲解证明及实现

目录 强连通分量SCC算法简介两个概念dfs结束时间转置图 SCC算法伪代码描述SCC算法正确性证明引理1&#xff1a;引理2&#xff1a;SCC证明不错找不漏找 代码实现 强连通分量 连通分量要求任意两点可达&#xff0c;而强连通分量要求任意两点互相可达&#xff0c;即必须存在a->…

最长公共子串LCS (Longest Common Subsequence) 算法

三个方法都有所借鉴&#xff0c;但代码部分是自己试着写出来的&#xff0c;虽然最后的运行结果都是正确的&#xff0c;但此过程中难免会有考虑不周全的地方&#xff0c;如发现代码某些地方有误&#xff0c;欢迎指正。同时有新的想法&#xff0c;也可以提出! 采用顺序结构存储串…

常考的经典算法--最长公共子序列(LCS)与最长公共子串(DP)

《1》最长公共子序列&#xff08;LCS&#xff09;与最长公共子串(DP) http://blog.csdn.net/u012102306/article/details/53184446 https://segmentfault.com/a/1190000007963594 http://www.cppblog.com/mysileng/archive/2013/05/14/200265.html 1. 问题描述 子串应该比…

LCS算法学习

LCS的定义 最长公共子序列&#xff0c;即Longest Common Subsequence&#xff0c;LCS。一个序列S任意删除若干个字符得到新序列T&#xff0c;则T叫做S的子序列&#xff1b;两个序列X和Y的公共子序列中&#xff0c;长度最长的那个&#xff0c;定义为X和Y的最长公共子序列。 字符…

算法系列之六:最长公共子序列(LCS)问题(连续子序列)的三种解法

最长公共子序列&#xff08;LCS&#xff09;问题有两种方式定义子序列&#xff0c;一种是子序列不要求不连续&#xff0c;一种是子序列必须连续。上一章介绍了用两种算法解决子序列不要求连续的最终公共子序列问题&#xff0c;本章将介绍要求子序列必须是连续的情况下如何用算法…

Hirschberg的LCS算法实现

解决Longest Common Subsequence(LCS)问题最常用的算法是Dyanmic programing&#xff0c;细节可以参考Ch15.4 of Introduction of Algorithm(2ED), MIT press, p 350。这个算法最大的问题是他的空间复杂度是O(m*n)。这样&#xff0c;当两个序列达到上万个节点时&#xff0c;内存…

SLIC算法

基础知识 在介绍SLIC之前&#xff0c;先来介绍以下Lab颜色空间的介绍。 Lab色彩模型是由亮度(L)要素和与有关色彩的a,b要素组成&#xff0c;L的值由0(黑色)到100(白色)&#xff0c;a表示从洋红色至绿色的范围(a为负值表示绿色而正值表示品红)&#xff0c;b表示从黄色至蓝色的…

动态规划之LCS算法

一、前言 LCS是Longest Common Subsequence的缩写&#xff0c;即最长公共子序列。一个序列&#xff0c;如果是两个或多个已知序列的子序列&#xff0c;且是所有子序列中最长的&#xff0c;则为最长公共子序列。 另外还有个分支问题&#xff1a;最长公共子串。子串的字符位置必…

LCS算法的C++实现

这两天忙里偷闲看了July的团队提供的LCS算法视频&#xff0c;真的如视频标题一样&#xff0c;十分钟搞定LCS算法。 感谢July大神&#xff0c;感谢其团队的邹博。 这里附上视频链接&#xff1a;http://www.julyedu.com/video/play?course17 说是十分钟搞定&#xff0c;其实是…

算法学习 - 最长公共子序列(LCS)C++实现

最长公共子序列 最长公共子序列的问题很简单&#xff0c;就是在两个字符串中找到最长的子序列&#xff0c;这里明确两个含义&#xff1a; 子串&#xff1a;表示连续的一串字符 。子序列&#xff1a;表示不连续的一串字符。 所以这里要查找的是不连续的最长子序列&#xff0c; …