NLP基础:词性标注实战(HMMCRFLSTM)

it2023-10-12  63

NLP基础:词性标注实战(HMM / CRF / LSTM)

1. 预备知识1.1 隐马尔可夫模型1.1.1 HMM 简介1.1.2 词性标注理论推导 1.2 条件随机场1.3 LSTM 2. 利用 HMM 实现词性标注2.1 语料库的准备2.2 统计得到概率分布A、B、pi 2.3 利用Viterbi算法实现标注2.4 运行结果

1. 预备知识

1.1 隐马尔可夫模型

1.1.1 HMM 简介

定义:指的是一个隐层的马尔可夫链(MC)随机生成不可观测的状态序列,再由状态序列生成可观测的观测序列的过程。三个概率分布:状态序列的初始概率分布pi,由状态序列生成观测序列的观测概率分布A(也称为混淆矩阵、发射矩阵),由上一个状态到下一个状态的状态转移概率分布B(转移矩阵),这三个分布决定了一个隐马尔可夫模型。两个假设:在词向标注的理论推导中会用到这两个假设。 (1)齐次性假设:HMM的状态仅仅与上一时刻状态有关,而与其他状态或观测无关。 (2)观测独立性假设:观测仅仅取决于该时刻的状态,而与其他状态或观测无关。三个问题: (1)概率计算问题:已知HMM的三个概率分布(A、B、pi)以及观测序列,计算得到该观测序列的概率,用到的算法有前向、后向算法。 (2)学习问题:已知观测序列,求解HMM的三个概率分布,用到的算法是Baum-Welch算法,也就是EM算法。 (3)预测问题:已知HMM的三个概率分布和观测序列,尝试得到与之对应的最有可能的状态序列。词性标注POS(Part-Of-Speech)属于此类问题,已知观测序列(句子),和三个概率分布(通过大量语料可统计出来),而我们要得到的是隐藏的状态序列,也就是标注结果。这里用到的算法是Viterbi算法(动态规划)。

1.1.2 词性标注理论推导

理论推导需要用到HMM中的两个假设,并且与HMM的三大概率分布(A、B、pi)联系了起来。

1.2 条件随机场

待更

1.3 LSTM

待更

2. 利用 HMM 实现词性标注

2.1 语料库的准备

语料库的形式如下图所示,样式为 word / pos。 根据语料库得到word2id tag2id字典,为后续获得HMM中的参数A、B、pi做好准备

#根据训练数据得到word2id tag2id字典,为后续获得参数A、B、pi做好准备 path = "./data/traindata.txt" word2id = {} id2word = {} tag2id = {} id2tag = {} with open(path, 'r') as f: all_lines = f.readlines() for line in all_lines: splits = line.strip().split('/') word = splits[0] tag = splits[1] if word not in word2id: word2id[word] = len(word2id) id2word[len(id2word)] = word if tag not in tag2id: tag2id[tag] = len(tag2id) id2tag[len(id2tag)] = tag assert len(word2id) == len(id2word) assert len(tag2id) == len(id2tag) #词典大小 V = len(word2id) #tag 的类别数目 N = len(tag2id) print("Get word2id tag2id success! Voca:{}, tag :{}".format(V, N))

词库大小为18978,标注的类型一共有54种。

Get word2id tag2id success! Voca:18978, tag :54

2.2 统计得到概率分布A、B、pi

理论推导是概率的乘积,但是各个小数相乘之后的结果会变得非常非常小,造成 underflow 的现象,因此需要转为 log 相加。而log函数要求输入必须大于0,而统计的结果肯定有为0的情况,因此在Normalize归一化时,需要考虑到smoothing。在这里采用简单的Laplace Smoothing.

import numpy as np #由数据得到HMM模型的 初始概率分布pi 、观测概率分布(发射矩阵)A、状态转移概率分布B #参数初始化 pi = np.zeros(N) A = np.zeros((N, V)) B = np.zeros((N, N)) #根据数据填充三个参数,填充内容为相应的频数,后续再转化为概率分布 previous_tag = ''#上一次的tag,最开始进去时为空 with open(path, 'r') as f: all_lines = f.readlines() for line in all_lines: splits = line.strip().split('/') word = splits[0] tag = splits[1] if previous_tag == '':#句子的开端,此时B不更新 pi[tag2id[tag]] += 1 A[tag2id[tag]][word2id[word]] += 1 else:#非句子开端,此时pi不更新 A[tag2id[tag]][word2id[word]] += 1 B[tag2id[previous_tag]][tag2id[tag]] += 1 if word == '.': previous_tag = '' else: previous_tag = tag #对参数进行规范化,考虑到可能为0的情况,而后续会计算log 因此需要进行平滑,这里采用Laplace平滑 sum_pi = sum(pi) pi = np.array([(x+1)/(sum_pi + N) for x in pi]) for i in range(N): sum_A = sum(A[i]) for j in range(V): A[i][j] = (A[i][j] + 1) / (sum_A + V) sum_B = sum(B[i]) for k in range(N): B[i][k] = (B[i][k] + 1) / (sum_B + N)

2.3 利用Viterbi算法实现标注

关键在于弄清dp[i][j]的具体含义,才能较快的写出状态转移方程;保存便于回溯的back_matrix矩阵时,注意数据类型要为int,因为默认是float64,回溯时会导致矩阵索引失败

#对于输入的字符串序列,利用维特比算法(DP算法)输出最佳标注序列 def pos_Viterbi(input_str): """ :param input_str :输入的句子(默认为英文) :return :返回最佳词性标注序列结果 """ input_str = input_str.strip().split()#按照空格分开 L = len(input_str)#输入的句子长度 dp = np.zeros((L, N))#需要填充的二维数组表 back_matrix = np.zeros((L, N), dtype=int)#保存本次最优序列的来源,便于回溯,注意数据类型要为int,否则默认是float64,回溯时矩阵索引会失败 for i in range(L): word_id = word2id[input_str[i]] for j in range(N): if i == 0:#第一个字符,填充的概率应为:pi(j)*A(i,j) dp[i][j] = np.log(pi[j]) + np.log(A[j][word_id]) else: best_score = -100000 for k in range(N): score = dp[i-1][k] + np.log(A[j][word_id]) + np.log(B[k][j]) if score >= best_score: best_score = score back_matrix[i][j] = k dp[i][j] = best_score #得到dp矩阵后,利用bakc_matrix进行回溯,得到结果 result = [0 for _ in range(L)] #存储tag id列表 result[L-1] = np.argmax(dp[L-1]) for index in range(L-2, -1, -1): result[index] = back_matrix[index + 1][result[index + 1]] for y in range(L): print((input_str[y]) + '/' + (id2tag[result[y]])) return [id2tag[ID] for ID in result] #test input_str = "Social Security number , passport number and details about the services provided for the payment" pos_Viterbi(input_str)

2.4 运行结果

Social/NNP Security/NNP number/NN ,/, passport/DT number/NN and/CC details/NNS about/IN the/DT services/NNS provided/VBN for/IN the/DT payment/NN

最新回复(0)