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

ce32720a2563e2e24433a268403eb5e2.gif

前文传送门:

从零开始学自然语言处理(一)—— jieba 分词

从零开始学自然语言处理(二)—— jieba 黑科技

从零开始学自然语言处理(三)——手把手带你用代码实现word2vec

从零开始学自然语言处理(四)——手把手带你实现word2vec(skip-gram)

从零开始学自然语言处理(五)—— 做 NLP 任务文本 id 化与预训练词向量初始化方法

从零开始学自然语言处理(六)—— 词性标注

从零开始学自然语言处理(七)—— 命名实体识别

从零开始学自然语言处理(八)—— 句法结构分析

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

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

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

正向最大匹配算法

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

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

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

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

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

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

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

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

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

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

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

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

ed6d86b61c09b38b82087b6b79f2f919.png

我们要注意的是,如果在匹配过程中,当前匹配长度为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_listdef 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()

7f93a644c911f467bc00b957fc6e1052.png

逆向最大匹配算法

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

#使用逆向最大匹配算法实现中文分词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_listdef 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()

a7eb964cd42928294ae7d349219adf0c.png

大家可以看到,逆向的时候,分出的是“学杂谈”,而不是“杂谈”,因为在从“谈”这个字开始匹配时,匹配长度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             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_listdef main():    words_dict = init()    print("请输入要分词的句子:")    input_str = input()    result = cut_words(input_str,words_dict)    print("分词结果:",result)if __name__ == '__main__':    main()

fddffde94af9e5c379dc907f7e436d27.png

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

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

精选好文推荐:

没有网站地图的你是否迷失在APP中?

网站地图下的迷途知返

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

593705dd676fa22fd7b61c6a57cd0f52.png

2f54c746116549ada7cba5769145d7df.png 点个在看,是我继续原创的动力!
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值