共计 4061 个字符,预计需要花费 11 分钟才能阅读完成。
前言
所有的故事都有开始,也终将完结。
本文将作为 NLP 汉字类似度的完结篇,为该系列画上一个句号。
起 -NLP 中文形近字类似度计算思路
承 - 中文形近字类似度算法实现,为汉字 NLP 尽一点绵薄之力
转 - 当代中国最贵的汉字是什么?
不足之处
之所以有本篇,是因为上一次的算法实现存在一些有余。
巴别塔
《圣经》中有对于巴别塔建造,最终人们因为语言问题而复工的故事。
创 11:6“看哪!他们成为一样的人民,都是一样的语言,现在既作起这事来,当前他们所要作的事,就没有不成就的了。创 11:7 咱们上来,在那里变乱他们的口音,使他们的语言彼此不通。”创 11:8 于是,耶和华使他们从那里扩散在全地上;他们就复工不造那城了。
为了防止语言问题,我一开始就实现了一个 exe4j 打包的比照程序,本人跑的很顺畅。
小伙伴一跑,运行失败。各种环境配置一顿操作,最初还是报错。
于是,我写了一个 python 繁难版本,便于做 NLP 钻研的小伙伴们学习。
https://github.com/houbb/nlp-hanzi-similar/releases/tag/pythn
java 是一种语言,python 是一种语言。
编程语言,让人和机器之间能够沟通,却让人与人之间产生了隔膜。
拆字
在 当代中国最贵的汉字是什么?一文中,咱们首次阐明了汉字的拆合。
汉字的拆分实现,外围目标之一就是为了欠缺汉字的类似度比拟。
通过比照汉字的拆分局部,而后获取拆字的类似度,进步比照的准确性。
拆字类似度
简略的需要
为了便于小伙伴们了解,咱们用产品经理的思维和大家介绍一下实现形式。
我的需要比较简单。你看,【明】能够拆分【日】【月】,【冐】也能够拆分为【日】【月】。比照一下,后果是显然的。怎么实现我不论,今天上线吧。
小伙伴们,应该曾经晓得怎么实现了吧?
应用体验
诚如产品所言,这个需要曾经实现。
maven 引入
<dependency>
<groupId>com.github.houbb</groupId>
<artifactId>nlp-hanzi-similar</artifactId>
<version>1.2.0</version>
</dependency>
应用
double rate1 = HanziSimilarHelper.similar('末', '未');
对应的后果为:0.9696969696969697
更多应用细节,参考开源地址:
https://github.com/houbb/nlp-hanzi-similar
写在完结前
波及的我的项目
汉字的类似度计算到这里算是告一段落。
次要波及的材料及我的项目有:
拼音
拆字
四角编码词库
汉字结构词库
汉字偏旁词库
笔画数词库
当然,还能够后果 opencc4j 进行繁简体的解决,此处不再延长。
之后的打算
NLP 的畛域还有很多货色须要大家攻克,毕竟中文 NLP 才刚刚开始。
技术尚未胜利,同志仍需致力。
据说最近鹅城的某位黄老爷惹得大家口碑载道。
很多小伙伴说,如果有一款软件能够实现【月丷夫马言卂彳山兀攴人言】的沟通性能,那么我必定会用。
所谓说者无心,听者无意。
写一个通信软件,次要是为了坚固下 netty 的学习,其余的都不重要。
尽管晓得就算有,大家必定也不太会扭转,然而老马还是筹备试试。
java 实现思路
正告,如果你头发曾经所剩无几,或者对实现并不感兴趣。
那么就能够珍藏 + 点赞 + 评论【不明觉厉】,而后来到了。
上面是干燥的代码实现环节。
程序员的思维
上面是程序员的思维。
首先要解决几个问题:
(1)汉字的拆分实现
这个间接复用曾经实现的汉字拆分实现。
List<String> stringList = ChaiziHelper.chai(charWord.charAt(0));
雷同的一个汉字能够有多种拆分形式,简略起见,咱们默认取第一个。
(2)类似的比拟
假如咱们比照 A B 两个汉字,能够拆分为如下的子集。
A = {A1, A2, …, Am}
B = {B1, B2, …, Bm}
/**
* 获取拆分后对应的拆分字符
* @param charWord 字符
* @return 后果
*/
private char[] getSplitChars(String charWord) {List<String> stringList = ChaiziHelper.chai(charWord.charAt(0));
// 这里应该抉择哪一个是有考究的。此处为了简略,默认抉择第一个。String string = stringList.get(0);
return string.toCharArray();}
拆分后的子集比照有多种实现形式,简略起见,咱们间接遍历元素,判断另一个子集是否存在。
当然,遍历的时候要以拆分数量较少的的为基准。
int minLen = Math.min(charsOne.length, charsTwo.length);
// 比拟
double totalScore = 0.0;
for(int i = 0; i < minLen; i++) {char iChar = charsOne[i];
String textChar = iChar+"";
if(ArrayPrimitiveUtil.contains(charsTwo, iChar)) {// 累加分数}
}
(3)拆分子集的权重
比方 一
月
两个汉字都是子集,然而因为笔画数不同,权重也不同。
咱们用一个子集的笔画数占整体汉字的笔画数计算权重。
int textNumber = getNumber(textChar, similarContext);
double scoreOne = textNumber*1.0 / numberOne * 1.0;
double scoreTwo = textNumber*1.0 / numberTwo * 1.0;
totalScore += (scoreOne + scoreTwo) / 2.0;
ps: 这里的除以 2, 是为了归一化。保障最初的后果在 0-1 之间。
(4)笔画数
获取笔画数的形式,咱们能够间接复用以前的办法。
如果没有匹配的,默认笔画数为 1。
private int getNumber(String text, IHanziSimilarContext similarContext) {Map<String, Integer> map = similarContext.bihuashuData().dataMap();
Integer number = map.get(text);
if(number == null) {return 1;}
return number;
}
java 残缺实现
咱们把所有的碎片拼接起来,就失去一个残缺的实现。
/**
* 拆字
*
* @author 老马啸东风
* @since 1.0.0
*/
public class ChaiziSimilar implements IHanziSimilar {
@Override
public double similar(IHanziSimilarContext similarContext) {String hanziOne = similarContext.charOne();
String hanziTwo = similarContext.charTwo();
int numberOne = getNumber(hanziOne, similarContext);
int numberTwo = getNumber(hanziTwo, similarContext);
// 拆分
char[] charsOne = getSplitChars(hanziOne);
char[] charsTwo = getSplitChars(hanziTwo);
int minLen = Math.min(charsOne.length, charsTwo.length);
// 比拟
double totalScore = 0.0;
for(int i = 0; i < minLen; i++) {char iChar = charsOne[i];
String textChar = iChar+"";
if(ArrayPrimitiveUtil.contains(charsTwo, iChar)) {int textNumber = getNumber(textChar, similarContext);
double scoreOne = textNumber*1.0 / numberOne * 1.0;
double scoreTwo = textNumber*1.0 / numberTwo * 1.0;
totalScore += (scoreOne + scoreTwo) / 2.0;
}
}
return totalScore * similarContext.chaiziRate();}
/**
* 获取拆分后对应的拆分字符
* @param charWord 字符
* @return 后果
*/
private char[] getSplitChars(String charWord) {List<String> stringList = ChaiziHelper.chai(charWord.charAt(0));
// 这里应该抉择哪一个是有考究的。此处为了简略,默认抉择第一个。String string = stringList.get(0);
return string.toCharArray();}
/**
* 获取笔画数
* @param text 文本
* @param similarContext 上下文
* @return 后果
*/
private int getNumber(String text, IHanziSimilarContext similarContext) {Map<String, Integer> map = similarContext.bihuashuData().dataMap();
Integer number = map.get(text);
if(number == null) {return 1;}
return number;
}
}
小结
本文引入了汉字拆字,进一步丰盛了类似度的实现。
当然,实现自身仍然有很多值得晋升的中央,比方拆分后的抉择,是否能够递归拆分等,这个还是留给前人钻研吧。
我是老马,期待与你的下次重逢。