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

  1. // dict-chris6.ser.gz 对应于长度为7的Set数组词典
  2. // 共计词数:0+7323+125336+142252+82139+26907+39243
  3. ChineseDictionary::loadDictionary(String serializePath) {
  4. Set<String>[] dict = new HashSet[MAX_LEXICON_LENGTH + 1];
  5. for (int i = 0; i <= MAX_LEXICON_LENGTH; i++) {
  6. dict[i] = Generics.newHashSet();
  7. }
  8. dict = IOUtils.readObjectFromURLOrClasspathOrFileSystem(serializePath);
  9. return dict;
  10. }

词典的索引值为词的长度,比如第0个词典中没有词,第1个词典为长度为1的词,第6个词典为长度为6的词。其中,第6个词典为半成词,比如,有词“《双峰》(电”、“80年国家领”、“1824年英”。

另一份为CRF训练模型文件ctb.gz

  1. CRFClassifier::loadClassifier(ObjectInputStream ois, Properties props) {
  2. Object o = ois.readObject();
  3. if (o instanceof List) {
  4. labelIndices = (List<Index<CRFLabel>>) o; // label索引
  5. }
  6. classIndex = (Index<String>) ois.readObject(); // 序列标注label
  7. featureIndex = (Index<String>) ois.readObject(); // 特征
  8. flags = (SeqClassifierFlags) ois.readObject(); // 模型配置
  9. Object featureFactory = ois.readObject(); // 特征模板,用于生成特征
  10. else if (featureFactory instanceof FeatureFactory) {
  11. featureFactories = Generics.newArrayList();
  12. featureFactories.add((FeatureFactory<IN>) featureFactory);
  13. }
  14. windowSize = ois.readInt(); // 窗口大小为2
  15. weights = (double[][]) ois.readObject(); // 特征+label 对应的权重
  16. Set<String> lcWords = (Set<String>) ois.readObject(); // Set为空
  17. else {
  18. knownLCWords = new MaxSizeConcurrentHashSet<>(lcWords);
  19. }
  20. reinit();
  21. }

不同于其他分词器采用B、M、E、S四种label来做分词,CoreNLP的中文分词label只有两种,“1”表示当前字符与前一字符连接成词,“0”则表示当前字符为另一词的开始——换言之前一字符为上一个词的结尾。

  1. class CRFClassifier {
  2. classIndex: class edu.stanford.nlp.util.HashIndex
  3. ["1","0"]
  4. }
  5. // 中文分词label对应的类
  6. public static class AnswerAnnotation implements CoreAnnotation<String>{}

特征

CoreNLP的特征如下(示例):

  1. class CRFClassifier {
  2. // 特征
  3. featureIndex: class edu.stanford.nlp.util.HashIndex
  4. size = 3408491
  5. 0=的膀cc2|C
  6. 1=身也pc|C
  7. 44=LSSLp2spscsc2s|C
  8. 45=科背p2p|C
  9. 46=迪。cc2|C
  10. ...
  11. =球-行pc2|CnC
  12. =音非cc2|CpC
  13. // 权重
  14. weights: double[3408491][2]
  15. [[2.2114868426005005E-5, -2.2114868091546352E-5]...]
  16. }

特征后缀只有3类:C, CpC, CnC,分别代表了三大类特征;均由特征模板生成:

  1. // 特征模板List
  2. featureFactories: ArrayList<FeatureFactory>
  3. 0 = Gale2007ChineseSegmenterFeatureFactory
  4. // 具体特征模板
  5. Gale2007ChineseSegmenterFeatureFactory::getCliqueFeatures() {
  6. if (clique == cliqueC) {
  7. addAllInterningAndSuffixing(features, featuresC(cInfo, loc), "C");
  8. } else if (clique == cliqueCpC) {
  9. addAllInterningAndSuffixing(features, featuresCpC(cInfo, loc), "CpC");
  10. addAllInterningAndSuffixing(features, featuresCnC(cInfo, loc - 1), "CnC");
  11. }
  12. }

特征模板只用到了两个特征簇cliqueCcliqueCpC,其中,cliqueC由函数featuresC()实现,cliqueCpC由函数featuresCpC()featuresCnC()


  1. Gale2007ChineseSegmenterFeatureFactory::featuresC() {
  2. if (flags.useWord1) {
  3. // Unigram 特征
  4. features.add(charc +"::c"); // c[0]
  5. features.add(charc2+"::c2"); // c[1]
  6. features.add(charp +"::p"); // c[-1]
  7. features.add(charp2 +"::p2"); // c[-2]
  8. // Bigram 特征
  9. features.add(charc +charc2 +"::cn"); // c[0]c[1]
  10. features.add(charc +charc3 +"::cn2"); // c[0]c[2]
  11. features.add(charp +charc +"::pc"); // c[-1]c[0]
  12. features.add(charp +charc2 +"::pn"); // c[-1]c[1]
  13. features.add(charp2 +charp +"::p2p"); // c[-2]c[-1]
  14. features.add(charp2 +charc +"::p2c"); // c[-2]c[0]
  15. features.add(charc2 +charc +"::n2c"); // c[1]c[0]
  16. }
  17. // 三个字符c[-1]c[0]c[1]对应的LBeginAnnotation、LMiddleAnnotation、LEndAnnotation 三种label特征
  18. // 结果特征分别以6种形式结尾,"-lb", "-lm", "-le", "-plb", "-plm", "-ple", "-c2lb", "-c2lm", "-c2le"
  19. // null || ".../models/segmenter/chinese/dict-chris6.ser.gz"
  20. if (flags.dictionary != null || flags.serializedDictionary != null) {
  21. dictionaryFeaturesC(CoreAnnotations.LBeginAnnotation.class,
  22. CoreAnnotations.LMiddleAnnotation.class,
  23. CoreAnnotations.LEndAnnotation.class,
  24. "", features, p, c, c2);
  25. }
  26. // 特征 c[1]c[0], c[1]
  27. if (flags.useFeaturesC4gram || flags.useFeaturesC5gram || flags.useFeaturesC6gram) {
  28. features.add(charp2 + charp + "p2p");
  29. features.add(charp2 + "p2");
  30. }
  31. // Unicode特征
  32. if (flags.useUnicodeType || flags.useUnicodeType4gram || flags.useUnicodeType5gram) {
  33. features.add(uTypep + "-" + uTypec + "-" + uTypec2 + "-uType3");
  34. }
  35. // UnicodeType特征
  36. if (flags.useUnicodeType4gram || flags.useUnicodeType5gram) {
  37. features.add(uTypep2 + "-" + uTypep + "-" + uTypec + "-" + uTypec2 + "-uType4");
  38. }
  39. // UnicodeBlock特征
  40. if (flags.useUnicodeBlock) {
  41. features.add(p.getString(CoreAnnotations.UBlockAnnotation.class) + "-"
  42. + c.getString(CoreAnnotations.UBlockAnnotation.class) + "-"
  43. + c2.getString(CoreAnnotations.UBlockAnnotation.class)
  44. + "-uBlock");
  45. }
  46. // Shape特征
  47. if (flags.useShapeStrings) {
  48. if (flags.useShapeStrings1) {
  49. features.add(p.getString(CoreAnnotations.ShapeAnnotation.class) + "ps");
  50. features.add(c.getString(CoreAnnotations.ShapeAnnotation.class) + "cs");
  51. features.add(c2.getString(CoreAnnotations.ShapeAnnotation.class) + "c2s");
  52. }
  53. if (flags.useShapeStrings3) {
  54. features.add(p.getString(CoreAnnotations.ShapeAnnotation.class)
  55. + c.getString(CoreAnnotations.ShapeAnnotation.class)
  56. + c2.getString(CoreAnnotations.ShapeAnnotation.class)
  57. + "pscsc2s");
  58. }
  59. if (flags.useShapeStrings4) {
  60. features.add(p2.getString(CoreAnnotations.ShapeAnnotation.class)
  61. + p.getString(CoreAnnotations.ShapeAnnotation.class)
  62. + c.getString(CoreAnnotations.ShapeAnnotation.class)
  63. + c2.getString(CoreAnnotations.ShapeAnnotation.class)
  64. + "p2spscsc2s");
  65. }
  66. if (flags.useShapeStrings5) {
  67. features.add(p2.getString(CoreAnnotations.ShapeAnnotation.class)
  68. + p.getString(CoreAnnotations.ShapeAnnotation.class)
  69. + c.getString(CoreAnnotations.ShapeAnnotation.class)
  70. + c2.getString(CoreAnnotations.ShapeAnnotation.class)
  71. + c3.getString(CoreAnnotations.ShapeAnnotation.class)
  72. + "p2spscsc2sc3s");
  73. }
  74. }
  75. }
  76. Gale2007ChineseSegmenterFeatureFactory::featuresCpC() {}
  77. 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的更多相关文章

  1. 开源中文分词工具探析(五):FNLP

    FNLP是由Fudan NLP实验室的邱锡鹏老师开源的一套Java写就的中文NLP工具包,提供诸如分词.词性标注.文本分类.依存句法分析等功能. [开源中文分词工具探析]系列: 中文分词工具探析(一) ...

  2. 开源中文分词工具探析(六):Stanford CoreNLP

    CoreNLP是由斯坦福大学开源的一套Java NLP工具,提供诸如:词性标注(part-of-speech (POS) tagger).命名实体识别(named entity recognizer ...

  3. 开源中文分词工具探析(三):Ansj

    Ansj是由孙健(ansjsun)开源的一个中文分词器,为ICTLAS的Java版本,也采用了Bigram + HMM分词模型(可参考我之前写的文章):在Bigram分词的基础上,识别未登录词,以提高 ...

  4. 开源中文分词工具探析(四):THULAC

    THULAC是一款相当不错的中文分词工具,准确率高.分词速度蛮快的:并且在工程上做了很多优化,比如:用DAT存储训练特征(压缩训练模型),加入了标点符号的特征(提高分词准确率)等. 1. 前言 THU ...

  5. 开源中文分词工具探析(七):LTP

    LTP是哈工大开源的一套中文语言处理系统,涵盖了基本功能:分词.词性标注.命名实体识别.依存句法分析.语义角色标注.语义依存分析等. [开源中文分词工具探析]系列: 开源中文分词工具探析(一):ICT ...

  6. 中文分词工具探析(二):Jieba

    1. 前言 Jieba是由fxsjy大神开源的一款中文分词工具,一款属于工业界的分词工具--模型易用简单.代码清晰可读,推荐有志学习NLP或Python的读一下源码.与采用分词模型Bigram + H ...

  7. 中文分词工具探析(一):ICTCLAS (NLPIR)

    1. 前言 ICTCLAS是张华平在2000年推出的中文分词系统,于2009年更名为NLPIR.ICTCLAS是中文分词界元老级工具了,作者开放出了free版本的源代码(1.0整理版本在此). 作者在 ...

  8. 基于开源中文分词工具pkuseg-python,我用张小龙的3万字演讲做了测试

    做过搜索的同学都知道,分词的好坏直接决定了搜索的质量,在英文中分词比中文要简单,因为英文是一个个单词通过空格来划分每个词的,而中文都一个个句子,单独一个汉字没有任何意义,必须联系前后文字才能正确表达它 ...

  9. 11大Java开源中文分词器的使用方法和分词效果对比,当前几个主要的Lucene中文分词器的比较

    本文的目标有两个: 1.学会使用11大Java开源中文分词器 2.对比分析11大Java开源中文分词器的分词效果 本文给出了11大Java开源中文分词的使用方法以及分词结果对比代码,至于效果哪个好,那 ...

随机推荐

  1. flask-信号

    flask中的十个内置信号:request_started = _signals.signal('request-started') # 请求到来前执行 request_finished = _sig ...

  2. Java-反射初级知识掌握

    PS:本文就Java基础需要掌握的反射知识做下汇总和分析. Java-reflect:java反射,对应我们研究框架.底层框架起到基本的知识储备. Class类Java的世界类,万事皆对象,所有的类都 ...

  3. 一步一步从原理跟我学邮件收取及发送 2.邮箱的登录和绕不开的base64

    一步一步从原理跟我学邮件收取及发送 2.邮箱的登录和绕不开的base64 好了,经过本系列上一篇文章 "1.网络命令的发送",假设大家已经掌握了 email 电子邮件的命令发送的方 ...

  4. os系统

    任务延时函数OSTimeDly 功能:调用该函数的任务将自己延时一段时间并执行一次任务调度,一旦规定的延时时间完成或有其它的任务通过调用OSTimeDlyResume()取消了延时,调用OSTimeD ...

  5. 关于数据库timestamp类型问题

    数据库使用timestamp类型字段,默人时间为0000-00-00 00:00:00 于是后台会报java.sql.SQLException: Value '0000-00-00 00:00:00' ...

  6. [国嵌笔记][021-022][ARM处理器工作模式]

    [ARM处理器工作模式] 处理器工作模式 1.User(urs):用户模式,linux应用程序运行在用户模式 2.FIQ(fiq):快速中断模式 3.IRQ(irq):中断模式 4.Superviso ...

  7. php ueditor 后台配置项返回格式出错,上传功能将不能正常使用!

    解决常见的有两种 1,可能是时区设置问题,有系统区分大小写. date_default_timezone_set("Asia/chongqing");改为 date_default ...

  8. 评论发布信息可插入QQ表情

    demo例子: HTML文本内容: <template> <div id="publish"> <!-- 发布内容输入框,利用Html5的新属性con ...

  9. 关于Serializable的serialVersionUID

    在实现了Serializable接口的class中,需要声明一个long serialVersionUID,用来标明当前class的版本号,但很多人在编程时,总是不原意去声明这个serialVersi ...

  10. Linuxc - Makefile完成项目的管理。

    Makefile完成项目的管理. root@jiqing-virtual-machine:~/cspace/les2# ls main.c Makefile max.c max.h min.c min ...