CoreNLP是由斯坦福大学开源的一套Java NLP工具,提供诸如:词性标注(part-of-speech (POS) tagger)、命名实体识别(named entity recognizer (NER))、情感分析(sentiment analysis)等功能。
【开源中文分词工具探析】系列:
- 开源中文分词工具探析(一):ICTCLAS (NLPIR)
- 开源中文分词工具探析(二):Jieba
- 开源中文分词工具探析(三):Ansj
- 开源中文分词工具探析(四):THULAC
- 开源中文分词工具探析(五):FNLP
- 开源中文分词工具探析(六):Stanford CoreNLP
- 开源中文分词工具探析(七):LTP
1. 前言
\]
其中,\(Z_w(x)\)为归一化因子,\(w\)为模型的参数,\(f_i(x,y)\)为特征函数。
2. 分解
以下源码分析基于3.7.0版本,分词示例见SegDemo
类。
模型
主要模型文件有两份,一份为词典文件dict-chris6.ser.gz
:
// dict-chris6.ser.gz 对应于长度为7的Set数组词典
// 共计词数:0+7323+125336+142252+82139+26907+39243
ChineseDictionary::loadDictionary(String serializePath) {
Set<String>[] dict = new HashSet[MAX_LEXICON_LENGTH + 1];
for (int i = 0; i <= MAX_LEXICON_LENGTH; i++) {
dict[i] = Generics.newHashSet();
}
dict = IOUtils.readObjectFromURLOrClasspathOrFileSystem(serializePath);
return dict;
}
词典的索引值为词的长度,比如第0个词典中没有词,第1个词典为长度为1的词,第6个词典为长度为6的词。其中,第6个词典为半成词,比如,有词“《双峰》(电”、“80年国家领”、“1824年英”。
另一份为CRF训练模型文件ctb.gz
:
CRFClassifier::loadClassifier(ObjectInputStream ois, Properties props) {
Object o = ois.readObject();
if (o instanceof List) {
labelIndices = (List<Index<CRFLabel>>) o; // label索引
}
classIndex = (Index<String>) ois.readObject(); // 序列标注label
featureIndex = (Index<String>) ois.readObject(); // 特征
flags = (SeqClassifierFlags) ois.readObject(); // 模型配置
Object featureFactory = ois.readObject(); // 特征模板,用于生成特征
else if (featureFactory instanceof FeatureFactory) {
featureFactories = Generics.newArrayList();
featureFactories.add((FeatureFactory<IN>) featureFactory);
}
windowSize = ois.readInt(); // 窗口大小为2
weights = (double[][]) ois.readObject(); // 特征+label 对应的权重
Set<String> lcWords = (Set<String>) ois.readObject(); // Set为空
else {
knownLCWords = new MaxSizeConcurrentHashSet<>(lcWords);
}
reinit();
}
不同于其他分词器采用B、M、E、S四种label来做分词,CoreNLP的中文分词label只有两种,“1”表示当前字符与前一字符连接成词,“0”则表示当前字符为另一词的开始——换言之前一字符为上一个词的结尾。
class CRFClassifier {
classIndex: class edu.stanford.nlp.util.HashIndex
["1","0"]
}
// 中文分词label对应的类
public static class AnswerAnnotation implements CoreAnnotation<String>{}
特征
CoreNLP的特征如下(示例):
class CRFClassifier {
// 特征
featureIndex: class edu.stanford.nlp.util.HashIndex
size = 3408491
0=的膀cc2|C
1=身也pc|C
44=LSSLp2spscsc2s|C
45=科背p2p|C
46=迪。cc2|C
...
=球-行pc2|CnC
=音非cc2|CpC
// 权重
weights: double[3408491][2]
[[2.2114868426005005E-5, -2.2114868091546352E-5]...]
}
特征后缀只有3类:C, CpC, CnC,分别代表了三大类特征;均由特征模板生成:
// 特征模板List
featureFactories: ArrayList<FeatureFactory>
0 = Gale2007ChineseSegmenterFeatureFactory
// 具体特征模板
Gale2007ChineseSegmenterFeatureFactory::getCliqueFeatures() {
if (clique == cliqueC) {
addAllInterningAndSuffixing(features, featuresC(cInfo, loc), "C");
} else if (clique == cliqueCpC) {
addAllInterningAndSuffixing(features, featuresCpC(cInfo, loc), "CpC");
addAllInterningAndSuffixing(features, featuresCnC(cInfo, loc - 1), "CnC");
}
}
特征模板只用到了两个特征簇cliqueC
与cliqueCpC
,其中,cliqueC
由函数featuresC()
实现,cliqueCpC
由函数featuresCpC()
与featuresCnC()
Gale2007ChineseSegmenterFeatureFactory::featuresC() {
if (flags.useWord1) {
// Unigram 特征
features.add(charc +"::c"); // c[0]
features.add(charc2+"::c2"); // c[1]
features.add(charp +"::p"); // c[-1]
features.add(charp2 +"::p2"); // c[-2]
// Bigram 特征
features.add(charc +charc2 +"::cn"); // c[0]c[1]
features.add(charc +charc3 +"::cn2"); // c[0]c[2]
features.add(charp +charc +"::pc"); // c[-1]c[0]
features.add(charp +charc2 +"::pn"); // c[-1]c[1]
features.add(charp2 +charp +"::p2p"); // c[-2]c[-1]
features.add(charp2 +charc +"::p2c"); // c[-2]c[0]
features.add(charc2 +charc +"::n2c"); // c[1]c[0]
}
// 三个字符c[-1]c[0]c[1]对应的LBeginAnnotation、LMiddleAnnotation、LEndAnnotation 三种label特征
// 结果特征分别以6种形式结尾,"-lb", "-lm", "-le", "-plb", "-plm", "-ple", "-c2lb", "-c2lm", "-c2le"
// null || ".../models/segmenter/chinese/dict-chris6.ser.gz"
if (flags.dictionary != null || flags.serializedDictionary != null) {
dictionaryFeaturesC(CoreAnnotations.LBeginAnnotation.class,
CoreAnnotations.LMiddleAnnotation.class,
CoreAnnotations.LEndAnnotation.class,
"", features, p, c, c2);
}
// 特征 c[1]c[0], c[1]
if (flags.useFeaturesC4gram || flags.useFeaturesC5gram || flags.useFeaturesC6gram) {
features.add(charp2 + charp + "p2p");
features.add(charp2 + "p2");
}
// Unicode特征
if (flags.useUnicodeType || flags.useUnicodeType4gram || flags.useUnicodeType5gram) {
features.add(uTypep + "-" + uTypec + "-" + uTypec2 + "-uType3");
}
// UnicodeType特征
if (flags.useUnicodeType4gram || flags.useUnicodeType5gram) {
features.add(uTypep2 + "-" + uTypep + "-" + uTypec + "-" + uTypec2 + "-uType4");
}
// UnicodeBlock特征
if (flags.useUnicodeBlock) {
features.add(p.getString(CoreAnnotations.UBlockAnnotation.class) + "-"
+ c.getString(CoreAnnotations.UBlockAnnotation.class) + "-"
+ c2.getString(CoreAnnotations.UBlockAnnotation.class)
+ "-uBlock");
}
// Shape特征
if (flags.useShapeStrings) {
if (flags.useShapeStrings1) {
features.add(p.getString(CoreAnnotations.ShapeAnnotation.class) + "ps");
features.add(c.getString(CoreAnnotations.ShapeAnnotation.class) + "cs");
features.add(c2.getString(CoreAnnotations.ShapeAnnotation.class) + "c2s");
}
if (flags.useShapeStrings3) {
features.add(p.getString(CoreAnnotations.ShapeAnnotation.class)
+ c.getString(CoreAnnotations.ShapeAnnotation.class)
+ c2.getString(CoreAnnotations.ShapeAnnotation.class)
+ "pscsc2s");
}
if (flags.useShapeStrings4) {
features.add(p2.getString(CoreAnnotations.ShapeAnnotation.class)
+ p.getString(CoreAnnotations.ShapeAnnotation.class)
+ c.getString(CoreAnnotations.ShapeAnnotation.class)
+ c2.getString(CoreAnnotations.ShapeAnnotation.class)
+ "p2spscsc2s");
}
if (flags.useShapeStrings5) {
features.add(p2.getString(CoreAnnotations.ShapeAnnotation.class)
+ p.getString(CoreAnnotations.ShapeAnnotation.class)
+ c.getString(CoreAnnotations.ShapeAnnotation.class)
+ c2.getString(CoreAnnotations.ShapeAnnotation.class)
+ c3.getString(CoreAnnotations.ShapeAnnotation.class)
+ "p2spscsc2sc3s");
}
}
}
Gale2007ChineseSegmenterFeatureFactory::featuresCpC() {}
Gale2007ChineseSegmenterFeatureFactory::featuresCnC() {}
三大类特征分别以“|C”为结尾(共计有32个)、以“|CpC”结尾(共计有37个)、以“|CnC”结尾(共计有9个);总计78个特征。个人感觉CoreNLP定义的特征过于复杂,大部分特征并没有什么用。CoreNLP后面处理流程跟其他分词器别无二样了,求每个label的权重加权之和,Viterbi解码求解最大概率路径,解析label序列得到分词结果。
CoreNLP分词速度巨慢,效果也一般,在PKU、MSR测试集上的表现如下:
测试集 | 分词器 | 准确率 | 召回率 | F1 |
---|---|---|---|---|
PKU | thulac4j | 0.948 | 0.936 | 0.942 |
CoreNLP | 0.901 | 0.894 | 0.897 | |
MSR | thulac4j | 0.866 | 0.896 | 0.881 |
CoreNLP | 0.822 | 0.859 | 0.840 |
3.参考资料
[1] Huihsin, Tseng, et al. "A conditional random field word segmenter." Fourth SIGHAN Workshop. 2005.
[2] Chang, Pi-Chuan, Michel Galley, and Christopher D. Manning. "Optimizing Chinese word segmentation for machine translation performance." Proceedings of the third workshop on statistical machine translation. Association for Computational Linguistics, 2008.
开源中文分词工具探析(五):Stanford CoreNLP的更多相关文章
-
开源中文分词工具探析(五):FNLP
FNLP是由Fudan NLP实验室的邱锡鹏老师开源的一套Java写就的中文NLP工具包,提供诸如分词.词性标注.文本分类.依存句法分析等功能. [开源中文分词工具探析]系列: 中文分词工具探析(一) ...
-
开源中文分词工具探析(六):Stanford CoreNLP
CoreNLP是由斯坦福大学开源的一套Java NLP工具,提供诸如:词性标注(part-of-speech (POS) tagger).命名实体识别(named entity recognizer ...
-
开源中文分词工具探析(三):Ansj
Ansj是由孙健(ansjsun)开源的一个中文分词器,为ICTLAS的Java版本,也采用了Bigram + HMM分词模型(可参考我之前写的文章):在Bigram分词的基础上,识别未登录词,以提高 ...
-
开源中文分词工具探析(四):THULAC
THULAC是一款相当不错的中文分词工具,准确率高.分词速度蛮快的:并且在工程上做了很多优化,比如:用DAT存储训练特征(压缩训练模型),加入了标点符号的特征(提高分词准确率)等. 1. 前言 THU ...
-
开源中文分词工具探析(七):LTP
LTP是哈工大开源的一套中文语言处理系统,涵盖了基本功能:分词.词性标注.命名实体识别.依存句法分析.语义角色标注.语义依存分析等. [开源中文分词工具探析]系列: 开源中文分词工具探析(一):ICT ...
-
中文分词工具探析(二):Jieba
1. 前言 Jieba是由fxsjy大神开源的一款中文分词工具,一款属于工业界的分词工具--模型易用简单.代码清晰可读,推荐有志学习NLP或Python的读一下源码.与采用分词模型Bigram + H ...
-
中文分词工具探析(一):ICTCLAS (NLPIR)
1. 前言 ICTCLAS是张华平在2000年推出的中文分词系统,于2009年更名为NLPIR.ICTCLAS是中文分词界元老级工具了,作者开放出了free版本的源代码(1.0整理版本在此). 作者在 ...
-
基于开源中文分词工具pkuseg-python,我用张小龙的3万字演讲做了测试
做过搜索的同学都知道,分词的好坏直接决定了搜索的质量,在英文中分词比中文要简单,因为英文是一个个单词通过空格来划分每个词的,而中文都一个个句子,单独一个汉字没有任何意义,必须联系前后文字才能正确表达它 ...
-
11大Java开源中文分词器的使用方法和分词效果对比,当前几个主要的Lucene中文分词器的比较
本文的目标有两个: 1.学会使用11大Java开源中文分词器 2.对比分析11大Java开源中文分词器的分词效果 本文给出了11大Java开源中文分词的使用方法以及分词结果对比代码,至于效果哪个好,那 ...
随机推荐
-
如何使用Log4net创建日志及简单扩展
第一步:在项目中添加对log4net.dll的引用,这里引用版本是1.2.10.0.第二步:程序启动时读取log4net的配置文件.如果是CS程序,在根目录的Program.cs中的Main方法中添加 ...
-
WPF,Silverlight与XAML读书笔记第四十三 - 多媒体支持之文本与文档
说明:本系列基本上是<WPF揭秘>的读书笔记.在结构安排与文章内容上参照<WPF揭秘>的编排,对内容进行了总结并加入一些个人理解. Glyphs对象(WPF,Silverlig ...
-
每日Scrum--No.9
Yesterday:测试软件 Today:写阶段性的总结 Problem: (1)晚上我们的团队进行了收尾工作:第一阶段的任务基本完成,软件主要实现了校园景点照片以及对应的介绍,查询最短路径,查询涉及 ...
-
2D Skeletal Animation Ready
骨骼动画 Cool 昨天研究了一天的2D骨骼动画,自己动手做了骨骼动画,感觉比用序列帧做动画方便多了,非常Cool ! 刚开始做骨骼动画用的是一整张图,做动画时在分配完权重之后,拉伸顶点上连接着的其它 ...
-
黑色遮罩引导蒙版 CSS实现方式
一.微云的实现 网站有一些改动的时候,为了让用户熟知新的操作位置,往往会增加一个引导,常见的方式就是使用一个黑色的半透明蒙版,然后需要关注的区域是镂空的. 然后上周五我去微云转悠的时候,也看到了引导层 ...
-
Python网络编程学习_Day11
一.协程 1.理论知识 协程,又称伪线程,是一种用户态的轻量级线程. 协程拥有自己的寄存器上下文和栈,协程调度切换时,将寄存器上下文和栈保存到其他地方,在切回来的时候,恢复先前保存的寄存器上下文和栈. ...
-
linux中的软连接和硬连接
1. 创建软连接的方法 ln -s /path/to/original /path/to/linkName 当我们对软连接文件进行修改后,对应的修改也会反映到原始的文件(反之亦然). 当我们删除软连接 ...
-
Grafana关键表结构分析
Grafana默认使用SQLite存储数据表,默认数据库文件存储在/var/lib/grafana/grafana.db中,可以将文件拷贝到Widnows中,使用Navicat for SQLite进 ...
-
ExecutorService实际上是一个线程池的管理工具
在Java5之后,并发线程这块发生了根本的变化,最重要的莫过于新的启动.调度.管理线程的一大堆API了.在Java5以后,通过Executor来启动线程比用 Thread的start()更好.在新特征 ...
-
输出九九乘法表(Python、Java、C、C++、JavaScript)
最近在学python的过程中,接触到了循环的知识,以及这个案例,于是写了下!感觉还不错,然后就用其它几种语言也试了下!! 接下来,就跟大家分享下实现方法!! 实现输出九九乘法表,主要用到的是循环的知识 ...