先介绍一下使用的资源,分词使用的语料来自于SIGHAN Bakeoff 2005的 icwb2-data.rar,《中文分词入门之资源》里有很详细的介绍:
/icwb2-data.rar/training/msr_training.utf8 用以训练HMM,其中包含已分词汇约2000000个
/icwb2-data.rar/testing/pku_test.utf8 测试集
/icwb2-data.rar/scripts/score 一个perl脚本,测试分词效果
我们使用经典的字符标注模型,首先需要确定标注集,在前面的介绍中,我们使用的是{B,E}的二元集合,研究表明基于四类标签的字符标注模型明显优于两类标签,原因是两类标签过于简单而损失了部分信息。四类标签的集合是 {B,E,M,S},其含义如下:
B:一个词的开始
E:一个词的结束
M:一个词的中间
S:单字成词
举例:你S现B在E应B该E去S幼B儿M园E了S
用四类标签为msr_training.utf8做好标记后,就可以开始用统计的方法构建一个HMM。我们打算构建一个2-gram(bigram)语言模型,也即一个1阶HMM,每个字符的标签分类只受前一个字符分类的影响。现在,我们需要求得HMM的状态转移矩阵 A 以及混合矩阵 B。其中:
Aij = P(Cj|Ci) = P(Ci,Cj) / P(Ci) = Count(Ci,Cj) / Count(Ci)
Bij = P(Oj|Ci) = P(Oj,Ci) / P(Ci) = Count(Oj,Ci) / Count(Ci)
公式中C = {B,E,M,S},O = {字符集合},Count代表频率。在计算Bij时,由于数据的稀疏性,很多字符未出现在训练集中,这导致概率为0的结果出现在B中,为了修补这个问题,我们采用加1的数据平滑技术,即:
Bij = P(Oj|Ci) = (Count(Oj,Ci) + 1)/ Count(Ci)
这并不是一种最好的处理技术,因为这有可能低估或高估真实概率,更加科学的方法是使用复杂一点的Good—Turing技术,这项技术的的原始版本是图灵当年和他的助手Good在破解德国密码机时发明的。求得的矩阵A如下:
B M E S
B 0.0 | 0.19922840916814916 | 0.8007715908318509 | 0.0
M 0.0 | 0.47583202978061256 | 0.5241679702193874 | 0.0
E 0.6309567616935934 | 0.0 | 0.0 | 0.36904323830640656
S 0.6343402140354506 | 0.0 | 0.0 | 0.36565844303914763
矩阵中出现的概率为0的元素表明B-B, B-S, M-B, M-S, E-M, E-E, S-M, S-E这8种组合是不可能出现的。这是合乎逻辑的。求得的矩阵B,部分如下:
o1 o2 o3 o4
B 7.8127868094E-7 1.460991186336E-4 0.007293548529516 6.047041844505E-4……
M ……
E …….
S ……
我们设定初始向量Pi = {0.5, 0.0, 0.0, 0.5}(M和E不可能出现在句子的首位)。至此,HMM模型构建完毕。其实用统计方法构建HMM并不复杂,麻烦的是写程序,特别是需要将分类标签和字符集合进行编码,这是个极其繁琐的过程。
我用的JAVA写程序,因此使用的Jahmm这个开源项目来进行相关的计算,对于一个HMM模型,现在我们可以写入一个观察序列,用Viterbi算法获得一个隐藏序列(分词结果)。具体做法是(举例为虚构):
1 . 对测试集按标点符号,空格,换行符拆分为一条一条的只包含字符的单句:
……
中共中央总书记
国家主席
……
2. 将句子转化为对应的编码,也即观察序列:
……
101 121 101 47 1010 32 1992
332 3241 893 2111
……
3. 输入观察序列,使用Viterbi算法输出隐藏序列(0:B,1:M,2:E,3:S)
……
0112333
0202
……
4. 输出最终分词结果:
……
中共中央/总/书/记
国家/主席
……
现在我们在对测试集pku_test.utf8分词,并用perl脚本进行评测,结果如下=== SUMMARY:
=== TOTAL INSERTIONS: 5627
=== TOTAL DELETIONS: 10639
=== TOTAL SUBSTITUTIONS: 18194
=== TOTAL NCHANGE: 34460
=== TOTAL TRUE WORD COUNT: 104372
=== TOTAL TEST WORD COUNT: 99360:
=== TOTAL TRUE WORDS RECALL: 0.724 正确切分出的词的数目/应切分出的词的总数
=== TOTAL TEST WORDS PRECISION: 0.760 正确切分出的词的数目/切分出的词的总数
=== F MEASURE: 0.742 F1 = 2 * Precision*Recall / (Precision + Recall)
=== OOV Recall Rate: 0.250 未登录词召回率
精度为:76%,召回为:72.4%。一个不太让人满意的结果,我们来看看他都干了些什么:
改判/被/告人/死/刑立/即/执行
这是一个较典型的例子,可见分词结果中出现了“告人”,“刑立”等字典中不存在的词,这些是由于HMM分词的“自由度”太大造成的,当然这种较大的“自由度”也有好处,比如:
检察院/鲍绍坤/检察/长
看!HMM成功的识别出一个人名,这是基于词典的分词方法做不到的。实际上,在ICTCLAS系统中(2004)就是用的HMM来识别人名。下一节将会介绍一个基于词典的混合HMM分词器。
现在正在学习基于词典的HMM分词,期待更新!
[回复]
同期待更新
[回复]
基于词典的混合HMM分词器写好了么?想借鉴一下
[回复]
能不能提供一个能运行的实例程序(包括训练语料),对新手来说非常急需。
[回复]
能提供您利用hmm实现的分词系统吗?这个很有意义
[回复]
真是巧了,我最近也写一个HMM的分词和标注程序,我用Python写的,还想要写好了,也贴到这里来呢。
我用了多一些的表记,对数字和英文缩写都做了特殊的 处理,平滑也采用 +1 方法。
程序仍在调试,这两天有结果,再分享!
[回复]
我原先按照博主的假设:Pi = {0.5, 0.0, 0.0, 0.5}去对msr_test.utf8进行分词效果很差。
后来我发现Pi应该根据训练语料库中B、M、E、S出现的概率得出来,并且实现验证这样做的效果得到大大提升。
[回复]
itenyh 回复:
27 6 月, 2012 at 00:30
提升的具体结果是怎样的? 你使用的Pi是多少?
[回复]
您好,最近学习crf++分词,但98年的分词语料库太旧了,有新的语料库吗,谢谢啊
[回复]
52nlp 回复:
11 10 月, 2012 at 09:47
其实98年的语料也不算老,这里还有一个搜狗词库的词典:
http://download.labs.sogou.com/dl/sogoulabdown/SogouW/SogouW.zip
[回复]
我自己用java实现了训练状态转移矩阵,但是结果跟你的稍微有点不同。http://sbp810050504.blog.51cto.com/2799422/1251640
求解,先行谢过了!
[回复]
您好,按照您的方法。
将训练集转化为 SSBMEBE这样的集合
因为 Aij = P(Cj|Ci) = P(Ci,Cj) / P(Ci) = Count(Ci,Cj) / Count(Ci)
在计算的时候,i的取值范围为[1,N-1]
而在count(Ci)的时候,i的取值范围是[1,N]
这会导致Aij的总和不为1,是这样理解的吗?
[回复]
itenyh 回复:
13 9 月, 2013 at 11:18
Exactly!
确切的说,如果训练集最后一个出现的字符为i的话,那么sig(j)Aij的确不会为1。
所以,作为最后一个字符的计数如你所说也应当只计算到N-1。
[回复]
文中
2. 将句子转化为对应的编码,也即观察序列:
101 121 101 47 1010 32 1992
332 3241 893 2111
这套编码是根据什么来的?
[回复]
我想用hmm做个英文短语识别的,有没有什么好的英文短语的语料库可以推荐下
[回复]