不像英文那样单词之间有空格作为天然的分界线, 中文词语之间没有明显界限。必须采用一些方法将中文语句划分为单词序列才能进一步处理, 这一划分步骤即是所谓的中文分词。
主流中文分词方法包括基于规则的分词,基于大规模语料库的统计学习方法以及在实践中应用最多的规则与统计综合方法。
隐马尔科夫模型(HMM)是中文分词中一类常用的统计模型, 本文将使用该模型构造分词器。关于HMM模型的介绍可以参见隐式马尔科夫模型.
方法介绍
中文分词问题可以表示为一个序列标注问题,定义两个类别:
-
E代表词语中最后一个字
-
B代表词的首个字
-
M代表词中间的字
-
S代表单字成词
对于分词结果:"我/只是/做了/一些/微小/的/工作",可以标注为"我E只B是E做B了E一B些E微B小E的S工B作E".
将标记序列"EBEBEBEBESBE"作为状态序列, 原始文本"我只是做了一些微小的工作"为观测序列. 分词过程即变成了求使给定观测序列出现概率最大的状态序列, 即解码问题。
这里需要说明一下,所谓出现概率最大是指在自然语言中出现概率最大。
根据语料库是否标注, HMM可以进行监督学习和无监督学习。若语料库未标注则需要使用EM算法进行无监督学习, 若语料库已经标注那么可以使用频率估计概率即监督学习方法。
程序实现
定义数据结构
上文中已定义用于标注序列的标签, 也即HMM中的状态:
STATES = {
'B', 'M', 'E', 'S'}
定义HMMSegger类来维护数据:
class HMMSegger:
def __init__(self):
self.trans_mat = {}
self.emit_mat = {}
self.init_vec = {}
self.state_count = {}
self.word_set = set()
self.line_num = 0
其中:
-
trans_mat
: 状态转移矩阵,trans_mat[state1][state2]
表示训练集中由state1转移到state2的次数。 -
emit_mat
: 观测矩阵,emit_mat[state][char]
表示训练集中单字char被标注为state的次数 -
init_vec
: 初始状态分布向量,init_vec[state]
表示状态state在训练集中出现的次数 -
state_count
: 状态统计向量,state_count[state]
-
word_set
: 词集合, 包含所有单词 -
line_num
: 训练集中的行号
初始化上述数据结构:
def setup(self):
for state in STATES:
# build trans_mat
self.trans_mat[state] = {}
for target in STATES:
self.trans_mat[state][target] = 0.0
# build emit_mat
self.emit_mat[state] = {}
# build init_vec
self.init_vec[state] = 0
# build state_count