jieba中文分词源码解析(一)

1、结巴的简单使用

from __future__ import unicode_literals
import sys
sys.path.append("/opt/python_workspace/jieba_demo/jieba-master/")

import jieba
import jieba.posseg
import jieba.analyse

print('='*40)
print('1. 分词')
print('-'*40)
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("今天是2015年9月3号,去天安门广场庆祝抗战胜利70周年")
print("Search Mode: " + "/ ".join(seg_list))  # 搜索引擎模式

上面的代码运行结果如下:

Full Mode: 去/ 北京/ 北京大学/ 大学/ 打篮球/ 篮球
Default Mode: 去/ 北京大学/ 打篮球
去/ 北京大学/ 打篮球
Search Mode: 今天/ 是/ 2015/ 年/ 9/ 月/ 3/ 号/ ,/ 去/ 天安/ 广场/ 天安门/ 天安门广场/ 庆祝/ 抗战/ 胜利/ 70/ 周年

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

2、结巴分词步骤源码解析

通过运行上面的例子,我们对结巴分词大致有了一个简单的认识。不难发现,我们上面的例子都是调用了cut这个方法,经过查看jieba的源码,发现cut是jieba分词的入口,这个函数在文件jieba/__init__.py ,代码如下:

#jieba分词的主函数,返回结果是一个可迭代的 generator
    def cut(self, sentence, cut_all=False, HMM=True):
        '''
        The main function that segments an entire sentence that contains
        Chinese characters into seperated words.

        Parameter:
            - sentence: The str(unicode) to be segmented.
            - cut_all: Model type. True for full pattern, False for accurate pattern.
            - HMM: Whether to use the Hidden Markov Model.
        '''
        # 解码为unicode
        sentence = strdecode(sentence)
        # 不同模式下的正则
        if cut_all:
            re_han = re_han_cut_all  #re.compile("([\u4E00-\u9FD5]+)", re.U)
            re_skip = re_skip_cut_all #re.compile("[^a-zA-Z0-9+#\n]", re.U)
        else:
            re_han = re_han_default #re.compile("([\u4E00-\u9FD5a-zA-Z0-9+#&\._]+)", re.U)
            re_skip = re_skip_default #re.compile("(\r\n|\s)", re.U)
        #设置不同模式下的cut_block分词方法
        if cut_all:
            cut_block = self.__cut_all  #全模式
        elif HMM:
            cut_block = self.__cut_DAG #精确模式,使用隐马模型
        else:
            cut_block = self.__cut_DAG_NO_HMM #精确模式,不使用隐马模型
        #先用正则对句子进行切分
        blocks = re_han.split(sentence)
        for blk in blocks:
            if not blk:
                continue
            if re_han.match(blk): # re_han匹配的串
                for word in cut_block(blk): #根据不同模式的方法进行分词
                    yield word
            else: # 按照re_skip正则表对blk进行重新切分
                tmp = re_skip.split(blk) # 返回list
                for x in tmp:
                    if re_skip.match(x):
                        yield x
                    elif not cut_all: # 精准模式下逐个字符输出
                        for xx in x:
                            yield xx
                    else:
                        yield x

下面根据cut函数来绘制出相应的流程图:
 

从图中可以看出,sentence先利用正则表达式切分,得到的词语列表blocks(re_han正则表达式使用了捕获括号,那么匹配的字符串也会被列入到list中返回),然后对切分后的每一个re_han匹配项blk词语利用cut_block方法进行具体的分词行为。

具体的分词流程概括起来如下:
1. 给定待分词的句子, 使用正则(re_han)获取匹配的中文字符(和英文字符)切分成的短语列表;
2. 利用get_DAG(sentence)函数获得待切分句子的DAG,首先检测(check_initialized)进程是否已经加载词库,若未初始化词库则调用initialize函数进行初始化,initialize中判断有无已经缓存的前缀词典cache_file文件,若有相应的cache文件则直接使用 marshal.load 方法加载前缀词典,若无则通过gen_pfdict对指定的词库dict.txt进行计算生成前缀词典,到jieba进程的初始化工作完成后就调用get_DAG获得句子的DAG;
3. 根据cut_block指定具体的方法(__cut_all,__cut_DAG,__cut_DAG_NO_HMM)对每个短语使用DAG进行分词 ,如cut_block=__cut_DAG时则使用DAG(查字典)和动态规划, 得到最大概率路径, 对DAG中那些没有在字典中查到的字, 组合成一个新的片段短语, 使用HMM模型进行分词, 也就是作者说的识别新词, 即识别字典外的新词;
4. 使用python的yield 语法生成一个词语生成器, 逐词语返回;

具体执行流程总结为下图:

以上就是对结巴分词的cut源码进行分析和总结,下面会对结巴分词的一些重要方法进行进一步的分析和总结。

猜你喜欢

转载自blog.csdn.net/u013982921/article/details/81122928