前向,逆向,双向最大匹配算法原理及实现

前向,逆向,双向最大匹配算法原理及实现

前向最大匹配算法

  • 原理

    正向最大匹配算法的基本思想为:假定分词词典中的最长词有个汉字字符,则用被处理文档的当前字串中的前个字作为匹配字段,查找字典。若字典中存在这样的一个字词,则匹配成功,匹配字段被作为一个词切分出来。如果词典中找不到这样的一个1字词,则匹配失败,将匹配字段中的最后一个字去掉,对剩下的字串重新进行匹配处理。如此进行下去,直到匹配成功,即切分出一个词或剩余字串的长度为零为止。这样就完成了一轮匹配,然后取下一个i字字串进行匹配处理,直到文档被扫描完为止。

    其算法描述如下:
    1)从左向右取待切分汉语句的m个字符作为匹配字段,m为机器词典中最长词条的字符数。
    2)查找机器词典并进行匹配。若匹配成功,则将这个匹配字段作为一个词切分出来。若匹配不成功,则将这个匹配字段的最后一个字去掉,剩下的字符串作为新的匹配字段,进行再次匹配,重复以上过程,直到切分出所有词为止。

  • 例子:

    例子:我们经常有意见分歧
    词典:['我们', '经常', '有', '有意见', '意见', '分歧']
    假设窗口大小为5
    ①[我们经常]有意见 ×
      [我们经常]    ×
      [我们经]     ×
      [我们]       √
    ②[经常有意见] ×
      [经常有意]  ×
      [经常有]    ×
      [经常]     √
    ③[有意见分歧]  ×
      [有意见分]  ×
      [有意见]   √
    ④[分歧]
    最终分词: 我们|经常|有意见|分歧
    
  • 代码实现

    words_dic = []  # 将字典载入进来,先要申请一个列表
    
    
    def _init_():
        # 读取词典文件,载入词典
        with open('dic/dic.txt', 'r', encoding='utf-8') as dic_input:
            for word in dic_input:
                words_dic.append(word.strip())
    
    
    # 实现正向最大匹配算法中的切词
    def cut_words(raw_sentence, words_dic):  # 原始的文本句子(最长的序列),字典
        # 统计词典中最长的词
        max_length = max(len(word) for word in words_dic)  # 对词典中的词进行遍历,选取最大的词
        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[0:max_cut_length]  # 切片
            while max_cut_length > 0:
                if subSentence in words_dic:  # 判断是否在词典中..
                    cut_word_list.append(subSentence)  # 在字典尾部添加切好的词
                    break
                elif max_cut_length == 1:  # 如果切分长度为1,则这个词为单词,也要将其添加进字典
                    cut_word_list.append(subSentence)
                    break
                else:  # 如果不在词典中同时也不等于1那么要将切词距离-1
                    max_cut_length = max_cut_length - 1
                    subSentence = subSentence[0:max_cut_length]
    
            sentence = sentence[max_cut_length:]  # 去掉已经切除的词
            words_length = words_length - max_cut_length
        words ='/'.join(cut_word_list)
        return words
    
    def main():
        '''
        用于用户交互接口
        :return:
        '''
        _init_()   # 先将字典加载进来
        while True:
            print("请输入分词序列")
            input_str = input()
            if not input_str:   # 如果为空则break
                break
            result = cut_words(input_str, words_dic)
            print('分词结果')
            print(result)
    
    if __name__ == "__main__":
        main()
    

逆向最大匹配算法

  • 原理

    逆向最大匹配的基本原理与正向最大匹配法相同,不同的是分词切分的方向与MM法相反。逆向最大匹配法从被处理文档的末端开始匹配扫描,每次取最末端的i个字符(为词典中最长词数)作为匹配字段,若匹配失败,则去掉匹配字段最前面的一个字,继续匹配。相应地,它使用的分词词典是逆序词典,其中的每个词条都将按逆序方式存放。在实际处理时,先将文档进行倒排处理,生成逆序文档。然后,根据逆序词典,对逆序文档用正向最大匹配法处理即可。

    由于汉语中偏正结构较多,若从后向前匹配,可以适当提高精确度。所以,逆向最大匹配法比正向最大匹配法的误差要小。统计结果表明,单纯使用正向最大匹配的错误率为1/169,单纯使用逆向最大匹配的错误率为1/245。比如之前的“南京市长江大桥”,按照逆向最大匹配,最终得到“南京市”“长江大桥”。当然,如此切分并不代表完全正确,可能有个叫“江大桥”的“南京市长”也说不定。

  • 例子

    例子:我们经常有意见分歧
    词典:['我们', '经常', '有', '有意见', '意见', '分歧']
    ①[有意见分歧] ×
      [意见分歧]    ×
      [见分歧]     ×
      [分歧]       √
    ②[经常有意见] ×
      [常有意见]  ×
      [有意见]    √
    ③[我们经常]  ×
      [们经常]  ×
      [经常]   √
    ④[我们]
    最终分词: 我们|经常|有意见|分歧
    

    有些例子里面同一句话的分词结果前向最大匹配和后向最大匹配不一定一致

  • 代码

    # 使用逆向最大匹配算法实现中文分词
    words_dic = []
    
    
    def _init_():
        # 读取字典文件,获取字典
        with open('dic/dic.txt', 'r', encoding='utf-8') as dic_input:
            for word in dic_input:
                words_dic.append(word.strip())
    
    
    # 实现逆向最大匹配算法中的切词
    def cut_words(raw_sentence, words_dic):
        # 统计词典中的最大长度
    
        max_length = max(len(word) for word in words_dic)
        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:]  # 切片
            while max_cut_length > 0:
                if subSentence in words_dic:  # 判断是否在词典中..
                    cut_word_list.append(subSentence)  # 在字典尾部添加切好的词
                    break
                elif max_cut_length == 1:  # 如果切分长度为1,则这个词为单词,也要将其添加进字典
                    cut_word_list.append(subSentence)
                    break
                else:  # 如果不在词典中同时也不等于1那么要将切词距离-1
                    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()  # 倒叙
        words = '/'.join(cut_word_list)
        return words
    
    
    def main():
        '''
        用于用户交互接口
        :return:
        '''
        _init_()   # 先将字典加载进来
        while True:
            print("请输入分词序列")
            input_str = input()
            if not input_str:   # 如果为空则break
                break
            result = cut_words(input_str, words_dic)
            print('分词结果')
            print(result)
    
    if __name__ == "__main__":
        main()
    


