网站开发项目提成,百度关键词分析工具,文山专业网站建设哪家好,网页设计图片加载不出文章目录 前言需求简介实操开始1. 添加pom.xml依赖2. 文本相似度工具类3. 案例验证4. 验证结果 总结 前言
请各大网友尊重本人原创知识分享#xff0c;谨记本人博客#xff1a;南国以南i、 提示#xff1a;以下是本篇文章正文内容#xff0c;下面案例可供参考
需求
当我… 文章目录 前言需求简介实操开始1. 添加pom.xml依赖2. 文本相似度工具类3. 案例验证4. 验证结果 总结 前言
请各大网友尊重本人原创知识分享谨记本人博客南国以南i、 提示以下是本篇文章正文内容下面案例可供参考
需求
当我们需要求两个或两个以上的字符串相似度百分比时可以使用HanLP汉语言处理包来帮助我们求两个文本的相似度百分比、海明距离、
简介 HanLPHan Language Processing 在汉语义处理方面具备强大的功能由中国科学院计算技术研究所自然语言处理与社会人文计算研究中心开发。它提供了包括分词、词性标注、命名实体识别、依存句法分析、情感分析、文本分类等多种自然语言处理任务的功能。 主要功能
分词 HanLP提供了多种分词模型包括基于规则的模型、基于神经网络的模型等能够准确地进行中文分词。词性标注 在分词的基础上HanLP还能对词语进行词性标注帮助理解词语在句子中的作用。命名实体识别 HanLP能够识别文本中的命名实体如人名、地名、组织机构名等这对于信息抽取等任务非常重要。依存句法分析 通过对句子中各个词语之间的依存关系建模HanLP能够分析句子结构提取句子的语义信息。情感分析 HanLP支持对文本进行情感分析判断文本所表达的情感倾向如正面、负面或中性。文本分类 HanLP还提供了文本分类的功能可以将文本按照预设的分类体系进行分类。
友情链接HanLP项目主页 、HanLP下载地址、详细介绍
实操开始
注意 本文以Java开发语言为案例
1. 添加pom.xml依赖 !--junit依赖--dependencygroupIdjunit/groupIdartifactIdjunit/artifactIdversion3.8.1/versionscopetest/scope/dependency!--hanlp语言处理依赖--dependencygroupIdcom.hankcs/groupIdartifactIdhanlp/artifactIdversionportable-1.7.6/version/dependency!-- https://mvnrepository.com/artifact/org.apache.commons/commons-lang3 --dependencygroupIdorg.apache.commons/groupIdartifactIdcommons-lang3/artifactIdversion3.4/version/dependency!-- https://mvnrepository.com/artifact/org.jsoup/jsoup --dependencygroupIdorg.jsoup/groupIdartifactIdjsoup/artifactIdversion1.10.3/version/dependency2. 文本相似度工具类
import com.hankcs.hanlp.seg.common.Term;
import com.hankcs.hanlp.tokenizer.StandardTokenizer;
import org.apache.commons.lang3.StringUtils;
import org.jsoup.Jsoup;
import org.jsoup.safety.Whitelist;import java.math.BigInteger;
import java.util.*;public class MySimHash {private String tokens; //字符串private BigInteger strSimHash;//字符产的hash值private int hashbits 64; // 分词后的hash数;public MySimHash(String tokens) {this.tokens tokens;this.strSimHash this.simHash();}private MySimHash(String tokens, int hashbits) {this.tokens tokens;this.hashbits hashbits;this.strSimHash this.simHash();}/*** 清除html标签** param content* return*/private String cleanResume(String content) {// 若输入为HTML,下面会过滤掉所有的HTML的tagcontent Jsoup.clean(content, Whitelist.none());content StringUtils.lowerCase(content);String[] strings { , \n, \r, \t, \\r, \\n, \\t, nbsp;};for (String s : strings) {content content.replaceAll(s, );}return content;}/*** 这个是对整个字符串进行hash计算** return*/private BigInteger simHash() {tokens cleanResume(tokens); // cleanResume 删除一些特殊字符int[] v new int[this.hashbits];ListTerm termList StandardTokenizer.segment(this.tokens); // 对字符串进行分词//对分词的一些特殊处理 : 比如: 根据词性添加权重 , 过滤掉标点符号 , 过滤超频词汇等;MapString, Integer weightOfNature new HashMapString, Integer(); // 词性的权重weightOfNature.put(n, 2); //给名词的权重是2;MapString, String stopNatures new HashMapString, String();//停用的词性 如一些标点符号之类的;stopNatures.put(w, ); //int overCount 5; //设定超频词汇的界限 ;MapString, Integer wordCount new HashMapString, Integer();for (Term term : termList) {String word term.word; //分词字符串String nature term.nature.toString(); // 分词属性;// 过滤超频词if (wordCount.containsKey(word)) {int count wordCount.get(word);if (count overCount) {continue;}wordCount.put(word, count 1);} else {wordCount.put(word, 1);}// 过滤停用词性if (stopNatures.containsKey(nature)) {continue;}// 2、将每一个分词hash为一组固定长度的数列.比如 64bit 的一个整数.BigInteger t this.hash(word);for (int i 0; i this.hashbits; i) {BigInteger bitmask new BigInteger(1).shiftLeft(i);// 3、建立一个长度为64的整数数组(假设要生成64位的数字指纹,也可以是其它数字),// 对每一个分词hash后的数列进行判断,如果是1000...1,那么数组的第一位和末尾一位加1,// 中间的62位减一,也就是说,逢1加1,逢0减1.一直到把所有的分词hash数列全部判断完毕.int weight 1; //添加权重if (weightOfNature.containsKey(nature)) {weight weightOfNature.get(nature);}if (t.and(bitmask).signum() ! 0) {// 这里是计算整个文档的所有特征的向量和v[i] weight;} else {v[i] - weight;}}}BigInteger fingerprint new BigInteger(0);for (int i 0; i this.hashbits; i) {if (v[i] 0) {fingerprint fingerprint.add(new BigInteger(1).shiftLeft(i));}}return fingerprint;}/*** 对单个的分词进行hash计算;** param source* return*/private BigInteger hash(String source) {if (source null || source.length() 0) {return new BigInteger(0);} else {/*** 当sourece 的长度过短会导致hash算法失效因此需要对过短的词补偿*/while (source.length() 3) {source source source.charAt(0);}char[] sourceArray source.toCharArray();BigInteger x BigInteger.valueOf(((long) sourceArray[0]) 7);BigInteger m new BigInteger(1000003);BigInteger mask new BigInteger(2).pow(this.hashbits).subtract(new BigInteger(1));for (char item : sourceArray) {BigInteger temp BigInteger.valueOf((long) item);x x.multiply(m).xor(temp).and(mask);}x x.xor(new BigInteger(String.valueOf(source.length())));if (x.equals(new BigInteger(-1))) {x new BigInteger(-2);}return x;}}/*** 计算海明距离,海明距离越小说明越相似;** param other* return*/private int hammingDistance(MySimHash other) {BigInteger m new BigInteger(1).shiftLeft(this.hashbits).subtract(new BigInteger(1));BigInteger x this.strSimHash.xor(other.strSimHash).and(m);int tot 0;while (x.signum() ! 0) {tot 1;x x.and(x.subtract(new BigInteger(1)));}return tot;}/*** .* 求百分比** param s2* return*/public double getSemblance(MySimHash s2) {double i (double) this.hammingDistance(s2);return 1 - i / this.hashbits;}}详细说明
/*--------------------------------------相似度算法说明--------------------------------------借鉴hashmap算法找出可以hash的key值因为我们使用的simhash是局部敏感哈希这个算法的特点是只要相似的字符串只有个别的位数是有差别变化。那这样我们可以推断两个相似的文本至少有16位的simhash是一样的。具体选择16位、8位、4位大家根据自己的数据测试选择虽然比较的位数越小越精准但是空间会变大。分为4个16位段的存储空间是单独simhash存储空间的4倍。之前算出5000w数据是 382 Mb扩大4倍1.5G左右还可以接受 通过这样计算我们的simhash查找过程全部降到了1毫秒以下。就加了一个hash效果这么厉害我们可以算一下原来是5000w次顺序比较现在是少了2的16次方比较前面16位变成了hash查找。后面的顺序比较的个数是多少2^16 65536 5000w/65536 763 次。。。。实际最后链表比较的数据也才 763次所以效率大大提高 到目前第一点降到3.6毫秒、支持5000w数据相似度比较做完了。还有第二点同一时刻发出的文本如果重复也只能保留一条和短文本相识度比较怎么解决。其实上面的问题解决了这两个就不是什么问题了。 之前的评估一直都是按照线性计算来估计的就算有多线程提交相似度计算比较我们提供相似度计算服务器也需要线性计算。比如同时客户端发送过来两条需要比较相似度的请求在服务器这边都进行了一个排队处理一个接着一个第一个处理完了在处理第二个等到第一个处理完了也就加入了simhash库。所以只要服务端加了队列就不存在同时请求不能判断的情况。 simhash如何处理短文本换一种思路simhash可以作为局部敏感哈希第一次计算缩小整个比较的范围等到我们只有比较700多次比较时就算使用我们之前精准度高计算很慢的编辑距离也可以搞定。当然如果觉得慢了也可以使用余弦夹角等效率稍微高点的相似度算法;*//*--------------------------------------分词说明--------------------------------------分词把需要判断文本分词形成这个文章的特征单词。 最后形成去掉噪音词的单词序列并为每个词加上权重我们假设权重分为5个级别1~5。只要相似的字符串只有个别的位数是有差别变化。那这样我们可以推断两个相似的文本 比如“ 美国“51区”雇员称内部有9架飞碟曾看见灰色外星人 ” 分词后为 “ 美国4 51区5 雇员3 称1 内部2 有1 9架3 飞碟5 曾1 看见3 灰色4 外星人5” 括号里是代表单词在整个句子里重要程度数字越大越重要。 2、hash通过hash算法把每个词变成hash值 比如“美国”通过hash算法计算为 100101, “51区”通过hash算法计算为 101011。这样我们的字符串就变成了一串串数字还记得文章开头说过的吗要把文章变为数字计算才能提高相似度计算性能现在是降维过程进行时。 3、加权通过 2步骤的hash生成结果需要按照单词的权重形成加权数字串 比如“美国”的hash值为“100101”通过加权计算为“4 -4 -4 4 -4 4”“51区”的hash值为“101011”通过加权计算为 “ 5 -5 5 -5 5 5”。 4、合并把上面各个单词算出来的序列值累加变成只有一个序列串。 比如 “美国”的 “4 -4 -4 4 -4 4”“51区”的 “ 5 -5 5 -5 5 5” 把每一位进行累加 “45 -4-5 -45 4-5 -45 45” 》 “9 -9 1 -1 1 9”。这里作为示例只算了两个单词的真实计算需要把所有单词的序列串累加。 5、降维把4步算出来的 “9 -9 1 -1 1 9” 变成 0 1 串形成我们最终的simhash签名。 如果每一位大于0 记为 1小于0 是统优先公司;/*--------------------------------------敏感哈希说明--------------------------------------算法找出可以hash的key值因为我们使用的simhash是局部敏感哈希这个算法的特点是只要相似的字 把需要判断文本分词形成这个文章的特征单词。最后形成去掉噪音词的只要相似的字符串只有个别的位数是有差别变化。那这样我们可以推断两个相似的文本单词序分词是代表单词在整个句子里重要程度数字越大越重要。2、hash通过hash算法把每个词变成hash值 比如“美国”通过hash算法计算为 100101, “51区”通过hash算法计算为 101011。 这样我们的字符串就变成了一串串数字还记得文章开头说过的吗要把文章变为数字加权通过 家可能会有疑问经过这么多步骤搞这么麻烦不就是为了得到个 0 1 字符串吗我直接把这个文本作为字符串输入v较前面16位变成了hash查找。后面的顺序比较的个数是多用hd5是用于生成唯一签来相差甚远hashmap也是用于键值对查找便于快速插入和查找的数据结构。不过我们主要解决的是文本相似度计算要比较的是两个文章是否相识当然我们降维生成了hashcode也是用于这个目的。看到这里估计大家就明白了我们使用的sim是这样的传统hash函数解决的是生成唯一值比如 md5、hashmap等。md5是用于生成唯一签名串只要稍微多加一个字符md5的两个数字看起来相差甚远hashmap也是用于键值对查找便于快速插入和查找的数据结构。不过我们主要解决的是文本相似度计算要比较的是两个文章是否相识当然我们降维生成了hashcode也是用于这个目的。看到这里估计大家就明白了我们使用的simhash就算把文章中的字符串变成 01 串也还是可以用于计算相似度的而传统的hashcode却不行。我们可以来做个测试两个相差只有一个字符的文本串“你妈妈喊你回家吃饭哦回家罗回家罗” 和 “你妈妈叫你回家吃饭啦回家罗回家罗”。短文本大量重复信息不会被过滤是不是;*//*--------------------------------------过滤说明--------------------------------------最后形成去掉噪音词的单词序分词是代表单词在整个句子里重要程度数字越大越重要。 最后形成去掉噪音词的单词序列并为每个词加上权重 2、hash通过hash算法把每个词变成hash值比如“美国”通过hash算法计算为 100101, “51区”通过hash算法计算为 101011。 这样我们的字符串就变成了一串串数字还记得文章开头说过的吗分为4个16位段的存储空间是单独simhash存储空间的4倍。之前算出5000w数据是 382 Mb扩大4倍1.5G左右还可以接受 要把文章变为数字加权通过 家可能会有疑问经过这么多步骤搞这么麻烦不就是为了得到个 0 1 字符串吗我直接把这个文本作为字符串输入用hd5是用于生成唯一签来相差甚远hashmap也是用于键值对查找便于快速插入和查找的数据结构。不过我们主要解决的是文本相似度计算要比较的是两个文章是否相识当然我们降维生成了hashcode也是用于这个目的。看到这里估计大家就明白了我们使用的sim是这样的传统hash函数解决的是生成唯一值比如 md5、hashmap等。md5是用于生成唯一签名串只要稍微多加一个字符md5的两个数字看起来相差甚远hashmap也是用于键值对查找便于快速插入和查找的数据结构。不过我们主要解决的是文本相似度计算要比较的是两个文章是否相识当然我们降维生成了hashcode也是用于这个目的。看到这里估计大家就明白了我们使用的simhash就算把文章中的字符串变成 01 串也还是可以用于计算相似度的而传统的hashcode却不行。我们可以来做个测试两个相差只有一个字符的文本串“你妈妈喊你回家吃饭哦回家罗回家罗” 和 “你妈妈叫你回家吃饭啦回家罗回家罗”。短文本大量重复信息不会被过滤;*/3. 案例验证 public static void main(String[] args) {String text 杏南一区;ListString itemList new LinkedListString();itemList.add(杏南一区);itemList.add(杏南二区);itemList.add(杏南三区);itemList.add(杏南四区);itemList.add(杏南五区);itemList.add(杏南六区);itemList.add(杏南七区);itemList.add(杏南八区);itemList.add(杏南九区);itemList.add(杏南十区);System.out.println();long startTime System.currentTimeMillis();MySimHash hash1 new MySimHash(text, 64);ListDouble list new LinkedListDouble();for (String str : itemList) {MySimHash hash2 new MySimHash(str, 64);//海明距离越小说明越相似System.out.println(海明距离: hash1.hammingDistance(hash2) ### 文本相似度: hash2.getSemblance(hash1));list.add(hash2.getSemblance(hash1));}long endTime System.currentTimeMillis();Double max Collections.max(list);int index list.indexOf(max);//获取集合下标System.out.println();System.out.println(耗时: (endTime - startTime));System.out.println(相似度集合内容: list.toString());System.out.println(集合中最大值: max ### 集合下标: index);System.out.println(对比内容: text ### 相似度最高: itemList.get(index));}4. 验证结果 总结
我是南国以南i记录点滴每天成长一点点学习是永无止境的转载请附原文链接
参考链接、