从零开始学Python自然语言处理(十)—— 最大匹配算法分词

浏览: 2962

前文传送门:

从零开始学自然语言处理(九)—— 依存句法分析

最大匹配算法分词是一种基于词典的分词方法。

最大匹配算法分为正向最大匹配算法和逆向最大匹配算法和双向最大匹配算法。

正向最大匹配算法

正向最大匹配算法,就是从左往右去扫描,然后寻找词的最大匹配。

首先需要规定一个词可能的最大长度,每次扫描的时候寻找当前开始位置往右看的这个长度的词,和字典中的词一个个匹配,如果没有匹配上,就缩短当前查找的词的长度继续寻找,直到找到或者当前长度变为1,也就是单个字。

最大匹配算法在使用前,必须有个词典,然后才能根据词典分词。

例如我们有个词典,词典中的内容为:

【欢迎,关注,欢迎关注,数据科学,数据,科学,学杂谈,杂谈,公众号,公众】

我们先不要纠结这个词典怎么得到的,我们开始看最大正向匹配算法的过程

例如,我们现在有一句话:“欢迎关注数据科学杂谈公众号”,我们使用最大正向匹配算法来分词。

首先,我们指定最大长度 maxlen = 4,这个最大长度通常可以选择词典中最长词的长度,这里词典中最长的词为“欢迎关注”和“数据科学”,它们的长度都是4。

从这句话的最左边开始,截取为4的长度,内容是“欢迎关注”,然后,将截取出来的内容和词典中的词一一匹配,发现匹配上了词典中的“欢迎关注”,然后我们将匹配上的内容放在分词结果列表中【欢迎关注】。

然后我们继续,接下来还是长度为4的,从“数”这个位置往右看,是“数据科学”,和词典中的“数据科学”匹配上了,所以将其放入分词结果列表【欢迎关注,数据科学】。

然后我们继续,从“杂”这个位置往右看,是“杂谈公众”,发现长度为4的“杂谈公众”匹配不上词典中的任何内容,接着缩短长度为3,发现“杂谈公”也无法匹配词典中的任何词,继续缩短长度,“杂谈”可以匹配上词典中的“杂谈”,将其放入分词结果列表中,此时,分词结果列表中有【欢迎关注,数据科学,杂谈】,然后继续从“公”开始向右看,此时要注意的是,我们剩下的句子长度只是3了,内容是“公众号”,这个比我们设定的maxlen小,我们之后匹配从3的长度开始考虑(其实之前每步都得考虑剩余句子长度,选择剩余句子长度和词典最大词长度中较小的长度作为下一次匹配起始长度)。所以匹配“公众号”,发现和词典中的“公众号”匹配上了,于是分词结果为【欢迎关注,数据科学,杂谈,公众号】,此时剩余句子长度为0,最大正向匹配算法结束了。

以下是使用最大正向匹配算法实现中文分词,词典是我自定义的,内容如图所示:

我们要注意的是,如果在匹配过程中,当前匹配长度为1的时候,一般词典不会写入单个字作为词,所以相当于没匹配上,此时直接将单个字作为分词结果加入分词结果列表。