双向最大匹配算法

  • 原理

    双向最大匹配法(Bi-directction Matching method)是将正向最大匹配法得到的分词结果和逆向最大匹配法得到的结果进行比较,然后按照最大匹配原则,选取词数切分最少的作为结果。据SunM.S.和Benjamin K.T.(1995)的研究表明,中文中90.0%左右的句子,正向最大匹配法和逆向最大匹配法完全重合且正确,只有大概9.0%的句子两种切分方法得到的结果不一样,但其中必有一个是正确的(歧义检测成功),只有不到1.0%的句子,使用正向最大匹配法和逆向最大匹配法的切分虽重合却是错的,或者正向最大匹配法和逆向最大匹配法切分不同但两个都不对(歧义检测失败)。这正是双向最大匹配法在实用中文信息处理系统中得以广泛使用的原因。

    规则:

    1. 如果正反向分词结果词数不同,则取分词数量较少的那个

    2. 如果分词结果词数相同

      ①分词结果相同,就说明没有歧义,可运回任意一个

      ②分词结果不同,返回其中单字较少的那个

  • 例子

    前面举例的“南京市长江大桥”,采用该方法,中间产生“南京市长江/大桥”和“南京市/长江大桥”两种结果,最终选取词数较少的“南京市/长江大桥”这一结果。

  • 代码(3个.py文件)

    前向切词

    # 实现正向最大匹配算法中的切词
    def cut_words(raw_sentence, words_dic):  # 原始的文本句子(最长的序列),字典
        # 统计词典中最长的词
        max_length = max(len(word) for word in words_dic)  # 对词典中的词进行遍历,选取最大的词
        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[0:max_cut_length]  # 切片
            while max_cut_length > 0:
                if subSentence in words_dic:  # 判断是否在词典中..
                    cut_word_list.append(subSentence)  # 在字典尾部添加切好的词
                    break
                elif max_cut_length == 1:  # 如果切分长度为1,则这个词为单词,也要将其添加进字典
                    cut_word_list.append(subSentence)
                    break
                else:  # 如果不在词典中同时也不等于1那么要将切词距离-1
                    max_cut_length = max_cut_length - 1
                    subSentence = subSentence[0:max_cut_length]
    
            sentence = sentence[max_cut_length:]  # 去掉已经切除的词
            words_length = words_length - max_cut_length
        return cut_word_list
    
    

    后向最大匹配切词

    def cut_words(raw_sentence, words_dic):
        # 统计词典中的最大长度
    
        max_length = max(len(word) for word in words_dic)
        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:]  # 切片
            while max_cut_length > 0:
                if subSentence in words_dic:  # 判断是否在词典中..
                    cut_word_list.append(subSentence)  # 在字典尾部添加切好的词
                    break
                elif max_cut_length == 1:  # 如果切分长度为1,则这个词为单词,也要将其添加进字典
                    cut_word_list.append(subSentence)
                    break
                else:  # 如果不在词典中同时也不等于1那么要将切词距离-1
                    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
    

    双向最大匹配调用前面的算法进行比较

    import FMM
    import BMM
    
    #使用双向最大匹配算法实现中文分词
    words_dic = []
    
    def init():
        """
        读取词典文件
        载入词典
        :return:
        """
        with open("dic/dic.txt", "r", encoding="utf8") as dic_input:
            for word in dic_input:
                words_dic.append(word.strip())
    
    #实现双向匹配算法中的切词方法
    def cut_words(raw_sentence,words_dic):
        bmm_word_list = BMM.cut_words(raw_sentence,words_dic)
        fmm_word_list = FMM.cut_words(raw_sentence,words_dic)
        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:
                    isSame = False
                if len(fmm_word_list[i]) == 1:
                    FSingle = FSingle + 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():
        """
        于用户交互接口
        :return:
        """
        init()
        while True:
            print("请输入您要分词的序列")
            input_str = input()
            if not input_str:
                break
            result = cut_words(input_str,words_dic)
            print("分词结果")
            print(result)
    
    if __name__ == "__main__":
        main()
    
    

猜你喜欢

转载自blog.csdn.net/qq_44795788/article/details/126368558