jieba源代码分析——四种分词模式(六)

2021SC@SDUSC
2021SC@SDUSC
本文主要解决分词的另一块:未登陆词,也就是我们常说的新词。对于这些新词,我们前面所说的前缀词典中是不存在的,那么之前的分词方法自然就不能适用了。为了解决这一问题,jieba使用了隐马尔科夫(HMM)模型。在上上篇博客也曾经提到过。这篇文章会详细讲下发现新词的函数代码。

搜索引擎模式的分词方法在一些业务场景是需要的,但是它的进一步切分方法比较粗暴,这里可以修改代码做一些定制化的分词规则,比如切出4字词,切出重要的1字词,某些词不可切等定制化分词规则,以获取自己想要的分词结果。

jieba分词会首先调用函数cut(sentence),cut函数会先将输入句子进行解码,然后调用__cut函数进行处理。__cut函数就是jieba分词中实现HMM模型分词的主函数。__cut函数会首先调用viterbi算法,求出输入句子的隐藏状态,然后基于隐藏状态进行分词。

def __cut(sentence):
    """
    jieba首先会调用函数cut(sentence),cut函数会先将输入的句子进行解码,
    然后调用__cut()函数进行处理。该函数是实现HMM模型分词的主函数
    __cut()函数首先调用viterbi算法, 求出输入句子的隐藏状态,然后基于隐藏状态分词
    """
    global emit_P
    # 通过viterbi算法求出隐藏状态序列
    prob, pos_list = viterbi(sentence, 'BMES', start_P, trans_P, emit_P)
    begin, nexti = 0, 0
    # print pos_list, sentence
    # 基于隐藏状态进行分词
    for i, char in enumerate(sentence):
        pos = pos_list[i]
        # 如果字所处的位置是开始位置 Begin
        if pos == 'B':
            begin = i
        # 如果字所处的位置是结束位置 END
        elif pos == 'E':
            # 这个子序列就一个分词
            yield sentence[begin:i + 1]
            nexti = i + 1
        # 如果单独成字 Single
        elif pos == 'S':
            yield char
            nexti = i + 1
    # 剩余的直接作为一个分词,返回
    if nexti < len(sentence):
        yield sentence[nexti:]

关于viterbi算法的代码解释具体如下,在这里主要针对分词,所以只简单介绍一下。

def viterbi(obs, states, start_p, trans_p, emit_p):
    """
    viterbi函数会先计算各个初始状态的对数概率值,
    然后递推计算,每时刻某状态的对数概率值取决于
    上一时刻的对数概率值、
    上一时刻的状态到这一时刻的状态的转移概率、
    这一时刻状态转移到当前的字的发射概率三部分组成。
    """
    V = [{}]  # tabular表示Viterbi变量,下标表示时间
    path = {}  # 记录从当前状态回退路径
    # 时刻t=0,初始状态
    for y in states:  # init
        V[0][y] = start_p[y] + emit_p[y].get(obs[0], MIN_FLOAT) 
        path[y] = [y]
    # 时刻t = 1,...,len(obs) - 1
    for t in xrange(1, len(obs)):
        V.append({})
        newpath = {}
        # 当前时刻所处的各种可能的状态
        for y in states:
            # 获得发射概率对数
            em_p = emit_p[y].get(obs[t], MIN_FLOAT)
            # 分别获得上一时刻的状态的概率对数、该状态到本时刻的状态的转移概率对数
            # 以及本时刻的状态的发射概率
            (prob, state) = max(
                [(V[t - 1][y0] + trans_p[y0].get(y, MIN_FLOAT) + em_p, y0) for y0 in PrevStatus[y]])
            V[t][y] = prob
            # 将上一时刻最有的状态 + 这一时刻的状态
            newpath[y] = path[state] + [y]
        path = newpath
    # 最后一个时刻
    (prob, state) = max((V[len(obs) - 1][y], y) for y in 'ES')
    #返回最大概率对数和最有路径
    return (prob, path[state])
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值