jieba分词流程及部分源码解读(一)
首先我们来看一下jieba分词的流程图:
结巴中文分词简介
1)支持三种分词模式:
精确模式:将句子最精确的分开,适合文本分析
全模式:句子中所有可以成词的词语都扫描出来,速度快,不能解决歧义
搜索引擎模式:在精确的基础上,对长词再次切分,提高召回
2)支持繁体分词
3)支持自定义词典
4)基于Trie树结构实现高效的词图扫描,生成句子汉字所有可能成词情况所构成的有向无环图(DAG)
5) 采用了动态规划查找最大概率路径,找出基于词频的最大切分组合 6)对于词库中不存在的词,也就是未登录词,采用了基于汉字成词能力的HMM模型,使用了Viterbi算法
接下来我们从源码分析一下:
从github上下载源代码后,打开 文件夹 jieba,找到__init__.py,结巴分词最主要的函数 cut 就定义在这个文件中。
__cut_DAG 函数调用了 get_DAG(sentence),这是用来生成每一块(sentence)的有向无环图DAG。要生成DAG就必须有语料库的辅助了,所以在 同样在 文件夹 jieba 下,可以找到一个文件:dict.txt。语料库的有3列,第一列是词,第二列是词频,第三列是词性。在程序中初始化语料库的动作在函数 initialize(DICTIONARY) 中,它通过一个包装器 require_initialized 在 get_DAG 函数被调用的时候才执行。代码如下:
def require_initialized(fn):
@wraps(fn) #wraps的作用是保留被包装函数的一些属性,比如__doc__
    def wrapped(*args, **kwargs):
        global initialized
        if initialized:
            return fn(*args, **kwargs)
        else:
            initialize(DICTIONARY)
            return fn(*args, **kwargs)
return wrapped
有向无环图构建
语料库Trie树加载完毕后,接下来我们来介绍如何进行DAG分词
以“正在学习大数据中的结巴分词”为例,作为待分词的输入文本。
jieba.__init__.py中实现了jieba分词接口函数cut(self, sentence, cut_all=False, HMM=True)。
jieba分词接口主入口函数,会首先将输入文本解码为Unicode编码,然后根据入参,选择不同的切分方式,本文主要以精确模式进行讲解,因此cut_all和HMM这两个入参均为默认值;
分词中get_DAG函数实现如下
# -*- coding: utf-8 -*-
import marshal
def get_DAG(sentence):
N = len(sentence)
    i,j=0,0
    p = trie
    DAG = {}
    while i<N:
        c = sentence[j]
        if c in p:
            p = p[c]
            if '' in p:
                if i not in DAG:
                    DAG[i]=[]
                DAG[i].append(j)
            j+=1
            if j>=N:
                i+=1
                j=i
                p=trie
        else:
            p = trie
            i+=1
            j=i
    for i in xrange(len(sentence)):
        if i not in DAG:
            DAG[i] =[i]
    return DAG
#动态规划,计算最大概率的切分组合
    def calc(self, sentence, DAG, route):
        N = len(sentence)
        route[N] = (0, 0)
         # 对概率值取对数之后的结果(可以让概率相乘的计算变成对数相加,防止相乘造成下溢)
        logtotal = log(self.total)
        # 从后往前遍历句子 反向计算最大概率
        for idx in xrange(N - 1, -1, -1):
           # 列表推倒求最大概率对数路径
           # route[idx] = max([ (概率对数,词语末字位置) for x in DAG[idx] ])
           # 以idx:(概率对数最大值,词语末字位置)键值对形式保存在route中
           # route[x+1][0] 表示 词路径[x+1,N-1]的最大概率对数,
           # [x+1][0]即表示取句子x+1位置对应元组(概率对数,词语末字位置)的概率对数
            route[idx] = max((log(self.FREQ.get(sentence[idx:x + 1]) or 1) -
                              logtotal + route[x + 1][0], x) for x in DAG[idx])
