之前在其他博客文章有提到如何对英文进行分词,也说后续会增加解释我们中文是如何分词的,我们都知道英文或者其他国家或者地区一些语言文字是词与词之间有空格(分隔符),这样子分词处理起来其实是要相对容易很多,但是像中文处理起来就没有那么容易,因为中文字与字之间,词与词之间都是紧密连接在一起的,所以第一件事需要处理的就是如何确认词。中文文章的最小组成单位是字,但是独立的字并不能很好地传达想要表达整体的意思或者说欠缺表达能力,所以一篇成文的文章依旧是以词为基本单位来形成有意义的篇章,所以词是最小并且能独立活动的语言成分。这也就说明在处理中文文本的时候,首先将句子转化为特定的词语结构(so-called 单词),这就是中文分词的重点。在这篇博客中,主要介绍常用的基于规则的中文分词技术有哪几种。

前方高能:因代码输出结果我都打印出来了,篇幅占了不少,但是为了说明问题,没办法,还请各位看官和看友见谅。???

规则分词

规则分词(Rule-based Tokenization)是通过设立词典并不断地对词典进行维护以确保分词准确性的分词技术。基于规则分词是一种匹配式的分词技术,要进行分词的时候通过在词典中寻找相应的匹配,找到则进行切分,否则则不切分。传统的规则式分词主要有三种:正向最大匹配法(Maximum Match Method)、逆向最大匹配法(Reversed Maximum Match Method)以及双向最大匹配(Bi-direction Matching Method)。

1.1 正向最大匹配法

正向最大匹配法基本思想是假定设定好的词典中的最长词(也就是最长的词语)含有 个汉字字符,那么则用被处理中文文本中当前字符串字段的前 i 个字作为匹配字段去跟词典的这 i 个进行匹配查找(如处理的当前中文字符串中要查找“学海无涯”这四个字组成的词语,我们就在词典中去寻找相匹配的四个字并作为基本切分单位输出),假如词典中含有这么一个 i 字词语则匹配成功,切分该词,反之则匹配失败,那么这个时候就会去掉需要被匹配字段中的最后一个汉字字符,并对剩下的字符串进行匹配处理(继续把去掉最后一个字符的字符串跟词典继续匹配),循环往复直到所有字段成功匹配,循环终止的条件是切分出最后一个词或者剩余匹配的字符串的长度为零。下面流程图是正向最大匹配算法的描述过程(根据个人理解画出,如果有哪里不对的,还请指出!)。

下面是Python代码实现的简单的正向最大匹配算法:

 1 class MaximumMatching(object):
 2
 3     def __init__(self):
 4         self.window_size = 6   # 定义词典中的最长单词的长度
 5
 6
 7     def tokenize(self, text):
 8         tokens = []            # 定义一个空列表来保存切分的结果
 9         index = 0              # 切分
10         text_length = len(text)
11
12         # 定义需要被维护的词典,其中词典最长词的长度为6
13         maintained_dic = ['研究', '研究生', '自然', '自然语言', '语言','自然语言处理', '处理', '是', '一个', '不错', '的', '科研', '方向']
14
15         while text_length > index:    # 循环结束判定条件
16 #             print(text_length)
17             print('index 4: ', index)
18             for size in range(self.window_size + index, index, -1):   # 根据窗口大小循环,直到找到符合的进行下一次循环
19                 print('index 1: ', index)
20                 print('window size: ', size)
21                 piece = text[index: size]      # 被匹配字段
22                 if piece in maintained_dic:    # 如果需要被匹配的字段在词典中的话匹配成功,新的index为新的匹配字段的起始位置
23                     index = size - 1
24                     print('index 2: ', index)
25                     break
26             index += 1
27             print('index 3', index, '\n')
28             tokens.append(piece)          # 将匹配到的字段保存起来
29         return tokens
30
31 if __name__ == '__main__':
32     text = '研究生研究自然语言处理是一个不错的研究方向'
33     tokenizer = MaximumMatching()
34     print(tokenizer.tokenize(text))

输出结果是(这里为了之后说明该算法的存在的一个短板,将循环的每一步都打印出来以便观察,输出结果可能有点长,为了说明问题所在,造成不便还请见谅):

