HMM

每个状态只依赖之前有限个状态
– N阶马尔科夫:依赖之前n个状态
– 1阶马尔科夫:仅仅依赖前一个状态
• p(w1,w2,w3,……,wn) = p(w1)p(w2|w1)p(w3|w1,w2)……p(wn|w1,w2,……,wn-1) =log p(w1)p(w2|w1)p(w3|w2)……p(wn|wn-1) +lambda( p(w1)+…+p(wn)-1)+…
例如:
p(w1=今天,w2=我,w3=写,w4=了,w5=一个,w6=程序)=p(w1=今天)p(w2=我|w1=今天)p(w3=写|w2=我)……p(w6=程序|w5=一个)

在分词中的应用:
依赖 依:B,赖:E

B:begin
M:middle
E:end
S:single

今天我写了一个程序。O
vd v O
BE S S S BE BE 就切为:今天/我/写/了/一个/程序

最大似然法(策略+算法)
– 状态转移概率ak,?
• P(St+1=l|St=k)=l紧跟k出现的次数/k出现的总次数
– 初始概率?k
• P(S1=k)=k作为序列开始的次数/观测序列总数

p(晴天,晴天)= p(晴天)*p(晴天|晴天)

底下的Hi,Av,AV和H都固定的,变的只有隐藏序列。
在这里插入图片描述
前向概率推导:下面的求和是隐藏状态的求和。
最后推导出:发射概率 * 转移概率 * 递归求前面的隐藏概率
在这里插入图片描述
什么时候用极大似然法,什么时候用前向后向算法:
在这里插入图片描述

hmm_train.py(隐马模型做训练)

import math


doc_path = 'C:\\Users\\zheng\\Desktop\\allfiles.txt'
mod_path = '.\\data\\hmm_mod.data'

def get_word_ch(word):
    ch_lst=[]
    i = 0
    word_len = len(word)
    while i<word_len:
        ch_lst.append(word[i])
        i+=1
    return  ch_lst

# print(get_word_ch("初始化模型参数"))

# 初始化模型参数
# 其中s状态为:B,M,E,S
STATUS_NUM = 4

# 1.初始概率
pi = [0.0 for col in range(STATUS_NUM)]
pi_sum = 0.0

# 2. 状态转移概率 a
A = [[0.0 for col in range(STATUS_NUM)] for row in range(STATUS_NUM)]
A_sum = [0.0 for col in range(STATUS_NUM)]

# 3.发射概率 b
B = [dict() for col in range(STATUS_NUM)]
B_sum = [0.0 for col in range(STATUS_NUM)]

# 打开文件,读取每一行
f_txt = open(doc_path,'r',encoding='utf-8')

while True:
    line = f_txt.readline()
    if not line:
        break
    line = line.strip()
    if len(line)<1: continue

    words = line.split()
    #一行中所有word
    ch_lst = []
    #一行中所有词的状态
    status_lst = []
    # 获取一句话中每个字符对应的B,M,E,S [0,1,2,3]状态
    for word in words:
        #一句话中所有单词
        cur_ch_lst = get_word_ch(word=word)
        #一句话的单词个数
        cur_ch_num = len(cur_ch_lst)

        # 初始化字符状态0
        cur_status_lst = [0 for ch in range(cur_ch_num)]

        # 如果只有单个字状态为3:S
        if cur_ch_num == 1:
            cur_status_lst[0] = 3
        else:
            # 标识B:0
            cur_status_lst[0] = 0
            # 标识E: 2
            cur_status_lst[-1] = 2
            # 标识M:1
            for i in range(1,cur_ch_num-1):
                cur_status_lst[i] = 1
        # 一行的所有词放到ch_lst,状态放到status_lst
        ch_lst.extend(cur_ch_lst)
        status_lst.extend(cur_status_lst)

    for i in range(len(ch_lst)):
        #取处状态
        cur_status = status_lst[i]
        #取处词
        cur_ch = ch_lst[i]
        # 存储初始量(统计每句话的开始概率π)
        if i == 0:
            pi[cur_status] += 1.0
            pi_sum += 1.0

        # 存储发射统计量B
        if cur_ch in B[cur_status]:#如果这个字符在它对应的状态的字典里面
            B[cur_status][cur_ch] += 1.0  #
        else:
            # 从状态发射到中文字符
            B[cur_status][cur_ch] =1.0
        B_sum[cur_status] += 1.0   # 统计多少个单词

        # 存储转移统计量 A
        if i+1 < len(ch_lst)-1:
            #下一个状态
            A[cur_status][status_lst[i+1]] += 1.0
            A_sum[cur_status] += 1.0

f_txt.close()

# 将统计结果转化成概率形式
for i in range(STATUS_NUM):
    pi[i]/=pi_sum
    # A
    for j in range(STATUS_NUM):
        A[i][j]/=A_sum[i]
    for ch in B[i]:
        B[i][ch] /= B_sum[i]

# 存储模型 -> 模型文件:将概率转化成log形式
f_mod =open(mod_path,'wb')
for i in range(STATUS_NUM):
    if pi[i]!=0.0:
        log_p = math.log(pi[i])
    else: log_p=0.0  #因为我们要相加的,所以必须设置成0
    f_mod.write(str(log_p).encode()+' '.encode())