if __name__=='__main__':
    sentence=u'正在学习大数据中的结巴分词'
    trie,FREQ,total,min_freq = marshal.load(open(u'D:\jieba.cache','rb'))#使用缓存载入重要变量
    rs=get_DAG(sentence)#获取DAG
    route={}
    calc(sentence,rs,0,route)#根据得分进行初步分词
    print route
基于词频最大切分组合(从上面get_DAG中部分代码详解)
我们已经有了词库(dict.txt)的前缀字典和待分词句子sentence的DAG,基于词频的最大切分 要在所有的路径中找出一条概率得分最大的路径,该怎么做呢? 
jieba中的思路就是使用动态规划方法,从后往前遍历,选择一个频度得分最大的一个切分组合。 
具体实现见代码,已给详细注释。
#动态规划,计算最大概率的切分组合
    def calc(self, sentence, DAG, route):
        N = len(sentence)
        route[N] = (0, 0)
         # 对概率值取对数之后的结果(可以让概率相乘的计算变成对数相加,防止相乘造成下溢)
        logtotal = log(self.total)
        # 从后往前遍历句子 反向计算最大概率
        for idx in xrange(N - 1, -1, -1):
           # 列表推倒求最大概率对数路径
           # route[idx] = max([ (概率对数,词语末字位置) for x in DAG[idx] ])
           # 以idx:(概率对数最大值,词语末字位置)键值对形式保存在route中
           # route[x+1][0] 表示 词路径[x+1,N-1]的最大概率对数,
           # [x+1][0]即表示取句子x+1位置对应元组(概率对数,词语末字位置)的概率对数
            route[idx] = max((log(self.FREQ.get(sentence[idx:x + 1]) or 1) -
                              logtotal + route[x + 1][0], x) for x in DAG[idx])
从代码中可以看出calc是一个自底向上的动态规划(重叠子问题、最优子结构),它从sentence的最后一个字(N-1)开始倒序遍历sentence的字(idx)的方式(为什么倒叙遍历,不懂的可以留言或是找我小猪),计算子句sentence[isdx~N-1]概率对数得分(这里利用DAG及历史计算结果route实现)。然后将概率对数得分最高的情况以(概率对数,词语最后一个字的位置)这样的tuple保存在route中。
那么登陆词部分解释完毕,下来就是未登陆词,利用Viterbi算法来解决未登录词的处理方法,后续更新
--------------------- 
作者:Jameslvt 
来源:CSDN 
原文:https://blog.csdn.net/Jameslvt/article/details/81118560 
版权声明:本文为博主原创文章,转载请附上博文链接!
jieba分词流程及部分源码解读(一)的更多相关文章
- CesiumJS 2022^ 源码解读[7] - 3DTiles 的请求、加载处理流程解析
		目录 1. 3DTiles 数据集的类型 2. 创建瓦片树 2.1. 请求入口文件 2.2. 创建树结构 2.3. 瓦片缓存机制带来的能力 3. 瓦片树的遍历更新 3.1. 三个大步骤 3.2. 遍历 ... 
- Bert系列(二)——源码解读之模型主体
		本篇文章主要是解读模型主体代码modeling.py.在阅读这篇文章之前希望读者们对bert的相关理论有一定的了解,尤其是transformer的结构原理,网上的资料很多,本文内容对原理部分就不做过多 ... 
- Bert系列(三)——源码解读之Pre-train
		https://www.jianshu.com/p/22e462f01d8c pre-train是迁移学习的基础,虽然Google已经发布了各种预训练好的模型,而且因为资源消耗巨大,自己再预训练也不现 ... 
- AFNetworking 3.0 源码解读(九)之 AFNetworkActivityIndicatorManager
		让我们的APP像艺术品一样优雅,开发工程师更像是一名匠人,不仅需要精湛的技艺,而且要有一颗匠心. 前言 AFNetworkActivityIndicatorManager 是对状态栏中网络激活那个小控 ... 
