首页 百问百答 帖子详情
jieba分词的原理
收藏
快速回复
百问百答 问答学习资料 683 0
jieba分词的原理
收藏
快速回复
百问百答 问答学习资料 683 0

jieba分词系统介绍:

涉及算法:

基于前缀词典实现词图扫描,生成句子中汉字所有可能成词情况所构成的有向无环图(DAG),采用动态规划查找最大概率路径,找出基于词频的最大切分组合; 对于未登录词,采用了基于汉字成词能力的 HMM模型,采用Viterbi算法进行计算; 基于Viterbi算法的词性标注; 分别基于tfidf和textrank模型抽取关键词;

jieba分词系统,主要实现三个模块

分词 词性标注 关键词抽取

其中,分词有三种模式,默认是精确模式

精确模式,试图将句子最精确地切开,适合文本分析; 全模式,把句子中所有的可以成词的词语都扫描出来, 速度非常快,但是不能解决歧义; 搜索引擎模式,在精确模式的基础上,对长词再次切分,提高召回率,适合用于搜索引擎分词;

结巴分词——基于前缀词典及动态规划实现分词

jieba分词主要是基于统计词典,构造一个前缀词典;然后利用前缀词典对输入句子进行切分,得到所有的切分可能,根据切分位置,构造一个有向无环图;通过动态规划算法,计算得到最大概率路径,也就得到了最终的切分形式。

1.构建前缀词典:

基于统计词典构造前缀词典,统计词典有三列,第一列是词,第二列是词频,第三列是词性

2.构建有向无环图:

根据前缀词典对输入文本进行切分,比如“北”,有北、北京、北京大学三种划分方式。因此,对于每个字,可以构建一个以位置为key,相应划分的末尾位置构成的列表为value的映射。

3.最大概率路径计算

在得到所有可能的切分方式构成的有向无环图后,我们发现从起点到终点存在多条路径,多条路径也就意味着存在多种分词结果。需要计算最大概率路径。

计算最大概率路径时,jieba采用从后往前的方式,采用动态规划计算最大概率路径。每到达一个节点,它前面的节点到终点的最大路径概率就已经计算出来。

具体实现:

1.为什么jieba没有使用trie树作为前缀词典存储的数据结构

对于get_DAG()函数来说,用Trie数据结构,特别是在Python环境,内存使用量过大。经实验,可构造一个前缀集合解决问题。该集合储存词语及其前缀,如set(['数', '数据', '数据结', '数据结构'])。在句子中按字正向查找词语,在前缀列表中就继续查找,直到不在前缀列表中或超出句子范围。大约比原词库增加40%词条。

2.最大概率路径计算

jieba中计算最大概率路径的主函数是calc(self,sentence,DAG,route)

函数是一个自底向上的动态规划问题,它从sentence的最后一个字(N-1)开始倒序遍历sentence的每个字(idx)的方式,计算子句sentence[idx ~ N-1]的概率对数得分。然后将概率对数得分最高的情况以(概率对数,词语最后一个位置)这样的元组保存在route中。

基于汉字成词能力的HMM模型识别未登录词:

如果没有前缀词典或者有些词不在前缀词典中,jieba分词一样可以分词,那么jieba分词是如何对未登录词进行分词呢?这就是本文将要讲解的,基于汉字成词能力的HMM模型识别未登录词。

利用HMM模型进行分词,主要是将分词问题视为一个序列标注(sequence labeling)问题,其中,句子为观测序列,分词结果为状态序列。首先通过语料训练出HMM相关的模型,然后利用Viterbi算法进行求解,最终得到最优的状态序列,然后再根据状态序列,输出分词结果。

1.序列标注

序列标注,就是将输入句子和分词结果当作两个序列,句子为观测序列,分词结果为状态序列,当完成状态序列的标注,也就得到了分词结果。

