中文分词工具探析(一):ICTCLAS (NLPIR)
【开源中文分词工具探析】系列:
- 开源中文分词工具探析(一):ICTCLAS (NLPIR)
- 开源中文分词工具探析(二):Jieba
- 开源中文分词工具探析(三):Ansj
- 开源中文分词工具探析(四):THULAC
- 开源中文分词工具探析(五):FNLP
- 开源中文分词工具探析(六):Stanford CoreNLP
- 开源中文分词工具探析(七):LTP
1. 前言
ICTCLAS是张华平老师推出的中文分词系统,于2009年更名为NLPIR。ICTCLAS是中文分词界元老级工具了,作者开放出了free版本的源代码(1.0整理版本在此). 作者在论文[1] 中宣称ICTCLAS是基于HHMM(Hierarchical Hidden Markov Model)实现,后在论文[2]中改成了基于层叠隐马尔可夫模型CHMM(Cascaded Hidden Markov Model)。我把HHMM的原论文[3]读了一遍,对照ICTCLAS源码,发现ICTCLAS本质上就是一个Bigram的Word-Based Generative Model,用HMM来做未登录词识别(修正分词)与词性标注,与HHMM没有半毛钱关系。Biagram语法模型对应于1阶Markov假设,则ICTCLAS分词模型的联合概率为
\begin{equation}
P(w_1^m) = \prod_i P(w_{i} | w_{i-1})
\label{eq:bigram}
\end{equation}
2. ICTCLAS分解
ICTCLAS Free版的C++源代码实在是佶屈聱牙,因本人水平有限,故参照的源码为sinboy的Java实现 ictclas4j ("com.googlecode.ictclas4j" % "ictclas4j" % "1.0.1"
)。ICTCLAS分词流程如下:
- 按照核心词典进行第一次切词;
- 在第一次切词的基础上,求解最大联合概率\eqref{eq:bigram},作者称之为“二元切分词图”;
- HMM识别未登录词,诸如:人名、翻译人名、地名、机构名等;
- 分词结果整理,词性标注。
词典
ICTCLAS的词典包括六种:
import org.ictclas4j.segment._
// 核心词典
val coreDict = new Dictionary("data/coreDict.dct")
// 二元词典
val bigramDict = new Dictionary("data/bigramDict.dct")
// 人名词典
val personTagger = new PosTagger(Utility.TAG_TYPE.TT_PERSON, "data/nr", coreDict)
// 翻译人名词典
val transPersonTagger = new PosTagger(Utility.TAG_TYPE.TT_TRANS_PERSON, "data/tr", coreDict)
// 地名词典
val placeTagger = new PosTagger(Utility.TAG_TYPE.TT_TRANS_PERSON, "data/ns", coreDict)
// 词性标注
val lexTagger = new PosTagger(Utility.TAG_TYPE.TT_NORMAL, "data/lexical", coreDict)
其中,核心词典(coreDict)用于初始切词,二元词典(bigramDict)记录了两个词联合出现的频数。coreDict与bigramDict的数据格式如下
# coreDict
块0
count:4
wordLen:0 frequency:3 handle:25856 word:(啊)
wordLen:0 frequency:69 handle:30976 word:(啊)
wordLen:2 frequency:0 handle:25856 word:(啊)呀
wordLen:2 frequency:0 handle:25856 word:(啊)哟
块1
count:133
wordLen:0 frequency:0 handle:26624 word:(阿)
wordLen:0 frequency:94 handle:27136 word:(阿)
...
# bigramDict
块0
count:9
wordLen:3 frequency:3 handle:3 word:(啊)@、
wordLen:3 frequency:4 handle:3 word:(啊)@。
wordLen:3 frequency:1 handle:3 word:(啊)@”
wordLen:3 frequency:39 handle:3 word:(啊)@!
wordLen:3 frequency:20 handle:3 word:(啊)@,
其中,frequency
表示词频,handle
表示词性:
让我们先看一个具体的例子:
wordLen:4 frequency:12 handle:28275 word:(爱)尔兰
handle = 28275, 转成HEX表示,为0x6E73。
把0x6E73解释为字符数组,0x6E -> 'n', 0x73 -> 's', 所以0x6E73 -> "ns"。
查北大的《汉语文本词性标注标记集》, ns ~ 地名 (名词代码n和处所词代码s并在一起)。
计算
为了计算联合概率\eqref{eq:bigram}最大值,ICTCLAS做了一个巧妙的转换——求对数并做Linear interpolation平滑:
\arg \max \prod_i P(w_{i} | w_{i-1}) & = \arg \min - \sum_i \log P(w_{i} | w_{i-1}) \\
& \approx \arg \min - \sum_i \log \left[ aP(w_{i-1}) + (1-a) P(w_{i}|w_{i-1}) \right]
\end{aligned}
\]
将所有的可能分词组合构建成一个有向图,边的权重设为(上式中)平滑后的log值。由此,将求解最大联合概率转化成一个图论中最短路径问题。有时最大联合概率对应的分词结果不一定是最优的。为了解决这个问题,ICTCLAS采取了N-best策略,即求解N-最短路径而不是直接求解最短路径。然后在N-最短路径的分词结果上,做HMM修正——识别未登录词与词性标注。为了更清晰地了解分词流程,我用scala把分词函数SegTag::split()
重新写了一遍:
import java.util
import org.ictclas4j.bean.{Dictionary, SegNode}
import org.ictclas4j.segment._
import org.ictclas4j.utility.{POSTag, Utility}
import scala.collection.JavaConversions._
val sentence = "深夜的穆赫兰道发生一桩车祸,女子丽塔在车祸中失忆了"
val pathCount = 1
// 存储分词结果
val stringBuilder = StringBuilder.newBuilder
// 按[空格|回车]分隔句子
val sentenceSeg = new SentenceSeg(sentence)
sentenceSeg.getSens.filter(_.isSeg).foreach { sen =>
// 原子切分
val as: AtomSeg = new AtomSeg(sen.getContent)
// 全切分:根据核心词典,生成所有的可能词
val segGraph: SegGraph = GraphGenerate.generate(as.getAtoms, coreDict)
// 二元切分词图
val biSegGraph: SegGraph = GraphGenerate.biGenerate(segGraph, coreDict, bigramDict)
val nsp: NShortPath = new NShortPath(biSegGraph, pathCount)
nsp.getPaths.foreach { onePath =>
// 得到初次分词路径
val segPath = getSegPath(segGraph, onePath)
val firstPath = AdjustSeg.firstAdjust(segPath)
// 处理未登陆词,进对初次分词结果进行优化
val optSegGraph: SegGraph = new SegGraph(firstPath)
personTagger.recognition(optSegGraph, firstPath)
transPersonTagger.recognition(optSegGraph, firstPath)
placeTagger.recognition(optSegGraph, firstPath)
// 根据优化后的结果,重新进行生成二叉分词图表
val optBiSegGraph: SegGraph = GraphGenerate.biGenerate(optSegGraph, coreDict, bigramDict)
// 重新求取N-最短路径
val optNsp: NShortPath = new NShortPath(optBiSegGraph, pathCount)
val optBipath: util.ArrayList[util.ArrayList[Integer]] = optNsp.getPaths
// 词性标记
optBipath.foreach { optOnePath =>
val optSegPath: util.ArrayList[SegNode] = getSegPath(optSegGraph, optOnePath)
lexTagger.recognition(optSegPath)
// 对分词结果做最终的调整,主要是人名的拆分或重叠词的合并
val adjResult = AdjustSeg.finaAdjust(optSegPath, personTagger, placeTagger)
val adjrs: String = outputResult(adjResult)
stringBuilder ++= adjrs
}
}
}
println(stringBuilder.toString())
// 深夜/t 的/u 穆赫兰/nr 道/q 发生/v 一/m 桩/q 车祸/n ,/w 女子/n 丽塔/nr 在/p 车祸/n 中/f 失/v 忆/vg 了/y
其中,调用了函数:
// 根据二叉分词路径生成分词路径
private def getSegPath(sg: SegGraph, biPath: util.ArrayList[Integer]): util.ArrayList[SegNode] = {
sg != null && biPath != null match {
case true =>
val path = biPath.map { t => sg.getSnList.get(t) }
new util.ArrayList[SegNode](path)
case _ => null
}
}
// 根据分词路径生成分词结果
private def outputResult(wrList: util.ArrayList[SegNode]): String = {
...
}
后记:据闻ICTCLAS的第一版是作者在中科院读硕期间完成的,虽说代码质量惹人吐槽,但是不得不惊叹于作者的代码功底以及在训练模型上所下的功夫。
3. 参考资料
[1] Zhang, Hua-Ping, et al. "HHMM-based Chinese lexical analyzer ICTCLAS." Proceedings of the second SIGHAN workshop on Chinese language processing-Volume 17. Association for Computational Linguistics, 2003.
[2] 刘群, et al. "基于层叠隐马模型的汉语词法分析." 计算机研究与发展 41.8 (2004): 1421-1429.
[3] Fine, Shai, Yoram Singer, and Naftali Tishby. "The hierarchical hidden Markov model: Analysis and applications." Machine learning 32.1 (1998): 41-62.
中文分词工具探析(一):ICTCLAS (NLPIR)的更多相关文章
- 中文分词工具探析(二):Jieba
1. 前言 Jieba是由fxsjy大神开源的一款中文分词工具,一款属于工业界的分词工具--模型易用简单.代码清晰可读,推荐有志学习NLP或Python的读一下源码.与采用分词模型Bigram + H ...
- 开源中文分词工具探析(三):Ansj
Ansj是由孙健(ansjsun)开源的一个中文分词器,为ICTLAS的Java版本,也采用了Bigram + HMM分词模型(可参考我之前写的文章):在Bigram分词的基础上,识别未登录词,以提高 ...
- 开源中文分词工具探析(四):THULAC
THULAC是一款相当不错的中文分词工具,准确率高.分词速度蛮快的:并且在工程上做了很多优化,比如:用DAT存储训练特征(压缩训练模型),加入了标点符号的特征(提高分词准确率)等. 1. 前言 THU ...
- 开源中文分词工具探析(五):FNLP
FNLP是由Fudan NLP实验室的邱锡鹏老师开源的一套Java写就的中文NLP工具包,提供诸如分词.词性标注.文本分类.依存句法分析等功能. [开源中文分词工具探析]系列: 中文分词工具探析(一) ...
- 开源中文分词工具探析(五):Stanford CoreNLP
CoreNLP是由斯坦福大学开源的一套Java NLP工具,提供诸如:词性标注(part-of-speech (POS) tagger).命名实体识别(named entity recognizer ...
- 开源中文分词工具探析(七):LTP
LTP是哈工大开源的一套中文语言处理系统,涵盖了基本功能:分词.词性标注.命名实体识别.依存句法分析.语义角色标注.语义依存分析等. [开源中文分词工具探析]系列: 开源中文分词工具探析(一):ICT ...
- 开源中文分词工具探析(六):Stanford CoreNLP
CoreNLP是由斯坦福大学开源的一套Java NLP工具,提供诸如:词性标注(part-of-speech (POS) tagger).命名实体识别(named entity recognizer ...
- 中文分词工具简介与安装教程(jieba、nlpir、hanlp、pkuseg、foolnltk、snownlp、thulac)
2.1 jieba 2.1.1 jieba简介 Jieba中文含义结巴,jieba库是目前做的最好的python分词组件.首先它的安装十分便捷,只需要使用pip安装:其次,它不需要另外下载其它的数据包 ...
- Java实现敏感词过滤 - IKAnalyzer中文分词工具
IKAnalyzer 是一个开源的,基于java语言开发的轻量级的中文分词工具包. 官网: https://code.google.com/archive/p/ik-analyzer/ 本用例借助 I ...
随机推荐
- iOS开发系列--打造自己的“美图秀秀”
--绘图与滤镜全面解析 概述 在iOS中可以很容易的开发出绚丽的界面效果,一方面得益于成功系统的设计,另一方面得益于它强大的开发框架.今天我们将围绕iOS中两大图形.图像绘图框架进行介绍:Quartz ...
- x:bind不支持样式文件 或 此Xaml文件必须又代码隐藏类才能使用{x:Bind} 解决办法
这两天学习UWP开发,发现一个很有趣的问题,就是我题目中的描述的. 我习惯了在ResourceDictionary中写样式文件,但是发现用x:Bind时会有问题 如果是写在Style里,则提示 “x: ...
- git 命令
切换仓库地址: git remote set-url origin xxx.git切换分支:git checkout name撤销修改:git checkout -- file删除文件:git rm ...
- [.NET] 打造一个很简单的文档转换器 - 使用组件 Spire.Office
打造一个很简单的文档转换器 - 使用组件 Spire.Office [博主]反骨仔 [原文]http://www.cnblogs.com/liqingwen/p/6024827.html 序 之前,& ...
- ASP.NET Core: You must add a reference to assembly mscorlib, version=4.0.0.0
ASP.NET Core 引用外部程序包的时候,有时会出现下面的错误: The type 'Object' is defined in an assembly that is not referenc ...
- 设置line-height:1.5和line-height:150%或者line-height:150px的区别
直接正题: 看一下line-height可能的值: 其实可以分为两类: (1)不带单位的(如line-height:1.5),这种是推荐使用的: (2)带单位的(如line-heigth:30px/1 ...
- Mysql存储引擎比较
Mysql作为一个开源的免费数据库,在平时项目当中会经常使用到,而在项目当中我们的着重点一般在设计使用数据库上而非mysql本身上,所以在提到mysql的存储引擎时,一般都不曾知道,这里经过网上相关文 ...
- Velocity笔记--使用Velocity获取动态Web项目名的问题
以前使用jsp开发的时候,可以通过request很轻松的获取到根项目名,现在换到使用velocity渲染视图,因为已经不依赖servlet,request等一些类的环境,而Web项目的根项目名又不是写 ...
- trigger事件模拟
事件模拟trigger 在操作DOM元素中,大多数事件都是用户必须操作才会触发事件,但有时,需要模拟用户的操作,来达到效果. 需求:页面初始化时触发搜索事件并获取input控件值,并打印输出(效果图如 ...
- .NET深入实战系列—Linq to Sql进阶
最近在写代码的过程中用到了Linq查询,在查找资料的过程中发现网上的资料千奇百怪,于是自己整理了一些关于Linq中容易让人困惑的地方. 本文全部代码基于:UserInfo与Class两个表,其中Cla ...