index 4:  0
index 1:  0
window size:  6
index 1:  0
window size:  5
index 1:  0
window size:  4
index 1:  0
window size:  3
index 2:  2
index 3 3 index 4:  3
index 1:  3
window size:  9
index 1:  3
window size:  8
index 1:  3
window size:  7
index 1:  3
window size:  6
index 1:  3
window size:  5
index 2:  4
index 3 5 index 4:  5
index 1:  5
window size:  11
index 2:  10
index 3 11 index 4:  11
index 1:  11
window size:  17
index 1:  11
window size:  16
index 1:  11
window size:  15
index 1:  11
window size:  14
index 1:  11
window size:  13
index 1:  11
window size:  12
index 2:  11
index 3 12 index 4:  12
index 1:  12
window size:  18
index 1:  12
window size:  17
index 1:  12
window size:  16
index 1:  12
window size:  15
index 1:  12
window size:  14
index 2:  13
index 3 14 index 4:  14
index 1:  14
window size:  20
index 1:  14
window size:  19
index 1:  14
window size:  18
index 1:  14
window size:  17
index 1:  14
window size:  16
index 2:  15
index 3 16 index 4:  16
index 1:  16
window size:  22
index 1:  16
window size:  21
index 1:  16
window size:  20
index 1:  16
window size:  19
index 1:  16
window size:  18
index 1:  16
window size:  17
index 2:  16
index 3 17 index 4:  17
index 1:  17
window size:  23
index 1:  17
window size:  22
index 1:  17
window size:  21
index 1:  17
window size:  20
index 1:  17
window size:  19
index 2:  18
index 3 19 index 4:  19
index 1:  19
window size:  25
index 2:  24
index 3 25 ['研究生', '研究', '自然语言处理', '是', '一个', '不错', '的', '研究', '方向']

通过上述输出结果的最后一行,我们可以看到通过正向最大匹配算法得到的切分结果还是很不错的,但是这并不意味着实际运用都会十分精准,这里主要有三个问题需要注意:

  1. 不断维护词典是非常困难的,新词总是层出不穷,人工维护是费时费力的,并不能保证词典能很好地覆盖到所有可能出现的词,特别是现在信息爆炸的时代每天新词出现的速度更不是人工维护词典能瞬间解决的,这对于解决实际问题就会造成一定程度的困扰;
  2. 直观地观察上述输出结果,其实有一个问题也很明显:执行效率并不好。可以看出在执行算法的时候,程序为了能找到一个合适的窗口,会循环往复的进行下去直到找到一个合适的。假设词典非常的大,初始窗口也相对大的情况下,那么匹配词段寻找的时间和循环的次数也会相应的增加,执行效率也就变得非常低下;
  3. 无法很好的解决歧义问题。举个例子,假设现在有一个最长词长度为5的词典,词典中有“南京市长”和“长江大桥”两个词语,那么“南京市长江大桥”通过上述正向最大匹配算法进行切分,我们首先通过对前五个字符进行匹配,发现没有符合合适的,那么此时就会去掉最后一个汉字,变成前面四个汉字进行匹配,发现匹配到了“南京市长”,然后我们用剩下的“江大桥”继续匹配,可能得到的结果是“江”和“大桥”这两个词语,通过这个例子可以看出,这样子的匹配结果并不是我们想要的,这个跟最初提到的第一个问题也有相关,维护词典的完整性是一件困难的事,这涉及到句子切分理解歧义的问题。

1.2 逆向最大匹配算法

逆向最大匹配算法的实现过程基本跟正向最大匹配算法无差,唯一不同的点是分词的切分是从后往前,跟正向最大匹配方法刚好相反。也就是说逆向是从字符串的最后面开始扫描,每次选取最末端的 i 个汉字字符作为匹配词段,若匹配成功则进行下一字符串的匹配,否则则移除该匹配词段的最前面一个汉字,继续匹配。需要注意的是,分词词典为逆向词典,即每个词条都以逆序的方式存放。当然这个也不一定非得这么处理,因为我们是逆向匹配,所以得到的结果是逆向的,只需要在最后反过来即可。

 1 class ReversedMaximumMatching(object):
 2
 3     def __init__(self):
 4         self.window_size = 6
 5
 6     def tokenize(self, text):
 7         tokens = []
 8         index = len(text)
 9
