CoreNLP是由斯坦福大学开源的一套Java NLP工具,提供诸如:词性标注(part-of-speech (POS) tagger)、命名实体识别(named entity recognizer (NER))、情感分析(sentiment analysis)等功能。


【开源中文分词工具探析】系列:

  1. 开源中文分词工具探析(一):ICTCLAS (NLPIR)
  2. 开源中文分词工具探析(二):Jieba
  3. 开源中文分词工具探析(三):Ansj
  4. 开源中文分词工具探析(四):THULAC
  5. 开源中文分词工具探析(五):FNLP
  6. 开源中文分词工具探析(六):Stanford CoreNLP
  7. 开源中文分词工具探析(七):LTP

1. 前言

CoreNLP的中文分词基于CRF模型:

\[P_w(y|x) = \frac{exp \left( \sum_i w_i f_i(x,y) \right)}{Z_w(x)}
\]

其中,\(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");
}
}

特征模板只用到了两个特征簇cliqueCcliqueCpC,其中,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.

最新文章

  1. SpringBoot IntelliJ创建简单的Restful接口
  2. Windows常用的DOS命令
  3. iOS ZBarSDK的基本使用:扫描
  4. android 插件化开发 开源项目列表
  5. ok6410内存初始化
  6. 多目标遗传算法 ------ NSGA-II (部分源码解析) 临时种群生成新父代种群 fillnds.c
  7. CSS常用操作-导航栏
  8. Java之异常处理,日期处理
  9. Vue实现商城里面多个商品计算,全选,删除
  10. Python学习笔记(五)--Python数据类型-数字及字符串
  11. OOM分析工具
  12. shell date命令
  13. follow
  14. tp3.2 phpexcel 简单导出多个sheet(execl表格)
  15. Docz 用 MDX 写 React UI 组件文档
  16. P1601高精度加法
  17. 离线安装Cloudera Manager 5和CDH5(最新版5.9.3) 完全教程(二)基础环境安装
  18. mock server搭建及接口测试简单实例
  19. Linux设备驱动中的IO模型---阻塞和非阻塞IO【转】
  20. DZY Loves Math系列

热门文章

  1. python全栈开发day21-2 几个装饰器总结
  2. windows下redis安装和配置
  3. 关于Maven打包Java Web项目以及热部署插件Jrebel的使用
  4. SpringMvc @ResponseBody
  5. Apache目录结构解释
  6. c#基础在winform操作数据库,实现增删改查
  7. Python3.x使用PyMysql连接MySQL数据库
  8. jQuery UI练习
  9. Java 中的“implements Runnable” 和“extends Thread”(转)
  10. webpack总结