用python实现FMM和BMM

词是自然语言中能够独立运用的最小单位,是自然语言处理的基本单位。

自动分词分析就是利用计算机对自然语言的形态进行分析,判断词的结构和类别等。

最大匹配法(Maximum Match Method)

  • 正向最大匹配算法(Forward MM,FMM)
  • 逆向最大匹配算法(Backward MM,BMM)
  • 双向最大匹配法(Bi-directional MM)

算法流程

前向最⼤匹配算法(FMM):

(1)待切分的汉字串 s1 ,已切分的汉字串 s2(初始为空);

(2)如果 s1 为空串,转到(6);

(3)从 s1 的左边复制⼀个⼦串 w 作为候选词, w 尽可能长,但不超过最⼤词长;

(4)如果在词表中能找到 w,或者 w 的长度为2(这个数值可以自己根据情况设定,通常为词表内最短的词的长度),那么,将 w 和⼀个界词标记⼀起加到 s2 的右边,并从 s1 的左边去掉 w,转 到(2);

(5)去掉w的最后⼀个汉字,转到(4);

(6)结束。

实例

给定词表:

['他', '是', '研究', '研究⽣', '⽣物', '物化', '化学', '⽣物化学', '的', '⼀位', '科学家', '学']

待划分句⼦:

"他是研究⽣物化学的⼀位科学家"

最⼤词长:

MaxWordLength = 8 (汉字)

划分过程

FMM划分过程:

1、考虑子串 "他是研究生物化学" ,判断其不再给定词表中,因此,删除最后一个汉字,考虑子串 "他是研究生物化",再次判断其不再给定词表中,因此,再次删除最后一个汉字;重复这个过程,直到最后只剩一个汉字或考虑的子串在给定的词表中,通过这一轮划分,可以将 '他' 划分出来。

2、将 '他' 从待划分句子中排出,考虑子串 "是研究生物化学的"。重复(1)中的步骤,最终可以把 '是' 划分出来。

3、重复上述的步骤,可以得到最终的划分结果。

FMM划分结果:他/是/研究⽣/物化/学/的/⼀/位/科学家

BMM的原理和FMM差不多,只不过BMM是从后往前划分,如上述例子,BMM先考虑子串 "化学的一位科学家",不再给定词表中,BMM删除的是子串的第一个汉字。

BMM划分结果:他/是/研究/⽣物化学/的/⼀位/科学家

从划分结果可以看出,FMM和BMM的划分并不同,在这个例子中显然BMM的划分更好。

代码实现

# -*- coding:utf-8 -*-


def FMM(user_dict, sentence):
    """"前向最大匹配算法 FMM"""
    segment_words = []      # 用于保存已经划分好的词
    max_len = max([len(item) for item in user_dict])    # 获得给定的词表中最长词的长度
    start = 0   # 起始划分位置,FMM从前往后划分
    nums = 1    # 记录划分轮数
    print("FMM:")
    print("*****开始切分*****")
    while start != len(sentence):
        index = start + max_len     # 每一轮划分考虑 index-start=max_len 长度
        if index > len(sentence):   # 如果当前的 index 位置超过了句子长度,则让其指向句子的最后
            index = len(sentence)
        for i in range(max_len):
            print(sentence[start:index])
            # 如果当前考虑的词在我们给定的词表中或者词的长度已经等于1,就将其加入到划分好的列表中
            if (sentence[start:index] in user_dict)\
            or (len(sentence[start:index]) == 1):
                segment_words.append(sentence[start:index])
                start = index
                break
            index -= 1
        print('当前已切分:', segment_words)
        print("————第 %d 轮结束————" % nums)
        nums += 1
    return segment_words


def BMM(user_dict, sentence):
    """"逆向最大匹配算法 BMM"""
    # 大部分代码同FMM一致,只需把start转换为end即可
    segment_words = []
    max_len = max([len(item) for item in user_dict])
    end = len(sentence)     # 起始划分位置,BMM从后往前划分
    nums = 1    # 记录划分轮数
    print("BMM:")
    print("*****开始切分*****")
    while end != 0:
        index = end - max_len   # 每一轮划分考虑 end-index=max_len 长度
        if index < 0:
            index = 0
        for i in range(max_len):
            print(sentence[index:end])
            if (sentence[index:end] in user_dict)\
            or (len(sentence[index:end]) == 1):
                segment_words.append(sentence[index:end])
                end = index
                break
            index += 1
        print('当前已切分:', segment_words)
        print("————第 %d 轮结束————" % nums)
        nums += 1
    return segment_words