以“去北京大学玩”为例,我们知道“去北京大学玩”的分词结果是“去 / 北京大学 / 玩”。对于分词状态,由于jieba分词中使用的是4-tag,因此我们以4-tag进行计算。4-tag,也就是每个字处在词语中的4种可能状态,B、M、E、S,分别表示Begin(这个字处于词的开始位置)、Middle(这个字处于词的中间位置)、End(这个字处于词的结束位置)、Single(这个字是单字成词)。具体如下图所示,“去”和“玩”都是单字成词,因此状态就是S,“北京大学”是多字组合成的词,因此“北”、“京”、“大”、“学”分别位于“北京大学”中的B、M、M、E。

2.HMM模型

HMM模型作的两个基本假设:

1.齐次马尔科夫性假设,即假设隐藏的马尔科夫链在任意时刻t的状态只依赖于其前一时刻的状态,与其它时刻的状态及观测无关,也与时刻t无关; P(states[t] | states[t-1],observed[t-1],...,states[1],observed[1]) = P(states[t] | states[t-1]) t = 1,2,...,T

2.观测独立性假设,即假设任意时刻的观测只依赖于该时刻的马尔科夫链的状态,与其它观测和状态无关, P(observed[t] | states[T],observed[T],...,states[1],observed[1]) = P(observed[t] | states[t]) t = 1,2,...,T

HMM模型有三个基本问题:

1.概率计算问题,给定模型 λ=(A,B,π) 和观测序列 O=(o1,o2,...,oT) ,怎样计算在模型 λ 下观测序列O出现的概率 P(O|λ) ,也就是Forward-backward算法; 2.学习问题,已知观测序列 O=(o1,o2,...,oT) ,估计模型 λ=(A,B,π) ,使得在该模型下观测序列的概率 P(O|λ) 尽可能的大,即用极大似然估计的方法估计参数; 3.预测问题,也称为解码问题,已知模型 λ=(A,B,π) 和观测序列 O=(o1,o2,...,oT) ,求对给定观测序列条件概率 P(S|O) 最大的状态序列 I=(s1,s2,...,sT) ,即给定观测序列。求最有可能的对应的状态序列; 其中,jieba分词主要中主要涉及第三个问题,也即预测问题。

HMM模型中的五元组表示:

1.观测序列; 2.隐藏状态序列; 3.状态初始概率; 4.状态转移概率; 5.状态发射概率;

3.维特比算法

Viterbi算法实际上是用动态规划求解HMM模型预测问题,即用动态规划求概率路径最大(最优路径)。这时候,一条路径对应着一个状态序列。

根据动态规划原理,最优路径具有这样的特性:如果最优路径在时刻t通过结点 i∗t ,那么这一路径从结点 i∗t 到终点 i∗T 的部分路径,对于从 i∗t 到 i∗T 的所有可能的部分路径来说,必须是最优的。因为假如不是这样,那么从 i∗t 到 i∗T 就有另一条更好的部分路径存在,如果把它和从 i∗t 到达 i∗T 的部分路径连接起来,就会形成一条比原来的路径更优的路径,这是矛盾的。依据这个原理,我们只需要从时刻t=1开始,递推地计算在时刻t状态i的各条部分路径的最大概率,直至得到时刻t=T状态为i的各条路径的最大概率。时刻t=T的最大概率就是最优路径的概率 P∗ ,最优路径的终结点 i∗T 也同时得到。之后,为了找出最优路径的各个结点,从终结点 i∗T 开始,由后向前逐步求得结点 i∗T−1,...,i∗1 ,最终得到最优路径 I∗=(i∗1,i∗2,...,i∗T) 。

由Viterbi算法得到状态序列,也就可以根据状态序列得到分词结果。其中状态以B开头,离它最近的以E结尾的一个子状态序列或者单独为S的子状态序列,就是一个分词。以”去北京大学玩“的隐藏状态序列”SBMMES“为例,则分词为”S / BMME / S“,对应观测序列,也就是”去 / 北京大学 / 玩”。

