分词学习(1)--正向最大匹配分词

来源:互联网 发布:anaconda linux的安装 编辑:程序博客网 时间:2024/05/01 17:49

汉字分词最简单的就是正向最大匹配分词了,其基本原理很简单,而且经常作为笔试题

     该算法主要分两个步骤:

1 一般从一个字符串的开始位置,选择一个最大长度的词长的片段,如果序列不足最大词长,则选择全部序列。

2 首先看该片段是否在词典中,如果是,则算为一个分出来的词,如果不是,则从右边开始,减少一个字符,然后看短一点的这个片段是否在词典中,依次循环,逐到只剩下一个字。

3 序列变为第2步骤截取分词后,剩下的部分序列。


      然后就开始写代码了,这里使用英文进行分词,如“itisatest”,需要分为“it is a test”,词典文件为count_1w.txt,数据来源于beautiful data(数据之美)这本书,附件中有。

之所以经常会面试写这个,主要是这个是个典型的应用分治思路的题目,只要分清楚了,代码其实非常简单易懂,如果几个步骤混在一起考虑,就非常容易糊涂。

对于这样稍微有点逻辑算法的题目,一般是思路就是case方法,就是用一个例子,看下算法怎么跑,然后再分析终止条件和一些特殊情况。当然,标准的设计初始条件,找出循环不变式的方法也是可以的,但是太麻烦了。。。。。。。。。。,一般都是先把代码写出来了,再来证明代码的正确性@#¥#@¥

          画个简单的例子图,这样写就容易的多,人的思维很难同时思考很多情况很多步骤,case方法是最直接的:


      


这里就是注意,黑点的位置就是字母的索引下标,就是唯一字母的前一个空位,这样理解更清楚一些。特别是做分词,黑点就是所有可能的分割位置。

有了这个case,就是分别写两个步骤的处理函数,然后主函数设置初始条件,并循环,调用两个函数,并判断终止条件即可。

具体代码如下(python):


#!/usr/bin/env python#coding=utf-8##############################################################function: 最大正向分词##############################################################import sys import math#global parameterDELIMITER = " " #分词之后的分隔符class MLSegment:    def __init__(self):        self.word_dict = {} #记录单词        self.gmax_word_length = 0 #单词的最大长度    #功能:从sequence中选取一个片段,    #      如果sequence长度大于最大词长,则从头取最大词长的片段    #      否则,则取整个sequence作为片段    #输入:sequence 需要处理的序列    #返回:(选择的片段,剩下的片段)    def get_segment(self, sequence):        if len(sequence) > self.gmax_word_length:            return (sequence[0:self.gmax_word_length], \                    sequence[self.gmax_word_length:])        else:            return (sequence,"")    #功能:从sequence选择一个最长的词    #      基本方法,先取整个sequence,看是否是词,如果不是,依次从右边    #      减少字母,看缩短的片段是否是词,如果减少到只剩下一个字母,则直接    #      作为一个词    #返回:(词,剩下的片段)    def select_max_length_word(self, sequence):        for length in range(len(sequence),1,-1):            word = sequence[0:length]            if self.word_dict.has_key(word):                return (word, sequence[length:] )        #至少分出一个字母        return (sequence[0:1],sequence[1:])    #最大长度分词    def ml_seg(self, sequence):        sequence = sequence.strip()        #初始化        segment_start_pos = 0        leave_segment = sequence        word_list = []        #开始循环        while True:            #step 1,获取一段片段,用来找词            (select_segment, leave_segment) = self.get_segment(leave_segment)            #step 2, 从选择的片段中获得可能的最长的词,以及剩余的部分片段            (word,word_right) = self.select_max_length_word(select_segment)            word_list.append(word)            #step 3,将剩余的词片段与剩下的片段组合,重新进入下一轮循环            leave_segment = word_right + leave_segment            #如果剩下的部分为空,退出循环            if leave_segment == "":                break        return DELIMITER.join(word_list)    #加载词典,为词\t词频的格式    def initial_dict(self,filename):        dict_file = open(filename, "r")        for line in dict_file:            sequence = line.strip()            key = sequence.split('\t')[0]            self.word_dict[key] = 1        #获取最大词长        self.gmax_word_length = max(len(key) for key in self.word_dict.iterkeys())#测试if __name__=='__main__':    myseg = MLSegment()    myseg.initial_dict("count_1w.txt")    sequence = "itisatest"    seg_sequence = myseg.ml_seg(sequence)    print "original sequence: " + sequence    print "segment result: " + seg_sequence                                               

这个是完全按照上面的思路写的,当然,因为截取片段的时候,其实只要记录截取的起始位置即可,然后逐步更新这个变量就行,不用记录所有的片段字符串,因此可以写一个改进方法,代码比上面的更清楚,但是理解起来会稍微麻烦一些。写代码一般都是从基础思路写起,然后慢慢改进的,不能一下就找出最优的写法:

#!/usr/bin/env python#coding=utf-8##############################################################function: 正向最大分词##############################################################import sysimport math#global parameterDELIMITER = " " #分词之后的分隔符class MLSegment:    def __init__(self):        self.word_dict = {} #记录单词        self.gmax_word_length = 0 #单词的最大长度    #最大长度分词    def ml_seg(self, sequence):        sequence = sequence.strip()        #初始化        segment_start_pos = 0 #开始        word_list = []        #循环体        while True:            #step 1,确定候选片段的最大长度            if  len(sequence) - segment_start_pos >  self.gmax_word_length :                max_segment_length = self.gmax_word_length            else:                max_segment_length =  len(sequence) - segment_start_pos            #step 2,从候选片段中选择候选词            for word_length in range(max_segment_length, 0, -1):                word = sequence[segment_start_pos:segment_start_pos+word_length]                if self.word_dict.has_key(word):                    word_list.append(word)                    break                if 1 == word_length and not self.word_dict.has_key(word):                    word_list.append(word)            #候选片段位置前移            segment_start_pos += word_length            #如果移动到了末尾位置,则退出            if segment_start_pos == len(sequence):                break        return DELIMITER.join(word_list)    #加载词典,为词\t词频的格式    def initial_dict(self,filename):        dict_file = open(filename, "r")        for line in dict_file:            sequence = line.strip()            key = sequence.split('\t')[0]            self.word_dict[key] = 1        #获取最大词长        self.gmax_word_length = max(len(key) for key in self.word_dict.iterkeys())#测试if __name__=='__main__':    myseg = MLSegment()    myseg.initial_dict("count_1w.txt")    sequence = "itisatest"    seg_sequence = myseg.ml_seg(sequence)    print "original sequence: " + sequence    print "segment result: " + seg_sequence

当然,还可以进一步的写递归的形式,会更简洁,但递归基本都是奇淫技巧,实际项目很少用到。


对"itisatest", 利用正向最大匹配,分割出来的是“itis atest”, 现在不符合需求,但是一个最基本的分词方法,在汉语分词上也有不错的效果,一般都作为分词算法的baseline。

后面一节就介绍最大概率分词,就可以解决这个问题。


字典文件和代码baidupan下载:http://pan.baidu.com/s/1dDikxg9


0 0
原创粉丝点击