中文分词

中文分词

最近工作中碰到很多query分析的技术,包括query的分词,纠错,改写,同义词,权重等等常见的自然语言处理任务,在这里简单的记录一下这些任务的常见算法,持续更新。

1:中文分词

传统中文分词技术根据实现原理和特点,可以分为以下2个类别:

1、基于词典分词算法

也称字符串匹配分词算法。该算法是按照一定的策略将待匹配的字符串和一个已建立好的“充分大的”词典中的词进行匹配,若找到某个词条,则说明匹配成功,识别了该词。常见的基于词典的分词算法分为以下几种:正向最大匹配法、逆向最大匹配法双向匹配分词法等。

正向最大匹配从最左字符开始,如果匹配就向右增加一个字符,继续匹配,如果未匹配,则切分该词。而逆向最大匹配从最右边开始选取最大长度的子串,如果未匹配,去掉最左边单词,继续匹配。

例子(正序最大匹配):’我一个人吃饭’
round 1
我 =》词典存在,匹配成功,增加一个字符
我一 =》 不存在, 返回我
一 =》存在,增加字符
一个 =》 存在,增加字符
一个人 =》 存在
一个人吃 =》 不存在,返回一个人
吃 =》 存在,增加
吃饭 =》 存在

分词结果:我/一个人/吃饭
例子(逆序最大匹配):’我一个人吃饭’
  反向最大匹配方式,最大长度为5
round 1
个人吃饭
人吃饭
吃饭 ====》得到一个词– 吃饭
round 2
我一个人
一个人
个人 ====》得到一个词– 个人
round 3
我一
一 ====》得到一个词– 一
round 4
我 ====》得到一个词– 我
最后反向最大匹配的结果是:
/我/一/个人/吃饭/

https://zhuanlan.zhihu.com/p/103392455对于最大匹配算法有比较好的解释,下面的代码用python实现了前向最大匹配和逆向最大匹配:

def bmmseg(sen,max,strs):
    '''
    sen: 待切分句子
    max: 最大切分长度
    strs: 词典列表
    '''
    maxs=max
    returnlist=[]
    while len(sen)>0:
        while max>0:
            if(max==1):
                returnlist.append(sen[-1:])
                sen=sen[:-1]
                max=maxs
                break
            else:
                word=sen[-max:]
                if(word in strs):
                    returnlist.append(word)
                    sen=sen[:-max]
                    max=maxs
                    break
                else:
                    max-=1
    return returnlist

def fmmseg(sen,max,strs):
    '''
    sen: 待切分句子
    max: 最大切分长度
    strs: 词典列表
    '''
    maxs=max
    returnlist=[]
    while len(sen)>0:
        while max>0:
            if(max==1):
                returnlist.append(sen[:1])
                sen=sen[1:]
                max=maxs
                break
            else:
                word=sen[:max]
                if(word in strs):
                    returnlist.append(word)
                    sen=sen[max:]
                    max=maxs
                    break
                else:
                    max-=1
    return returnlist

此外,基于前向和逆向最大匹配,我们可以实现双向最大匹配算法。双向最大匹配算法的难点主要在于如何利用前向结果和逆向结果,有的算法使用了这样的规则:

双向最大匹配算法:

一般是综合考虑了正向和逆向最大匹配的结果,加入了一些启发式的规则来对分词结果进行进一步消歧的。

启发式规则:

1.如果正反向分词结果词数不同,则取分词数量较少的那个。

2.如果分词结果词数相同

    a.分词结果相同,就说明没有歧义,可返回任意一个。

    b.分词结果不同,返回其中单字较少的那个。

https://blog.csdn.net/u012684062/article/details/78444323这篇博客介绍了一个基于双向最大匹配和N-gram的分词算法。

N-gram的作用主要是根据语料库的统计数据得到分词结果之间的条件概率,然后选择联合概率最大的分词结果,比如说『thenonprofit』可以被切分为the non profit和then on profit(引用自https://medium.com/@phylypo/nlp-text-segmentation-with-ngram-b5506dbb514c):

<s> the 258483382
the non 739031
non profit  218392
<s> then 11926082
then on 1263045
on profit   105801
P(the non profit) = P(the|<s>) * P(non|the) * P(profit|non)
                  = P(<s> the) * P(the non) * P(non profit)
                  = 258483382/t* 739031/t   * 218392/t = 3.88E-17
P(then on profit) = P(then|<s>) * P(on|then) * P(profit|on)
                  = P(<s> then) * P(then on) * P(on profit)
                  = 11926082/t* 1263045/t   * 105801/t = 1.48E-18

基于词典的分词算法是应用最广泛、分词速度最快的。很长一段时间内研究者都在对基于字符串匹配方法进行优化,比如最大长度设定、字符串存储和查找方式以及对于词表的组织结构,比如采用TRIE索引树、哈希索引等。

jieba分词里面的全模式就和最大前缀匹配方法类似,从此表里面选出最长的前缀:

def __cut_all(self, sentence):
        '''
        我来到北京清华大学
        {0: [0], 1: [1, 2], 2: [2], 3: [3, 4], 4: [4], 5: [5, 6, 8], 6: [6, 7], 7: [7, 8], 8: [8]}
        结果:我/ 来到/ 北京/ 清华/ 清华大学/ 华大/ 大学
        算法:
            完全根据dag产出,感觉和最大前缀匹配很类似,完全依赖于词典
        '''
        dag = self.get_DAG(sentence)
        old_j = -1
        eng_scan = 0
        eng_buf = u''
        for k, L in iteritems(dag):
            if eng_scan == 1 and not re_eng.match(sentence[k]):
                eng_scan = 0
                yield eng_buf
            if len(L) == 1 and k > old_j:
                word = sentence[k:L[0] + 1]
                if re_eng.match(word):
                    if eng_scan == 0:
                        eng_scan = 1
                        eng_buf = word
                    else:
                        eng_buf += word
                if eng_scan == 0:
                    yield word
                old_j = L[0]
            else:
                for j in L:
                    #去除当前词
                    if j > k:
                        yield sentence[k:j + 1]
                        old_j = j
        if eng_scan == 1:
            yield eng_buf

2、基于统计的机器学习算法

这类目前常用的是算法是HMM、CRF、SVM、深度学习等算法,比如stanford、Hanlp分词工具是基于CRF算法。以CRF为例,基本思路是对汉字进行标注训练,不仅考虑了词语出现的频率,还考虑上下文,具备较好的学习能力,因此其对歧义词和未登录词的识别都具有良好的效果。

参考:

jieba分词:jieba分词(动态规划)jieba分词详解

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值