中文分词演进(查词典,hmm标注,无监督统计)新词发现

本文围绕中文分词和新词发现展开。中文分词有查词典和字标注两种思路,介绍了AC自动机、最大匹配法、最大概率组合等查词典方法,以及HMM、双向LSTM的seq2seq等字标注方法,还有基于语言模型的无监督分词。新词发现则阐述了判断成词的指标及新方法和更好的算法。

查词典和字标注

目前中文分词主要有两种思路:查词典和字标注。

  • 首先,查词典的方法有:机械的最大匹配法、最少词数法,以及基于有向无环图的最大概率组合,还有基于语言模型的最大概率组合,等等。
    查词典的方法简单高效(得益于动态规划的思想),尤其是结合了语言模型的最大概率法,能够很好地解决歧义问题,但对于中文分词一大难度——未登录词(中文分词有两大难度:歧义和未登录词),则无法解决;
  • 为此,人们也提出了基于字标注[BIOS, BME]的思路,所谓字标注,就是通过几个标记(比如4标注的是:single,单字成词;begin,多字词的开头;middle,三字以上词语的中间部分;end,多字词的结尾),把句子的正确分词法表示出来。这是一个序列(输入句子)到序列(标记序列)的过程,能够较好地解决未登录词的问题,但速度较慢,而且对于已经有了完备词典的场景下,字标注的分词效果可能也不如查词典方法。
    总之,各有优缺点(似乎是废话~),实际使用可能会结合两者,像结巴分词,用的是有向无环图的最大概率组合,而对于连续的单字,则使用字标注的HMM模型来识别。

1 中文分词

1.1 查词典优化之AC自动机

1.1.1 AC 自动机

本文首先要实现的是查词典方法的分词。1、给定一批词,查找给定句子中是不是含有这个词;2、如果有的话,怎么解决歧义性问题。
第一步,在计算机中称为“多模式匹配”。
这步看上去简单,但事实上要高效地实现并不容易。
一个完备的词典,少说也有十几万词语,如果一个个枚举查找,那计算量是吃不消的,事实上我们在查字典的时候,会首先看首字母,然后只在首字母相同的那一块找,然后又比较下一个字母,依此下去。
这需要两个条件:
1、一个做好特殊排序的词典;我们有所谓的前缀树(trie)
2、有效的查找技巧,我们有一些经典的算法,比如AC自动机(Aho and Corasick)。

对于AC自动机,就是一个使用了trie数据结构的高效多模式匹配算法。
我们也不用亲自实现它,因为Python已经有对应的库了:pyahocorasick。
因此,我们只需要关心怎么使用它就行了。
官方的教程已经很详细地介绍了pyahocorasick的基本使用方法了,这里也不赘述。(遗憾的是,虽然pyahocorasick已经同时支持python2和python3了,但是在python2中,它只支持bytes字符串不支持unicode字符串,而在python3中,则默认使用unicode编码,这对我们写程度会带来一点困惑,当然,不是本质性的。)
pyahocorasick构建AC自动机有一个很人性化的地方,它能够以“键-注释”这样成对的形式添加词汇(请留意dic.add_word(i, (i, log(j/total)))这一句),这样,我们可以在注释这里,添加我们想要的信息,比如频数、词性等,然后在查找的时候会一并返回。有了上述AC自动机,我们就能很方便地构建一个全模式分词,也就是把词典中有的词都扫描出来(其实这本来就是AC自动机的本职工作)。

构建一个基于AC自动机的分词系统,首先需要有一个文本词典,假设词典有两列,每一行是词和对应的频数,用空格分开。那么就可以构建一个AC自动机。

1.1.3 最大匹配法

最大匹配法是指从左到右逐渐匹配词库中的词语,匹配到最长的词语为止。
上面说的最大匹配法,准确来说是“正向最大匹配法”,类似地,还有“逆向最大匹配法”,顾名思义,是从右到左扫描句子进行最大匹配,效果一般比正向最大匹配要好些。如果用AC自动机来实现,唯一的办法就是对词典所有的词都反序存储,然后对输入的句子也反序,然后进行正向最大匹配了。

def max_match_cut(sentence):
    sentence = sentence.decode('utf-8')
    words = ['']
    for i in sentence:
        i = i.encode('utf-8')
        if dic.match(words[-1] + i):
            words[-1] += i
        else:
            words.append(i)
    return words

1.1.4 最大概率组合

基于最大概率组合的方法,是目前兼顾了速度和准确率的比较优秀的方法。它说的是:对于一个句子,如果切分为词语w1,w2,…,wn是最优的切分方案,那么应该使得下述概率最大:P(w1,w2,…,wn)

直接估计这概率是不容易的,一般用一些近似方案,比如
P(w1,w2,…,wn)≈P(w1)P(w2|w1)P(w3|w2)…P(wn|wn−1)
这里P(wk|wk−1)就称为语言模型,它已经初步地考虑了语义了。当然,普通分词工具是很难估计P(wk|wk−1)的,一般采用更加简单的近似方案。P(w1,w2,…,wn)≈P(w1)P(w2)P(w3)…P(wn)
放到图论来看,这就是有向无环图里边的最大概率路径了。

def max_proba_cut(sentence):
    paths = {
   
   0:([], 0)}
    end = 0
    for i,j in dic.iter(sentence):
        start,end = 1+i-len(j[0]), i+1
        if start not in paths:
            last = max([i for i in paths if i < start])
            paths[start] = (paths[last][0]+[sentence[last:start]], paths[last][1]-10)
        proba = paths[start][1]+j[1]
        if end not in paths or proba > paths[end][1]:
            paths[end] = (paths[start][0]+[j[0]], proba)
    if end < len(sentence):
        return paths[end][0] + [sentence[end:]]
    else:
        return paths[end][0]
def min_words_cut(sentence):
    paths = {
   
   0:([], 0)}
    end = 0
    for i
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值