if __name__ == '__main__':
    user_dict = ['时间', '就', '是', '生命']      # 给定词表
    sentence = "时间就是生命"                     # 待划分的例句
    # 使用FMM划分
    segment_words = FMM(user_dict, sentence)
    print("最终结果:\n", segment_words)
    print()
    # 使用BMM划分
    segment_words = BMM(user_dict, sentence)
    print("最终结果:\n", segment_words[::-1])   # 由于BMM是逆向划分的,将其倒转就与例句中词的出现顺序一致了

上述代码考虑的实例:

词表:['时间', '就', '是', '生命']

例句:"时间就是生命"

划分结果:

FMM:
*****开始切分*****
时间
当前已切分: ['时间']
————第 1 轮结束————
就是
当前已切分: ['时间', '就']
————第 2 轮结束————
是生
当前已切分: ['时间', '就', '是']
————第 3 轮结束————
生命
当前已切分: ['时间', '就', '是', '生命']
————第 4 轮结束————
最终结果:
 ['时间', '就', '是', '生命']

BMM:
*****开始切分*****
生命
当前已切分: ['生命']
————第 1 轮结束————
就是
当前已切分: ['生命', '是']
————第 2 轮结束————
间就
当前已切分: ['生命', '是', '就']
————第 3 轮结束————
时间
当前已切分: ['生命', '是', '就', '时间']
————第 4 轮结束————
最终结果:
 ['时间', '就', '是', '生命']

进程已结束,退出代码为 0

从这个例子可以看出,FMM和BMM的划分结果相同,我们现在将代码的词表和例句改为上述的例子:

词表:['他', '是', '研究', '研究⽣', '⽣物', '物化', '化学', '⽣物化学', '的', '⼀位', '科学家', '学']

例句:"他是研究⽣物化学的⼀位科学家"

划分结果:

FMM:
*****开始切分*****
他是研究
他是研
他是
当前已切分: ['他']
————第 1 轮结束————
是研究⽣
是研究
是研
当前已切分: ['他', '是']
————第 2 轮结束————
研究⽣物
研究⽣
当前已切分: ['他', '是', '研究⽣']
————第 3 轮结束————
物化学的
物化学
物化
当前已切分: ['他', '是', '研究⽣', '物化']
————第 4 轮结束————
学的⼀位
学的⼀
学的
当前已切分: ['他', '是', '研究⽣', '物化', '学']
————第 5 轮结束————
的⼀位科
的⼀位
的⼀
当前已切分: ['他', '是', '研究⽣', '物化', '学', '的']
————第 6 轮结束————
⼀位科学
⼀位科
⼀位
当前已切分: ['他', '是', '研究⽣', '物化', '学', '的', '⼀位']
————第 7 轮结束————
科学家
当前已切分: ['他', '是', '研究⽣', '物化', '学', '的', '⼀位', '科学家']
————第 8 轮结束————
最终结果:
 ['他', '是', '研究⽣', '物化', '学', '的', '⼀位', '科学家']

BMM:
*****开始切分*****
位科学家
科学家
当前已切分: ['科学家']
————第 1 轮结束————
学的⼀位
的⼀位
⼀位
当前已切分: ['科学家', '⼀位']
————第 2 轮结束————
物化学的
化学的
学的
当前已切分: ['科学家', '⼀位', '的']
————第 3 轮结束————
⽣物化学
当前已切分: ['科学家', '⼀位', '的', '⽣物化学']
————第 4 轮结束————
他是研究
是研究
研究
当前已切分: ['科学家', '⼀位', '的', '⽣物化学', '研究']
————第 5 轮结束————
他是
当前已切分: ['科学家', '⼀位', '的', '⽣物化学', '研究', '是']
————第 6 轮结束————
当前已切分: ['科学家', '⼀位', '的', '⽣物化学', '研究', '是', '他']
————第 7 轮结束————
最终结果:
 ['他', '是', '研究', '⽣物化学', '的', '⼀位', '科学家']

进程已结束,退出代码为 0

FMM的划分结果:

['他', '是', '研究⽣', '物化', '学', '的', '⼀位', '科学家']

BMM的划分结果:

['他', '是', '研究', '⽣物化学', '的', '⼀位', '科学家']

可以看出两者的划分结果并不相同。

Guess you like

Origin blog.csdn.net/lyb06/article/details/130163460