词是自然语言中能够独立运用的最小单位,是自然语言处理的基本单位。
自动分词分析就是利用计算机对自然语言的形态进行分析,判断词的结构和类别等。
最大匹配法(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的划分结果:
['他', '是', '研究', '⽣物化学', '的', '⼀位', '科学家']
可以看出两者的划分结果并不相同。