用于计算文本相似性的余弦定理
最编程
2024-03-16 19:42:10
...
前言
余弦相似度,又称为余弦相似性,是通过计算两个向量的夹角余弦值来评估他们的相似度。余弦相似度将向量根据坐标值,绘制到向量空间中。用向量空间中两个向量夹角的余弦值作为衡量两个个体间差异的大小。余弦值越接近1,就表明夹角越接近0度,也就是两个向量越相似,反之越接近0就表示两个向量相似度越低,这就叫"余弦相似性"。
正文
重温余弦定理
先简单的重温一下高中数学知识,余弦定理
这个公式大家不知道还有没有印象呢?没有的话我们看下下面的图
此时a=(xa,ya)
,b=(xb,0)
,那么怎么计算各边长的长度呢?
此时将各边长代入上图的公式当中,最后可以得出最终的计算公式
文本相似度计算步骤
那么在我们的文本相似度计算中,都有哪些步骤呢?
- 分词,比如有两行文本,第一句:
你好,我是小王,我是个程序员”
,将会分割成你好/我/是/小王/我/是/个/程序员
。第二句:你好,我是设计师
,将会分成你好/我/是/设计师
- 统计词频,其实就是统计所有语句中的每个词在当前句子出现的次数,第一句:
你好1,我2,是2,小王1,个1,程序员1,设计师0
,第二句你好1,我1,是1,小王0,个0,程序员0,设计师1
- 组合词频向量,第一句
(1,2,2,1,1,1,0)
,第二句(1,1,1,0,0,0,1)
。 - 将数据代入上面的公式计算相似度
maven 引入ikanalyzer依赖
这里使用ikanalyzer来实现一个简单的分词功能
<dependency>
<groupId>com.janeluo</groupId>
<artifactId>ikanalyzer</artifactId>
<version>2012_u6</version>
</dependency>
IKUtils分词工具类,代码比简单,唯一一个方法返回的是语句分词的List对象
/**
* 分词相关工具类
* @author wangzh
*/
public class IKUtils {
/**
* 以List的格式返回文本分词的结果
* @param text
* @return
*/
public static List<String> divideText(String text){
if(null == text || "".equals(text.trim())){
return null;
}
List<String> resultList = new ArrayList<>();
StringReader re = new StringReader(text);
IKSegmenter ik = new IKSegmenter(re, true);
Lexeme lex = null;
try {
while ((lex = ik.next()) != null) {
resultList.add(lex.getLexemeText());
}
} catch (Exception e) {
//TODO
}
return resultList;
}
}
下面是主要的代码逻辑,相关步骤已注释在代码里面
public class Analysis {
public static void main(String[] args) {
Map<String,int[]> resultMap = new HashMap<>();
//测试文本
String text1 = "你好,我是小王,我是个程序员";
String text2 = "你好,我是设计师";
//统计
statistics(resultMap, IKUtils.divideText(text1),1);
statistics(resultMap, IKUtils.divideText(text2),0);
//计算类
final Calculation calculation = new Calculation();
resultMap.forEach((k,v)->{
int[] arr = resultMap.get(k);
calculation.setNumerator(calculation.getNumerator() + arr[0] * arr[1]);
calculation.setElementA(calculation.getElementA() + arr[0] * arr[0]);
calculation.setElementB(calculation.getElementB() + arr[1] * arr[1]);
});
System.out.println("文本相似度:" + calculation.result());
}
/**
* 组合词频向量
* @param words
* @param direction
* @return
*/
private static void statistics(Map<String,int[]> map,List<String> words ,int direction){
if(null == words || words.size() == 0){
return ;
}
int[] in = null;
boolean flag = direction(direction);
for (String word : words){
int[] wordD = map.get(word);
if(null == wordD){
if(flag){
in = new int[]{1,0};
}else {
in = new int[]{0,1};
}
map.put(word,in);
}else{
if(flag){
wordD[0]++;
}else{
wordD[1]++;
}
}
}
}
//判断不同句子
private static boolean direction(int direction){
return direction == 1?true:false;
}
}
用于计算余弦相似度的类
public class Calculation{
private double elementA;
private double elementB;
private double numerator;
public double result(){
return numerator / Math.sqrt(elementA * elementB);
}
//省略get/set
}
输出结果:
文本相似度:0.7216878364870323
从结果可以看出这两句话大致上还是比较相似的。用通俗一点的话来说就是有72%的相似度。
参考图例:
www.jianshu.com/p/f4606ae11…
公众号博文同步Github仓库,有兴趣的朋友可以帮忙给个Star哦,码字不易,感谢支持。
github.com/PeppaLittle…
推荐阅读
《如何优化代码中大量的if/else,switch/case?》
《如何提高使用Java反射的效率?》
《Java日志正确使用姿势》
关注「深夜里的程序猿」,分享最干的干货
上一篇: C++ 教程
推荐阅读
-
35 岁实现财务*,腾讯程序员手握2300万提前退休?-1000万房产、1000万腾讯股票、加上300万的现金,一共2300万的财产。有网友算了一笔账,假设1000万的房产用于自住,剩下1300万资产按照平均税后20-50万不等进行计算,大约花上26-60年左右的时间才能赚到这笔钱。也就是说,普通人可能奋斗一辈子,才能赚到这笔钱。在很多人还在为中年危机而惶惶不可终日的时候,有的人的35岁,就已经安全着陆,试问哪个打工人不羡慕?但问题是有这样财富积累必然有像样的实力做靠山。没有人可以不劳而获。 看到这里,肯定有人说,那么对于普通人来说,卷可能真就成了唯一的出路。但是卷也有轻松的卷,“偷懒”的卷法,对于程序员而言,刨除掉一时无法改掉的开会传统占用的大部分时间,如何把有限的时间和精力放在真正重要的架构设计、需求设计上,而不是重复的造*,编码、改bug、手动测试。因此在科技改变生活的今天,学会使用AI工具成为程序员们的必备技能。 以全栈式全自动的软件开发工具飞算SoFlu软件机器人为例,作为全球首款面向微服务架构设计和最佳实践的软件机器人,SoFlu软件机器人改变了原来手工编码的作业模式,通过可视化拖拽方式以及参数配置就能实现等同于编写复杂代码的业务逻辑,在设计业务逻辑时就完成了微服务应用开发,做到“业务即图,图即代码”,实现“软件开发,十倍提效”。 而作为飞算SoFlu软件机器人的一个重要组成部分,FuncGPT(慧函数)支持所有类型函数创建。通过自然语言描述Java函数需求,实时生成高质量、高可读性的Java函数代码。生成代码可直接复制到IDEA,或一键导入Java全自动开发工具函数库。 FuncGPT(慧函数)具有五大核心能力: ● 自然语言:通过自然语言即可生成函数,降低软件开发门槛。 ● 秒级函数开发:全自动秒级开发函数,秒级完成,效率千倍提升。 ● 一站式函数开发应用:函数生成即可直接应用,效率、安全有保障。 ● 质量可靠:生成的函数代码符合业界规范、具备良好的可读性,符合最优解。 ● 灵活生成:可以根据开发人员的具体需求生成并支持调整和修改。 以“将文件流转换为File对象”这个需求为例,以下是FuncGPT(慧函数)秒级生成的代码: / 类名:FileConverter // 函数名:convertStreamToFile // 函数功能:将文件流转换为File对象 // POM依赖包:无 import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; public class FileConverter { /** * 将文件流转换为File对象 * @param inputStream 文件流 * @param filePath 文件路径 * @param fileName 文件名 * @return 转换后的File对象 * @throws IOException 如果发生I/O异常 */ public static File convertStreamToFile(InputStream inputStream, String filePath, String fileName) throws IOException { File file = new File(filePath + File.separator + fileName); // 创建File对象 try (FileOutputStream outputStream = new FileOutputStream(file)) { // 创建文件输出流 byte buffer = new byte[1024]; int bytesRead; while ((bytesRead = inputStream.read(buffer)) != -1) { // 从文件流读取数据并写入文件 outputStream.write(buffer, 0, bytesRead); } } return file; // 返回转换后的File对象 } } // 函数示例 // 将文件流转换为File对象示例 // 入参:inputStream,文件流 // 入参:filePath,文件路径 // 入参:fileName,文件名 // 出参:file,转换后的File对象 // 调用示例: // InputStream inputStream = new FileInputStream("example.txt"); // String filePath = "C:\\Users\\User\\Documents"; // String fileName = "example.txt"; // File file = FileConverter.convertStreamToFile(inputStream, filePath, fileName); // System.out.println(file.getAbsolutePath); // 输出结果:例如,将文件流转换为File对象后,文件的绝对路径为:C:\Users\User\Documents\example.txt // 则输出结果为:C:\Users\User\Documents\example.txt 通过分析,不难发现以上代码:
-
9U_VPX 信号处理器,用于传感器大数据的异构计算平台
-
用于计算人名出现次数最多的 Python 代码 用于计算人名出现次数的 python 代码
-
什么用于控制命令和协调计算机各部件的工作?
-
用于流式计算自动订阅的 DolphinDB Node 启动教程
-
处理计算机网络和构建网络知识体系的文本
-
NeurIPS 2022 | 最强斗地主AI!网易互娱AI Lab提出基于完美信息蒸馏的方法-完美信息蒸馏(PTIE) 在斗地主游戏中,非完美信息的引入主要是由于三位玩家均不能看到别人的手牌,对于任意一位玩家而言,仅可知道其余两位玩家当前手牌的并集,而难于精准判断每位玩家当前手牌。完美信息蒸馏的思路是针对这种非完美问题,构建一个第三方角色,该角色可以看到三位玩家的手牌,该角色在不告知每位玩家完美信息的情况下通过信息蒸馏的方式引导玩家打出当前情况下合理的出牌。 以强化学习常用的 Actor-Critic 算法为例,PTIE 在 Actor-Critic 算法的应用中可以利用 Critic 的 Value 输出作为蒸馏手段来提升 Actor 的表现。具体而言即在训练中 Critic 的输入为完美信息(包含所有玩家的手牌信息),Actor 的输入为非完美信息(仅包含自己手牌信息),此种情况下 Critic 给予的 Value 值包含了完美信息,可以更好地帮助 Actor 学习到更好的策略。 从更新公式上来看,正常的 Actor-Critic 算法 Actor 更新的方式如下: 在 PTIE 模式下,对于每个非完美信息状态 h,我们可以在 Critic 中构建对应的完美信息状态 D(h),并用 Critic 的输出来更新 Actor 的策略梯度,从而达到完美信息蒸馏的效果。 PTIE 框架的整体结构如下图所示: 无论是训练还是执行过程中智能体都不会直接使用完美信息,在训练中通过蒸馏将完美信息用于提升策略,从而帮助智能体达到一个更高的强度。 PTIE 的另一种蒸馏方式是将完美信息奖励引入到奖励值函数的训练中,PerfectDou 提出了基于阵营设计的完美信息奖励 node reward,以引导智能体学习到斗地主游戏中的合作策略,其定义如下: 如上所示,完美信息部分 代表 t 时刻地主手牌最少几步可以出完,在斗地主游戏中可以近似理解为是距游戏获胜的距离, 代表 t 时刻地主阵营和农民阵营距游戏获胜的距离之差, 为调节系数。通过此种奖励设计,在训练时既可以一定程度地引入各玩家的手牌信息(出完的步数需要知道具体手牌才能计算),同时也鼓励农民以阵营的角度做出决策,提升农民的合作性。 特征构建: PerfectDou 针对牌类游戏的特点主要构建了两部分特征:牌局状态特征和动作特征。其中牌局状态特征主要包括当前玩家手牌牌型特征、当前玩家打出的卡牌牌型特征、玩家角色、玩家手牌数目等常用特征,动作特征主要用于刻画当前状态下玩家的所有可能出牌,包括了每种出牌动作的牌型特征、动作的卡牌数目、是否为最大动作等特征。 牌型特征为 12 * 15 的矩阵,如下图所示: 该矩阵前 4 行代表对应每种卡牌的张数,5-12 行代表该种卡牌的种类和对应位置。 网络结构和动作空间设计 针对斗地主游戏出牌组合数较多的问题,PerfectDou 基于 RLCard 的工作上对动作空间进行了简化,对占比最大的两个出牌牌型:飞机带翅膀和四带二进行了动作压缩,将整体动作空间由 27472 种缩减到 621 种。 PerfectDou 策略网络结构如下图所示: 策略网络结构同样分为两部分:状态特征部分和动作特征部分。 在状态特征部分,LSTM 网络用于提取玩家的历史行为特征,当前牌局状态特征和提取后的行为特征会再通过多层的 MLP 网络输出当前的状态信息 embedding。 在动作特征部分,每个可行动作同样会经过多层 MLP 网络进行编码,编码后的动作特征会与其对应的状态信息 embedding 经过一层 MLP 网络计算两者间的相似度,并经由 softmax 函数输出对应的动作概率。 实验结果
-
适用于 Mac 计算机的 Remove-It 卸载工具
-
慢速解释:用于计算机视觉视频理解的双模态 CNN
-
matlab 是用于数值计算的高级编程语言吗?