• 【中文分词】隐马尔可夫模型HMM


    Nianwen Xue在《Chinese Word Segmentation as Character Tagging》中将中文分词视作为序列标注问题(sequence tagging problem),由此引入监督学习算法来解决分词问题。

    1. HMM

    首先,我们将简要地介绍HMM(主要参考了李航老师的《统计学习方法》)。HMM包含如下的五元组:

    • 状态值集合(Q={q_1, q_2, cdots, q_N}),其中(N)为可能的状态数;
    • 观测值集合(V={v_1, v_2, cdots, v_M}),其中(M)为可能的观测数;
    • 转移概率矩阵(A=left[ a_{ij} ight]),其中(a_{ij})表示从状态(i)转移到状态(j)的概率;
    • 发射概率矩阵(在[2]中称之为观测概率矩阵(B=left[ b_{j}(k) ight]),其中(b_{j}(k))表示在状态(j)的条件下生成观测(v_k)的概率;
    • 初始状态分布(pi).

    一般地,将HMM表示为模型(lambda = (A, B, pi)),状态序列为(I),对应测观测序列为(O)。对于这三个基本参数,HMM有三个基本问题:

    • 概率计算问题,在模型(lambda)下观测序列(O)出现的概率;
    • 学习问题,已知观测序列(O),估计模型(lambda)的参数,使得在该模型下观测序列(P(O|lambda))最大;
    • 解码(decoding)问题,已知模型(lambda)与观测序列(O),求解条件概率(P(I|O))最大的状态序列(I)

    2. 中文分词

    将状态值集合(Q)置为({ B, E, M, S}),分别表示词的开始、结束、中间(begin、end、middle)及字符独立成词(single);观测序列即为中文句子。比如,“今天天气不错”通过HMM求解得到状态序列“B E B E B E”,则分词结果为“今天/天气/不错”。

    通过上面例子,我们发现中文分词的任务对应于解码问题:对于字符串(C={ c_1, cdots, c_n }),求解最大条件概率

    [max P(t_1, cdots, t_n | c_1, cdots, c_n) ]

    其中,(t_i)表示字符(c_i)对应的状态。应如何求解状态序列呢?解决的办法便是Viterbi算法;其实,Viterbi算法本质上是一个动态规划算法,利用到了状态序列的最优路径满足这样一个特性:最优路径的子路径也一定是最优的。定义在时刻(t)状态为(i)的概率最大值为(delta_t(i)),则有递推公式:

    egin{equation}
    delta_{t+1}(i) = max { [delta_t(j) a_{ji}] b_i(o_{t+1}) }
    label{eq:hmm}
    end{equation}

    其中,(o_{t+1})即为字符(c_{t+1})

    3. 开源实现

    以下的源码分析基于Jieba 0.36版本。

    Jieba的jieba.finalseg实现HMM中文分词。prob_start.py定义初始状态分布(pi)

    P={'B': -0.26268660809250016,
     'E': -3.14e+100,
     'M': -3.14e+100,
     'S': -1.4652633398537678}
    

    prob_trans.py转移概率矩阵(A)

    P={'B': {'E': -0.510825623765990, 'M': -0.916290731874155},
     'E': {'B': -0.5897149736854513, 'S': -0.8085250474669937},
     'M': {'E': -0.33344856811948514, 'M': -1.2603623820268226},
     'S': {'B': -0.7211965654669841, 'S': -0.6658631448798212}}
    

    prob_emit.py定义了发射概率矩阵(B),比如,P("和"|M)表示状态为M的情况下出现“和”这个字的概率;

    P={'B': {'一': -3.6544978750449433,
           '丁': -8.125041941842026,
           '七': -7.817392401429855,
        ...}
     'S': {':': -15.828865681131282,
           '一': -4.92368982120877,
           ...}
     ...}
    

    关于训练模型的生成,作者在这里有解释,来源主要有两个:标准的切分语料 + ICTCLAS切分的txt小说。还有一个大家可能会疑惑的问题,为什么Jieba中的概率矩阵中出现了负数?不急,我们先来看看Viterbi算法的实现——jieba.finalseg.viterbi函数:

    PrevStatus = {
        'B': 'ES',
        'M': 'MB',
        'S': 'SE',
        'E': 'BM'
    }
    
    def viterbi(obs, states, start_p, trans_p, emit_p):
        V = [{}]  # tabular
        path = {}
        for y in states:  # init
            V[0][y] = start_p[y] + emit_p[y].get(obs[0], MIN_FLOAT)
            path[y] = [y]
        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])
    

    为了适配中文分词任务,Jieba对Viterbi算法做了如下的修改:

    • 状态转移时应满足PrevStatus条件,即状态(B)的前一状态只能是(E)或者(S),...
    • 最后一个状态只能是(E)或者(S),表示词的结尾。

    与此同时,Jieba在实现公式eqref{eq:hmm}时,对其求对数,将相乘转化成了相加:

    [ln delta_{t+1}(i) = max { ln delta_t(j) + ln a_{ji} + ln b_i(o_{t+1}) } ]

    这就回答了上面的问题——为什么概率矩阵中出现了负数,是因为对其求了对数。

    Jieba的HMM分词:

    from jieba.finalseg import cut
    
    sentence = "小明硕士毕业于中国科学院计算所,后在日本京都大学深造"
    print('/'.join(cut(sentence)))
    

    分词结果为“小明/硕士/毕业于/中国/科学院/计算/所/,/后/在/日/本京/都/大学/深造”,我们发现:关于“日本京都”出现分词错误的情况。这是因为最大条件概率(P(I|O))对应的状态序列不一定是分词正确的标注序列。此外,HMM做了两个基本假设:

    • 齐次Markov性假设,即任意时刻t的状态仅与前一时刻状态相关,与其他时刻的状态、时刻t均无关;
    • 观测独立性假设,任意时刻t的观测仅依赖于该时刻HMM的状态,与其他的观测及状态均无关。

    HMM受限于这两个假设(字符(c_t)仅与前一字符(c_{t-1})相关),而不能学习到更多的特征,泛化能力有限。

    4. 参考资料

    [1] Xue, Nianwen. "Chinese word segmentation as character tagging." Computational Linguistics and Chinese Language Processing 8.1 (2003): 29-48.
    [2] 李航. "统计学习方法." 清华大学出版社, 北京 (2012).
    [3] Itenyh, Itenyh版-用HMM做中文分词二:模型准备.
    [4] Django梦之队, 对Python中文分词模块结巴分词算法过程的理解和分析.(源链接挂了,为转载链接)

  • 相关阅读:
    20145334赵文豪 《Java程序设计》第3周学习总结
    2145334赵文豪《Java程序设计》第2周学习总结
    20145334赵文豪 《Java程序设计》第1周学习总结
    20145326蔡馨熤《信息安全系统设计基础》第0周学习总结
    20145326 《Java程序设计》课程总结
    20145326 《Java程序设计》实验五——Java网络编程及安全实验报告
    20145326 《Java程序设计》第10周学习总结
    20145326 《Java程序设计》第9周学习总结
    20145326实验四 Android开发基础
    20145326蔡馨熠 实验三 "敏捷开发与XP实践"
  • 原文地址:https://www.cnblogs.com/en-heng/p/6164145.html
Copyright © 2020-2023  润新知