赞
踩
命名实体识别(Named Entity Recognition, NER)是指在文本中识别出特殊对象,如人、地点、组织机构等。
基于规则的方法:利用专家手工制订的规则进行命名实体识别。举例:“赵某出生于山东省菏泽市曹县……于 11 月 22 日将刘某诉至菏泽市曹县人民法院”,构建规则,满足“地名+人民法院”的词认定为组织机构。
基于传统机器学习的方法:基于传统机器学习的方法又可分为有监督和无监督的方式。有监督的方法将 NER 转换为多分类或序列标记任务。根据标注好的数据,人工构建特征工程,然后应用机器学习算法训练模型使其对数据的模式进行学习。例如隐马尔可夫模型(HMM)、支持向量机(SVM)和条件随机场(CRF)等。
基于深度学习的方法:以端到端的方式自动检测对应输入语料中的实体类别,通过深度学习的方式自动发现隐藏的特征,抽取与实体相对应的语义信息,是现在主流的做法。
粗粒度命名实体识别对实体类别的划分比较简单,通常只是把实体划分为人名、地名、 组织机构和其他四种类型。
● PER,即 Person,表示人物,如 Michael Jordan、姚明。
● ORG,即 Organization,表示机构,如 World Health Organization、市政府。
● LOC,即 Location,表示地点,如 New York、北京。
● MISC,即 Miscellaneous,表示其他实体,需要区分于标注 O,如“东京奥运会”是一个其他实体,而O表示不属于实体的其他词。
IOBES 标注方式为例 :
● B,即 Begin,表示实体开头。
● I,即 Intermediate,表示实体中间。
● E,即 End,表示实体结尾。
● S,即 Single,表示单个词构成的实体。
● O,即 Other,表示其他,用于标记无关单词及字符。
举例:Michael Jordan visited Mars 被标注为 B-PER, E-PER, O, S-LOC。
除此之外,有IOB、BIO等标注方式。
命名实体识别任务的一个经典的解决方法是 :BiLSTM-CRF 模型。它由双向的 LSTM 网络后叠加一个 CRF 层组成,其结构如图所示:
案例分析:
以 CoNLL2003英语语料库数据集为例, 该数据集采用 BIO 标注法,实体被分为四种类型:人物(PER),地名(LOC),组织(ORG), 其他实体(MISC),即共 9 种标签,下图为数据集的一个例子。
class Conll03Reader: # 定义一个数据集读取类。 def read(self, data_path): # 用于读取训练集、验证集和测试集。 data_parts = ['train', 'valid', 'test'] dataset = {} for data_part in tqdm(data_parts): dataset[data_part] = self.read_file(str(os.path.join(data_path, data_part+’txt’))) return dataset def read_file(self, file_path): # 用于从数据集中获取第一列的词,和第四列的实体标注。 samples, tokens, tags = [], [], [] with open(file_path,'r', encoding='utf-8') as fb: for line in fb: line = line.strip('\n’) if line == '-DOCSTART- -X- -X- O': # 去除数据头 pass elif line ==‘’: # 每当一句话结束,将这句话中,词和对应标注的元组构成列表添加到samples中。 if len(tokens) != 0: samples.append((tokens, tags)) tokens = [] tags = [] else: # 数据分割,只要开头的词和最后一个实体标注。 contents = line.split(' ') tokens.append(contents[0]) tags.append(contents[-1]) return samples # 定义网络模型 class BiLSTM_CRF(nn.Module): def __init__(self, vocab_size, tag_to_ix, embedding_dim, hidden_dim): # 定义类初始化函数 super(BiLSTM_CRF, self).__init__() self.embedding_dim = embedding_dim self.hidden_dim = hidden_dim self.vocab_size = vocab_size self.tag_to_ix = tag_to_ix self.tagset_size = len(tag_to_ix) self.word_embeds = nn.Embedding(vocab_size, embedding_dim) self.lstm = nn.LSTM(embedding_dim, hidden_dim // 2, num_layers=1, bidirectional=True) self.hidden2tag = nn.Linear(hidden_dim, self.tagset_size) # 转移矩阵,transaction[i][j]表示从label_j转移到label_i的概率,虽然是随机生成的,但是后面会迭代更新 self.transitions = nn.Parameter(torch.randn(self.tagset_size, self.tagset_size)) # 设置任何标签都不可能转移到开始标签,而结束标签不可能转移到其他任何标签 self.transitions.data[tag_to_ix[START_TAG], :] = -10000 self.transitions.data[:, tag_to_ix[STOP_TAG]] = -10000 # 随机初始化lstm的输入(h_0,c_0) self.hidden = self.init_hidden() # 获取LSTM的输出 def _get_lstm_features(self, sentence): # 输入:id化的自然语言序列 # 输出:序列中每个字符的发射分数 self.hidden = self.init_hidden() embeds = self.word_embeds(sentence).view(len(sentence), 1, -1) # lstm模型的输出矩阵维度为(seq_len,batch,num_direction*hidden_dim) lstm_out, self.hidden = self.lstm(embeds, self.hidden) # 把batch维度去掉,以便接入全连接层 lstm_out = lstm_out.view(len(sentence), self.hidden_dim) # 用一个全连接层将其转换为(seq_len,tag_size)维度,才能生成最后的发射分数 lstm_feats = self.hidden2tag(lstm_out) return lstm_feats # 前向传播,得到一个最佳的路径以及路径得分 def forward(self, sentence): # 先输入BiLSTM模型中得到它的发射分数 lstm_feats = self._get_lstm_features(sentence) # 使用维特比解码得到最大概率的标注路径,作为标注序列。 score, tag_seq = self._viterbi_decode(lstm_feats) return score, tag_seq # 将标签映射到数字类别便于计算 tag_to_ix = {"B-PER": 0, "I-PER": 1,"B-ORG":2,"I-ORG":3,"B-LOC":4,"I-LOC":5,"B-MISC":6,"I-MISC":7, "O": 8, START_TAG: 9, STOP_TAG: 10} # 训练过程,文本序列输入模型,对应的标注序列转化为张量。 for epoch in range(EPOCH): for sentence, tags in training_data: # 训练前将梯度清零 optimizer.zero_grad() # 准备输入 sentence_in = prepare_sequence(sentence, word_to_ix) targets = torch.tensor([tag_to_ix[t] for t in tags], dtype=torch.long) # 前向传播,计算损失函数 loss = model.neg_log_likelihood(sentence_in, targets)# 将文本序列输入模型,得到标注路径和路径得分,并同时计算正确标注序列的路径得分,从而计算loss。 # 反向传播计算loss的梯度 loss.backward() # 通过梯度来更新模型参数 optimizer.step() # 损失函数 def neg_log_likelihood(self, sentence, tags): feats = self._get_lstm_features(sentence) # 预测路径得分 forward_score = self._forward_alg(feats) # 正确路径得分 gold_score = self._score_sentence(feats, tags) # 原本CRF是要最大化gold_score - forward_score,但深度学习一般都最小化损失函数,使得模型收敛,所以给该式子取反。 return forward_score - gold_score
CRF需要维护两个矩阵,发射矩阵和转移矩阵。发射矩阵由BiLSTM的输出得到,转移矩阵则预先定义并随机初始化,在计算过程中迭代更新。
细粒度命名实体识别是将实体识别并划分为更多的类型。细粒度命名实体识别往往对应着复杂的实体类别体系,实体类别体系可以构建树形层次结构。
FIGER 和 OntoNotes 是细粒度实体分类常用的数据集。其他细粒度命名实体识别数据集:TypeNet 拥有1080个类别,UFET 拥有10000多个细分类别。
(1)类别体系不统一、标注难度大。
即便存在详细的标注标准,细粒度实体类别体系依然没有达成统一。当类别划分不清楚时,会很难指导标注人员去标注数据。
解决方案:基于当前已有的通用知识模型(Free base 等)实现类别界定。利用大规模知识图谱等进行远程监督标注。
(2)远程监督的噪声干扰。
该方法忽视了实体的上下文内容信息,导致引入了包含上下文无关的类别噪声。解决方案:基于启发的方法把有冲突的类型的mention给删掉。
(3)实体标注少、标签不平衡。
解决方案:迁移学习方法、小样本方法。
(4)实体标签有层次。
解决方案:利用层次信息提高识别效率,比如迁移学习等。
(5)实体边界嵌套。
比如‘北京大学”,“北京”,“大学”,采用序列标注方法很难奏效。
解决方案:可以尝试机器阅读理解等方法。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。