NLP(四十)利用seqeval模塊獲取序列實(shí)體識(shí)別結(jié)果
??在文章NLP(二十三)序列標(biāo)注算法評(píng)估模塊seqeval的使用中,筆者首次介紹了seqeval模塊,它可以幫助我們很好地完成序列標(biāo)注算法的模型效果評(píng)估,并且能在Keras模型訓(xùn)練過(guò)程中引入。
??其實(shí),在seqeval模塊中還有一個(gè)get_entities函數(shù),它能幫助我們迅速地從一個(gè)標(biāo)注序列中獲取完整實(shí)體,支持常規(guī)的BIO、BMESO等標(biāo)注方式。讓我們來(lái)看下該函數(shù)的源代碼:
def get_entities(seq, suffix=False):
"""Gets entities from sequence.
Args:
seq (list): sequence of labels.
Returns:
list: list of (chunk_type, chunk_start, chunk_end).
Example:
>>> from seqeval.metrics.sequence_labeling import get_entities
>>> seq = ['B-PER', 'I-PER', 'O', 'B-LOC']
>>> get_entities(seq)
[('PER', 0, 1), ('LOC', 3, 3)]
"""
# for nested list
if any(isinstance(s, list) for s in seq):
seq = [item for sublist in seq for item in sublist + ['O']]
prev_tag = 'O'
prev_type = ''
begin_offset = 0
chunks = []
for i, chunk in enumerate(seq + ['O']):
if suffix:
tag = chunk[-1]
type_ = chunk.split('-')[0]
else:
tag = chunk[0]
type_ = chunk.split('-')[-1]
if end_of_chunk(prev_tag, tag, prev_type, type_):
chunks.append((prev_type, begin_offset, i-1))
if start_of_chunk(prev_tag, tag, prev_type, type_):
begin_offset = i
prev_tag = tag
prev_type = type_
return chunks
該函數(shù)的輸入為標(biāo)注序列,輸出結(jié)果為實(shí)體列表,包含實(shí)體類型、實(shí)體開(kāi)始下標(biāo)和結(jié)束下標(biāo)。
??我們以文章NLP入門(六)pyltp的介紹與使用中的命名實(shí)體識(shí)別程序?yàn)槔?,同時(shí)采用自己提取標(biāo)注序列中的實(shí)體識(shí)別信息和使用seqeval模塊提取標(biāo)注序列中的實(shí)體識(shí)別信息兩種方式,實(shí)現(xiàn)代碼如下:
# -*- coding: utf-8 -*-
import os
from pyltp import Segmentor, Postagger
# 分詞
cws_model_path = os.path.join(os.path.dirname(__file__), 'ltp_v3.4/cws.model') # 分詞模型路徑,模型名稱為`cws.model`
lexicon_path = os.path.join(os.path.dirname(__file__), 'ltp_v3.4/lexicon.txt') # 參數(shù)lexicon是自定義詞典的文件路徑
segmentor = Segmentor()
segmentor.load_with_lexicon(cws_model_path, lexicon_path)
sent = "據(jù)韓聯(lián)社12月28日反映,美國(guó)防部發(fā)言人杰夫·莫萊爾27日表示,美國(guó)防部長(zhǎng)蓋茨將于2011年1月14日訪問(wèn)韓國(guó)。"
# sent = "記者4日從中國(guó)航空工業(yè)集團(tuán)有限公司獲悉,AG600項(xiàng)目研制加速推進(jìn),001架機(jī)在成功完成陸上、水上、海上首飛之后,于3月4日在湖北荊門漳河機(jī)場(chǎng)完成滅火任務(wù)系統(tǒng)首次科研試飛,飛機(jī)狀態(tài)良好。"
# sent = "大臨鐵路通車當(dāng)天,81歲的佤族老人田學(xué)明專程趕到臨滄站,觀看列車發(fā)車?!拔易畲蟮膲?mèng)想,就是有一天火車能開(kāi)進(jìn)阿佤山。今天,我的夢(mèng)想終于實(shí)現(xiàn)了!”"
words = segmentor.segment(sent) # 分詞
# 詞性標(biāo)注
pos_model_path = os.path.join(os.path.dirname(__file__), 'ltp_v3.4/pos.model') # 詞性標(biāo)注模型路徑,模型名稱為`pos.model`
postagger = Postagger() # 初始化實(shí)例
postagger.load(pos_model_path) # 加載模型
postags = postagger.postag(words) # 詞性標(biāo)注
ner_model_path = os.path.join(os.path.dirname(__file__), 'ltp_v3.4/ner.model') # 命名實(shí)體識(shí)別模型路徑,模型名稱為`pos.model`
from pyltp import NamedEntityRecognizer
recognizer = NamedEntityRecognizer() # 初始化實(shí)例
recognizer.load(ner_model_path) # 加載模型
netags = list(recognizer.recognize(words, postags)) # 命名實(shí)體識(shí)別
print(list(words))
print(netags)
# 用自己的方法提取識(shí)別結(jié)果中的人名,地名,組織機(jī)構(gòu)名
persons, places, orgs = set(), set(), set()
i = 0
for tag, word in zip(netags, words):
j = i
# 人名
if 'Nh' in tag:
if str(tag).startswith('S'):
persons.add(word)
elif str(tag).startswith('B'):
union_person = word
while netags[j] != 'E-Nh':
j += 1
if j < len(words):
union_person += words[j]
persons.add(union_person)
# 地名
if 'Ns' in tag:
if str(tag).startswith('S'):
places.add(word)
elif str(tag).startswith('B'):
union_place = word
while netags[j] != 'E-Ns':
j += 1
if j < len(words):
union_place += words[j]
places.add(union_place)
# 機(jī)構(gòu)名
if 'Ni' in tag:
if str(tag).startswith('S'):
orgs.add(word)
elif str(tag).startswith('B'):
union_org = word
while netags[j] != 'E-Ni':
j += 1
if j < len(words):
union_org += words[j]
orgs.add(union_org)
i += 1
print('人名:', ','.join(persons))
print('地名:', ','.join(places))
print('組織機(jī)構(gòu):', ','.join(orgs))
# 用seqeval提取識(shí)別結(jié)果中的人名,地名,組織機(jī)構(gòu)名
from seqeval.metrics.sequence_labeling import get_entities
from collections import defaultdict
seq_result = get_entities(netags)
words = list(words)
ner_result_dict = defaultdict(list)
for seq_res in seq_result:
ner_result_dict[seq_res[0]].append("".join(words[seq_res[1]:seq_res[2]+1]))
print('人名:', ner_result_dict["Nh"])
print('地名:', ner_result_dict["Ns"])
print('組織機(jī)構(gòu):', ner_result_dict["Ni"])
# 釋放模型
segmentor.release()
postagger.release()
recognizer.release()
輸出結(jié)果如下:
['據(jù)', '韓聯(lián)社', '12月', '28日', '反映', ',', '美', '國(guó)防部', '發(fā)言人', '杰夫·莫萊爾', '27日', '表示', ',', '美', '國(guó)防部長(zhǎng)', '蓋茨', '將', '于', '2011年', '1月', '14日', '訪問(wèn)', '韓國(guó)', '。']
['O', 'S-Ni', 'O', 'O', 'O', 'O', 'B-Ni', 'E-Ni', 'O', 'S-Nh', 'O', 'O', 'O', 'S-Ns', 'O', 'S-Nh', 'O', 'O', 'O', 'O', 'O', 'O', 'S-Ns', 'O']
人名: 蓋茨,杰夫·莫萊爾
地名: 韓國(guó),美
組織機(jī)構(gòu): 美國(guó)防部,韓聯(lián)社
人名: ['杰夫·莫萊爾', '蓋茨']
地名: ['美', '韓國(guó)']
組織機(jī)構(gòu): ['韓聯(lián)社', '美國(guó)防部']
我們?cè)賴L試兩個(gè)句子,識(shí)別結(jié)果如下:
['記者', '4日', '從', '中國(guó)', '航空', '工業(yè)', '集團(tuán)', '有限公司', '獲悉', ',', 'AG600', '項(xiàng)目', '研制', '加速', '推進(jìn)', ',', '001架機(jī)', '在', '成功', '完成', '陸上', '、', '水上', '、', '海上', '首', '飛', '之后', ',', '于', '3月', '4日', '在', '湖北', '荊門', '漳河', '機(jī)場(chǎng)', '完成', '滅火', '任務(wù)', '系統(tǒng)', '首', '次', '科研', '試飛', ',', '飛機(jī)', '狀態(tài)', '良好', '。']
['O', 'O', 'O', 'B-Ni', 'I-Ni', 'I-Ni', 'I-Ni', 'E-Ni', 'O', 'O', 'O', 'O', 'O', 'O', 'O', 'O', 'O', 'O', 'O', 'O', 'O', 'O', 'O', 'O', 'O', 'O', 'O', 'O', 'O', 'O', 'O', 'O', 'O', 'B-Ns', 'I-Ns', 'I-Ns', 'E-Ns', 'O', 'O', 'O', 'O', 'O', 'O', 'O', 'O', 'O', 'O', 'O', 'O', 'O']
人名:
地名: 湖北荊門漳河機(jī)場(chǎng)
組織機(jī)構(gòu): 中國(guó)航空工業(yè)集團(tuán)有限公司
人名: []
地名: ['湖北荊門漳河機(jī)場(chǎng)']
組織機(jī)構(gòu): ['中國(guó)航空工業(yè)集團(tuán)有限公司']
['大臨鐵路', '通車', '當(dāng)天', ',', '81', '歲', '的', '佤族', '老人', '田學(xué)明', '專程', '趕到', '臨滄站', ',', '觀看', '列車', '發(fā)車', '。', '“', '我', '最', '大', '的', '夢(mèng)想', ',', '就', '是', '有', '一', '天', '火車', '能', '開(kāi)進(jìn)', '阿佤山', '。', '今天', ',', '我', '的', '夢(mèng)想', '終于', '實(shí)現(xiàn)', '了', '!', '”']
['O', 'O', 'O', 'O', 'O', 'O', 'O', 'O', 'O', 'S-Nh', 'O', 'O', 'O', 'O', 'O', 'O', 'O', 'O', 'O', 'O', 'O', 'O', 'O', 'O', 'O', 'O', 'O', 'O', 'O', 'O', 'O', 'O', 'O', 'S-Ns', 'O', 'O', 'O', 'O', 'O', 'O', 'O', 'O', 'O', 'O', 'O']
人名: 田學(xué)明
地名: 阿佤山
組織機(jī)構(gòu):
人名: ['田學(xué)明']
地名: ['阿佤山']
組織機(jī)構(gòu): []
從上我們可以發(fā)現(xiàn):
使用seqeval的實(shí)現(xiàn)方式與自己的實(shí)現(xiàn)方式效果一致;
seqeval的實(shí)現(xiàn)方式更加簡(jiǎn)潔高效,從代碼上看,seqeval只需3-4行代碼,而自己實(shí)現(xiàn)需20-30行代碼。
??本文介紹了如何使用seqeval模塊快速地從標(biāo)注序列中提取實(shí)體識(shí)別結(jié)果,這是我們?cè)谧雒麑?shí)體識(shí)別任務(wù)時(shí)經(jīng)常會(huì)碰到的問(wèn)題,使用seqeval能夠提升我們的工作效果,使代碼更加簡(jiǎn)潔優(yōu)雅。
??本次分享到此結(jié)束,感謝大家閱讀~
??2021年3月5日于上海楊浦