具体实现

jieba分词中HMM模型识别未登录词的源码目录在jieba/finalseg/下,

init.py 实现了HMM模型识别未登录词;

prob_start.py 存储了已经训练好的HMM模型的状态初始概率表;

prob_trans.py 存储了已经训练好的HMM模型的状态转移概率表;

prob_emit.py 存储了已经训练好的HMM模型的状态发射概率表;

jieba分词会首先调用函数cut(sentence),cut函数会先将输入句子进行解码,然后调用__cut函数进行处理。__cut函数就是jieba分词中实现HMM模型分词的主函数。__cut函数会首先调用viterbi算法,求出输入句子的隐藏状态,然后基于隐藏状态进行分词。

jieba分词实现Viterbi算法是在viterbi(obs, states, start_p, trans_p, emit_p)函数中实现。viterbi函数会先计算各个初始状态的对数概率值,然后递推计算,每时刻某状态的对数概率值取决于上一时刻的对数概率值、上一时刻的状态到这一时刻的状态的转移概率、这一时刻状态转移到当前的字的发射概率三部分组成。

jieba的词性标注

jieba分词的词性标注过程非常类似于jieba分词的分词流程,同时进行分词和词性标注。

在词性标注的时候,首先基于正则表达式(汉字)进行判断,

1)如果是汉字,则会基于前缀词典构建有向无环图,然后基于有向图计算最大概率路径,同时在前缀词典中查找所分出的词的词性,如果没有找到,则将其词性标注为“x”(非语素字 非语素字只是一个符号,字母x通常用于代表未知数、符号);如果HMM标志位置位,并且该词为未登录词,则通过隐马尔科夫模型对其进行词性标注;

2)如果是其它,则根据正则表达式判断其类型,分别赋予“x”,“m”(数词 取英语numeral的第3个字母,n,u已有他用),“eng”(英文)。

具体实现:

jieba分词的词性标注功能,是在jieba/posseg目录下实现的。

其中,init.py实现了词性标注的大部分函数;

char_state_tab.py存储了离线统计的字及其对应的状态;

prob_emit.py存储了状态到字的发射概率的对数值;

prob_start.py存储了初始状态的概率的对数值;

prob_trans.py存储了前一时刻的状态到当前时刻的状态的转移概率的对数值;

viterbi.py实现了Viterbi算法;

jieba分词的词性标注接口的主调函数是cut函数,位于jieba/posseg/init.py文件中。

__cut_DAG函数会首先根据离线统计的词典(每行分别为字、频率、词性)构建前缀词典这个词典。然后基于前缀词典构建有向无环图,然后基于有向无环图计算最大概率路径,对句子进行分割。基于分割结果,如果该词在词--词性词典中,则将词典中该词的词性赋予给这个词,否则赋予“x”;如果前缀词典中不存在该词,则这个词是未登录词,则利用隐马尔科夫模型对其进行词性标注;如果上述两个条件都没有满足,则将词性标注为“x”。

__cut_detail函数是利用隐马尔科夫模型进行词性标注的主函数。

__cut_detail函数首先利用正则表达式对未登录词组成的句子进行分割,然后根据正则表达式进行判断,如果匹配上,则利用隐马尔科夫模型对其进行词性标注;否则,进一步根据正则表达式,判断其类型。

其中,__cut是隐马尔科夫模型进行词性标注的执行函数。

jieba的关键词抽取

jieba分词系统中实现了两种关键词抽取算法,分别是基于TF-IDF关键词抽取算法和基于TextRank关键词抽取算法,两类算法均是无监督学习的算法,下面将会通过实例讲解介绍如何使用jieba分词的关键词抽取接口以及通过源码讲解其实现的原理。

from jieba import analyse

tfidf = analyse.extract_tags #调用tfidf

textrank = analyse.textrank #调用textrank

0
收藏
回复
在@后输入用户全名并按空格结束,可艾特全站任一用户