NLP入门(1)-词典分词方法及实战

分词是自然语言处理中最基本的任务之一,而词典分词是最简单、最常见的分词算法,仅需一部词典和一套查词典的规则即可。

利用词典分词,最主要的是定制合适的切分规则。规则主要有正向最长匹配、逆向最长匹配和双向最长匹配三种。本文主要介绍上述三种规则。

1、词典准备

既然是词典分词,那么我们首先需要找到一部字典,这里使用Hanlp提供的mini版本的中文词典:https://github.com/hankcs/HanLP/blob/master/data/dictionary/CoreNatureDictionary.mini.txt

下载下来之后保存在本地项目路径下,词典的内容如下:

可以看到第一列是我们需要的词,后面每两列代表一种词性以及词频。我们只需要得到所有的词即可:

with open('data/CoreNatureDictionary.mini.txt') as f:
    dict_set = set([s.split('\t')[0] for s in f.readlines()])

dict_set

得到输出如下:

好了,词典准备完毕,接下来介绍几种词典分词方法。

2、切分规则

常用的切分规则有正向最长匹配、逆向最长匹配和双向最长匹配,它们都基于完全切分过程,因此本节先介绍完全切分过程,再介绍三种匹配规则。

2.1 完全切分

完全切分顾名思义,就是找出一段文本中所有在字典中的单词。假设我们要分词的句子是“自然语言处理入门实战”,完全切分的代码如下:

#完全切分
def fully_segment(text,dic):
    seg_list = []
    for i in range(0,len(text)-1):
        for j in range(i+1,len(text)):
            word = text[i:j+1] # 注意这里是j+1
            if word in dic:
                seg_list.append(word)
    return seg_list

print(','.join(fully_segment('自然语言处理入门实战',dict_set)))

输出为:

但完全切分不是真正意义上的分词,我们需要完善一下规则,考虑到越长的单词表达的意义越丰富,于是定义单词越长优先级越高。具体来说,在以某个字符为起点逐次向前或向后查找的过程中,优先输出更长的单词。这种规则统称为最长匹配算法。包括正向最长匹配、逆向最长匹配和双向最长匹配。

接下来,我们就来介绍下面这几种匹配算法。

2.2 正向最长匹配

正向匹配的过程示意图如下所示:

上图中,我们只给出了部分的分词过程,不过可以看到,它是一个从前向后匹配的过程,通过代码进一步理解下:

def forward_segment(text,dic):
    seg_list = []
    i = 0
    while i < len(text):
        longest_word = text[I]
        for j in range(i+1,len(text)+1):
            word = text[i:j]
            if word in dic and len(word) > len(longest_word):
                longest_word = word
        seg_list.append(longest_word)
        i += len(longest_word)
    return seg_list

print(forward_segment("就读于中国人民大学",dict_set))

最终的分词结果如下:

可以看到,这样切分并不是我们想要的结果,因为中国人民大学切分为中国、人民和大学更加合理(或者当作一个专有名词,不进行切分),但在正向最长匹配中,因为中国人同样出现在了词典中,因此中国人便优先作为一个词输出了。

2.3 逆向最长匹配

接下来再看看逆向匹配的过程:

上图中,我们只给出了部分的分词过程,不过可以看到,它是一个从后向前匹配的过程,每次都认为从零位置到当前下标i位置的单词是最长的,然后基于词典不断往后移动j,直到切片对应的单词出现在词典中,通过代码进一步理解下:

def backward_segment(text,dic):
    seg_list = []
    i = len(text) - 1
    while i >= 0:
        longest_word = text[I]
        for j in range(0,i):
            word = text[j:i+1]
            if word in dic and len(word) > len(longest_word):
                longest_word = word
        seg_list.append(longest_word)
        i -= len(longest_word)
    seg_list.reverse()
    return seg_list

print(backward_segment("就读于中国人民大学",dict_set))

最终的切分结果如下:

可以看到切分结果符合我们的预期,但是从上面的流程图直观的就可以看到,逆向最长匹配的时间复杂度是明显高于正向匹配的,因为逆向匹配每次都需要从句子的最开始进行扫描。

2.4 双向最长匹配

对于同一个句子,正向最长匹配和逆向最长匹配带来的结果可能不同,那到底哪种更好呢?先看下面几个例子:

print(forward_segment("就读于中国人民大学",dict_set))
print(forward_segment("电视上的电影节目",dict_set))
print(forward_segment("项目的研究",dict_set))
print(forward_segment("角色本人将会参与配音",dict_set))
print(backward_segment("就读于中国人民大学",dict_set))
print(backward_segment("电视上的电影节目",dict_set))
print(backward_segment("项目的研究",dict_set))
print(backward_segment("角色本人将会参与配音",dict_set))

分词结果如下:

可以看到,有时候正向匹配的效果更好,有时候逆向匹配的效果更好,但似乎逆向匹配的匹配成功次数更多。那能不能从两种结果中挑选一种比较好的呢?于是我们又有了双向最长匹配。规则如下:

1)同时执行正向和逆向最长匹配,若两者的词数不同,则返回词数更少的那一个;
2)否则,返回两者中单字更少的那一种;
3)当单子数量也相同时,优先返回逆向最长匹配的结果。

双向匹配的代码如下:

def count_single_word(word_list):
    return sum(1 for word in word_list if len(word) == 1)

def bidiectional_segment(text,dic):
    f = forward_segment(text,dic)
    b = backward_segment(text,dic)
    if len(f) < len(b):
        return b
    elif len(f) > len(b):
        return f
    else:
        if count_single_word(f) < count_single_word(b):
            return f
        else:
            return b

print(bidiectional_segment("就读于中国人民大学",dict_set))
print(bidiectional_segment("电视上的电影节目",dict_set))
print(bidiectional_segment("项目的研究",dict_set))
print(bidiectional_segment("角色本人将会参与配音",dict_set))

最终的切分结果如下:

第三个句子还是没有获得正确的切分,因为在分词长度以及单字个数都一致的情况下,优先返回的是逆向匹配的结果。由此可见,基于词典匹配的规则分词方法,效果是比较差的。

好了,几种基于词典的规则分词方法就介绍道这里,下一篇我们将介绍如何对分词结果的精度进行评价,总不能一个个用肉眼去看吧,哈哈。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,692评论 6 501
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,482评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,995评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,223评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,245评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,208评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,091评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,929评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,346评论 1 311
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,570评论 2 333
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,739评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,437评论 5 344
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,037评论 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,677评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,833评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,760评论 2 369
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,647评论 2 354

推荐阅读更多精彩内容

  • 转载请注明:终小南 » 中文分词算法总结 什么是中文分词众所周知,英文是以 词为单位的,词和词之间是靠空格隔开,而...
    kirai阅读 9,826评论 3 24
  • 转自 进击的Coder 公众号 原理 中文分词,即 Chinese Word Segmentation,即将一个汉...
    Epiphron阅读 11,559评论 2 56
  • 一、关于分词 原则 颗粒度越大越好:用于进行语义分析的文本分词,要求分词结果的颗粒度越大,即单词的字数越多,所能表...
    小白一枚ha阅读 780评论 0 0
  • 常用概念: 自然语言处理(NLP) 数据挖掘 推荐算法 用户画像 知识图谱 信息检索 文本分类 常用技术: 词级别...
    御风之星阅读 9,176评论 1 25
  • 不死鸟——传说中的神鸟,能浴火重生,然而写它的她最终还是真的走了! 不知道从何时起,自己变得多愁善感起...
    从心开始_6484阅读 497评论 0 0