10         maintained_dic = ['研究', '研究生', '自然', '自然语言', '语言','自然语言处理', '处理', '是', '一个', '不错', '的', '科研', '方向']
11
12         while index > 0:
13             print('Index 1: ', index)
14             for size in range(index - self.window_size, index):
15                 print('Window Check point: ', size)
16                 w_piece = text[size: index]
17                 print('Checked Words', w_piece)
18                 if w_piece in maintained_dic:
19                     index = size + 1
20                     print('Index 2: ', index)
21                     break
22             index -= 1
23             print('Index 3: ', index, '\n')
24             tokens.append(w_piece)
25         print(tokens)
26         tokens.reverse()
27
28         return tokens
29
30
31 if __name__ == '__main__':
32
33     text = '研究生研究自然语言处理是一个不错的研究方向'
34     tokenizer = ReversedMaximumMatching()
35     print(tokenizer.tokenize(text))

上述代码的输出结果为:

Index 1:  21
Window Check point:  15
Checked Words 错的研究方向
Window Check point:  16
Checked Words 的研究方向
Window Check point:  17
Checked Words 研究方向
Window Check point:  18
Checked Words 究方向
Window Check point:  19
Checked Words 方向
Index 2:  20
Index 3:  19 Index 1:  19
Window Check point:  13
Checked Words 个不错的研究
Window Check point:  14
Checked Words 不错的研究
Window Check point:  15
Checked Words 错的研究
Window Check point:  16
Checked Words 的研究
Window Check point:  17
Checked Words 研究
Index 2:  18
Index 3:  17 Index 1:  17
Window Check point:  11
Checked Words 是一个不错的
Window Check point:  12
Checked Words 一个不错的
Window Check point:  13
Checked Words 个不错的
Window Check point:  14
Checked Words 不错的
Window Check point:  15
Checked Words 错的
Window Check point:  16
Checked Words 的
Index 2:  17
Index 3:  16 Index 1:  16
Window Check point:  10
Checked Words 理是一个不错
Window Check point:  11
Checked Words 是一个不错
Window Check point:  12
Checked Words 一个不错
Window Check point:  13
Checked Words 个不错
Window Check point:  14
Checked Words 不错
Index 2:  15
Index 3:  14 Index 1:  14
Window Check point:  8
Checked Words 言处理是一个
Window Check point:  9
Checked Words 处理是一个
Window Check point:  10
Checked Words 理是一个
Window Check point:  11
Checked Words 是一个
Window Check point:  12
Checked Words 一个
Index 2:  13
Index 3:  12 Index 1:  12
Window Check point:  6
Checked Words 然语言处理是
Window Check point:  7
Checked Words 语言处理是
Window Check point:  8
Checked Words 言处理是
Window Check point:  9
Checked Words 处理是
Window Check point:  10
Checked Words 理是
Window Check point:  11
Checked Words 是
Index 2:  12
Index 3:  11 Index 1:  11
Window Check point:  5
Checked Words 自然语言处理
Index 2:  6
Index 3:  5 Index 1:  5
Window Check point:  -1
Checked Words
Window Check point:  0
Checked Words 研究生研究
Window Check point:  1
Checked Words 究生研究
Window Check point:  2
Checked Words 生研究
Window Check point:  3
Checked Words 研究
Index 2:  4
Index 3:  3 Index 1:  3
Window Check point:  -3
Checked Words
Window Check point:  -2
Checked Words
Window Check point:  -1
Checked Words
Window Check point:  0
Checked Words 研究生
Index 2:  1
Index 3:  0 ['方向', '研究', '的', '不错', '一个', '是', '自然语言处理', '研究', '研究生']
['研究生', '研究', '自然语言处理', '是', '一个', '不错', '的', '研究', '方向']

倒数第二行输出结果是逆向匹配得到的结果,此时我们仅需对其reverse一下即可得到最终需要的结果。除此之外,通过观察上述的结果,亦可发现一个在正向最大匹配算法中同样遇到的问题,那就是程序的执行效率并不高,因为算法需要不断的去检测匹配的字段,这个在需要维护的词典是非常庞大的情况下是相当耗时间和耗资源的。

1.3 双向最大匹配算法

双向最大匹配算法其实是在正向最大匹配和逆向最大匹配两个算法的基础上延伸出来的,基本的思想很简单,主要可以分为如下两大步骤:

  1. 当正向和反向的分词结果的词语数目是不一样的,那么这个时候就选取分词数量较少的那组分词结果;
  2. 倘若分词结果词语数量是一样的,又可以分为两种子情况去考虑:
    • 分词结果完全一样,那么就不具备任何歧义,即表示正向反向的分词结果皆可得到满足;
    • 如果不一样,那么就选取分词结果中单个汉字数目较少的那一组。

