一、词性标注引言
     
 词性用来描述一个词在上下文中的作用,比如描述一个概念的词叫作名词,在下文引用这个名词的词叫作代词。在文本挖掘中,有时候根据词性我们能够寻找到想要的关键字。
     
 一个词可能会有多个词性,解决标注歧义问题最简单的一个方法是从单词所有可能的词性中选出这个词最常用的词性作为这个词的词性,也就是一个概率最大的词性。比如“改革”大部分时候作为一个名词出现,那么可以机械地把这个词标注成名词,但是这样标注的准确率会比较低,因为只考虑了频率特征。考虑词所在的上下文可以提高标准的准确率。比如在动词后接名词的概率很大。"推进/改革"中的"推进"是动词,所以后面的"改革"很有可能是名词。这样的特征叫作上下文特征。
 
     
 二、隐马尔科夫模型
     
 
词性标注的任务是:给定词序列W=w1,w2,...,wn,寻找词性标注序列T=t1,t2,...,tn,使得P(t1,t2,...,tn
| w1,w2,...,wn)这个条件概率最大。
     
  使用贝叶斯公式和马尔科夫链(n=2)计算这个公式:
 上述推导可以很容易记录HMM的几个关键要素,而不用去死记硬背。
     
  上述推导可以很容易记录HMM的几个关键要素,而不用去死记硬背。     
  因为词是已知的,所以这里把词w叫作显状态。
     
  因为词性是未知的,所以把词性t叫作隐状态。
     
  条件概率P(ti|ti-1)叫作隐状态之间的转移概率。
     
  条件概率P(wi|ti)叫作隐状态到显状态的发射概率,也叫作隐状态生成显状态的概率。
     
  词性标注中的隐马尔科夫模型示意图:
 在初始概率、转移概率以及发射概率已知的情况下,可以从观测到的显状态序列计算出可能性最大的隐状态序列。
     
 
在初始概率、转移概率以及发射概率已知的情况下,可以从观测到的显状态序列计算出可能性最大的隐状态序列。     
 
     
 三、词性标注示例
     
 以标注[他][会][来]这句话为例,说明隐马尔科夫模型的计算过程。为了简化计算,假设只有词性:代词(r)、动词(v)、名词(n)和方位词(f)。这里,[他]只可能是代词,[会]可能是动词或者名词,[来]可能是方位词或者动词。
     
 简化版的语言模型描述如下:
     
 START: go(R, 1.0) emit(start, 1.0)
     
 F: emit(来, 0.1)   go(N, 0.9)
  go(END, 0.1)
     
 V: emit(来, 0.4)  
 emit(会, 0.3)  
 go(F, 0.1)  
 go(V,0.3)  
 go(N, 0.5)  
 go(END, 0.1)
     
 N: emit(会, 0.1)  
 go(F, 0.5)  
 go(V, 0.3)  
 go(END, 0.2)
     
 R: emit(他, 0.3)  
 go(V, 0.9)  
 go(N, 0.1)
     
 
     
 1、初始概率表
     
 2、转移概率表
     
  3、发射概率
     
  4、"他/会/来"的转移概率加发射概率图
 从上图可见,G依赖E和F的结果,而E和F又分别依赖C和D的计算结果。因为重复求解节点C和D,所以采用动态规划的方法求解最佳节点序列。当前节点概率的计算依据是:
     
 
从上图可见,G依赖E和F的结果,而E和F又分别依赖C和D的计算结果。因为重复求解节点C和D,所以采用动态规划的方法求解最佳节点序列。当前节点概率的计算依据是:     
(1)上一个阶段的节点概率P(Prev)
     
(2)上一个阶段的节点到当前节点的转移概率P(ti | ti-1)
     
(3)当前节点的发射概率P(wi | ti)
     
 寻找当前节点的最大概率示意图如下:
     
6、对于每一个节点Next,循环考察这个节点的上一个阶段所有可能的节点Prev1到Prevm,计算节点概率的循环等式是:
 实际计算时,采用log相加的方式来避免向下溢出。所以循环等式变成log累计概率、log转移概率和log发射概率三项相加的形式。这个用动态规划求解最佳词性序列的思想叫作维特比(Viterbi)算法。
     
 实际计算时,采用log相加的方式来避免向下溢出。所以循环等式变成log累计概率、log转移概率和log发射概率三项相加的形式。这个用动态规划求解最佳词性序列的思想叫作维特比(Viterbi)算法。     
 维特比求解方法由两个过程组成:前向累计概率计算过程和反向回溯过程。
     
