题图:by Lucas Davies前端
分词,我想是大多数大前端开发人员,都不会接触到的一个概念。这个不影响咱们了解它,毕竟咱们要多方向发展。今天就来简单介绍一些分词,我尽可能用简介的语言来描述这个概念,而且最后再提供一个解决方案,但愿对你有帮助。java
分词简单来说就是把一句话,按照词义,切分红一个个单独的词。这么说可能没什么感受,先看看它适用的场景。分词是文本挖掘的基础,一般会用于天然语言处理、分词搜索、推荐等等领域。python
先理解一下分词的概念。nginx
分词就是将连续的字序列按照必定的规范从新组合成词序列的过程。在英文中,单词之间会以空格做为分割符,将词与词之间进行分割,可是对于中文,没有一个显式的分割符。git
正是由于缺少这种显式的分割符,致使咱们对中文中的词,进行分割的时候会出现不少的误差。github
中文分词有难度,不过也有成熟的解决方案。现有的分词算法,大概可分为三类:算法
1. 基于字符串匹配的分词算法小程序
这种分词方法,又叫机械分词算法,它会提早维护一个大的字典,而后将句子和字典中的词进行匹配,若匹配成功,则能够进行分词处理。api
固然,它实际上会更复杂一些,由于当字典足够大的时候,就又涉及到不一样的匹配算法,这里就不展开讲了。一般会基于 Trie 树结构,来实现高效的词图扫描。缓存
2. 基于理解的分词算法
这种分词方法,经过让计算机,模拟人对句子的理解,达到识别词组的效果。其基本思想是在分词的同事进行句法、语义的分析,利用句法和语义信息来处理歧义现象。
它一般会包含三部分:分词子系统、句法语义子系统、总控部分。在总控部分的协调下,分词子系统能够得到有关词、句子等的句法和语义信息,来对分词歧义进行判断,即它模拟了人对句子的理解过程。因为汉语语言知识的笼统、复杂性,难以将各类语言信息组织成机器可直接读取的形式,所以目前基于理解的分词系统还处在试验阶段。
3. 基于统计的分词算法
给出大量已经分词的文本,利用统计机器学习模型学习词语切分的规律(称为训练),从而实现对未知文本的切分。
随着大规模语料库的创建,统计机器学习方法的研究和发展,基于统计的中文分词方法渐渐成为了主流方法。
虽然分词的算法,讲解起来很简单,可是从现有的经验来看,几乎是不存在通用且效果很是好的分词系统。
每一个领域,都有其独特的词汇,这很难经过有限的训练数据,捕捉到全部的语言特征。例如:经过人民日报训练的分词系统,在网络玄幻小说上,分词的效果就不会好。
这是必然的,在分词系统中,没有银弹。
不一样的场景,对分词的要求也差别很大,一般能够从两个维度进行区分:分词速度、分词准确性。
例如分词搜索,对速度要求就高于准确性的要求。而一些问答系统中,则须要对文本实现较深的理解,要求准确性高于速度要求。
不一样的领域,不一样的使用场景,对分词的要求是不一样的,因此咱们不能片面的去理解分词的准确率。而且随着新词的增长,训练数据的变化,分词的准确率也是在波动的。这也是为何,如今吹嘘分词准确率的公司愈来愈少的缘由。
分词是能够解决实际问题的功能,通过这么长时间的反复迭代更新,市面上一家产生了一批有特点的分词系统。例如:IK、Jieba、Ansj、Hanlp、Stanford分词 等等。
有兴趣能够一个个了解,接下来就其中的一个开源库 Jieba,进行讲解。
jieba 是开源的,号称是 Python 中,最好的中文分词组件。而且是基于 MIT 的协议,使用起来无后顾之忧。
jieba 使用起来也很是的简单,几行代码就能够实现分词调用和词性标注,并且速度还不错。
它内部维护了一个词典,是根据人民日报分析得到,在超出词典以外的新词,会基于 HMM 模型进行识别。
它提供三种分词模式:精准模式、全模式、搜索模式。全模式是找到全部可能的词语,搜索模式是在精确模式的基础上对长词进行切分,提升分割率。
在分词的速度上,精确模式能达到 400KB/s,全模式下能达到 1.5MB/s。同时除了 Python 版本以外,还有不一样的人基于 Python 版的 jieba ,扩展出多种语言实现,包括:JavaScript、Java、Golang、R、PHP 等。
jieba 的代码对 Python 2/3 均兼容,在使用以前,须要经过命令 pip install jieba
或者 pip3 install jieba
进行安装。
具体 Api,就不展开讲了,有兴趣能够去查看 Github 上的文档(文末有地址)。
这里提供一个简单的代码示例,来感觉一下 jieba 的方便与强大。
# encoding=utf-8
import jieba
seg_list = jieba.cut("我来到北京清华大学", cut_all=True)
print("Full Mode: " + "/ ".join(seg_list)) # 全模式
seg_list = jieba.cut("我来到北京清华大学", cut_all=False)
print("Default Mode: " + "/ ".join(seg_list)) # 精确模式
seg_list = jieba.cut("他来到了网易杭研大厦") # 默认是精确模式
print(", ".join(seg_list))
seg_list = jieba.cut_for_search("小明硕士毕业于中国科学院计算所,后在日本京都大学深造") # 搜索引擎模式
print(", ".join(seg_list))
复制代码
输出的结果:
【全模式】: 我/ 来到/ 北京/ 清华/ 清华大学/ 华大/ 大学
【精确模式】: 我/ 来到/ 北京/ 清华大学
【新词识别】:他, 来到, 了, 网易, 杭研, 大厦 (此处,“杭研”并无在词典中,可是也被Viterbi算法识别出来了)
【搜索引擎模式】: 小明, 硕士, 毕业, 于, 中国, 科学, 学院, 科学院, 中国科学院, 计算, 计算所, 后, 在, 日本, 京都, 大学, 日本京都大学, 深造
复制代码
前面也提到,jieba 自身维护了一个词组的字典,若是自身需求上有专有名词须要拆分,还能够经过 jieba.Tokenizer(dictionary=DEFAULT_DICT)
自定义一个字典信息。
匹配的算法,提及来就复杂了,这里就简单介绍一下 jiaba 分词匹配的原理。
首先,jieba 分词已经自带了一个 dict.txt 的词典,里面有 2w 多个词条,包括出现的次数和词性,这是做者本身基于人民日报为主的资料,训练的出来的。
jieba 会先将这个词典中的数据,放到一个 Trie 树中,Trie 树是有名的前缀树,当一个词语的前面几个字同样的时候,就标识他们具备相同的前缀,就可使用 Trie 数来存储,具备查找速度快的优点。
其次,在须要对句子进行分词的时候,再根据前面生成的 Trie 数,生成有向无环图(DAG),这一步的意义在于,消除分词中的歧义,提升切分准确度,找出这句话中,全部可能的词。
到这一步,基本上就完成了,全部字典中记录的词,进行分词的过程。
可是若是你把 dict.txt 这个字典删除,jieba 依然能够进行分词,只是拆分出来的词,大部分的长度为 2。这是由于,对于未在字典中收录的词,基于隐马尔科夫模型(HMM)来预测分词,使用的是 Viterbi 算法。
HMM 模型中,将中文词汇按照 BEMS 四个状态来标记, B 是开始 begin 位置, E 是 end, 是结束位置, M 是 middle, 是中间位置, S 是 singgle, 单独成词的位置, 没有前, 也没有后. 也就是说, 他采用了状态为(B,E,M,S)这四种状态来标记中文词语, 好比北京能够标注为 BE, 即 北/B 京/E, 表示北是开始位置, 京是结束位置, 中华民族能够标注为 BMME , 就是开始, 中间, 中间, 结束.
做者经过对大量语料的训练,获得了 finalseg 目录下的训练结果,有兴趣能够自行研究。
到这里基本上就清晰了,jieba 分词的过程主要有如下三步:
这就是 jieba 分词的执行过程。
jieba 发展到如今,已经支持众多的版本。Java 版并不是原做者开发,而是 hanban 参考原做者的分词原理,进行开发的。
不过 Java 版并无原版 Python 版本那么强大,作了部分阉割,例如关键词提取就没有实现。
有兴趣能够直接去看 Github : https://github.com/huaban/jieba-analysis/
1. 引入依赖(稳定版)
<dependency>
<groupId>com.huaban</groupId>
<artifactId>jieba-analysis</artifactId>
<version>1.0.2</version>
</dependency>
复制代码
2. 如何使用
@Test
public void testDemo() {
JiebaSegmenter segmenter = new JiebaSegmenter();
String[] sentences =
new String[] {"这是一个伸手不见五指的黑夜。我叫孙悟空,我爱北京,我爱Python和C++。", "我不喜欢日本和服。", "雷猴回归人间。",
"工信处女干事每个月通过下属科室都要亲口交代24口交换机等技术性器件的安装工做", "结果婚的和还没有结过婚的"};
for (String sentence : sentences) {
System.out.println(segmenter.process(sentence, SegMode.INDEX).toString());
}
}
复制代码
3. 性能评估
做者在测试机上进行测试,配置为:
Processor 2 Intel(R) Pentium(R) CPU G620 @ 2.60GHz
Memory:8GB
复制代码
测试结果还算理想,单线程,对测试文本逐行分词,并循环调用上万次的效率分析。
循环调用一万次
第一次测试结果:
time elapsed:12373, rate:2486.986533kb/s, words:917319.94/s
第二次测试结果:
time elapsed:12284, rate:2505.005241kb/s, words:923966.10/s
第三次测试结果:
time elapsed:12336, rate:2494.445880kb/s, words:920071.30/s
循环调用2万次
第一次测试结果:
time elapsed:22237, rate:2767.593144kb/s, words:1020821.12/s
第二次测试结果:
time elapsed:22435, rate:2743.167762kb/s, words:1011811.87/s
第三次测试结果:
time elapsed:22102, rate:2784.497726kb/s, words:1027056.34/s
统计结果:词典加载时间1.8s左右,分词效率每秒2Mb多,近100万词。
2 Processor Intel(R) Core(TM) i3-2100 CPU @ 3.10GHz
12G 测试效果
time elapsed:19597, rate:3140.428063kb/s, words:1158340.52/s
time elapsed:20122, rate:3058.491639kb/s, words:1128118.44/s
复制代码
jieba(Java)版本,自己也是自带词典的,因此在 Android 下引入,会增大 Apk 的体积,这没有什么很好的规避方法。并且由于设备的配置,还会影响到分词的效率。
不过若是非要使用在 Android 设备上,例如对搜索词进行一个预处理,也是能够的。
jieba(java) 使用 maven 管理,因此须要 Gradle 简单配置一下,让其支持。
1. 配置 build.gradle
repositories {
google()
jcenter()
mavenCentral()
}
复制代码
2. 引入依赖
api 'com.huaban:jieba-analysis:1.0.2'
复制代码
引入以后,使用细节就没什么好说的了,和 Java 版本无差异。
参考:
https://github.com/fxsjy/jieba
https://github.com/huaban/jieba-analysis/
https://blog.csdn.net/John_xyz/article/details/54645527
http://www.infoq.com/cn/articles/nlp-word-segmentation
公众号后台回复成长『成长』,将会获得我准备的学习资料,也能回复『加群』,一块儿学习进步;你还能回复『提问』,向我发起提问。
推荐阅读:
写做是核心竞争力 | Google 工程师解密“猜画小歌” | 图解:HTTP 范围请求 | Android P 适配经验 | 技术创业选择清单 | HTTP传输编码 | 什么正在消耗你? | HTTP 内容编码 | 图解 HTTP 缓存 | 聊聊 HTTP 的 Cookie | 辅助模式实战 | Accessibility 辅助模式 | 小程序 Flex 布局 | 好的 PR 让你更靠谱 | 密码管理之道