f_mod.write('\n'.encode())

# A转移矩阵
for i in range(STATUS_NUM):
    for j in range(STATUS_NUM):
        if A[i][j]!=0.0:
            log_p = math.log(A[i][j])
        else:log_p = 0.0
        f_mod.write((str(log_p)+' ').encode())
    f_mod.write('\n'.encode())#这样写就行

# 发射概率
for i in range(STATUS_NUM):
    for ch in B[i]:
        if B[i][ch]!=0.0: log_p = math.log(B[i][ch])
        else:log_p = 0.0
        f_mod.write((str(ch)+' '+str(log_p)+' ').encode())
    f_mod.write('\n'.encode())
f_mod.close()

hmm_seg.py(隐马做切词)


test_txt = '.\\data\\test.txt'
mod_path = '.\\data\\hmm_mod.data'
def get_word_ch(word):
    ch_lst=[]
    i = 0
    word_len = len(word)
    while i<word_len:
        ch_lst.append(word[i])
        i+=1
    return  ch_lst

# init 参数
STATUS_NUM = 4

B = [dict() for col in range(STATUS_NUM)]
#模型
f_mod =open(mod_path,'r',encoding='utf-8')

# 加载初始log概率
pi = [float(i) for i in f_mod.readline().split()]

# 加载转移矩阵
A = [[float(j) for j in f_mod.readline().split()] for i in range(STATUS_NUM)]

# 加载发射矩阵
for i in range(STATUS_NUM):
    B_i_tokens = f_mod.readline().split()
    token_num = len(B_i_tokens)
    j = 0
    while j+1 < token_num:
        B[i][B_i_tokens[j]] = float(B_i_tokens[j+1])
        # if j<= 10:
        #     print(str(B_i_tokens[j])+":"+B_i_tokens[j+1])
        j += 2
f_mod.close()

# seg切词
f_txt = open(test_txt,'r',encoding='utf-8')
while True:
    line = f_txt.readline()
    if not line: break
    line = line.strip()

    ch_lst = get_word_ch(line)
    ch_num = len(ch_lst)

    if ch_num < 1:
        print()
        continue

    # 初始化状态矩阵
    status_matrix = [[[0.0, 0] for col in range(ch_num)] for st in range(STATUS_NUM)]

    # init
    for i in range(STATUS_NUM):
        if ch_lst[0] in B[i]:
            cur_B = B[i][ch_lst[0]]
        else:cur_B = -100000.0
        if pi[i] == 0.0:
            cur_pi = -100000.0
        else:
            cur_pi = pi[i]
        status_matrix[i][0][0] = cur_pi+cur_B
        status_matrix[i][0][1] = i

    # viterbi算法
    for i in range(1,ch_num):#从第2列开始
        for j in range(STATUS_NUM):
            max_p = None  # 存最大概率
            max_status = None  # 存状态的
            for k in range(STATUS_NUM): #k是j的前一列的状态,从k到j需要转移概率
                cur_A = A[k][j] #k到j跳转概率
                if cur_A == 0.0: cur_A = -10000.0  # 取到0得做平滑处理
                cur_p = status_matrix[k][i-1][0] +cur_A #因为是log,所以+cur_A相当于前一个k处的概率乘以跳转概率
                if max_p is None or max_p<cur_p:
                    max_p=cur_p
                    max_status = k   #贪心算法是在这几个里面取最大的,然后在这4个里面还是取最大的。动态规划是前面4个都经过我,从这4个到我的乘机概率选最大的。
            if ch_lst[i] in B[j]:
                cur_B = B[j][ch_lst[i]]
            else:
                cur_B = -100000.0
            status_matrix[j][i][0] = max_p +cur_B #保存最大概率
            status_matrix[j][i][1] = max_status   #保存上一层的状态,便于我们最后往回找的时候的路径。

    # 获取最终最大的概率,对应找到这条路径
    max_end_p = None
    max_end_status = None
    for i in range(STATUS_NUM):  #从最后一列往前数,4个状态那个概率最大的
        if max_end_p is None or status_matrix[i][ch_num-1][0]>max_end_p:
            max_end_p = status_matrix[i][ch_num-1][0]  #最后一列最大的概率
            max_end_status = i      #找出概率最大的是哪个状态
    best_status_lst = [0 for ch in range(ch_num)]    #初始化最优路径
    best_status_lst[ch_num-1] = max_end_status      #保存最有路径的状态

    i = ch_num -1
    cur_best_status = max_end_status

    #便利处最优路径
    while i>0:
        pre_best_status = status_matrix[cur_best_status][i][1]
        # 存最优路劲
        best_status_lst[i-1] = pre_best_status
        cur_best_status = pre_best_status
        i -= 1  #往前遍历
    print(line)  #原始的
    print(best_status_lst)   #最优的

    #最后按BMES切词
    out_s = ""
    out_s+= ch_lst[0]
    for i in range(1,ch_num):
        if best_status_lst[i-1] in {2,3} or best_status_lst[i] in {0,3}:
            out_s += "  "  #如果在E,S  或者 B,S中,则按空格切分
        out_s+=str(ch_lst[i])
    out_s+='\n'
    print(out_s)

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值