下面Python代码的实现的前面两部分基本沿用上面两个算法的代码实现(为了保持完整性,就全部放出来了),第三部分是结合上述思想写出来,测了几个用例基本都没问题,如果有问题,欢迎指出:

 1 import operator
 2
 3
 4 class BiDirectionMatching(object):
 5
 6     def __init__(self):
 7         self.window_size = 6
 8         self.dic = ['研究', '研究生', '生命', '命', '的', '起源']
 9
10
11     def mm_tokenize(self, text):
12         tokens = []            # 定义一个空列表来保存切分的结果
13         index = 0              # 切分
14         text_length = len(text)
15
16         while text_length > index:    # 循环结束判定条件
17             for size in range(self.window_size + index, index, -1):   # 根据窗口大小循环,直到找到符合的进行下一次循环
18                 piece = text[index: size]      # 被匹配字段
19                 if piece in self.dic:    # 如果需要被匹配的字段在词典中的话匹配成功,新的index为新的匹配字段的起始位置
20                     index = size - 1
21                     break
22             index += 1
23             tokens.append(piece)          # 将匹配到的字段保存起来
24
25         return tokens
26
27     def rmm_tokenize(self, text):
28         tokens = []
29         index = len(text)
30
31         while index > 0:
32             for size in range(index - self.window_size, index):
33                 w_piece = text[size: index]
34                 if w_piece in self.dic:
35                     index = size + 1
36                     break
37             index -= 1
38             tokens.append(w_piece)
39         tokens.reverse()
40
41         return tokens
42
43     def bmm_tokenize(self, text):
44         mm_tokens = self.mm_tokenize(text)
45         print('正向最大匹配分词结果:', mm_tokens)
46         rmm_tokens = self.rmm_tokenize(text)
47         print('逆向最大匹配分词结果:', rmm_tokens)
48
49         if len(mm_tokens) != len(rmm_tokens):
50             if len(mm_tokens) > len(rmm_tokens):
51                 return rmm_tokens
52             else:
53                 return mm_tokens
54         elif len(mm_tokens) == len(rmm_tokens):
55             if operator.eq(mm_tokens, rmm_tokens):
56                 return mm_tokens
57             else:
58                 mm_count, rmm_count = 0, 0
59                 for mm_tk in mm_tokens:
60                     if len(mm_tk) == 1:
61                         mm_count += 1
62                 for rmm_tk in rmm_tokens:
63                     if len(rmm_tk) == 1:
64                         rmm_count += 1
65                 if mm_count > rmm_count:
66                     return rmm_tokens
67                 else:
68                     return mm_tokens
69
70
71 if __name__ == '__main__':
72     text = '研究生命的起源'
73     tokenizer = BiDirectionMatching()
74     print('双向最大匹配得到的结果:', tokenizer.bmm_tokenize(text))   

输出结果为:

正向最大匹配分词结果: ['研究生', '命', '的', '起源']
逆向最大匹配分词结果: ['研究', '生命', '的', '起源']
双向最大匹配得到的结果: ['研究', '生命', '的', '起源']

通过上述结果可以看出,针对给出的例子切分词语的最终数目是一样,那么就得进行进一步比较,而又可以得知切分结果并不是一一相同的,那么就得比较分词结果中哪一组中的单个汉字字符数较少,并且返回较少单个汉字的那一组结果。

总体来说,规则分词一个核心的点就是需要有一个完整的词典以便尽可能完整的覆盖到可能会被用到的或者查询的词语,因为这牵扯到了能否很好的去进行分词,而这是一个比较庞大的工程。在下一篇博客中将会看看是如何运用统计学来进行中文分词的,主要会涉及到隐马尔可夫模型,并看看如何用维特比算法是如何在隐马尔可夫模型中起到作用的。

转载于:https://www.cnblogs.com/jielongAI/p/10362314.html

