自然语言处理—规则分词法

什么是规则分词

基于规则的分词是一种机械分词方法,主要是通过维护词典,在切分语句时,将语句的每个字符串与词表中的词进行逐一匹配,找到则切分,否则不予切分。

按照匹配切分的方式,主要有正向最大匹配法、逆向最大匹配法以及双向最大匹配法三种方法。

正向最大匹配法(MM法)

1.算法描述

如图所示,正向最大匹配法的具体步骤为:

  1. 从左向右取待切分汉语句的m个字符作为匹配字段,m是机器词典中最长词条的字符数;
  2. 查找机器词典并进行匹配。匹配成功则将匹配字段作为一个词切分出来,匹配失败则将匹配字段的最后一个字去掉,剩下的字符串作为新的匹配字段,进行再匹配,一直重复上述过程知道切分出所有词。

自然语言处理—规则分词法

2.主要思想: 

假定分词词典中最长词有i个汉字字符,则用被处理文档的当前字符串中的前i个字作为匹配字段,查找字典,从而得出分词结果。

3.范例:

我们现有的分词词典中最长的长度为5,词典中有南京市长江大桥三词,现采用 MM 法对句子南京市长江大桥进行分词,那么首先从句子中取出前5个字南京市长江,发现词典中没有该词,于是缩小长度,取前4个字南京市长,发现词典中还是没有该词,于是继续缩小长度,取前3个字南京市,词典中存在该词,于是该词被确认切分。再将剩下的长江大桥按照同样方式进行切分,得到长江大桥,最终切分为南京市/长江/大桥3个词。

#test.py
def cutA(sentence, dictA):
    # sentence:要分词的句子
    result = []
    sentenceLen = len(sentence)
    n = 0
    maxDictA = max([len(word) for word in dictA])
    # 完成正向匹配算法的代码描述,并将结果保存到result变量中
    # result变量为分词结果
    while sentenceLen>0:
        word=sentence[0:maxDictA]
        while word not in dictA:
            if len(word)==1:
                break
            word=word[0:len(word)-1]
        result.append(word)
        sentence=sentence[len(word):]
        sentenceLen=len(sentence)   
    print(result)  # 输出分词结果


#main.py
from test import cutA
str = input()
dict = []
for i in range(0, str.split(" ").__len__()):
  dict.append(str.split(" ")[i])

sentence = input()
cutA(sentence, dict)

 逆向最大匹配法

 1.算法描述:

逆向最大匹配( RMM 法)法的基本思想与 MM 法相同,不同的是分词切分的方向与 MM 法相反。逆向最大匹配法从右到左来进行切分。每次取最右边(末端)的 m 个字符作为匹配字段, 若匹配失败,则去掉匹配字段最左边(前面)的一个字,继续匹配。

2.范例:

比如:南京市长江大桥,按照逆向最大匹配,分词词典中最长词条的字符数长度为5,分词词典中有南京市长长江大桥两词,现采用 RMM 法对句子南京市长江大桥进行分词,那么首先从句子中从右到左取出前5个字市长江大桥,发现词典中没有该词,于是缩小长度,取前4个字长江大桥,词典中存在该词,于是该词被确认切分。再将剩下的南京市按照同样方式进行切分,得到南京市,最终切分为南京市/长江大桥2个词。当然,如此切分并不代表完全正确,可能有个叫江大桥南京市长也说不定。

由于汉语中偏正结构偏多,逆向最大匹配分词更多时候比正向最大匹配分词准确率高

test.py
def cutB(sentence,dictB):
    result = []
    sentenceLen = len(sentence)
    maxDictB = max([len(word) for word in dictB])
    # 任务:完成逆向最大匹配算法的代码描述
    while sentenceLen>0:
        word=sentence[-maxDictB:]
        while word not in dictB:
            if len(word)==1:
                break
            word=word[1:]
        result.append(word)
        sentence=sentence[0:len(sentence)-len(word)]
        sentenceLen=len(sentence)   
    print(result[::-1],end="")

#main.py
from test import cutB
str = input()
dict = []
for i in range(0, str.split(" ").__len__()):
  dict.append(str.split(" ")[i])

sentence = input()

cutB(sentence, dict)

双向最大匹配法

双向最大匹配的基本思想是将正向最大匹配法得到的分词结果和逆向最大匹配法得到的分词结果进行比较,然后按照最大匹配原则,选取词数切分最少的作为结果。该匹配算法的具体描述如下:

  • 比较正向最大匹配和逆向最大匹配结果;

  • 如果分词数量结果不同,那么取分词数量较少的那个;

  • 在分词数量结果相同的情况下,如果分词结果相同,则可以返回任何一个;如果分词结果不同,则返回单字数比较少的那个。

比如北京大学生前来应聘这个句子,如果通过正向最大匹配算法得到的结果为北京大学 / 生前 / 来 / 应聘,其中分词数量为4,单字数为 1;而通过逆向最大匹配算法所得到的结果为北京/ 大学生/ 前来 / 应聘,其中分词数量为4,单字数为0。则根据双向最大匹配算法,逆向匹配单字数少,因此返回逆向匹配的结果。 

经研究表明,90%的中文使用正向最大匹配分词和逆向最大匹配分词能得到相同的结果,而且保证分词正确;9%的句子是正向最大匹配分词和逆向最大匹配分词切分有分歧的,但是其中一定有一个是正确的;不到1%的句子是正向和逆向同时犯相同的错误:给出相同的结果但都是错的。因此,在实际的中文处理中,双向最大匹配分词能够胜任几乎全部的场景。

#test.py
class BiMM():
    def __init__(self):
        self.window_size = 3  # 字典中最长词数

    def MMseg(self, text, dict): # 正向最大匹配算法
        result = []
        index = 0
        text_length = len(text)
        while text_length > index:
            for size in range(self.window_size + index, index, -1):
                piece = text[index:size]
                if piece in dict:
                    index = size - 1
                    break
            index += 1
            result.append(piece)
        return result

    def RMMseg(self, text, dict): # 逆向最大匹配算法
        result = []
        index = len(text)
        while index > 0:
            for size in range(index - self.window_size, index):
                piece = text[size:index]
                if piece in dict:
                    index = size + 1
                    break
            index = index - 1
            result.append(piece)
        result.reverse()
        return result

    def main(self, text, r1, r2):
    #完成双向最大匹配算法的代码描述
        r1_count=0
        r2_count=0
        if len(r1)>len(r2):
            print(r2,end="")
        elif len(r1)<len(r2):
            print(r1,end="")
        else:
            for i in r1:
                if len(i)==1:
                    r1_count=r1_count+1
            for j in r2:
                if len(j)==1:
                    r2_count=r2_count+1
            if r1_count==r2_count:
                print(r1,end="")
            elif r1_count>r2_count:
                print(r2,end="")
            else:
                print(r1,end="")



#main.py
from test import BiMM
dict = [ ]
str=input()
for i in range(0, str.split(" ").__len__()):
  dict.append(str.split(" ")[i])
text = input()
tokenizer = BiMM()
r1 = tokenizer.MMseg(text, dict)
r2 = tokenizer.RMMseg(text, dict)
tokenizer.main(text, r1, r2)

测试输入:

这里 有 苹果 香蕉 红苹果

这里有红苹果和香蕉

输出:

['这里', '有', '红苹果', '和', '香蕉']

上一篇:箭头函数(=>)


下一篇:Gvim+Emmet.vim 那些事。