jieba 分词的原理与使用

jieba 分词的原理与使用

  • 作者:Geticsen
  • 时间:2019-12-20
  • 46人已阅读
简介 文本处理的最先前就是要进行文本语句的分词,对于英语来说分词直接用逗号分号直接字符串切割就好,但是对于中文分词其实还是个很难的事情

一、 jieba系统简介

"结巴"中文分词:做最好的Python中文分词组件。

特点:

支持三种分词模式

支持繁体分词

支持自定义词典

MIT授权协议

涉及算法:

       基于前缀词典实现词图扫描,生成句子中汉字所有可能成词情况所构成的有向无环图(DAG),采用动态规划查找最大概率路径,找出基于词频的最大切分组合;

       对于未登录词,采用了基于汉字成词能力的 HMM模型,采用Viterbi算法进行计算;

       基于Viterbi算法的词性标注;

       分别基于tfidf和textrank模型抽取关键词;

二、 jieba系统框架

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

       分词

       词性标注

       关键词抽取

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

        精确模式,试图将句子最精确地切开,适合文本分析;

        全模式,把句子中所有的可以成词的词语都扫描出来, 速度非常快,但是不能解决歧义;

         搜索引擎模式,在精确模式的基础上,对长词再次切分,提高召回率,适合用于搜索引擎分词;

三、jieba分词原理

1.基于前缀词典实现高效的词图扫描,生成句子中汉字所有可能成词情况所构成的有向无环图 (DAG)

       1. 根据dict.txt生成trie树,字典在生成trie树的同时, 把每个词的出现次数转换为频率(jieba自带一个dict.txt的词典, 里面有2万多条词, 包含了词条出现的次数和词性(作者基于人民日报语料等资源训练得出来)。trie树结构的词图扫描, 说的就是把这2万多条词语, 放到一个trie树中, trie树是有名的前缀树, 也就是说一个词语的前面几个字一样, 就表示他们具有相同的前缀, 就可以使用trie树来存储, 具有查找速度快的优势)。

        2.对待分词句子, 根据dict.txt生成的trie树, 生成DAG, 通俗的讲, 就是将句子根据给定的词典进行查词典操作, 生成所有可能的句子切分。jieba在DAG中记录的是句子中某个词的开始位置, 从0到n-1(n为句子的长度), 每个开始位置作为字典的键, value是个list, 其中保存了可能的词语的结束位置(通过查字典得到词, 开始位置+词语的长度得到结束位置)

        3. 有向无环图构建:然后基于前缀词典,对输入文本进行切分,对于“去”,没有前缀,那么就只有一种划分方式;对于“北”,则有“北”、“北京”、“北京大学”三种划分方式;对于“京”,也只有一种划分方式;对于“大”,则有“大”、“大学”两种划分方式,依次类推,可以得到每个字开始的前缀词的划分方式。

在得到所有可能的切分方式构成的有向无环图后,我们发现从起点到终点存在多条路径,多条路径也就意味着存在多种分词结果。因此,我们需要计算最大概率路径,也即按照这种方式切分后的分词结果的概率最大。在采用动态规划计算最大概率路径时,每到达一个节点,它前面的节点到终点的最大路径概率已经计算出来。

2.动态规划查找最大概率路径, 找出基于词频的最大切分组合

      1.查找待分词句子中已经切分好的词语(全模式下的分词list), 得出查找该词语出现的频率(次数/总数), 如果没有该词(基于词典一般都是有的), 就把词典中出现频率最小的那个词语的频率作为该词的频率。

       2.根据动态规划查找最大概率路径的方法, 对句子从右往左反向计算最大概率(这里反向是因为汉语句子的重心经常落在后面(右边), 因为通常情况下形容词太多, 后面的才是主干。因此, 从右往左计算, 正确率要高于从左往右计算, 这里类似于逆向最大匹配), P(NodeN)=1.0, P(NodeN-1)=P(NodeN)*Max(P(倒数第一个词))…依次类推, 最后得到最大概率路径, 得到最大概率的切分组合。

