首先我们来看一下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分词流程及部分源码解读(一)的更多相关文章

  1. CesiumJS 2022^ 源码解读[7] - 3DTiles 的请求、加载处理流程解析

    目录 1. 3DTiles 数据集的类型 2. 创建瓦片树 2.1. 请求入口文件 2.2. 创建树结构 2.3. 瓦片缓存机制带来的能力 3. 瓦片树的遍历更新 3.1. 三个大步骤 3.2. 遍历 ...

  2. Bert系列(二)——源码解读之模型主体

    本篇文章主要是解读模型主体代码modeling.py.在阅读这篇文章之前希望读者们对bert的相关理论有一定的了解,尤其是transformer的结构原理,网上的资料很多,本文内容对原理部分就不做过多 ...

  3. Bert系列(三)——源码解读之Pre-train

    https://www.jianshu.com/p/22e462f01d8c pre-train是迁移学习的基础,虽然Google已经发布了各种预训练好的模型,而且因为资源消耗巨大,自己再预训练也不现 ...

  4. AFNetworking 3.0 源码解读(九)之 AFNetworkActivityIndicatorManager

    让我们的APP像艺术品一样优雅,开发工程师更像是一名匠人,不仅需要精湛的技艺,而且要有一颗匠心. 前言 AFNetworkActivityIndicatorManager 是对状态栏中网络激活那个小控 ...

  5. seajs 源码解读

    之前面试时老问一个问题seajs 是怎么加载js 文件的 在网上找一些资料,觉得这个写的不错就转载了,记录一下,也学习一下 seajs 源码解读 seajs 简单介绍 seajs是前端应用模块化开发的 ...

  6. HttpClient 4.3连接池参数配置及源码解读

    目前所在公司使用HttpClient 4.3.3版本发送Rest请求,调用接口.最近出现了调用查询接口服务慢的生产问题,在排查整个调用链可能存在的问题时(从客户端发起Http请求->ESB-&g ...

  7. Laravel 源码解读系列第四篇-Auth 机制

    前言 Laravel有一个神器: php artisan make:auth 能够快速的帮我们完成一套注册和登录的认证机制,但是这套机制具体的是怎么跑起来的呢?我们不妨来一起看看他的源码.不过在这篇文 ...

  8. AbstractQueuedSynchronizer源码解读

    1. 背景 AQS(java.util.concurrent.locks.AbstractQueuedSynchronizer)是Doug Lea大师创作的用来构建锁或者其他同步组件(信号量.事件等) ...

  9. AbstractQueuedSynchronizer源码解读--续篇之Condition

    1. 背景 在之前的AbstractQueuedSynchronizer源码解读中,介绍了AQS的基本概念.互斥锁.共享锁.AQS对同步队列状态流转管理.线程阻塞与唤醒等内容.其中并不涉及Condit ...

随机推荐

  1. select2 如何自定义提示信息-布布扣-bubuko.com

    标签:color   dom   layui   href   默认事件   替换   each   ase   options 最近项目中使用到了select2来美化和丰富下拉框的功能,这款插件功能 ...

  2. 关于Vector CANoe的讨论

    默认排序​ 踩猫尾巴 汽车电子攻城狮 27 人赞同了该回答 好像是很久以前的问题啊,为什么会现在收到邀请. 我觉得 @lijuqqkiko 介绍的足够啦. 我再额外发散一点吧. 目前在CAN总线测试和 ...

  3. 模拟15 题解(waiting)

    T1 60%算法 定义f[i][j]表示枚举到i位置,已经使用过了j个队, $f[i][j]+=f[i-1][t] ( t \in [max(0,j-k),j])$滚动一下 这是个O(n^3)的,考虑 ...

  4. 【Codeforces Round #430 (Div. 2) B】Gleb And Pizza

    [链接]点击打开链接 [题意] 在这里写题意 [题解] 根据圆心到原点的距离这个东西判断一下圆在不在那个环里面就好 [错的次数] 0 [反思] 在这了写反思 [代码] #include <cst ...

  5. ML面试1000题系列(71-80)

    本文总结ML面试常见的问题集 转载来源:https://blog.csdn.net/v_july_v/article/details/78121924 71.看你是搞视觉的,熟悉哪些CV框架,顺带聊聊 ...

  6. 运行Jmeter时,响应数据中文乱码问题解决办法

    需要修改jmeter中的配置,在Jmeter安装目录/bin/jmeter.properties文件中进行修改: sampleresult.default.encoding默认为ISO-8859-1, ...

  7. vue-waterfall2 基于Vue.js 瀑布流组件

    vue-waterfall2 1.宽度自适应,数据绑定特效(适用于上拉加载更多) 2.自定义程度高 3.使用极为简便,适用于PC/移动端 4.提供resize(强制刷新布局-适用于下拉刷新)/mix( ...

  8. 安装tomcat(fedora16)

    sudo yum install tomcat6 sudo yum install tomcat6-webapps sudo yum install tomcat6-admin-webapps   s ...

  9. database homework3

    查询所有大于60分的学生的姓名和学号 (DISTINCT: 去重) mysql> select student.sname,student.sid,score.number from stude ...

  10. 安装LoadRunner11时,缺少vc2005_sp1_with_atl_fix_redist错误的解决方案

    安装LoadRunner11时,会报缺少vc2005_sp1_with_atl_fix_redist错误,类似下图所示: 由提示信息可知,这里是由于本机缺少该组件所致,解决方案就是安装此组件,可以去网 ...