基于HMM的词性标注

环境:python2.7 , 数据:人民日报1998年一月标注数据

原文地址:https://blog.csdn.net/say_c_box/article/details/78550659 ,原文的代码在求发射概率的时候可能有些许错误,现更改代码如下:

举个例子来说:小明和小芳是两个城市的学生,现在小明知道小芳在下雨天时待在家看电视的概率为60%、出去逛街的概率为10%,洗衣服的概率为30%;晴天时洗衣服的概率为45%,出去逛街的概率为50%,在家看电视的概率为5%;阴天时出去逛街的概率为55%,洗衣服的概率为30%,看电视的概率为15%。那么三天内小芳出现“逛街-洗衣服-看电视”的情况的概率是多少?

HMM的构成主要有以下五个参数:

1.模型的状态数(晴天,雨天,阴天)

2.模型的观测数(逛街,洗衣,看电视)

3.模型的状态转移概率矩阵A_{ij},表示step n-1 到step n 状态i下一个是状态j的概率

4.模型的状态发射概率矩阵B_{i}(k)表示i状态下产生k观测状态的概率

5.\pi 初始状态分布概率矩阵

下面说一下具体参数的计算过程:

a_{ij} = \frac{A_{ij}}{\sum_{j=1}^{N}A_{ij}}

a_{ij}为状态i 转为状态j 的概率,A_{ij}为状态i转为状态j的频数,分母表示状态i到所有状态的总数,也就是状态i出现的次数。

b_{i}(k)=\frac{B_{I}(k)}{\sum_{j=1}^{N}b_{j}(k)}

状态i 下出现k观测值的发射概率为:状态i下的出现观测值为k的频数 / 所有状态下出现k的频数

 

\pi(i)   是初始状态分布概率矩阵,就词性标注来看,表示首个观测值为状态i的概率。

首先求解上面的参数:

 

def ChinesePOS():
    #转移概率Aij,t时刻由状态i变为状态J的频率
    #观测概率Bj(k),由状态J观测为K的概率
    #PAI i 初始状态q出现的频率
    #先验概率矩阵
    pi = {}
    a = {}
    b = {}
#所有的词语
    ww = {}
#所有的词性
    pos = []


    #每个词性出现的频率


    frep = {}
    #每个词出现的频率
    frew = {}
    fin = codecs.open("处理语料.txt","r","utf8")

    for line in fin.readlines():
        temp = line.strip().split(" ")
        for i in range(1,len(temp)):
            word = temp[i].split("/")
            if len(word) == 2:
                if word[0] not in ww:
                    ww[word[0]] = 1

                if word[1] not in pos:
                    pos.append(word[1])
    fin.close()
    ww = ww.keys()


    for i in pos:
        #初始化相关参数
        pi[i] = 0
        frep[i] = 0
        a[i] = {}
        b[i] = {}

        for j in pos:
            a[i][j] = 0
        for j in ww:
            b[i][j] = 0

    for w in ww:
        frew[w] = 0
    line_num = 0
    #计算概率矩阵
    fin= codecs.open("处理语料.txt","r","utf8")
    for line in fin.readlines():
        if line == "\n":
            continue
        tmp = line.strip().split(" ")
        n = len(tmp)
        line_num += 1

        for i in range(1,n):

            word = tmp[i].split("/")
            pre = tmp[i-1].split("/")
            #计算词性频率和词频率
            frew[word[0]] += 1
            frep[word[1]] += 1
            if i ==1:
                pi[word[1]] += 1
            else :
                a[pre[1]][word[1]] += 1

            b[word[1]][word[0]] += 1

    for i in pos:
        #计算各个词性的初始概率
        pi[i] = float(pi[i])/line_num

        for j in pos:
            if a[i][j] == 0:
                a[i][j] = 0.5

        for j in ww:
            if b[i][j] == 0:
                b[i][j] = 0.5
    for i in pos:

        for j in pos:
            #求状态i的转移概率分布
            a[i][j] = float(a[i][j])/(frep[i])

        for j in ww:
            #求词j的发射概率分布
            b[i][j] = float(b[i][j])/(frew[j])
    return a,b,pi,pos,frew,frep


    print "game over"

参数求解完毕运用动态规划的viterbi算法求解最佳路径:

def viterbi(a,b,pi,str_token,pos,frew,frep):
    # dp = {}
    #计算文本长度
    num = len(str_token)
    #绘制概率转移路径
    dp = [{} for i in range(0,num)]
    #状态转移路径
    pre = [{} for i in range(0,num)]
    for k in pos:
        for j in range(num):
            dp[j][k] = 0
            pre[j][k] = ''
#句子初始化状态概率分布(首个词在所有词性的概率分布)
    for p in pos:


        if b[p].has_key(str_token[0]):

            dp[0][p] = pi[p]*b[p][str_token[0]]* 1000
        else:
            dp[0][p] = pi[p]*0.5*1000


    for i in range(0,num):
        for j in pos:
            if (b[j].has_key(str_token[i])):
                sep = b[j][str_token[i]] * 1000
            else:
                #计算发射概率,这个词不存在,应该置0.5/frew[str_token[i]],这里默认为1
                sep = 0.5 * 1000

            for k in pos:
                #计算本step i 的状态是j的最佳概率和step i-1的最佳状态k(计算结果为step i 所有可能状态的最佳概率与其对应step i-1的最优状态)
                #
                if (dp[i][j] dp[num-1][max_state]:
            max_state = j
    # print

    i = num -1
#根据最大观测值max_state和前面求的pre找到概率最大的一条。
    while i>=0:
        resp[i] = max_state
        max_state = pre[i][max_state]
        i -= 1
    for i in range(0,num):
        print str_token[i] +"\\" +resp[i].encode("utf8")

主入口函数如下:

if __name__ == "__main__":
    a,b,pi,pos,frew,frep = ChinesePOS()
    #北京/ns 举行/v 新年/t 音乐会/n
    str_token = [u"北京",u"举行",u"新年",u"音乐会"]
    viterbi(a, b, pi, str_token, pos, frew,frep)

运行结果如下:

北京\ns
举行\v
新年\t
音乐会\n

 

数据下载链接:链接: https://pan.baidu.com/s/1tFvC9Mekyz-vQMtTrwUmvw 提取码: mb7e

你可能感兴趣的:(自然语言处理)