一、缘由
接触自然语言处理(NLP)有段时间,理论知识有些了解,挺想动手写些东西,想想开源界关于NLP的东西肯定不少,其中分词是NLP的基础,遂在网上找了些资源,其中结巴分词是国内程序员用python开发的一个中文分词模块, 源码已托管在github: 源码地址 ,代码用python实现,源码中也有注释,但一些细节并没有相应文档,因此这里打算对源码进行分析,一来把知识分享,让更多的童鞋更快的对源码有个认识,二来使自己对分词这一块有个更深入的理解。
二、中文分词介绍
- 为什么中文分词
- 词是最小的能够独立活动的有意义的语言成分
- 汉语是以字位单位,不像西方语言,词与词之间没有空格之类的标志指示词的边界
- 分词问题为中文文本处理的基础性工作,分词的好坏对后面的中文信息处理其关键作用
- 中文分词的难点
- 分词规范,词的定义还不明确 (《统计自然语言处理》宗成庆)
- 歧义切分问题,交集型切分问题,多义组合型切分歧义等
结婚的和尚未结婚的 =>
结婚/的/和/尚未/结婚/的
结婚/的/和尚/未/结婚/的 - 未登录词问题
有两种解释:一是已有的词表中没有收录的词,二是已有的训练语料中未曾出现过的词,第二种含义中未登录词又称OOV(Out of Vocabulary)。对于大规模真实文本来说,未登录词对于分词的精度的影响远超歧义切分。一些网络新词,自造词一般都属于这些词。
- 汉语分词方法
- 基于字典、词库匹配的分词方法(基于规则)
基于字符串匹配分词,机械分词算法。将待分的字符串与一个充分大的机器词典中的词条进行匹配。分为正向匹配和逆向匹配;最大长度匹配和最小长度匹配;单纯分词和分词与标注过程相结合的一体化方法。所以常用的有:正向最大匹配,逆向最大匹配,最少切分法。实际应用中,将机械分词作为初分手段,利用语言信息提高切分准确率。优先识别具有明显特征的词,以这些词为断点,将原字符串分为较小字符串再机械匹配,以减少匹配错误率,或将分词与词类标注结合。 - 基于词频度统计的分词方法(基于统计)
相邻的字同时出现的次数越多,越有可能构成一个词语,对语料中的字组频度进行统计,基于词的频度统计的分词方法是一种全切分方法。jieba是基于统计的分词方法,jieba分词采用了动态规划查找最大概率路径, 找出基于词频的最大切分组合,对于未登录词,采用了基于汉字成词能力的HMM模型,使用了Viterbi算法。 - 基于知识理解的分词方法。
该方法主要基于句法、语法分析,并结合语义分析,通过对上下文内容所提供信息的分析对词进行定界,它通常包括三个部分:分词子系统、句法语义子系统、总控部分。在总控部分的协调下,分词子系统可以获得有关词、句子等的句法和语义信息来对分词歧义进行判断。这类方法试图让机器具有人类的理解能力,需要使用大量的语言知识和信息。由于汉语语言知识的笼统、复杂性,难以将各种语言信息组织成机器可直接读取的形式。因此目前基于知识的分词系统还处在试验阶段。
- 基于字典、词库匹配的分词方法(基于规则)
三、jieba中文分词介绍
- 特点
- 支持三种分词模式:
a. 精确模式,试图将句子最精确地切开,适合文本分析;
b. 全模式,把句子中所有的可以成词的词语都扫描出来, 速度非常快,但是不能解决歧义;
c. 搜索引擎模式,在精确模式的基础上,对长词再次切分,提高召回率,适合用于搜索引擎分词。 - 支持繁体分词
- 支持自定义词典
- MIT 授权协议
- 支持三种分词模式:
- 安装
全自动安装:easy_install jieba 或者 pip install jieba 或者把源代码下载下来直接安装,解压后直接运行setup脚本:python setup.py install - jieba的主要功能
详见这里 jieba主要功能其中主要包括:分词;添加自定义词典(开发者可以指定自己自定义的词典,以便包含 jieba 词库里没有的词);关键词提取;词性标注;并行分词;ChineseAnalyzer for Whoosh 搜索引擎等 - jieba分词的算法策略
- 基于前缀词典实现高效的词图扫描,生成句子中汉字所有可能成词情况所构成的有向无环图 (DAG)
- 采用了动态规划查找最大概率路径, 找出基于词频的最大切分组合
- 对于未登录词,采用了基于汉字成词能力的 HMM 模型,使用了 Viterbi 算法
- jieba源码组织形式
下面是jieba源码的树结构
jieba
|-- Changelog
|-- extra_dict
| |-- dict.txt.big
| |-- dict.txt.small
| |-- idf.txt.big
| `-- stop_words.txt
|-- jieba
| |-- analyse
| | |-- analyzer.py
| | |-- idf.txt
| | |-- __init__.py
| | |-- textrank.py
| | `-- tfidf.py
| |-- _compat.py
| |-- dict.txt
| |-- finalseg
| | |-- __init__.py
| | |-- prob_emit.p
| | |-- prob_emit.py
| | |-- prob_start.p
| | |-- prob_start.py
| | |-- prob_trans.p
| | `-- prob_trans.py
| |-- __init__.py
| |-- __main__.py
| `-- posseg
| |-- char_state_tab.p
| |-- char_state_tab.py
| |-- __init__.py
| |-- prob_emit.p
| |-- prob_emit.py
| |-- prob_start.p
| |-- prob_start.py
| |-- prob_trans.p
| |-- prob_trans.py
| `-- viterbi.py
|-- LICENSE
|-- setup.py
`-- test
|-- *.py
|-- parallel
| |-- extract_tags.py
| `-- test*.py
`-- userdict.txt
代码行数统计(没有统计test文件夹下的代码):
256 ./posseg/prob_start.py
5307 ./posseg/prob_trans.py
304 ./posseg/__init__.py
89372 ./posseg/prob_emit.py
61087 ./posseg/char_state_tab.py
53 ./posseg/viterbi.py
578 ./__init__.py
4 ./finalseg/prob_start.py
4 ./finalseg/prob_trans.py
107 ./finalseg/__init__.py
35226 ./finalseg/prob_emit.py
31 ./_compat.py
50 ./__main__.py
111 ./analyse/tfidf.py
37 ./analyse/analyzer.py
104 ./analyse/textrank.py
18 ./analyse/__init__.py
192649 总用量
其中prob*.py的文件是作者事先训练好的模型参数(λ=(A,B,π)),如状态转移概率、发射概率等。真正的代码数也就:304+53+578+107+31+50+111+37+104+18=1393行(不包括test文件中的代码),当然作者的代码写的比较简洁。
- jieba分词模型的参数数据(λ=(A,B,π))是如何生成的?
即文件finalseg/prob_*.py,中初始化概率,状态转移概率,发射概率怎么算出来的?
来源主要有两个: 一个是网上能下载到的1998人民日报的切分语料还有一个msr的切分语料; 另一个是作者自己收集的一些txt小说,用ictclas把他们切分(可能有一定误差)。 然后用python脚本统计词频 具体详情。
要统计的主要有三个概率表:
1) 位置转换概率(状态转移概率),即B(开头),M(中间),E(结尾),S(独立成词)四种状态的转移概率;
2) 位置到单字的发射概率,比如P(“和”|M)表示一个词的中间出现”和”这个字的概率;
3) 词语以某种状态开头的概率,其实只有两种,要么是B,要么是S。
接下来就具体介绍jieba源码了,具体源码见github注释。
参考:
- 《 统计自然语言处理》 宗成庆
- 《统计学习方法》 李航
- http://www.cnblogs.com/flish/archive/2011/08/08/2131031.html