#使用正向最大匹配算法实现中文分词
def init():
    words_dict = []#存放载入的词典
    with open(r"F:/dict.txt","r",encoding="utf8"as dict_input:
        for word in dict_input:
            #print(word)
            words_dict.append(word.strip())
    return words_dict

#实现正向最大匹配算法中的切词方法
def cut_words_fmm(raw_sentence,words_dict):
    #统计字典中最长的词
    max_length = max(len(word) for word in words_dict)#找到句子中最长的词
    sentence = raw_sentence
    #统计序列长度
    word_length = len(sentence)
    #存储切分好的词语
    cut_word_list = []
    while word_length > 0:
        max_cut_length = min(max_length,word_length)#选取词长和句子长中较小的一个
        subSentence = sentence[0:max_cut_length]
        while max_cut_length > 0:
            if subSentence in words_dict:#如果这个最长的词在我们的词典中,那么它就是最长的词了
                cut_word_list.append(subSentence)
                break
            elif max_cut_length == 1:#如果遇到单个字的时候
                cut_word_list.append(subSentence)
                break
            else:#如果这个词不在字典中,并且也不是单字作为一个词的,就要把匹配长度-1
                max_cut_length = max_cut_length -1
                subSentence = subSentence[0:max_cut_length]#这时要把右边的词去掉,然后继续循环,注意这里没有使用break跳出循环
        sentence = sentence[max_cut_length:]#把找的最大的词去掉,剩下的继续循环
        word_length = word_length - max_cut_length
    return cut_word_list

def main():
    word_dict = init()
    print(word_dict)
    print("请输入要分词的句子:")
    input_str = input()
    result = cut_words_fmm(input_str,word_dict)
    print("分词结果:",result)

if __name__ == '__main__':
    main()

逆向最大匹配算法

逆向最大匹配算法和正向匹配算法其实基本一样,唯一区别是正向最大匹配算法是从左到右扫描匹配,而逆向匹配算法是从右向左扫描匹配。

#使用逆向最大匹配算法实现中文分词
def init():
    words_dict = []#存放载入的词典
    with open(r"F:\dict.txt","r",encoding="utf8"as dic_input:
        for word in dic_input:
            words_dict.append(word.strip())
    return words_dict

#实现逆向最大匹配算法中的切词方法
def cut_words_bmm(raw_sentence,words_dict):
    #统计词典中词的最大长度
    max_length = max(len(word) for word in words_dict)
    sentence = raw_sentence.strip()
    #统计序列长度
    words_length = len(sentence)
    #存储切分好的词
    cut_word_list = []
    #判断是否需要继续切词
    while words_length > 0:
        max_cut_length = min(max_length, words_length)  #选取词长和句子长中较小的一个
        subSentence = sentence[-max_cut_length:]#从后往前取max_cut_length这么长
        while max_cut_length > 0:
            if subSentence in words_dict:
                cut_word_list.append(subSentence)
                break
            elif max_cut_length == 1:
                cut_word_list.append(subSentence)
                break
            else:
                max_cut_length = max_cut_length -1
                subSentence = subSentence[-max_cut_length:]
        sentence = sentence[0:-max_cut_length]
        words_length = words_length - max_cut_length
    cut_word_list.reverse()#切完之后的词是乱序的  这里为其逆序一下
    return  cut_word_list

def main():
    word_dict = init()
    print(word_dict)
    print("请输入要分词的句子:")
    input_str = input()
    result = cut_words_bmm(input_str,word_dict)
    print("分词结果:",result)

if __name__ == '__main__':
    main()

大家可以看到,逆向的时候,分出的是“学杂谈”,而不是“杂谈”,因为在从“谈”这个字开始匹配时,匹配长度3的时候,从右向左匹配上了词典中的“学杂谈”。

双向最大匹配算法

是将正向最大匹配算法和逆向最大匹配算法得到的结果进行比较,得到正确的分词方法。其算法为,如果正、反向分词结果词数不同,则取分词数量较少的那个,如果分词词数相同:

(1)此时看分词的结果是否相同,如果相同说明没有歧义,可返回任意一个。

(2)如果分词结果不同,则返回分词结果中单个字较少的那个。

#使用双向最大匹配算法实现中文分词
def init():
    words_dict = []#存放载入的词典
    with open(r"F:\dict.txt","r",encoding="utf8"as dic_input:
        for word in dic_input:
            words_dict.append(word.strip())
    return words_dict

#实现逆向最大匹配算法中的切词方法
def cut_words_bmm(raw_sentence,words_dict):
    #统计词典中词的最大长度
    max_length = max(len(word) for word in words_dict)
    sentence = raw_sentence.strip()
    #统计序列长度
    words_length = len(sentence)
    #存储切分好的词
    cut_word_list = []
    #判断是否需要继续切词
    while words_length > 0:
        max_cut_length = min(max_length, words_length)  #选取词长和句子长中较小的一个
        subSentence = sentence[-max_cut_length:]#从后往前取max_cut_length这么长
        while max_cut_length > 0:
            if subSentence in words_dict:
                cut_word_list.append(subSentence)
                break
            elif max_cut_length == 1:
                cut_word_list.append(subSentence)
                break
            else:
                max_cut_length = max_cut_length -1
                subSentence = subSentence[-max_cut_length:]
        sentence = sentence[0:-max_cut_length]
        words_length = words_length - max_cut_length
    cut_word_list.reverse()#切完之后的词是乱序的  这里为其逆序一下
    return  cut_word_list

#实现正向最大匹配算法中的切词方法
def cut_words_fmm(raw_sentence,words_dict):
    #统计字典中最长的词
    max_length = max(len(word) for word in words_dict)#找到句子中最长的词
    sentence = raw_sentence
    #统计序列长度
    word_length = len(sentence)
    #存储切分好的词语
    cut_word_list = []
    while word_length > 0:
        max_cut_length = min(max_length,word_length)#选取词长和句子长中最小的一个
        subSentence = sentence[0:max_cut_length]
        while max_cut_length > 0:
            if subSentence in words_dict:#如果这个最长的词在我们的词典中,那么它就是最长的词了
                cut_word_list.append(subSentence)
                break
            elif max_cut_length == 1:#如果是单字作为一个的时候
                cut_word_list.append(subSentence)
                break
            else:#如果这个词不在字典中,并且也不是单字作为一个词的,就要把匹配长度-1
                max_cut_length = max_cut_length -1
                subSentence = subSentence[0:max_cut_length]#这时要把右边的词去掉,然后继续循环,注意这里没有使用break跳出循环
        sentence = sentence[max_cut_length:]#把找的最大的词去掉,剩下的继续循环
        word_length = word_length - max_cut_length
    return cut_word_list

#实现双向最大匹配算法中的切词方法
def cut_words(raw_sentence,words_dict):
    bmm_word_list = cut_words_bmm(raw_sentence,words_dict)
    fmm_word_list = cut_words_fmm(raw_sentence,words_dict)
    bmm_word_list_size = len(bmm_word_list)
    fmm_word_list_size = len(fmm_word_list)
    if bmm_word_list_size != fmm_word_list_size:
        if bmm_word_list_size < fmm_word_list_size:
            return bmm_word_list
        else:
            return fmm_word_list
    else:
        FSingle = 0
        BSingle = 0
        isSame = True
        for i in range(len(fmm_word_list)):
            if fmm_word_list[i] not in bmm_word_list:#如果fmm和bmm的分词结果是不相同的
                isSame = False
            if len(fmm_word_list[i]) == 1:
                FSingle = FSingle + 1#如果fmm列表里的词长度为1,也就是说是单个词,那么就把单个词的数量+1
            if len(bmm_word_list[i]) == 1:
                BSingle = BSingle + 1
        if isSame:
            return fmm_word_list
        elif BSingle > FSingle:
            return fmm_word_list
        else:
            return bmm_word_list

def main():
    words_dict = init()
    print("请输入要分词的句子:")
    input_str = input()
    result = cut_words(input_str,words_dict)
    print("分词结果:",result)

if __name__ == '__main__':
    main()

我们从结果看出,其最终结果和正向最大匹配算法的结果一致,原因是正向的词数为4,而反向词数为5,选择了词数少的作为结果。

关注“数据科学杂谈”公众号,带你零基础学习自然语言处理~

扫码下图关注我们不会让你失望!

推荐 0
本文由 ID王大伟 创作,采用 知识共享署名-相同方式共享 3.0 中国大陆许可协议 进行许可。
转载、引用前需联系作者,并署名作者且注明文章出处。
本站文章版权归原作者及原出处所有 。内容为作者个人观点, 并不代表本站赞同其观点和对其真实性负责。本站是一个个人学习交流的平台,并不用于任何商业目的,如果有任何问题,请及时联系我们,我们将根据著作权人的要求,立即更正或者删除有关内容。本站拥有对此声明的最终解释权。

0 个评论

要回复文章请先登录注册