结巴分词原理


在我的上一篇博客 概率图模型中,有介绍一些常见的概率图模型。而在日常工作中,结巴分词也是常用的中文分词包,且其中使用了HMM模型,结合 概率图模型中的理论知识,可以帮助我们进一步了解HMM算法(当然不仅限于此)。

结巴分词简介

首先,我们通过readme看看结巴分词能够做什么:

分词:
官方给出了使用的分词算法:
在这里插入图片描述
关键词抽取:
在这里插入图片描述
在这里插入图片描述
词性标注:
在这里插入图片描述
接下来,我们分三个小节分别介绍他们。

分词

为了能够更好的理解,用一个实例举例:

去北京大学玩

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

构造前缀词典

我们知道,结巴分词含有自带的词典,也可以支持用户自己添加词典。利用该词典构造前缀词典,该前缀词典被用于构造DAG。词典的形式如下:

词典格式和 dict.txt 一样,一个词占一行;每一行分三部分:词语、词频(可省略)、词性(可省略),用空格隔开,顺序不可颠倒。

根据词典,我们可以构造前缀词典,对示例“去北京大学玩”,其前缀词典的形式如下:

北京大学 2053
北京大 0 # 不存在前缀词,词频为0
北京 34488
北 17860
京 6583
大学 20025
大 144099
学 17482
去 123402
玩 4207

源码如下:

# f是离线统计的词典文件句柄
def gen_pfdict(self, f):
    # 初始化前缀词典
    lfreq = {
   }
    ltotal = 0
    f_name = resolve_filename(f)
    for lineno, line in enumerate(f, 1):
        try:
            # 解析离线词典文本文件
            line = line.strip().decode('utf-8')
            # 词和对应的词频
            word, freq = line.split(' ')[:2]
            freq = int(freq)
            lfreq[word] = freq
            ltotal += freq
            # 获取该词所有的前缀词
            for ch in xrange(len(word)):
                wfrag = word[:ch + 1]
                # 如果某前缀词不在前缀词典中,则将对应词频设置为0
                if wfrag not in lfreq:
                    lfreq[wfrag] = 0
        except ValueError:
            raise ValueError(
                'invalid dictionary entry in %s at Line %s: %s' % (f_name, lineno, line))
    f.close()
    return lfreq, ltotal

构造有向无环图

在这里插入图片描述
对jieba分词中,每个字都以其在句子中的位置去标记。其最终会生成一个dict,key代表每个词的开头,value代表着每个词的结尾(可能有多个)。对示例“去北京大学玩”而言,其DAG结果如下:

{
0: [0] # key=0,value=[0]代表 这个词为[0:0]即【去】
1: [1,2,4] # key=1,value=[1,2,4]代表有三个词,分别为[1:1],[1:2],[1:4],即北、北京、北京大学
2: [2]
3: [3,4]
4: [4]
5: [5]
}

实现方式如下(采用伪代码的形式讲述,有兴趣的朋友可以去GitHub看他的源码):

def get_DAG(self, sentence):
	dag = {
   } # 最终生成的dag
	for k in range(len(sentense)):
		end_lst = [] # 结尾的position list
	    # 对每个position构造以该position开头的子串
		for j in range(k+1,len(sentence)):
			term = sentence[k:j]
			if term 在前缀词典,且词频>0:
				end_lst.append(j-1)
			elif term 在前缀词典,且词频=0:
				continue
			elif term 不在前缀词典中:
				# 说明有未登录词
				break
		dag[k] = end_lst
	return dag

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

在得到所有可能的切分方式构成的有向无环图后,我们发现从起点到终点存在多条路径,多条路径也就意味着存在多种分词结果,比如:

# 路径1
0 -> 1 -> 2 -> 3 -> 4 -> 5
# 分词结果1
去 / 北 / 京 / 大 / 学 / 玩
# 路径2
0 -> 1 , 2 -> 3 -> 4 -> 5
# 分词结果2
去 / 北京  /  大 / 学 / 玩
# 路径3
0 -> 1 , 2 -> 3 , 4 -> 5
# 分词结果3
去 / 北京  /  大学  /  玩
# 路径4
0 -> 1 , 2 , 3 , 4 -> 5
# 分词结果4
去 / 北京大学    /     玩
...

上一节构造的DAG是带权的,其权重为该词在前缀词典中的词频。我们的目标是,求得一个路径,使得其权重最大。

从源码可以看出,采用动态规划的方式对其进行求解(源码位置):
在这里插入图片描述
其中,logtotal为构建前缀词频时所有的词频之和的对数值,目的是为了防止下溢问题。route含义为:(最大概率对数,最大概率对数对应的词语的最后一个位置)。
其状态转移方程为:
r i = m a x ( l o g w i → j Z + r j ) , i → j ∈ D A G r_i = max(log \frac {w_{i \rightarrow j}}{Z}+r_j),i \rightarrow j \in DAG ri=max(log

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值