Hanlp中使用纯JAVA实现CRF分词算法
与基于隐马尔可夫模型的最短路径分词、N-最短路径分词相比,基于条件随机场(CRF)的分词对未登陆词有更好的支持。本文(HanLP)使用纯Java实现CRF模型的读取与维特比后向解码,内部特征函数采用 双数组Trie树(DoubleArrayTrie)储存,获得了一个高性能的中文分词器。数组
开源项目函数
本文代码已集成到HanLP中开源:http://hanlp.com/性能
CRF简介spa
CRF是序列标注场景中经常使用的模型,比HMM能利用更多的特征,比MEMM更能抵抗标记偏置的问题。blog
CRF训练get
这类耗时的任务,仍是交给了用C++实现的CRF++。关于CRF++输出的CRF模型,请参考《CRF++模型格式说明》。博客
CRF解码table
解码采用维特比算法实现。而且稍有改进,用中文伪码与白话描述以下:模板
首先任何字的标签不只取决于它本身的参数,还取决于前一个字的标签。可是第一个字前面并无字,何来标签?因此第一个字的处理稍有不一样,假设第0个字的标签为X,遍历X计算第一个字的标签,取分数最大的那一个。
如何计算一个字的某个标签的分数呢?某个字根据CRF模型提供的模板生成了一系列特征函数,这些函数的输出值乘以该函数的权值最后求和得出了一个分数。该分数只是“点函数”的得分,还需加上“边函数”的得分。边函数在本分词模型中简化为f(s',s),其中s'为前一个字的标签,s为当前字的标签。因而该边函数就能够用一个4*4的矩阵描述,至关于HMM中的转移几率。
实现了评分函数后,从第二字开始便可运用维特比后向解码,为全部字打上BEMS标签。
实例
仍是取经典的“商品和服务”为例,首先HanLP的CRFSegment分词器将其拆分为一张表:
null表示分词器尚未对该字标注。
代码
上面说了这么多,其实个人实现很是简练:
标注结果
标注后将table打印出来:
最终处理
将BEMS该合并的合并,获得:
而后将词语送到词典中查询一下,没查到的暂时看成nx,并记下位置(由于这是个新词,为了表示它的特殊性,最后词性设为null),再次使用维特比标注词性:
新词识别
CRF对新词有很好的识别能力,好比:
输出:
null表示新词。
转载自hankcs的博客