隐马尔可夫实现中文分词小例子

Posted CsCHEESE

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了隐马尔可夫实现中文分词小例子相关的知识,希望对你有一定的参考价值。

利用隐马尔可夫实现中文分词模型

本文代码参考了:
https://github.com/Dod-o/Statistical-Learning-Method_Code/blob/master/HMM/HMM.py
在中文分词的时候主要是要考虑到每一个词是由几个字组成的,所以在利用单个句子看作一个状态链,将所有的单个字划分为几个状态:
1.B 作为一个词的开始
2.E 作为一个词的结束
3.S 作为单个字就是一个词
4.M 作为一个词中间的部分
据一个例子:
“我喜欢吃火龙果”:其中的“我”这个字的状态就是“S”“喜欢”这个是一个词,其中的“喜”字是“B”,“欢”是“E”,“火龙果”中的“火”是“B”“龙”是“M”,“果”是“E”。通过训练前面在HMM介绍中的三个参数:就是状态的初始分布,即每一个状态初始概率,比如类似于S或者B这个状态在一个句子的开始的概率一定会很大,以及不同的字在不同状态下的概率,比如“我”这个词在S这个状态下的概率就会很高,和不同状态之间的转移概率,来对观测数据(未被分词的句子)进行分词,分词主要是在S,或者E状态后面进行分词,因为这个状态是一个单词的结束。

训练模型

1

1stat_dict = {'B':0,'M':1,'E':2,'S':3}
2    initial_distribution = np.zeros(4)
3    A = np.zeros((4,4))
4    B = np.zeros((4,65536))

首先初始化三个变量,由于是4个状态,所以在初始分布是4和状态转移的概率矩阵是4*4的,在状态观测矩阵当中,找到的是所有汉字的ASCII码,这样可以涵盖所有的汉字。

2

 1    for line in train_data.readlines():
2        curline = line.strip().split()
3        world_label_state = []
4        for i in range(len(curline)):
5            if(len(curline[i]) == 1):
6                label = 'S'
7            else:
8                label = 'B' + 'M' * (len(curline[i]) - 2) + 'E'
9
10            if i == 0:
11                initial_distribution[stat_dict[label[0]]] += 1
12            for j in range(len(label)):
13                B[stat_dict[label[j]]][ord(curline[i][j])] += 1
14            world_label_state.extend(label)
15        for i in range(1,len(world_label_state)):
16            A[stat_dict[world_label_state[i-1]]][stat_dict[world_label_state[i]]] += 1

是对训练数据的标记,训练数据已经被分词好的,用空格隔开,将分好的句子标上状态,如果是第一个单词的汉字数量为1那么就是状态“S”,然后根据前面定义好的dict根据标注信息找到对应的数值(如:B对应1),这里利用的训练模型是直接计算的,找到不同状态的出现次数,不同状态下各个观测的汉字出现的次数直接计算频率,在这个代码片段中是在计数。

3

 1sum = np.sum(initial_distribution)
2    for i in range(len(initial_distribution)):
3        if(initial_distribution[i] == 0):
4            initial_distribution[i] = -3.14e+100
5        else:
6            initial_distribution[i] = np.log(initial_distribution[i] / sum)
7
8    for i in range(len(A)):
9        sum = np.sum(A[i])
10        for j in range(len(A[i])):
11            if(A[i][j] == 0):
12                A[i][j] = -3.14e+100
13            else:
14                A[i][j] = np.log(A[i][j] / sum)
15    for i in range(len(B)):
16        sum = np.sum(len(B[i]))
17        for j in range(len(B[i])):
18            if(B[i][j] == 0):
19                B[i][j] = -3.14e+100
20            else:
21                B[i][j] = np.log(B[i][j] / sum)
22    return initial_distribution,A,B

在这个代码片段中主要是,针对之前的计数来计算概率,但是由于很多的计数为0,这样计算完也就是0,在后面求的时候一个数值为0,整个链的概率都是0了导致粒度很粗所以在这里参考文章开头的代码利用的是log,如果利用的是log的话就不需要乘法直接变成加法,并且对为0的数值直接附上一个很小的数值。

进行分词

1

 1res = []
2    for line in artical:
3        delta = [[0 for i in range(4)] for i in range(len(line))]
4        for i in range(4):
5            delta[0][i] = initial_distribution[i] + B[i][ord(line[0])]
6        last_state = [[0 for i in range(4)] for j in range(len(line))]
7        for t in range(1,len(line)):
8            for j in range(4):
9                temp = [0] * 4
10                for k in range(4):
11                    temp[k] = delta[t-1][k] + A[k][j]
12                maxValue = max(temp)
13                maxIndex = temp.index(maxValue)
14                last_state[t][j] = maxIndex
15                delta[t][j] = maxValue + B[j][ord(line[t])]

在这里主要是一个维比特算法的实现,正向的递推计算。首先是开始的初始状态下,delta记录的是在时刻t和之前的观测已知的条件下,分别为4个状态的最大概率值。并且last——state代表的是达到这个最大概率的上一个状态是什么。都是以观测序列长度为行数,以状态
数为列数的。

2

 1sequence = []
2        endMaxValue = delta[len(line) - 1].index(max(delta[len(line) - 1]))
3        sequence.append(endMaxValue)
4        for j in range(len(line) - 1,0,-1):
5            endMaxValue = last_state[j][endMaxValue]
6            sequence.append(endMaxValue)
7        sequence.reverse()
8        part_artical = ''
9        for i in range(len(line)):
10            part_artical += line[i]
11            if((sequence[i] == 2 or sequence[i] == 3and i != len(line) - 1):
12                part_artical += '|'
13        res.append(part_artical)

这段代码就是根据找到的每一个状态之前的最佳状态来倒着生成一个预测的状态序列,然后根据状态序列进行分词。res是结果。

效果

['深圳有个打工者阅览室', '去年12月,我在广东深圳市出差]
[深圳|有个|打|工者|阅览室,去年|12月|,|我|在|广东|深圳|市出|差|,]
利用的都是文章开头提到的的训练集和测试集,整体效果一般只是作为一个马尔可夫利用的小例子。
整体代码可以参考:
https://github.com/lbndpcoder/ML 

或者原博主的 

https://github.com/Dod-o/Statistical-Learning-Method_Code/blob/master/HMM/HMM.py


以上是关于隐马尔可夫实现中文分词小例子的主要内容,如果未能解决你的问题,请参考以下文章

HMM(隐马尔可夫)中文分词

隐马尔可夫模型(HMM)中文分词

HMM(隐马尔科夫)用于中文分词

Python实现HMM(隐马尔可夫模型)

HMM的中文分词方法

词性标注-隐马尔可夫模型