一、命名实体识别介绍
命名实体识别(英语:Named Entity Recognition,简称NER)是指识别文本中具有特定意义的实体,主要包括人名、地名、机构名、专有名词等等,并把我们需要识别的词在文本序列中标注出来。 例如有一段文本:济南市成立自由贸易试验区. 我们要在上面文本中识别一些区域和地点,那么我们需要识别出来内容有: 济南市(地点),自由贸易试验区(地点). 在我们今天使用的NER数据集中,一共有7个标签(每个字都要对应一个标签,标签前缀B表示当前字为实体的第一个字begin,I表示实体除第一个以外的字,也就是内部的字in):
序号 | 标签 | 含义 |
---|
1 | B-ORG | 组织或公司(organization) | 2 | I-ORG | 组织或公司(organization) | 3 | B-PER | 人名(person) | 4 | I-PER | 人名(person) | 5 | O | 其他非实体(other) | 6 | B-LOC | 地名(location) | 7 | I-LOC | 地名 |
比如说"自贸区"对应的标注是:自(B-LOC)贸(I-LOC)区(1I-LOC),这三个字都对应一个"地名"的标签,但是第一个字属于实体开头的字,所以使用"B"开头的标签,后面两个字的标签都是"I"开头.
二、隐马尔可夫模型介绍HMM
HMM模型是概率图模型的一种,属于生成模型,笼统的说,我们上面说的"BIO"的实体标签,就是一个不可观测的隐状态,而HMM模型描述的就是由这些隐状态序列(实体标记)生成可观测结果(可读文本)的过程. 在我们今天的问题当中,隐状态序列是实体标记序列,而可观测序列是我们可读的原始语料文本序列. 以“自贸区”为例: 隐藏状态序列:B-LOC | I-LOC | I-LOC 观测结果序列: 自 | 贸 | 区 隐马尔可夫模型的示意图如下所示; HMM 的定义建立在两个基本假设的前提上,这两个假设是 HMM 的重点,一定要了解模型的 2 个假设。 1)齐次马尔科夫假设 齐次马尔科夫假设,通俗地说就是 HMM 的任一时刻 t 的某一状态只依赖于其前一时刻的状态,与其它时刻的状态及观测无关,也与时刻 t 无关。 2)观测独立假设 观测独立性假设,是任一时刻的观测只依赖于该时刻的马尔科夫链的状态,与其他观测及状态无关。 以上这两个假设是 HMM 的核心,之后的公式推导都是依赖这两个假设成立的基础上进行的。
三、隐马尔可夫模型三个重要参数
1.HMM的状态转移概率 我们上面提到了P(I(t)|I(t-1))指的是隐状态i从t一1时刻转向t时刻的概率,比如说我们现在实体标签一共有7种,也就是N =7(注意N是所有可能的实体标签种类的集合),也就是Qhidden = {qo q1…,q6}(注意我们实体标签编号从O算起),假设在t -1时刻任何一种实体标签都可以在t时刻转换为任何一种其他类型的实体标签,则总共可能的转换的路径一共有NN种,所以我们可以做一个NN的矩阵来表示所有可能的隐状态转移概率.
2.HMM的发射概率 我们之前提到了任意时刻观测Ot,只依赖于当前时刻的隐状态It,也就是P(O(t)|I(t)),也叫做发射概率,指的是隐状态生成观测结果的过程.设我们的字典里有M个字,Vobs.= {v(0), v(1),…,v(m-1)}(注意这里下标从0算起,所以最后的下标是m-1,一共有m种观测),则每种实体标签(隐状态)可以生成m种不同的汉字(也就是观测),这一过程可以用一个发射概率矩阵来表示,他的维度是N*M. 3.HMM初始隐状态概率
四、隐马尔可夫模型的参数估计
Π的计算可以理解为qi在第一个字的次数/总次数 A的计算可以理解为qi转移到qj的次数/qi出现的总次数 B的计算可以理解为 qi和Vk同时出现的次数/qj出现的次数
五、维特比算法
维特比算法viterbi algorithm使用了动态规划算法来解决类似HMM和CRF的预测问题, 用维特比算法可以找到概率最大路径,也就是最优路径,在我们今天要解决的序列标注问题中,就要通过维特比算法,来找到文本所对应的最优的实体标注序列. 如果用一句话来概括维特比算法,那就是: 在每一时刻,计算当前时刻落在每种隐状态的最大概率,并记录这个最大概率是从前一时刻哪个隐状态转移过来的,最后再从结尾回溯最大概率,最终得到最大概率路径,也就是最优路径 可以用下图(有向无环图)表示维特比算法的每个状态及最大概率关系: 上图从左往右时序递增,虚线由初始状态概率决定,实线则是转移概率。由于受到观测序列的约束,不同状态发射观测的概率不同,所以每个节点本身也必须计算自己的花费,由发射概率决定。又由于 Yt+1 仅依赖于 Yt,所以网状图可以动态规划的搜索,也就是维特比算法
1.初始化,t=1 时初始最优路径的备选由 N 个状态组成,它们的前驱为空。 其中,δ 存储在时刻 t 以 Si 结尾的所有局部路径的最大概率。ψ 存储局部最优路径末状态 Yt 的前驱状态。
2.递推,t >= 2 时每条备选路径像贪吃蛇一样吃入一个新状态,长度增加一个单位,根据转移概率和发射概率计算花费。找出新的局部最优路径,更新 δ、ψ 两个数组 3.终止,找出最终时刻 δt,i 数组中的最大概率 P*,以及相应的结尾状态下标 i*t。 4.回溯,根据前驱数组 ψ 回溯前驱状态,取得最优路径状态下标。
六、代码实现
利用训练数据获取HMM模型对应的参数A,B和Pi
""" HMM 参数构建 """
import numpy as np
N, M = len(tag2id), len(word2id)
A = np.zeros(shape=(N, N), dtype=float)
B = np.zeros(shape=(N, M), dtype=float)
Pi = np.zeros(shape=N, dtype=float)
""" 构建转移概率矩阵 """
for tag_list in train_tag_lists:
seq_len = len(tag_list)
for i in range(seq_len - 1):
current_tagid = tag2id[tag_list[i]]
next_tagid = tag2id[tag_list[i+1]]
A[current_tagid][next_tagid] += 1
A[A == 0.] = 1e-10
A = A / np.sum(a=A, axis=1, keepdims=True)
""" 构建观测概率矩阵 """
for tag_list, word_list in zip(train_tag_lists, train_word_lists):
assert len(tag_list) == len(word_list)
for tag, word in zip(tag_list, word_list):
tag_id = tag2id[tag]
word_id = word2id[word]
B[tag_id][word_id] += 1
B[B == 0.] = 1e-10
B = B / np.sum(a=B, axis=1, keepdims=True)
""" 构建初始状态概率 """
for tag_list in train_tag_lists:
init_tagid = tag2id[tag_list[0]]
Pi[init_tagid] += 1
Pi[Pi == 0.] = 1e-10
Pi = Pi / np.sum(a=Pi)
""" 维特比算法 """
def viterbi(word_list, word2id, tag2id):
"""
使用维特比算法对给定观测序列求状态序列, 这里就是对字组成的序列,求其对应的标注。
维特比算法实际是用动态规划解隐马尔可夫模型预测问题,即用动态规划求概率最大路径(最优路径)
这时一条路径对应着一个状态序列
"""
ALog = np.log(A)
BLog = np.log(B)
PiLog = np.log(Pi)
seq_len = len(word_list)
viterbi = np.zeros(shape=(N, seq_len), dtype=float)
backpointer = np.zeros(shape=(N, seq_len), dtype=float)
start_wordid = word2id.get(word_list[0], None)
Bt = BLog.T
if start_wordid is None:
bt = np.log(np.ones(shape=N, dtype=float) / N)
else:
bt = Bt[start_wordid]
viterbi[:, 0] = PiLog + bt
backpointer[:, 0] = -1
for step in range(1, seq_len):
wordid = word2id.get(word_list[step], None)
if wordid is None:
bt = np.log(np.ones(N) / N)
else:
bt = Bt[wordid]
for tag_id in range(len(tag2id)):
max_prob = np.max(a=viterbi[:, step - 1] + ALog[:, tag_id], axis=0)
max_id = np.argmax(a=viterbi[:, step - 1] + ALog[:, tag_id], axis=0)
viterbi[tag_id, step] = max_prob + bt[tag_id]
backpointer[tag_id, step] = max_id
best_path_prob = np.max(a=viterbi[:, seq_len - 1], axis=0)
best_path_pointer = np.argmax(a=viterbi[:, seq_len - 1], axis=0)
best_path_pointer = int(best_path_pointer)
best_path = [best_path_pointer]
for back_step in range(seq_len-1, 0, -1):
best_path_pointer = backpointer[best_path_pointer, back_step]
best_path_pointer = int(best_path_pointer)
best_path.append(best_path_pointer)
assert len(best_path) == len(word_list)
id2tag = dict((id_, tag) for tag, id_ in tag2id.items())
tag_list = [id2tag[id_] for id_ in reversed(best_path)]
return tag_list
""" 利用HMM识别ner_char_data目录下test.txt中的数据"""
pred_tag_lists = []
for word_list in test_word_lists:
pred_tag_list = viterbi(word_list, word2id, tag2id)
pred_tag_lists.append(pred_tag_list)
""" HMM 评测 """
from evaluating import Metrics
metrics = Metrics(test_tag_lists, pred_tag_lists, remove_O=False)
metrics.report_scores()
metrics.report_confusion_matrix()
|