3.对于未登录词,采用了基于汉字成词能力的 HMM 模型,使用了 Viterbi 算法

        image.png

      1.利用HMM模型将中文词汇按照BEMS四个状态来标记, B是开始begin位置, E是end结束位置, M是middle中间位置, S是singgle单独成词的位置。jieba采用(B,E,M,S)这四种状态来标记中文词语, 比如北京可以标注为 BE, 即 北/B 京/E, 表示北是开始位置, 京是结束位置, 中华民族可以标注为BMME, 就是开始, 中间, 中间, 结束.

     2.作者利用大量语料进行训练, 得到了三个概率表。分别是1)位置转换概率,即B(开头),M(中间),E(结尾),S(独立成词)四种状态的转移概率,P(E|B) = 0.851, P(M|B) = 0.149,说明当我们处于一个词的开头时,下一个字是结尾的概率要远高于下一个字是中间字的概率,符合我们的直觉,因为二个字的词比多个字的词更常见。2)位置到单字的发射概率,比如P(“和”|M)表示一个词的中间出现”和”这个字的概率;3) 词语以某种状态开头的概率,其实只有两种,要么是B,要么是S。这个就是起始向量, 就是HMM系统的最初模型状态。实际上, BEMS之间的转换有点类似于2元模型, 就是2个词之间的转移。二元模型考虑一个单词后出现另外一个单词的概率,是N元模型中的一种。

      3. 给定一个待分词的句子, 就是观察序列, 对HMM(BEMS)四种状态的模型来说, 就是为了找到一个最佳的BEMS序列, 这个就需要使用viterbi算法来得到这个最佳的隐藏状态序列。通过训练得到的概率表和viterbi算法, 就可以得到一个概率最大的BEMS序列, 按照B打头, E结尾的方式, 对待分词的句子重新组合, 就得到了分词结果. 比如 对待分词的句子 ‘全世界都在学中国话’ 得到一个BEMS序列 [S,B,E,S,S,S,B,E,S], 通过把连续的BE凑合到一起得到一个词, 单独的S放单, 就得到一个分词结果了。

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

HMM的两个基本假设:

1.齐次马尔科夫性假设,即假设隐藏的马尔科夫链在任意时刻t的状态只依赖于其前一时刻的状态,与其它时刻的状态及观测无关,也与时刻t无关;

2.观测独立性假设,即假设任意时刻的观测只依赖于该时刻的马尔科夫链的状态,与其它观测和状态无关。

3.Viterbi算法实际上是用动态规划求解HMM模型预测问题,即用动态规划求概率路径最大(最优路径)。

四、jieba分词过程

加载字典, 生成trie树。

给定待分词的句子, 使用正则获取连续的 中文字符和英文字符, 切分成 短语列表, 对每个短语使用DAG(查字典)和动态规划, 得到最大概率路径, 对DAG中那些没有在字典中查到的字, 组合成一个新的片段短语, 使用HMM模型进行分词, 也就是作者说的识别未登录词。

使用python的yield 语法生成一个词语生成器, 逐词语返回。

五、python实例jieba分词使用

全局模式:

import  jieba
seg_list = jieba.cut("长大了我要当太空人,爷爷奶奶可高兴了,给我爱吃的喜之郎果冻。", cut_all=True)
print("全模式: " + "/ ".join(seg_list))  # 全模式

结果:

全模式: 长大/ 了/ 我/ 要/ 当/ 太空/ 太空人/ / / 爷爷/ 爷爷奶奶/ 奶奶/ 可/ 高兴/ 了/ / / 给/ 我/ 爱/ 吃/ 的/ 喜之郎/ 果冻/ /

精确模式:

import  jieba
list = [
    "长大了我要当太空人,爷爷奶奶可高兴了,给我爱吃的喜之郎果冻。",
    "你妈已经三天没打你了,三天了……",
    "你的头像另一半是什么",
    "是个男的",
    "不是最爱吃的大嘴巴子?"
    "你吃果冻的样子就像蔡徐鲲",
    "最后楼主吃着果冻被发射到遥远的太空,爷爷奶奶可高兴了。"
]
for line in list:
    seg_list = jieba.cut(line, cut_all=False)
    print("精准模式: " + "/ ".join(seg_list))  # 精确模式

 结果:

精准模式: 长大/ 了/ 我/ 要/ 当/ 太空/ 人/ ,/ 爷爷奶奶/ 可/ 高兴/ 了/ ,/ 给/ 我/ 爱/ 吃/ 的/ 喜之郎/ 果冻/ 。
精准模式: 你/ 妈/ 已经/ 三天/ 没/ 打/ 你/ 了/ ,/ 三天/ 了/ …/ …
精准模式: 你/ 的/ 头像/ 另一半/ 是/ 什么
精准模式: 是/ 个/ 男/ 的
精准模式: 不是/ 最爱/ 吃/ 的/ 大嘴巴/ 子/ ?/ 你/ 吃/ 果冻/ 的/ 样子/ 就/ 像/ 蔡/ 徐鲲
精准模式: 最后/ 楼主/ 吃/ 着/ 果冻/ 被/ 发射/ 到/ 遥远/ 的/ 太空/ ,/ 爷爷奶奶/ 可/ 高兴/ 了/ 。

搜索引擎模式:

import  jieba
seg_list = jieba.cut_for_search("小明硕士毕业于中国科学院计算所,后在日本京都大学深造")  # 搜索引擎模式
print("/".join(seg_list))

结果:

小明/硕士/毕业/于/中国/科学/学院/科学院/中国科学院/计算/计算所/,/后/在/日本/京都/大学/日本京都大学/深造

在处理文本的时候除了分词还得去停等词以及去除无意的符号表情,颜文字等

具体的使用要根据自己的环境要求来使用了但是jieba已经提供了特别简单方便的操作就可以完成分词了。

文章评论

Top