基于规则的中文分词 - NLP中文篇相关推荐

  1. python 中文分词_python中文分词,使用结巴分词对python进行分词(实例讲解)

    在采集 中文分词是中文文本处理的一个基础性工作,结巴分词利用进行中文分词. 其基本实现原理有三点: 1.基于Trie树结构实现高效的词图扫描,生成句子中汉字所有可能成词情况所构成的有向无环图(DAG) ...

  2. mysql 中文分词_MySQL 中文分词原理

    一,首先我们来了解一下其他几个知识点: 1. Mysql的索引意义? 索引是加快访问表内容的基本手段,尤其是在涉及多个表的关联查询里.当然,索引可以加快检索速度,但是它也同时降低了索引列的插入,删除和 ...

  3. 中文分词_中文分词及其应用

    一.中文分词原理 中文分词是指将一个汉字序列切分成一个一个单独的词.分词就是将连续的字序列按照一定的规范重新组合成词序列的过程.现有的分词方法可分为三大类,分别是基于字符串匹配的分词方法.基于理解的分 ...

  4. 中文分词_中文分词01

    中文分词简介 在语言理解中,词是最小的能够独立活动的有意义的语言成分.将词确定下来是理解自然语言的第一步,只有跨越了这一步,中文才能像英文那样过渡到短语划分.概念抽取以及主题分析,以至自然语言理解,最 ...

  5. 中文分词_中文分词最佳纪录刷新,两大模型分别解决中文分词及词性标注问题...

    伊瓢 发自 中关村 量子位 报道 | 公众号 QbitAI 中文分词的最佳效果又被刷新了. 在今年的ACL 2020上,来自创新工场大湾区人工智能研究院的两篇论文中的模型,刷新了这一领域的成绩. WM ...

  6. paddlenlp 任务清单 中文分词、中文纠错、文本相似度、情感分析、词性标注等一键

    PaddleNLP Taskflow https://github.com/PaddlePaddle/PaddleNLP/blob/develop/docs/model_zoo/taskflow.md ...

  7. 什么是中文分词及中文分词的应用

    什么是中文分词?     何为分词?中文分词与其他的分词又有什么不同呢?分词就是将连续的字序列按照一定的规范重新组合成词序列的过程.我们知道,在英文的行文中,单词之间是以空格作为自然分界符的,而中文只 ...

  8. 什么是中文分词以及中文分词的应用简介

    什么是中文分词?     何为分词?中文分词与其他的分词又有什么不同呢?分词就是将连续的字序列按照一定的规范重新组合成词序列的过程.我们知道,在英文的行文中,单词之间是以空格作为自然分界符的,而中文只 ...

  9. SnowNlp中文分词和中文关键词提取只能提取单个字不能提取分词的解决方法

    文章目录 问题描述 原因 解决方法 问题描述 # -*- coding: utf-8 -*- # 导入SnowNLP库 from snownlp import * import jieba impor ...

最新文章

  1. 使用内核定时器的second字符设备驱动及测试代码
  2. 【网址收藏】PowerShell因为在此系统中禁止执行脚本的解决方法
  3. CSS连载-控制背景与CSS精灵
  4. Python爬虫(五)
  5. 启动失败代码2_菲斯曼燃气壁挂炉故障代码大全及解决方法
  6. 三大特性学习目标 java 1614782356
  7. STM32 存在字节对齐指针变量的地址都必须是4的倍数
  8. JAVA遍历map元素
  9. canvas drawbitmap不出现_JS实现简单的画板(canvas),可在PC和移动端实现。
  10. 机器学习笔记——决策树之回归树
  11. 【ICLR 2018】模型集成的TRPO算法【附代码】
  12. 计算机原理及应用课程,课程描述
  13. yamdi 实现添加元数据的注入flv文件,实现Nginx搭建flv视频浏览器上点播拖拽
  14. 地学计算方法/地统计学(第三章区域化变量理论)
  15. 工程力学和计算机专业,工程力学本科专业介绍
  16. 从数据库导出数据到EXCEL换行的问题解决方法
  17. “四舍六入五成双规则” 与 C语言如何实现“四舍五入”
  18. 易语言程序转c语言,c语言实现简单的易语言
  19. 图解Linux中EXT4与EXT3的区别
  20. Moto Defy刷机卡M无法进入RSD状态解决方法

热门文章

  1. 2012意大利之行1:从深圳到罗马
  2. electron与jquery起冲突,使用jquery报错解决方法
  3. 所谓带芯片的TYPEC头解剖
  4. 《R语言数据挖掘》读书笔记:五、聚类分析
  5. 计算机专业答辩开场白,计算机专业论文答辩开场白范文
  6. MySQL期末考试题目 题目1:查询employee数据表中的第5—8行的数据;题目2:查询每个部门的员工人数以及平均工资;题目3:查询‘孙威’的基本信息,包括员工编号、所在部门名称、岗位等级以及基本
  7. android人脸识别——HowOld测测你的年龄和性别
  8. android 杂项-备忘
  9. 第十篇 -- Windows 下免费的GIF录制工具
  10. mysql中日期相减_Excel教程:Excel日期问题的小妙招