赞
踩
前文已经谈到几种分词方法的手段和困难,本文将从最基本原理开始,一步一步分析我自己的分词器是如何开发的。文章分为两部分,第一部分谈论了我的一个有趣的尝试,这个尝试是对于最终分词程序没有直接帮助的,所以可以选择性跳过。第二部分谈论HMM模型在程序中的应用,是实现分词器的基本原理。
一、有趣的尝试
从最初出发点开始,我们需要确定那些字的组合是词语。每两个字能否组合成词语,其实取决于两个字同时以相同顺序出现在文章中的次数有关,次数越高,代表他们越有可能组合成词。假设一个字A出现在一篇文章中的概率为P(A),另一个字B出现在文中的概率为P(B),那么AB同时出现在文章中的概率为P(A)·P(B)。此时如果根据统计,发现P(AB)与P(A)·P(B)相近,我们就可以认为AB是一个词语。毫无疑问,如果语料库足够全面,统计学习能够从一定规模的语料库中发现最可能的词语组合。
最初我做了一个尝试,从百度百科中下载了各种内容的文章,覆盖了科学,教育,动物,人文,体育,健康,网络,社会等大约二十多个词条的文章。内容如下:
然后用JAVA写了一个程序来统计所有相邻两个字的组合和出现次数并输出。得到了一个大小为365K的txt文件。展示如下:
其中冒号左边的字代表前一个字,后边罗列的是所有在这个字后边出现过的字和对应的次数。其中可以发现在“规”字后边出现较多的字有“律”、“模”、“定”、“划”。在“观”字后边有“的”、“赏”、“看”。“矛”与“盾”的组合也较多。
如下再罗列一些结果的截图供参考:
由于屏幕大小原因,无法截取所有的搭配,但是通过一些初略的观察可以发现,除了一些常见的,如 “的”、“,” 等与任何词语都高频出现外,其他大部分高频字都与前一个字构成一个合法的词汇。我仅仅通过二十篇文章初略统计就有一个比较明显的效果,所以如果语料库足够大,内容足够丰富,相信是能够通过统计的方式来确定合法词语的搭配的。
这是我最初对统计方法确定合法词汇搭配的尝试,虽然对后来的开发并没有直接帮助,但确实让我对语言中,字与字的相互关系有了一个直观的感受,这对于后续的学习和开发极为重要。
二、基于HMM开发分词器
2.1 隐马尔科夫原理
通过上一部分的可以发现,虽然我们有很多方法可以确定词语的合法组合,但是字与字在依据话中如何划分其实与上下文是密不可分的。就像第一篇文章中谈及的“人才”两字,并不是任何时候都会放在一起。简单的字典分词,有点类似于脱离意思的机械分词,处理歧义和未登录词时,会有一定的困难。
既然前边说到,字与字的组合与上下文密切相关,那我们将一个字对应某一个词语中的位置作为这个词当前的状态,将状态分为四种情况:词头B,词中M,词尾E和单子成词S,用X表示状态,则Xset = {B, M, E, S}。现在我们就可以试图寻找一个类似的数学模型来描述这种状态出现的概率,从而判断分词的结果。
我们知道,字在一个句子中的出现是一个随机的过程,并且它的出现并不是独立事件。根据我们的语言习惯,字的出现其实与前边是什么字有着密切的联系。这种当前随机状态受之前n-1个状态影响的随机过程有一个很好的数学模型——马尔科夫随机过程。
希望详细了解马尔科夫随机过程,可以参考博文《隐马尔科夫模型详解》。
简而言之,对于一个观察序列O = {O0, O1, ... , On} 如果第n个对象On对应的状态表示为Xn,则其独立观察概率是 P(Xn) 。若之前的 n - 1 个状态表示为 Yi, 其中i = 1, ... , n - 1,则第n的观察对象On对应一个状态的概率就为:
P(On) = P(Xn)·P(Xn | Yn - 1, Y n - 2, ... Y 1)。
其中P(X)是独立观察概率,P(X | Y)是状态转移概率,即前n-1个状态为前提时,当前状态X的出现概率。这个状态转移概率是隐含在马尔科夫过程中的一个因素,因此整个模型称之为隐马尔科夫模型。
所以对于任意的观察序列O = {O0, O1, O2, ... , On}出现的概率可以表示为:
P(O) = Sum(P(Xn)·P(Xn | Yn - 1, Y n - 2, ... Y 1)), n = 1, 2, ... 。 (1)
但是困难的问题来了,每当我们计算第n个状态的概率时,我们需要得到之前n-1个概率的情况,如果有X有m种状态,我们需要进行2n*m^n次乘法。但是细心回过头来分析,我们发现,中文汉字的词语,以两字词语居多。根据黄昌宁博士的论文表述,对Bakeoff-2003和Bakeoff-2005的全部8个训练语料库词长的频率统计,1~2字词占了90%的比例,3字或者3字以下词占了95%,5字和5字一下次更是占据了99%的比例。所以考虑一个字的出现概率,其实无需考虑前边n-1种状态,只需要考虑前边1~2个状态就可以覆盖绝大部分中文词汇了。
其实这样的词语窗口大小在开发中也有规范,只考虑当前字的概率称之为unigram,考虑前一个字的概率称之为bigram,考虑前两个字的概率情况称之为trigram。为了简单起见,我采用了bigram模型。也就是考虑前一个字的情况下来判断当前字的概率。则上式(1)表示为:
P(O) = Sum(P(Xn) · P(Xn | Yn-1)) (2)
这样一来,问题就简单多了。对于一个句子,其中的字的序列组成了观察序列O,统计学习的结果就是确定了各个观察序列中元素标记各种位置的独立概率和状态转移概率。此时使用动态规划,找出使整个句子概率P(O)最大的标记方式作为分词结果即可。
2.2 统计学习
现在我们只需要根据式(2)的定义,来对一个完整的中文语料库进行统计学习就可以训练一个中文分词器了。我使用了微软亚洲研究院提供的中文语料库(本系列前言中附有下载链接)。
微软语料库内容如下:
其内容是人工手动处理的分词结果,词语与词语间由空格分开。所以进行统计学习,我们需要先为这些词打上位置标记来标记它的状态。如下是我的JAVA代码:
- // 预处理中文语料库
- protected void processTrainingMaterial() {
- File f = new File(this.TrainingMaterialPath);
- if (!f.exists()) {
- System.err.println("未找到中文语料库文件: "
- + this.TrainingMaterialPath);
- } else {
- try {
- if (!f.exists()) {
- f.createNewFile();
- }
- FileInputStream fis = new FileInputStream(f);
- InputStreamReader re = new InputStreamReader(fis,
- this.DefaultFileFormat);
- BufferedReader reader = new BufferedReader(re);
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。