中文分词:双向最大匹配实现

背景

双向最大匹配法(Bi-directction Matching method,BM)

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

数据准备

准备中文词库,提供一个中文词库:https://github.com/ling0322/webdict

数据预处理

只考虑中文分词,所以将其他的非中文字符都统一替换成空格。

正向最大匹配(FMM)

从左至右,一次匹配,每次尽量匹配较长的词语。设词库中词语长度最长的为max_len,设每次截取的长度为len,初始化截取长度len=max_len(当然len不能超过当前句子长度,即取再取一个min),每次截取后len减一,若发现截取词语与在词库中,则退出循环,说明找到了最大的匹配词语,然后对剩下的句子依次进行。

实例

将“我们在野生动物园”拆分
其中19为词库中词语最大长度
在这里插入图片描述
结果解释:
因为词库中词语最长为19,该句子的长度为8,所以从长度8开始截取,从第一个字开始,第一次截取为整个句子,发现词库中没有整个句子,再截取长度为7,长度为6…,最终发现从第一字开始,长度为2的词语存在,即“我们”;再从‘在’开始匹配,过程与前面的一样。
ps:‘在野’的确是个词语
在这里插入图片描述
从上面的结果,可以发现正向最大匹配不一定正确

逆向最大匹配(BMM)

过程与正向最大匹配相反,从后往前依次找最大长度的词语。

实例

在这里插入图片描述

双向最大匹配(Bi_MM)

1、正向最大匹配,得到分词结果ans1
2、逆向最大匹配,得到分词结果ans2
3、若ans1等于ans2,返回任意一个
4、若ans1不等于ans2
    4.1>返回ans1与ans2词语数量较少的
    4.2>若ans1与ans2词语数量相等,返回ans1与ans2单字较少的(单字数量还相等,任意返回一个)

代码

import re
import numpy as np

#中文匹配,把非中文字符换成空格
def extractChinese(s):
    import re
    pattern="[\u4e00-\u9fa5]+"#中文正则表达式
    regex = re.compile(pattern) #生成正则对象
    results = regex.findall(s) #匹配
    return " ".join(results)

#读取词语,以及词频,在下面的双向最大匹配中,词频没有实际意义
max_len=0 #记录最大单词长度
word_dic=dict()
with open("webdict_with_freq.txt", "r",encoding='utf-8') as f:
    for line in f.readlines():
        str=re.split('[, \n]',line)
        word_dic[str[0]]=int(str[1])
        max_len=max(max_len,len(str[0]))
print(max_len)

#读取测试集
test=[]
with open("test.txt", "r",encoding='utf-8') as f:
    for line in f.readlines():
        str = extractChinese(line)
        str=re.split('[ ]',str)
        for i in str:
            test.append(i)

#正向最大匹配
def FMM(text):
    ans=[]
    id=0
    while id<len(text):
        LL= min(len(text)-id,max_len)
        flag=False #判断是否找到最大匹配
        while LL>0:
            tt=text[id:id+LL]
            #print(tt)
            if word_dic.__contains__(tt):
                ans.append(tt)
                flag=True
                break
            LL-=1
        if flag:
            #找到最大匹配
            id+=LL
        else:
            #词库中没有这个字开头的词,直接跳过
            id+=1
    return ans

#逆向最大匹配
def BMM(text):
    ans = []
    id = len(text)
    while id>0:
        LL = min(id, max_len)
        flag = False  # 判断是否找到最大匹配
        while LL > 0:
            tt = text[id-LL:id]
            #print(tt)
            if word_dic.__contains__(tt):
                ans.append(tt)
                flag = True
                break
            LL -= 1
        if flag:
            # 找到最大匹配
            id -= LL
        else:
            # 词库中没有这个字开头的词,直接跳过
            id -= 1
    return ans[::-1]

def Bi_MM(text):
    ans1=FMM(text)
    ans2 = BMM(text)

    if ans1==ans2:
        return ans1
    else:
        if len(ans1)<len(ans2):
            return ans1
        elif len(ans2)<len(ans1):
            return ans2
        else:
            cnt1=0
            cnt2=0
            for i in ans1:
                if len(i)==1:
                    cnt1+=1
            for i in ans2:
                if len(i)==1 :
                    cnt2+=1
            if cnt1<=cnt2:
                return ans1
            else:
                return ans2

ans=''
for line in test:
    cut=Bi_MM(line)
    for i in cut:
        ans+=i
        ans+=" "
    ans+='\n'
fo = open('ans.txt', 'w', encoding='utf-8')
fo.write(ans)
fo.close()

结果

在这里插入图片描述
在这里插入图片描述

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值