Java实现文本查重的方法详解
作者:Barcke
Ansj 是一个开源的 Java 中文分词工具,基于中科院的 ictclas 中文分词算法,采用隐马尔科夫模型(HMM),比其他常用的开源分词工具(如 MMseg4j)的分词准确率更高,下面我们就来使用它实现文本查重功能吧
ansj 分词法介绍
Ansj 是一个开源的 Java 中文分词工具,基于中科院的 ictclas 中文分词算法,采用隐马尔科夫模型(HMM),比其他常用的开源分词工具(如 MMseg4j)的分词准确率更高。作者为孙健(ansjsun),目前实现了中文分词、中文姓名识别、用户自定义词典、关键字提取、自动摘要、关键字标记等功能,适用于对分词效果要求高的各种项目。 虽然 Ansj 分词的基本原理与 ictclas 的一样,但是 Ansj 做了一些工程上的优化,比如:用 DAT 高效地实现检索词典、邻接表实现分词 DAG、支持自定义词典与自定义消歧义规则等。
Ansj 分词器 git地址: github.com/NLPchina/ansj_seg
配置文件
在ansj中配置文件名为library.properties,这是一个不可更改的约定。
字段名 | 默认值 | 说明 |
---|---|---|
isNameRecognition | true | 是否开启人名识别 |
isNumRecognition | true | 是否开启数字识别 |
isQuantifierRecognition | true | 是否数字和量词合并 |
isRealName | false | 是否取得真实的词,默认情况会取得标注化后的 |
isSkipUserDefine | false | 是否用户辞典不加载相同的词 |
dic | library/default.dic | 自定义词典路径 |
dic_[key] | 你的词典路径 | 针对不同语料调用不同的自定义词典 |
ambiguity | library/ambiguity.dic | 歧义词典路径 |
ambiguity_[key] | library/ambiguity.dic | 歧义词典路径 |
crf | null | crf 词典路径,不设置为默认 |
crf_[key] | 你的模型路径 | 针对不同语料调用不同的分词模型 |
synonyms | 默认的同义词典 | 针对不同语料调用不同的分词模型 |
synonyms_[key] | 你的同义词典路径 | 针对不同语料调用不同的分词模型 |
默认的配置文件:
#path of userLibrary this is default library dic=library/default.dic #redress dic file path ambiguityLibrary=library/ambiguity.dic #set real name isRealName=true #isNameRecognition default true isNameRecognition=true #isNumRecognition default true isNumRecognition=true #digital quantifier merge default true isQuantifierRecognition=true
目前支持的分词策略:
名称 | 用户自定义词典 | 数字识别 | 人名识别 | 机构名识别 | 新词发现 |
---|---|---|---|---|---|
BaseAnalysis | X | X | X | X | X |
ToAnalysis | √ | √ | √ | X | X |
DicAnalysis | √ | √ | √ | X | X |
IndexAnalysis | √ | √ | √ | X | X |
NlpAnalysis | √ | √ | √ | √ | √ |
计算余弦相似度
余弦相似度是常见的相似度衡量手段,能够用以比对两个向量间的相似水准。如下代码呈现了计算两篇论文之余弦相似度的方式:
余弦相似度是一种常用的计算两个向量相似性的方法。它基于向量的点积和向量的模。
计算余弦相似度的原理如下:
- 向量表示:将需要比较的对象表示为向量。
- 点积运算:计算两个向量的点积,即对应元素相乘后再求和。
- 向量模的计算:分别计算每个向量的模,通常使用欧几里得范数。
- 计算余弦相似度:用两个向量的点积除以它们的模的乘积。
具体公式为:
余弦相似度 = 向量 A 与向量 B 的点积 / (向量 A 的模 × 向量 B 的模)
余弦相似度的取值范围在 -1 到 1 之间:
- 值为 1:表示两个向量完全相同,即具有最大相似度。
- 值为 0:表示两个向量相互垂直,即没有相似性。
- 值为 -1:表示两个向量完全相反,即具有最小相似度。
余弦相似度的优点包括:
- 不受向量大小影响:它比较的是向量的方向,而不是它们的绝对大小。
- 对噪声相对不敏感:在存在一些噪声或误差的情况下仍能给出相对合理的相似度度量。
在实际应用中,余弦相似度常用于:
- 文本相似性度量:比较文本向量的相似度。
- 图像相似性分析:衡量图像特征向量的相似程度。
- 推荐系统:找到用户或物品之间的相似性。
/** * 计算余弦相似度 * @param vec1 map1 * @param vec2 map2 * @return 相似度 */ public double calculateCosSimilarity(Map<String, Integer> vec1, Map<String, Integer> vec2) { double dotProduct = 0.0; double norm1 = 0.0; double norm2 = 0.0; for (Map.Entry<String, Integer> entry : vec1.entrySet()) { String word = entry.getKey(); int count = entry.getValue(); dotProduct += count * vec2.getOrDefault(word, 0); norm1 += Math.pow(count, 2); } for (Map.Entry<String, Integer> entry : vec2.entrySet()){ int count = entry.getValue(); norm2 += Math.pow(count, 2); } return dotProduct / (Math.sqrt(norm1) * Math.sqrt(norm2)); } /** * 计算余弦相似度 * @param context1 文本1 * @param context2 文本2 * @return 相似度 */ public double calculateCosSimilarity(String context1, String context2){ return calculateCosSimilarity(participleNlp(context1).stream().collect(Collectors.groupingBy(o -> o, Collectors.summingInt(o -> 1))), participleNlp(context2).stream().collect(Collectors.groupingBy(o -> o, Collectors.summingInt(o -> 1)))); }
具体实现看这里
mavaen 依赖
采用 ansj 5.1.6的版本
<dependency> <groupId>org.ansj</groupId> <artifactId>ansj_seg</artifactId> <version>5.1.6</version> </dependency>
util 代码如下
package cn.ideamake.business.tools.util; import lombok.experimental.UtilityClass; import org.ansj.domain.Term; import org.ansj.splitWord.analysis.NlpAnalysis; import java.util.List; import java.util.Map; import java.util.stream.Collectors; /** * @author Barcke * @version 1.0 * @projectName business-tools * @className TextPlagiarismCheckUtil * @date 2024/4/16 09:40 * @slogan: 源于生活 高于生活 * @description: 文本查重工具 分词采用 ansj 分词方法 **/ @UtilityClass public class TextPlagiarismCheckUtil { // 分词方法 可替换!!! start /** * Nlp分词方式 * @param context 文本信息 * @return 分词后的list */ public List<String> participleNlp(String context){ return participleNlpToTerm(context).stream().map(Term::getName).collect(Collectors.toList()); } /** * Nlp分词方式 * @param context 文本信息 * @return 分词后的Term */ public List<Term> participleNlpToTerm(String context){ return NlpAnalysis.parse(context).getTerms(); } // 分词方法 可替换!!! end /** * 计算余弦相似度 * @param vec1 map1 * @param vec2 map2 * @return 相似度 */ public double calculateCosSimilarity(Map<String, Integer> vec1, Map<String, Integer> vec2) { double dotProduct = 0.0; double norm1 = 0.0; double norm2 = 0.0; for (Map.Entry<String, Integer> entry : vec1.entrySet()) { String word = entry.getKey(); int count = entry.getValue(); dotProduct += count * vec2.getOrDefault(word, 0); norm1 += Math.pow(count, 2); } for (Map.Entry<String, Integer> entry : vec2.entrySet()){ int count = entry.getValue(); norm2 += Math.pow(count, 2); } return dotProduct / (Math.sqrt(norm1) * Math.sqrt(norm2)); } /** * 计算余弦相似度 * @param context1 文本1 * @param context2 文本2 * @return 相似度 */ public double calculateCosSimilarity(String context1, String context2){ return calculateCosSimilarity(participleNlp(context1).stream().collect(Collectors.groupingBy(o -> o, Collectors.summingInt(o -> 1))), participleNlp(context2).stream().collect(Collectors.groupingBy(o -> o, Collectors.summingInt(o -> 1)))); } /** * 判断文本是否重复 * @param context1 文本1 * @param context2 文本2 * @param threshold 阈值 * @return 是否重复 */ public boolean ifPlagiarism(String context1, String context2, double threshold){ return calculateCosSimilarity(context1, context2) > threshold; } /** * 判断文本是否重复 默认阈值 0.7 * @param context1 文本1 * @param context2 文本2 * @return 是否重复 */ public boolean ifPlagiarism(String context1, String context2){ return ifPlagiarism(context1, context2, 0.7); } }
使用案例
public static void main(String[] args) { String str = "java实现论文查重,文本查重方案 采用 ansj 分词法(barcke) -----" ; String str2 = "java实现论文查重,文本查重方案 采用 ansj 分词法(barcke) -----" ; String str3 = "java实现cke) -----" ; System.out.println("分词数据(ansj分词法):" + TextPlagiarismCheckUtil.participleNlp(str)); System.out.println("重复率:" + TextPlagiarismCheckUtil.calculateCosSimilarity(str, str2) + "是否重复(默认阈值0.7):" + TextPlagiarismCheckUtil.ifPlagiarism(str, str2)); System.out.println("重复率:" + TextPlagiarismCheckUtil.calculateCosSimilarity(str, str3) + "是否重复(默认阈值0.7):" + TextPlagiarismCheckUtil.ifPlagiarism(str, str3)); }
执行结果:
到此这篇关于Java实现文本查重的方法详解的文章就介绍到这了,更多相关Java文本查重内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!