jieba库:Tokenizer()类详解:(七)cut_DAG各个模式

2021SC@SDUSC


__cut_DAG_NO_HMM()

re_eng = re.compile('[a-zA-Z0-9]', re.U)
def __cut_DAG_NO_HMM(self, sentence):
    # 构建有向无环图
    DAG = self.get_DAG(sentence)
    route = {}
    # 计算最大概率路径
    self.calc(sentence, DAG, route)
    x = 0
    N = len(sentence)
    buf = ''
    # 使用最大概率路径进行分词
    while x < N:
        y = route[x][1] + 1
        # 选择最大概率路劲的词汇
        l_word = sentence[x:y]
        # 如果匹配到字母或数字或词汇长度为1,则添加到buf中
        if re_eng.match(l_word) and len(l_word) == 1:
            buf += l_word
            x = y
        # 否则向迭代器返回非空的buf/将词汇传给迭代器
        else:
            if buf:
                yield buf
                buf = ''
            yield l_word
            x = y
    # 若最后buf非空返回给迭代器,buf置空
    if buf:
        yield buf
        buf = ''

例子:

 _cut_DAG()

源码:

def __cut_DAG(sentence):
    # 构建有向无环图
    DAG = jieba.get_DAG(sentence)
    route = {}
    # 计算最大概率路径
    jieba.calc(sentence, DAG, route)
    x = 0
    N = len(sentence)
    buf = ''
    # 使用最大概率路径进行分词
    while x < N:
        y = route[x][1] + 1
        l_word = sentence[x:y]
        # l_word长度为一,将词汇添加到buf
        if y - x == 1:
            buf += l_word
        else:
            if buf:
                # buf长度为1,向迭代器返回buf,buf置空
                if len(buf) == 1:
                    yield buf
                    buf = ''
                else:
                    # buf长度大于1
                    # 如果没在词频字典中找到,使用finalseg.cut切分,结果逐个返回给迭代器
                    if not jieba.dt.FREQ.get(buf):
                        recognized = jieba.finalseg.cut(buf)
                        for t in recognized:
                            yield t
                    else:
                        # buf在词频字典中,则逐个返回给迭代器
                        for elem in buf:
                            yield elem
                    buf = ''
            #此时buf为空,将l_word返回给迭代器
            yield l_word
        x = y
    # 处理完成后避免buf中还有词汇,再次操作一次。
    if buf:
        if len(buf) == 1:
            yield buf
        elif not jieba.dt.FREQ.get(buf):
            recognized = jieba.finalseg.cut(buf)
            for t in recognized:
                yield t
        else:
            for elem in buf:
                yield elem

其中recognized变量使用 finalseg.cut()进行分词的结果,看到finalseg。

finalseg是基于HMM模型的中文分词,使用了viterbi算法。

def cut(sentence):
    sentence = strdecode(sentence)
    # 切割句子
    blocks = re_han.split(sentence)
    for blk in blocks:
        # 处理中文
        if re_han.match(blk):
            for word in __cut(blk):
                if word not in Force_Split_Words:
                    yield word
                else:
                    for c in word:
                        yield c
        #处理英文、数字
        else:
            tmp = re_skip.split(blk)
            for x in tmp:
                if x:
                    yield x

可以看到finalseg.cut()对中文进行了切分、分词(使用__cut()方法)

def __cut(sentence):
    global emit_P
    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]
        if pos == 'B':
            begin = i
        elif pos == 'E':
            yield sentence[begin:i + 1]
            nexti = i + 1
        elif pos == 'S':
            yield char
            nexti = i + 1
    if nexti < len(sentence):
        yield sentence[nexti:]

__cut()方法主要是对viterbi算法的结果进行一些处理,对sentence的每个字符进行了处理后返回给迭代器。

viterbi:

def viterbi(obs, states, start_p, trans_p, emit_p):
    V = [{}]  # tabular
    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)
            # 分别获取上一时刻的状态的概率,该状态到本时刻的状态的转移概率,本时刻的状态的发射概率
            # 其中,PrevStatus[y]是当前时刻的状态所对应上一时刻可能的状态
            (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])

维特比算法

维特比算法的基础可以概括成下面三点:

1.如果概率最大的路径p(或者说最短路径)经过某个点,比如图中的X22,那么这条路径上的起始点S到X22的这段子路径Q,一定是S到X22之间的最短路径。否则,用S到X22的最短路径R替代Q,便构成一条比P更短的路径,这显然是矛盾的。证明了满足最优性原理

2.从S到E的路径必定经过第i个时刻的某个状态,假定第i个时刻有k个状态,那么如果记录了从S到第i个状态的所有k个节点的最短路径,最终的最短路径必经过其中一条,这样,在任意时刻,只要考虑非常有限的最短路即可。

3. 结合以上两点,假定当我们从状态i进入状态i+1时,从S到状态i上各个节的最短路径已经找到,并且记录在这些节点上,那么在计算从起点S到第i+1状态的某个节点Xi+1的最短路径时,只要考虑从S到前一个状态i所有的k个节点的最短路径,以及从这个节点到Xi+1,j的距离即可。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值