(1)在阶段"start"计算:
     
 Best(A) = 1
     
(2)在阶段"他"计算:
     
 Best(B) = Best(A) * P(r|start) * P(他|r) = 1 * 1 *
0.3 = 0.3
     
(3)在阶段"会"计算:
     
 Best(C) = Best(B) * P(v|r) * P(会|v) = 0.3 * 0.9 *
0.3 = 0.081
     
 Best(D) = Best(B) * P(n|r) * P(会|n) = 0.3 * 0.1 *
0.6 = 0.018
     
 (4)在阶段"来"计算:
     
 Best(E) = Max[Best(C) * P(v|v), Best(D) * P(v|n)]
* P(来|v) = 0.081 * 0.3 * 0.4 = 0.00972
     
 Best(F) = Max[Best(C) * P(f|v), Best(D) * P(f|n)]
* P(来|f) = 0.081 * 0.1 * 0.1 = 0.00081
     
 (5)在阶段"end"计算:
     
 Best(G) = Max[Best[E] * P(end|v), Best(F) *
P(end|f)] * P(|end) = 0.00972 * 0.1 * 0.1 = 0.000972
     
 执行回溯过程即可发现最佳隐状态序列。G的最佳前驱节点是E,E的前面是C,C的前面是B,B的前面是A,所以猜测词性输出:
他/r    会/v  
 来/v。
     
 四、存储数据
1、计算转移概率
2、POSTransFreq.txt的数据格式:
a:b:62
a:c:451
a:d:296
...
     
 五、HMM的代码实现
1、词的标注类型
| 
12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 
 |  | package pos;
 // 词的标注类型
 public enum PartOfSpeech {
 start,//开始
 end,//结束
 a,//形容词
 ad,//副形词
 ag,//形语素
 an,//名形词
 b,//区别词
 c,//连词
 d,//副词
 dg,//副语素
 e,//叹词
 f,//方位词
 g,//语素
 h,//前接成分
 i,//成语
 j,//简称略语
 k,//后接成分
 l,//习用语
 m,//数词
 n,//名词
 ng,//名语素
 nr,//人名
 ns,//地名
 nt,//机构团体
 nx,//字母专名
 nz,//其他专名
 o,//拟声词
 p,//介词
 q,//量词
 r,//代词
 s,//处所词
 t,//时间词
 tg,//时语素
 u,//助词
 ud,//结构助词
 ug,//时态助词
 uj,//结构助词的
 ul,//时态助词了
 uv,//结构助词地
 uz,//时态助词着
 v,//动词
 vd,//副动词
 vg,//动语素
 vn,//名动词
 w,//标点符号
 x,//非语素字
 y,//语气词
 z,//状态词
 unknown //未知
 }
 
 | 
 
 
2、加载语料库的hmm值
| 
12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 
 |  | package pos;
 import java.io.*;
 import java.util.StringTokenizer;
 
 // 加载语料库的hmm值
 public class HMMProb {
 private static HMMProb dic = null;
 
 public static HMMProb getInstance(){
 if(dic == null){
 dic = new HMMProb();
 }
 return dic;
 }
 
 // 定义语料库的转移概率
 private int[][] transFreq = new int[PartOfSpeech.values().length][PartOfSpeech.values().length];
 // 每个词性的频次
 private int[] typeFreq = new int[PartOfSpeech.values().length];
 // 所有词的总频次
 private int totalFreq;
 
 
 public double getTypeProb(PartOfSpeech curState) {
 return (double) typeFreq[curState.ordinal()];
 }
 
 
 public double getTransProb(PartOfSpeech curState, PartOfSpeech toTranState) {
 double transValue = 0.9 * (double) transFreq[curState.ordinal()][toTranState.ordinal()] / (double) typeFreq[curState.ordinal()];
 double smoothValue = 0.1 * typeFreq[curState.ordinal()] / totalFreq;
 return Math.log(transValue + smoothValue);
 }
 
 // 加载语料库
 public HMMProb(){
 try {
 FileInputStream file = new FileInputStream("D:\\workspace\\IDEA\\NLPSystem\\src\\data\\POSTransFreq.txt");
 
 |