赞
踩
本文的idea主要来源于LSTM+CRF的命名实体识别,在命名实体识别中,可以通过BIO或者BIOSE等标注进行人名、地名、机构名或者其他专有名词的识别,那么把三元组的主语、谓语、宾语(也可理解为:实体-关系-实体)三个部分当成三个需要识别的专有名词,也就可以实现三元组的抽取了,基于此想法,具体实践看看效果。
本文主要基于历史文章中的人物关系抽取,数据来源于http://www.lishixinzhi.com和http://www.uuqgs.com/
主语开头:B-SUBJECT
主语非开头:I-SUBJECT
谓语开头:B-PREDICATE
谓语非开头:I-PREDICATE
宾语开头:B-OBJECT
宾语非开头:I-OBJECT
其他:O
keras
本次抽取本质上还是基于LSTM的一个分类问题,至于CRF层,完全是为了保证序列的输出严格性,因为CRF对于预测序列有较强的的限制性,比如B-PRESON后面只能为I-PERSON或者O之类的限制。
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
embedding_1 (Embedding) (None, 91, 100) 60000
_________________________________________________________________
bidirectional_1 (Bidirection (None, 91, 100) 60400
_________________________________________________________________
time_distributed_1 (TimeDist (None, 91, 7) 707
_________________________________________________________________
crf_1 (CRF) (None, 91, 7) 119
=================================================================
Total params: 121,226
Trainable params: 61,226
Non-trainable params: 60,000
# 获取词典映射
word2id, tag2id, id2word, id2tag = getWordAndTagId('train.txt')
# 获取句子和标注
sentences, tags=getSentencesAndTags('train.txt')
# 将句子和标注转换为id
sentencesIds, tagsIds = sentencesAndTags2id(sentences, tags,word2id, tag2id)
# 将句子和标注进行填充,确保输入维度一致
sentencesIds = pad_sequences(sentencesIds, padding='post')
tagsIds = pad_sequences(tagsIds, padding='post')
print(sentencesIds.shape)
print(tagsIds.shape)
# 载入模型
model=model(len(word2id),100,sentencesIds.shape[1],len(tag2id))
# 训练
history = model.fit(sentencesIds, tagsIds.reshape([len(tagsIds),-1,1]), epochs=500)
关于训练数据,未找到合适的标注数据,只能自己标注了,如下:
长 B-SUBJECT 孙 I-SUBJECT 无 I-SUBJECT 忌 I-SUBJECT 看 O 到 O 外 B-PREDICATE 甥 I-PREDICATE 承 B-OBJECT 乾 I-OBJECT 、 O 李 B-OBJECT 泰 I-OBJECT 都 O 完 O 了 O 。 O 唐 B-SUBJECT 玄 I-SUBJECT 宗 I-SUBJECT 有 O 两 O 个 O 同 O 母 O 妹 B-PREDICATE 妹 I-PREDICATE : O 金 B-OBJECT 仙 I-OBJECT 公 I-OBJECT 主 I-OBJECT 和 O 玉 B-OBJECT 真 I-OBJECT 公 I-OBJECT 主 I-OBJECT 。 O ...此处省略n多 李 B-SUBJECT 文 I-SUBJECT 有 O 两 O 个 O 妹 B-PREDICATE 妹 I-PREDICATE , O 一 O 个 O 叫 O 宇 B-OBJECT 宇 I-OBJECT , O 一 O 个 O 叫 O 佳 B-OBJECT 佳 I-OBJECT 。 O
主要是低频词过滤字与id的映射(word2id)、预测类别与id的映射(lable2id),具体实现方式各有不同,不做重点讲解,但要特别注意未登录词的处理:
word_size = len(words)
word2id = {count[0]: index for index, count in enumerate(words,start=1)}
id2word = {index: count[0] for index, count in enumerate(words,start=1)}
tag2id = {count[0]: index for index, count in enumerate(tags)}
id2tag = {index: count[0] for index, count in enumerate(tags)}
# 填充词
word2id['<PAD>'] = 0
# 未登录词
word2id['<UNK>'] = word_size + 1
def getSentencesAndTags(filePath): ''' 从文件里面获取句子和标注 :param filePath: :return: ''' with open(filePath,encoding='utf-8') as file: wordsAndtags=[line.split() for line in file] sentences=[] tags=[] sentence=[] tag=[] for wordAndTag in wordsAndtags: if len(wordAndTag)==2: sentence.append(wordAndTag[0]) tag.append(wordAndTag[1]) else: sentences.append(sentence) tags.append(tag) sentence=[] tag = [] return sentences,tags
将输入的文本,通过词典,转换成数字序列:
def sentencesAndTags2id(sentences,tags,word2id, tag2id):
'''
将句子和标注转换为id
:param sentences:
:param tags:
:param word2id:
:param tag2id:
:return:
'''
sentencesIds = [[word2id.get(char,len(word2id)) for char in sentence] for sentence in sentences]
tagsIds = [[tag2id[char] for char in tag] for tag in tags]
return sentencesIds,tagsIds
为了保证数据的维度一致,进行句子填充
from keras_preprocessing.sequence import pad_sequences
sentencesIds = pad_sequences(sentencesIds, padding='post')
tagsIds = pad_sequences(tagsIds, padding='post')
def model(vocabSize,embeddingDim,inputLength,tagSize):
model = Sequential()
model.add(Embedding(vocabSize + 1,embeddingDim,input_length=inputLength,mask_zero=True))
model.add(Bidirectional(LSTM(50, return_sequences=True)))
model.add(TimeDistributed(Dense(tagSize)))
crf_layer = CRF(tagSize, sparse_target=True)
model.add(crf_layer)
model.compile('adam', loss=crf_layer.loss_function, metrics=[crf_layer.accuracy])
model.summary()
return model
通过简单的测试结果如下:
比较简单的句子上都能取得比较好的成果,但是由于训练数据不够,还是会出现无法抽取到结果或者抽取错误的情况,比如:
本文主要针对历史故事的人物关系进行抽取,从数据获取,到数据标注,到模型训练。由于时间和人力关系,很多方面都采取了简单模式,比如数据标注,数据量远远没有达到一个量级,比如测试环节,主要还是通过人为观察抽取结果来验证是否准确,无法达到工业级别。但对于三元组的抽取,可以作为一种参考和借鉴。
tensorflow=1.14.0
keras=2.2.4
keras-contrib=2.0.8
keras-contrib安装参考:https://github.com/keras-team/keras-contrib
另外由于本人没能及时回复的原因,代码已放在
LSTM+CRF人物关系抽取实战下载,找到资源下载模块自取
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。