命名实体识别

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

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值