- seajs 源码解读
		之前面试时老问一个问题seajs 是怎么加载js 文件的 在网上找一些资料,觉得这个写的不错就转载了,记录一下,也学习一下 seajs 源码解读 seajs 简单介绍 seajs是前端应用模块化开发的 ... 
- HttpClient 4.3连接池参数配置及源码解读
		目前所在公司使用HttpClient 4.3.3版本发送Rest请求,调用接口.最近出现了调用查询接口服务慢的生产问题,在排查整个调用链可能存在的问题时(从客户端发起Http请求->ESB-&g ... 
- Laravel 源码解读系列第四篇-Auth 机制
		前言 Laravel有一个神器: php artisan make:auth 能够快速的帮我们完成一套注册和登录的认证机制,但是这套机制具体的是怎么跑起来的呢?我们不妨来一起看看他的源码.不过在这篇文 ... 
- AbstractQueuedSynchronizer源码解读
		1. 背景 AQS(java.util.concurrent.locks.AbstractQueuedSynchronizer)是Doug Lea大师创作的用来构建锁或者其他同步组件(信号量.事件等) ... 
- AbstractQueuedSynchronizer源码解读--续篇之Condition
		1. 背景 在之前的AbstractQueuedSynchronizer源码解读中,介绍了AQS的基本概念.互斥锁.共享锁.AQS对同步队列状态流转管理.线程阻塞与唤醒等内容.其中并不涉及Condit ... 
随机推荐
- Laravel5.5/6 报错call to undefined function openssl cipher iv length()
			在安装laravel5.5后, 访问显示报错. call to undefined function openssl cipher iv length() 经查为php7.1的OpenSSL扩展加载失 ... 
- 【python爬虫】加密代理IP的使用与设置一套session请求头
			1:代理ip请求,存于redis: # 请求ip代理连接,更新redis的代理ip def proxy_redis(): sr = redis.Redis(connection_pool=Pool) ... 
- jsp之jstl(展示所有商品、重写登录案例)
			jsp之jstl jstl: jsp标准的标签库语言,apache的,是用来替代java脚本 使用步骤: 1.导入jar包 (jstl.jar和standard.jar) 2.在页面上导入标签库 &l ... 
- PHP 从 MongoDb 中查询数据怎么样实现
			一.软件环境(版本非必须) php v5.6 扩展:MongoDB nginx v1.11 mongodb v3.2 note: 必须安装MongoDB扩展 二.连接 $client = new Mo ... 
- 去掉goland中间的令人烦躁的竖线
			去掉“configured in code Style options”前面的勾即可. 
- IO-01. 表格输出
			本题要求编写程序,按照规定格式输出表格. 输入格式: 本题目没有输入. 输出格式: 要求严格按照给出的格式输出下列表格: ------------------------------------Pro ... 
- Directx教程(28) 简单的光照模型(7)
			原文:Directx教程(28) 简单的光照模型(7) 现实生活中的点光源都是随着距离衰减的,比如一个电灯泡在近处会照的很亮,远处光线就很弱.本节中我们在前面光公式的基础上,再给漫反射和 ... 
- linux服务器配置防火墙使用端口
			重启后生效方法: 1.开启: chkconfig iptables on 2.关闭: chkconfig iptables off 即时生效,重启后失效 : 1.开启: service iptabl ... 
- QT语言翻译
			QT中多语言的实现方式: 1.代码中tr运用 2.使用工具生成ts文件 3.翻译ts文件 4.生成qm文件 5.程序加载 以下内容程序加载时放入即可. QString appPath = QCoreA ... 
- hdu 1272 使用set和并查集
			http://acm.hdu.edu.cn/showproblem.php?pid=1272 这道题就是求图是不是连通无环,我觉得其实就是看看图是不是一棵最小生成树. 所以要是图满足条件,就必然有n个 ... 
