NER 有哪些类型
NER根据实体一般分为三类:连续实体,嵌套实体和不连续实体。其中连续实体是最常见的使用最多的,嵌套实体也已经有了很多成熟的方法模型,但是对于不连续实体讨论的相对较少,所以下面主要讨论连续实体和嵌套实体的NER模型。
NER标注方式
首先说说NER的标注方式,主要有BIEOS、BMEOS、BIO,其中 B 表示实体名称的开始位置,I/M 表示中间位置,E 表示实体的结尾位置,S 表示单个字符的实体名称,O 表示非实体名称的字符。假设我们定义了电影名、人名和地区三类实体,分别用 MOV、PER 和 LOC 来表示,下面以结合 BIEOS 作为示例展示一下具体标注数据的格式:
《 O
影 MOV-S
》 O
是 O
由 O
张 PER-B
艺 PER-I
谋 PER-E
导 O
演 O
, O
孙 PER-B
俪 PER-E
和 O
邓 PER-B
超 PER-E
主 O
演 O
的 O
一 O
部 O
电 O
影 O
。O
连续实体NER模型
嵌套实体的NER模型主要分:机器学习算法和深度学习算法,机器学习算法可以用HMM(隐马尔科夫模型)、CRF(条件随机场)模型;深度学习模型选择比较多,下面重点介绍:
BiLSTM + CRF
from keras.preprocessing.sequence import pad_sequences
import numpy as np
from functools import reduce
from keras.layers.embeddings import Embedding
from keras.models import Sequential
from keras.layers import Bidirectional, LSTM
from keras_contrib.layers import CRF
from keras_contrib.losses import crf_loss
from keras_contrib.metrics import crf_viterbi_accuracy
class NER():
def __init__(self, vocab, embedding_dim, labels_category):
self.vocab = vocab
self.embedding_dim = embedding_dim
self.labels_category = labels_category
self.model = self.build_model()
def build_model(self):
model = Sequential()
model.add(Embedding(len(self.vocab), self.embedding_dim, mask_zero=True))
model.add(Bidirectional(LSTM(100, return_sequences=True)))
model.add(CRF(len(self.labels_category), sparse_target=True))
model.summary()
model.compile('adam', loss=crf_loss, metrics=[crf_viterbi_accuracy])
return model
def train(self, data, label, BATCH_SIZE, EPOCHS):
self.model.fit(data, label, batch_size=BATCH_SIZE, epochs=EPOCHS,validation_split=0.2)
self.model.save('crf.h5')
def predict(self, sentence, maxlen):
model = self.model
char2id = [self.vocab.get(i, 0) for i in sentence]
pad_num = maxlen - len(char2id)
input_data = pad_sequences([char2id], pad_num)
model.load_weights('crf.h5')
result = model.predict(input_data)[0][-len(char2id):]
result_label = [np.argmax(i) for i in result]
return result_label
这是预训练模型出现之前,用的比较多的深度学习模型,当然这个模型embedding层是随机初始化的,效果不如使用word2vec或glove训练的词向量,可以适当修改第20行的embedding层的初始化来使用训练好的词向量。
Bert + CRF
from bert4keras.backend import keras, K
from bert4keras.models import build_transformer_model
from bert4keras.tokenizers import Tokenizer
from bert4keras.optimizers import Adam
from bert4keras.snippets import sequence_padding, DataGenerator
from bert4keras.snippets import open, ViterbiDecoder, to_array
from bert4keras.layers import ConditionalRandomField
from keras.layers import Dense
from keras.models import Model
model = build_transformer_model(
config_path,
checkpoint_path,
)
output_layer = 'Transformer-%s-FeedForward-Norm' % (bert_layers - 1)
output = model.get_layer(output_layer).output
output = Dense(num_labels)(output)
CRF = ConditionalRandomField(lr_multiplier=crf_lr_multiplier)
output = CRF(output)
model = Model(model.input, output)
model.summary()
model.compile(
loss=CRF.sparse_loss,
optimizer=Adam(learing_rate),
metrics=[CRF.sparse_accuracy]
)
上面的代码主要来自苏神开源的bert4keras,当然你可以在bert与CRF层之间加BiLSTM层。
嵌套实体NER模型
嵌套实体的NER识别模型来自苏神的GlobalPointer.
from bert4keras.backend import keras, K
from bert4keras.backend import multilabel_categorical_crossentropy
from bert4keras.layers import GlobalPointer
from bert4keras.models import build_transformer_model
from bert4keras.tokenizers import Tokenizer
from bert4keras.optimizers import Adam
from bert4keras.snippets import sequence_padding, DataGenerator
from bert4keras.snippets import open, to_array
from keras.models import Model
def global_pointer_crossentropy(y_true, y_pred):
"""给GlobalPointer设计的交叉熵
"""
bh = K.prod(K.shape(y_pred)[:2])
y_true = K.reshape(y_true, (bh, -1))
y_pred = K.reshape(y_pred, (bh, -1))
return K.mean(multilabel_categorical_crossentropy(y_true, y_pred))
def global_pointer_f1_score(y_true, y_pred):
"""给GlobalPointer设计的F1
"""
y_pred = K.cast(K.greater(y_pred, 0), K.floatx())
return 2 * K.sum(y_true * y_pred) / K.sum(y_true + y_pred)
model = build_transformer_model(config_path, checkpoint_path)
output = GlobalPointer(len(categories), 64)(model.output)
model = Model(model.input, output)
model.summary()
model.compile(
loss=global_pointer_crossentropy,
optimizer=Adam(learning_rate),
metrics=[global_pointer_f1_score]
)
参考
https://github.com/bojone/bert4keras
GlobalPointer:用统一的方式处理嵌套和非嵌套NER
https://www.cnblogs.com/jiangxinyang/p/16303339.html
https://github.com/bojone/GlobalPointer