赞
踩
1.jieba库是较好的中文分词词库
2.jieba库分词思想有三步:定义前缀词典,构建字典树;生成句子有向图,路径规划输出最优路径;基于HMM模型求解decoding问题
3.生成句子有向图本质是贝叶斯网络,路径规划实质是求解联合概率分布
4.jieba的HMM模型的隐状态有四种:BMES,基于大量语料库初始化参数(初始概率分布,转移矩阵和发射矩阵)
分词是对文本预处理重要环节,英文分词可以针对空格切分达到很好的效果。而中国文字博大精深,需要用一定的方法处理。本文就介绍基于python第三方库jieba分词库,它是比较好的中文分词词库。jieba库的分词原理主要有三项:
基于前缀词典实现高效的词图扫描,生成句子中汉字所有可能成词情况所构成的有向无环图(DAG)
采用了动态规划查找最大概率路径, 找出基于词频的最大切分组合
对于未登录词,采用了基于汉字成词能力的HMM模型,使用Viterbi算法求解
前缀词典与DAG
首先是前缀词典的扫描。jieba库是利用了Tier树进行高效扫描,Tier树中文名叫字典树、前缀树。它的用途主要是将字符串整合成树形。
比如由“清华”、“清华大学”、“清白”、“中华”、“华夏”五个中文词构成的Tier树:
这个树里面每一个方块代表一个节点,其中根节点Root不代表任何字符;紫色代表分支节点;绿色代表叶子节点。除根节点外每一个节点都只包含一个字符。从根节点到叶子节点,路径上经过的字符连接起来,构成一个词。而叶子节点内的数字代表该词在字典树中所处的链路(字典中有多少个词就有多少条链路),具有共同前缀的链路称为串。
字典树有以下三个特点:
具有相同前缀的词必须位于同一个串内
比如“清华”和“清白”两个词语,都有“清”这个前缀,那么在字典树上只需要构建一个“清”节点即可,这在一定程度减少存储空间。
字典树中的词只可共用前缀,不可共用词的其他部分
比如“华夏”和“中华”,这两个词都有共同的后缀“华”,但在字典树上必须是两条独立的链路,即字典树仅依靠公共前缀压缩字典存储空间。再看“清华大学”这个词语,“大学”也是一个词语,但是“清华大学”的后缀,所以“大学”必须从根节点开始重新构建。
一个完整的词必须从根节点开始,至叶子节点结束
字典树实质是一个有限状态自动机(Definite Automata,DFA),它从一个节点(状态)转移到另一个节点(状态)的行为完全由状态转移函数控制,而状态转移函数本质上是一种映射,这意味着:逐字搜索字典树时,从一个字符到下一个字符比对是不需要遍历该节点的所有子节点的。
构建完字典树后就可以生成句子的有向无环图(DAG)。
假设对于待切分的字符串(句子)有m个字符,考虑每个字符左右两边的位置,则有m+1个点,编号从0~m,把候选词看成边,根据前缀词典生成一个切分图。比如句子“我毕业于北京大学”:
“我”这条边起点是0,终点是1;“毕业”这条边起点是1,终点是3,依次类推,根据词典,我们可以得到两条路径:
路径1:0-1-3-4-6-8 切分方案S1:我/毕业/于/北京/大学
路径2:0-1-3-4-8 切分方案S2:我/毕业/于/北京大学
动态规划
对于方案S1和S2,我们最终选取哪种切分方案呢。jieba库的字典树会标记每个词的频率,它存储在dict.txt文件中:
该文件每一行代表一个词语,每一行存储【词名 词频 词性】三个字段。
注:词性标注表在网上有大量资料,读者可以自行查找
在知道每个词出现的频率之后,就可以基于动态规划的方法来寻找概率最大的分词路径,这里采取从右往左去寻找最优路径。具体怎么做,其实就是算每种路径的联合概率分布,还记得有向无环图(DAG)其实是一种贝叶斯网络嘛,我们在概率图模型中介绍过其联合概率分布的公式:
传送门:概率图模型(模型表示)
比如对于上一节的两种路径S1和S2,它其实对应着两个贝叶斯网络:
分别计算他们的联合概率分布,最大的那个即为最终切分方案
注:jieba分词是从右往左,所以实际中方向与上图箭头相反
HMM的译码分词
如果遇到一些词典中没有的词语,那怎么办呢。jieba采取了HMM模型进行生成,它主要是进行译码(Decoding)问题:
回顾一下HMM模型,要求解上述状态序列,需要知道HMM的参数λ=(π,A,B),即初始状态概率分布π,状态转移矩阵A和发射矩阵B.
传送门:
隐马尔可夫模型(Baum Welch算法与Viterbi算法)
那么在jieba库里,这三个参数是什么呢。jieba库首先定义了四种状态BEMS,B是开始,begin位置;E是end,是结束位置;M是middle,是中间位置;S是singgle,单独成词的位置,没有前,也没有后。采用(B、E、M、S)这四种状态来标记中文词语,比如北京可以标注为 BE;中华民族可以标注为BMME,就是开始、中间、中间、结束。
然后采用大量的语料进行训练,得到了HMM的参数λ,分别存储在finalseg目录下的三个文件(prob_start.py、prob_trans.py 、prob_emit.py 、 )
我们来大概浏览一下这三个文件的内容,
初始状态分布π
状态转移矩阵A:
发射矩阵B:
jieba库应用
总结一下,jieba库的分词原理是:生成字典树——给定句子生成DAG——路径规划输出概率最大切分组合——不在字典树的词语基于HMM模型求解Decoding问题
这一节我们简单介绍一下jieba库在代码层面的具体应用。
分词
- import jieba
- jieba.cut(sentence,cut_all=False,HMM=True,use_paddle=False)
- jieba.cut_for_search(sentence,HMM=True)
cut 方法接受四个输入参数: 需要分词的字符串;cut_all 参数用来控制是否采用全模式;HMM 参数用来控制是否使用 HMM 模型;use_paddl参数是否使用训练序列标注(双向GRU)网络模型实现分词。
cut_for_search 方法接受两个参数:需要分词的字符串;是否使用 HMM 模型。该方法适合用于搜索引擎构建倒排索引的分词,粒度比较细。
上面两个方法返回的结构是 generator,利用jieba.lcut 以及 jieba.lcut_for_search 直接返回 list
注:paddle需要额外安装,官网见参考资料
字典
- jieba.load_userdict(filename)
- jieba.add_word(word, freq=None, tag=None)
- jieba.del_word(word)
- jieba.suggest_freq(segment, tune=False)
load_userdict方法添加自定义词典,词典格式和 dict.txt 一样,词频和词性可省略,顺序不可颠倒;add_word和 del_word可在程序中动态修改词典;suggest_freq可调节单个词语的词频
关键词提取
- import jieba.analyse
-
- #基于TF-IDF算法的关键词抽取
- jieba.analyse.extract_tags(sentence, topK=20, withWeight=False, allowPOS=())
- jieba.analyse.TFIDF(idf_path=None) #新建TFIDF 实例,idf_path 为IDF 频率文件
- jieba.analyse.set_idf_path(file_name)# 切换成自定义语料库,file_name为自定义语料库的路径
-
- #基于TextRank算法的关键词抽取
- jieba.analyse.textrank(sentence, topK=20, withWeight=False, allowPOS=('ns', 'n', 'vn', 'v'))
- jieba.analyse.TextRank()
-
- #关键词提取所使用停止词
- jieba.analyse.set_stop_words(file_name)
sentence为待提取的文本,topK 为返回权重最大的关键词的个数,默认 20,withWeight 为是否一并返回关键词权重值,默认 False,allowPOS 仅包括指定词性的词,默认为空。
TFIDF方法表示新建 TFIDF 实例,同理,TextRank方法新建自定义 TextRank 实例
词性标注
- import jieba
- import jieba.posseg as pseg
-
- words = pseg.cut(sentence) #jieba默认模式
-
- jieba.enable_paddle()
- words = pseg.cut(sentence, use_paddle=True) # paddle模式
- for word, flag in words:
- print(' %s %s ' % (word, flag))
-
- jieba.posseg.POSTokenizer(tokenizer=None)
POSTokenizer 方法是新建自定义分词器,tokenizer 参数可指定内部使用Tokenizer 分词器。jieba.posseg.dt 为默认词性标注分词器。
其他
- import jieba
- #返回词语在原文的起止位置
- re = jieba.tokenize(sentence,mode='search',HMM=True) #默认模式
- for i in re:
- print("word %s\t\t start: %d \t\t end:%d" % (i[0], i[1], i[2]))
-
- #延迟加载,初始化字典
- jieba.initialize() # 手动初始化(可选)
- jieba.set_dictionary('data/dict.txt.big')
jieba 采用延迟加载,import jieba 和 jieba.Tokenizer() 不会立即触发词典的加载,一旦有必要才开始加载词典构建前缀字典。也可以改变主词典的路径。
参考资料:
https://github.com/fxsjy/jieba
https://www.paddlepaddle.org.cn/
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。