上一节我们谈完了Resnik教授基于UMDHMM设计的词性标注的练习,不过自始至终,还没有见到一个词性标记的影子。虽然这一过程展示了自然语言处理中EM算法在无监督学习任务中的重要作用,但是这类方法的标注准确性还相对较低,在实际应用中多是那些建立在有词性标注训练集基础上的机器学习算法,如最大熵模型、决策树等,所学习的词性标注器能获得较高的标注准确率。本节我们就以一个标注好的训练集为基础,来学习一个最简单的HMM词性标注器。
  首先就是准备训练集,作为一个练习,52nlp也本着尽量简单的原则,所以这里仍然选用Resnik教授所使用的example0.train,这个训练集虽然包含了一百句“似英语”的句子,但是只有一行,所以我们首先做一个断句处理,不过这些句子只采用了“.”和“?”作为句尾标志,因此断句相对简单。不过实际处理中英文断句问题比较麻烦,也有很多学者这方面做了很多研究工作。这里52nlp写了一个简单的sentsplit.pl脚本来处理这个训练集:
  ./sentsplit.pl example0.train example0.sentences
  example0.sentences就成了每一句为一行的训练集,如下所示:

the plane can fly .
the typical plane can see the plane .
a typical fly can see .
who might see ?
the large can might see a can .
the can can destroy a large can .

  但是,这个训练集只包含纯粹的单词句子,因此需要做一下词性标注,当然人工标注并检查是最好的了,但是我不懂,于是找了一个开源的词性标注工具对这些句子进行了标注,关于这个词性标注器的细节,下一节我会具体介绍,先来看标注后得到的包含词性标记的训练集example0.all,部分示例如下:

the/at plane/nn can/md fly/vb ./.
the/at typical/jj plane/nn can/md see/vb the/at plane/nn ./.
a/at typical/jj fly/nn can/md see/vb ./.
who/wps might/md see/vb ?/.
the/at large/jj can/nn might/md see/vb a/at can/nn ./.

  无论什么方法,建立HMM词性标注器的关键就是根据这个训练集来学习一个合适的HMM模型了。我们先来确定HMM模型中的隐藏状态(词性标记)和观察符号(词型),这里只考察能从训练集中观察的到的词性标记和词型,因此上一节用到的create_key.pl这个脚本就可以派上用处了。对于确定训练集中的词型,利用example0.sentences就可以:
  ../create_key.pl words.key < example0.sentences > example0.seq   
  所得到的words.key就包含了训练集中的词型及其数字编号:

1 the
2 plane
8 a
4 fly
3 can
7 see
12 large
11 ?
10 might
9 who
6 typical
5 .
13 destroy

  注意另一个副产品example0.seq在这一节里并不需要。同样我们也需要利用create_key.pl来确定训练集中的词性标记及其编号,不过这里我们需要先将example0.all中的词性标记序列抽取出来。这里52nlp写了一个简单的脚本extractpos.pl来处理此事:
  ./extractpos.pl example0.all example0.pos
  所得到的example0.pos文件部分示例如下:

at nn md vb .
at jj nn md vb at nn .
at jj nn md vb .
wps md vb .
at jj nn md vb at nn .
at nn md vb at jj nn .

  有了这个文件,就可以再次利用create_key.pl了:
  ../create_key.pl pos.key < example0.pos > example0.posseq
  所得到的pos.key就包含了训练集中的词性标记及其数字编号:

4 vb
6 jj
3 md
2 nn
7 wps
5 .
1 at

  同样,另一个副产品example0.posseq这里也不需要。
  确定好了该HMM模型中的隐藏状态(词性标记)和观察符号(词型)后,下一步便是要计算HMM模型中其他三个基本要素了,包括初始概率向量pi, 状态转移矩阵A,混淆矩阵B。
  我们先预处理一下语料库,主要的目标是对一元词性、二元词性及词型与词性的组合进行计数,这里52nlp写了一个脚本pretrain.pl来处理此事:
  ./pretrain.pl example0.all lex ngram
  所得到的lex文件主要是统计词型及其词性标记的组合在训练集中出现的次数:

typical jj 25
large jj 22
might md 42
fly nn 20
a at 58
? . 57
plane nn 34
the at 35
who wps 57
can nn 39
see vb 45
destroy vb 9
fly vb 46
. . 43
can md 58

  ngram文件主要包含的是一元词性及二元词性在训练集中的出现次数:

vb 100
jj 47
md 100
nn 93
wps 57
. 100
at 93
vb . 50
md vb 100
vb at 50
at jj 47
wps md 57
nn . 50
at nn 46
jj nn 47
nn md 43

  有了这几个预处理文件,我们就可以训练一个简单的HMM词性标注模型了,这里52nlp写了一个约100行的脚本hmmtrain.pl来处理此事:
  ./hmmtrain.pl words.key pos.key ngram lex example.hmm
  其中前四个是输入(准备)文件,最后一个example.hmm是输出文件,也就是本节的核心目标:一个合适的HMM词性标注模型,我们来简单看一下example.hmm:

M= 13
N= 7
A:
0.0100 0.4700 0.0100 0.0100 0.0100 0.4800 0.0100
...
B:
0.3396 0.0094 0.0094 0.0094 0.0094 0.0094 0.0094 0.5566 0.0094 0.0094 0.0094 0.0094 0.0094
...
pi:
0.1576 0.1576 0.1695 0.1695 0.1695 0.0797 0.0966

  有兴趣的读者,可以对比一下上一节利用BaumWelch算法(前向-后向算法)所学习的HMM词性标注模型example0.hmm。
  关于这个脚本,其中对于状态转移矩阵A,混淆矩阵B的计算采用了最简单的加一平滑来处理那些在训练集中的未出现事件, 关于加一平滑,不清楚读者可以在“MIT自然语言处理第三讲:概率语言模型(第四部分)” 中找到参考,或者任何一本自然语言处理书中关于ngram语言模型的章节都会介绍的。
  现在我们就可以作上一节最后一个词性标注的练习了,仍然选择训练集中的第91句:

the can can destroy the typical fly .

  可以利用Resnik教授的words2seq.pl来对此句进行转换,或者利用上一节已经处理好的UMDHMM可读的example0.test

T= 8
1 3 3 13 1 6 4 5

  现在就可以使用testvit及刚刚训练好的example.hmm来作词性标注了:
  ../testvit example.hmm example0.test
  同样得到了一个隐藏状态序列:


Optimal state sequence:
T= 8
1 2 3 4 1 6 2 5

  不过这次我们已经有了词性标记序列及其数字编号,可以对应着把它们写出来:

at nn md vb at jj nn .

  与测试句子合在一起即是:

the/at can/nn can/md destroy/vb the/at typical/jj fly/nn ./.

  对照example.all里的第91句:

the/at can/nn can/md destroy/vb the/at typical/jj fly/nn ./.

  二者是一样的,不过这个绝不能说明此HMM词性标注器是100%正确的。
  好了,本节就到此为止了,这一节的相关例子及小脚本可以单独按链接下载,也可以打包在这里供下载:52nlpexample.tar.gz
  不过这套小工具还不足以处理实际问题中的词性标注问题,下一节我将介绍一个更加健壮的HMM词性标注开源工具。

未完待续:词性标注6

注:原创文章,转载请注明出处“我爱自然语言处理”:www.52nlp.cn

本文链接地址:https://www.52nlp.cn/hmm-application-in-natural-language-processing-one-part-of-speech-tagging-5

作者 52nlp

《HMM在自然语言处理中的应用一:词性标注5》有15条评论
  1. 师兄,你好!我最近花了2个星期的时间几乎看完了这个博客中的全部文章,感谢你的无私奉献!
    我之前对hmm有些了解,也做过些实验,在这片文章的hmmtrain.pl中计算初始概率pi时,用是“$p = $n / $sum;”,n是训练集中某个状态(词性)出现的次数,sum是训练集中全部状态(词性)的出现次数。
    但我之前的理解是,初始概率只与每一个观察值序列(一个训练样本,即一句话)的第一个观察值(第一个词)相关,n是某个状态(词性)作为初始状态(句首)的次数,sum是训练样本(句子)的个数。
    不知道我这样理解对不对,请赐教,谢谢。

    [回复]

  2. 师兄,你好!我最近花了2个星期的时间几乎看完了这个博客中的文章,感谢你的无私奉献!
    这篇文章中hmmtrain.pl在计算hmm的初始概率Pi时,用的是“$p = $n / $sum”,n是某个状态(词性)在训练集中出现的次数,sum是全部状态(词性)在训练集中出现的次数总和。
    我之前也了解过hmm,但我的理解是,初始概率只与每一个训练样本(一句话)的第一个状态(第一个词性)相关,也就是说,n是某个状态(词性)出现在初始状态(句首)的次数,sum是训练样本的个数(句子数)。
    不知道我的理解对不对,请赐教,谢谢!

    [回复]

    52nlp 回复:

    我觉得你的理解没任何问题,应该比我的还准确,我当时算的时候只是考虑了随机落到某个词性的概率,没有考虑第一个位置。不过我觉得对于HMM词性标注来说,由于它考虑的是整个句子的标记序列好坏,估计初始概率对于整个标记的影响甚微,尤其对于训练语料库充足的情况,甚至我觉得随机生成初始概率都没有多大影响,不过这只是我的猜测!
    另外,不用客气,欢迎常来看看!

    [回复]

  3. 你好,我最近在看使用HMM进行中文分词的方法,但我对perl语言不是很熟悉,你文中的几个脚本extractpos.pl,pretrain.pl现在不能下载,不知能否发一份给我,这对我的理解有很大帮助,谢谢

    [回复]

    52nlp 回复:

    谢谢提醒!上次换虚拟主机的时候忘记这几个文件了,刚才已经重新上传了,现在可以看了。

    [回复]

  4. 非常感谢,看了你的文章对HMM模型的认识终于清晰了。令外下载的软件链接好像又挂了能重传下么?

    [回复]

    52nlp 回复:

    整理完后会通知大家,抱歉啊,近期比较忙。

    [回复]

  5. 看了文章,很受启发。只是附件下载的链接都失效了,是否能发一份到我邮箱,谢谢!

    [回复]

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注