结巴分词是基于python的开源分词工具。在其根目录下的结构为
jieba功能介绍:
结巴分词从流程图中可看到分为两部分,一个是登录词的分词,另一个是未登录词的分词。什么是登录词呢?其实就是词库,例如jieba工程下的dict.txt,就是词库,也就是登录词,打开可看到有三列,例如“B超 3 n”, 第一列是词,第二列是词频,第三列是词性。
先查看下登录词是怎么分词的,从流程图中可看到,是通过建立DAG词图和计算全局概率Route分词的。
在jieba目录的test新建一个test.py,我们调试下DAG的构建,测试sentence=”我们一起学猫叫”,运行结果为:{0: [0, 1], 1: [1], 2: [2, 3], 3: [3], 4: [4], 5: [5, 6], 6: [6]},这个字典即为DAG,key为字所在的位置,value为从字开始能在FREQ中的匹配到的词末尾位置所在的list。句子中的第一个字为’我’,所在位置即key为0,FREQ会在get_DAG方法中做介绍。
get_DAG(sentence) 函数功能为把输入的句子生成有向无环图,
#encoding=utf-8
from __future__ import unicode_literals
import sys
sys.path.append("../")
import jieba
import jieba.posseg
import jieba.analyse
sentence="我们一起学猫叫"
sentence_dag = jieba.get_DAG(sentence)
print(sentence_dag)
route = {}
jieba.calc(sentence, sentence_dag, route) # 根据得分进行初步分词
print(route)
seg_list = jieba.cut(sentence)
print(", ".join(seg_list))
接下来,我们查看get_DAG,找到jieba根目录中init.py中,这里描述了结巴分词的核心代码。jieba分词接口主入口函数,会首先将输入文本解码为Unicode编码。
def get_DAG(self, sentence):
self.check_initialized()
DAG = {} #DAG空字典,用来构建DAG有向无环图
N = len(sentence) #赋值N词的长度
for k in xrange(N): #创建N词长度的列表,进行遍历
tmplist = [] #从字开始能在FREQ中的匹配到的词末尾位置所在的list
i = k
frag = sentence[k] #取传入词中的值,例如k=0,frag=我
while i < N and frag in self.FREQ: # 当传入的词,在FREQ中时,就给tmplist赋值,构建字开始可能去往的所有的路径列表
if self.FREQ[frag]: #每个词,在FREQ中查找,查到,则将下标传入templist中
tmplist.append(i) #添加词语所在位置
i += 1 #查找我,后继续查找“我们”是否也在语料库中,直到查不到推出循环
frag = sentence[k:i + 1] #截取传入值得词语,i=1,时截取 我,i=2时截取我们
if not tmplist: #当传入值,在语料库中查询不到时,
tmplist.append(k)
DAG[k] = tmplist #赋值DAG 词典
return DAG
函数calc(self, sentence, DAG, route)就是计算概率的过程。其中语句 xrange(N - 1, -1, -1)是从句子的末尾开始计算,
route[idx] = max((log(FREQ.get(sentence[idx:x + 1]) or 1) - logtotal + route[x + 1][0], x) for x in DAG[idx])
max函数返回的是一个元组,计算方法是log(freq/total)+后一个字得到的最大概率路径的概率。这里即为动态规划查找最大概率路径。注意的是动态规划的方向是
从后往前。
#动态规划查找最大概率路径
def calc(self, sentence, DAG, route):
N = len(sentence)
route[N] = (0, 0) #route[N]:最大路径的值,(0,0):当前这个词的末尾坐标
# total 为dict.txt词表中,共有多少个词,共60101967 个词语
# 对概率值取对数之后的结果(可以让概率相乘的计算变成对数相加,防止相乘造成下溢)
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])
route计算返回结果为:{0: (-39.59958487630383, 1), 1: (-40.47334553305173, 1), 2: (-33.188277160361466, 3), 3: (-31.88719763919758, 3), 4: (-24.955566907701893, 4), 5: (-16.81294083908711, 6), 6: (-7.232624376933066, 6), 7: (0, 0)}
可根据route分词得到 “我们, 一起, 学, 猫叫”