-
Notifications
You must be signed in to change notification settings - Fork 416
NER 说明文档
冬日新雨 edited this page Jul 8, 2020
·
15 revisions
- 该工具包是针对基于模型的 NER 任务,辅助性工具包,旨在加快模型开发,加速模型的并行预测效率。
- 该工具包中,对 NER 模型的输入、输出数据做了规范范式,数据格式有两种,一种是易于理解的
entity
格式,另外一种是方面模型接收的tag
格式,同时,模型一般接收的数据分为字符(char)级别和词汇(word)级别作为输入 token,样例如下:
>>> # 字 token
>>> text = ['胡', '静', '静', '在', '水', '利', '局', '工', '作', '。']
>>> entity = [{'text': '胡静静', 'offset': [0, 3], 'type': 'Person'},
{'text': '水利局', 'offset': [4, 7], 'type': 'Orgnization'}]
>>> tag = ['B-Person', 'I-Person', 'E-Person', 'O', 'B-Orgnization',
'I-Orgnization', 'E-Orgnization', 'O', 'O', 'O']
>>> # 词 token
>>> text = ['胡静静', '在', '重庆', '水利局', '人事科', '工作', '。']
>>> entity = [{'text': '胡静静', 'offset': [0, 1], 'type': 'Person'},
{'text': '水利局', 'offset': [2, 5], 'type': 'Orgnization'}]
>>> tag = ['S-Person', 'O', 'B-Orgnization', 'I-Orgnization', 'E-Orgnization', 'O', 'O']
- 本工具包中的所有 NER 数据处理均基于以上两种数据格式、两个 token 级别的转换。
- tag 标签的标注标准是 B(Begin)I(Inside)O(Others)E(End)S(Single) 格式。
给定文本以及其 entity,返回其对应的 tag 格式标签。
>>> import jionlp as jio
>>> token_list = '胡静静在水利局工作。' # 字级别
>>> token_list = ['胡', '静', '静', '在', '水',
'利', '局', '工', '作', '。'] # 字或词级别
>>> ner_entities =
[{'text': '胡静静', 'offset': [0, 3], 'type': 'Person'},
{'text': '水利局', 'offset': [4, 7], 'type': 'Orgnization'}]
>>> print(jio.ner.entity2tag(token_list, ner_entities))
# ['B-Person', 'I-Person', 'E-Person', 'O', 'B-Orgnization',
# 'I-Orgnization', 'E-Orgnization', 'O', 'O', 'O']
- 不支持批量处理,仅支持单条数据处理。
给定文本以及其 tag,返回其对应的 entity 格式标签。
>>> import jionlp as jio
>>> token_list = '胡静静在水利局工作。' # 字级别
>>> token_list = ['胡', '静', '静', '在', '水',
'利', '局', '工', '作', '。'] # 字或词级别
>>> tags = ['B-Person', 'I-Person', 'E-Person', 'O', 'B-Orgnization',
'I-Orgnization', 'E-Orgnization', 'O', 'O', 'O']
>>> print(jio.ner.tag2entity(token_list, tags))
# [{'text': '胡静静', 'offset': [0, 3], 'type': 'Person'},
# {'text': '水利局', 'offset': [4, 7], 'type': 'Orgnization'}]]
- 不支持批量处理,仅支持单条数据处理。
- 该步骤在实施时,一般在模型预测(predict、inference)阶段完成后,BIOES 标注标准下,实体的范式为
S|B(nI)E
,因此,必定会存在不满足范式的 tag,造成实体无法识别,因此,函数中提供了参数verbose(bool)
,指示是否打印无法转为 entity 的 tag。
给定字符级别的 token 文本以及其 entity,返回其对应的词汇级别的 entity。
>>> import jionlp as jio
>>> char_token_list = '胡静静喜欢江西红叶建筑公司' # 字级别
>>> char_token_list = [
'胡', '静', '静', '喜', '欢', '江', '西',
'红', '叶', '建', '筑', '公', '司'] # 字或词级别
>>> char_entity_list = [
{'text': '胡静静', 'offset': [0, 3], 'type': 'Person'},
{'text': '江西红叶建筑公司', 'offset': [5, 13], 'type': 'Company'}]
>>> word_token_list = ['胡静静', '喜欢', '江西', '红叶', '建筑', '公司']
>>> print(jio.ner.char2word(char_entity_list, word_token_list))
# [{'text': '胡静静', 'offset': [0, 1], 'type': 'Person'},
# {'text': '江西红叶建筑公司', 'offset': [2, 6], 'type': 'Company'}]
- 不支持批量处理,仅支持单条数据处理。
- 所有转换在 entity 格式基础上。
- 由于分词器的词汇边界可能和标注实体的边界不一致造成偏差,会导致一部分实体无法转换为词级别,称为分词偏差。因此,提供参数
verbose(bool)
打印无法转换的实体。 - 分词偏差,根据经验,jieba 分词器错误率在 4.62%,而 pkuseg 分词器错误率在 3.44%。
给定词汇级别的 token 文本以及其 entity,返回其对应的字符级别的 entity。
>>> import jionlp as jio
>>> word_entity_list = [
{'type': 'Person', 'offset': [0, 1], 'text': '胡静静'},
{'type': 'Company', 'offset': [2, 6], 'text': '江西红叶建筑公司'}]
>>> word_token_list = ['胡静静', '喜欢', '江西', '红叶', '建筑', '公司']
>>> print(jio.ner.word2char(word_entity_list, word_token_list))
# [{'text': '胡静静', 'offset': [0, 3], 'type': 'Person'},
# {'text': '江西红叶建筑公司', 'offset': [5, 13], 'type': 'Company'}]
- 不支持批量处理,仅支持单条数据处理。
- 所有转换在 entity 格式基础上。
给定各个类别的实体数据,采用基于 Trie 树的前向最大匹配方法,匹配找出文本中的实体。
>>> import jionlp as jio
>>> entity_dicts = {
'Person': ['张大山', '岳灵珊', '岳不群'],
'Organization': ['成都市第一人民医院', '四川省水利局']}
>>> lexicon_ner = jio.ner.LexiconNER(entity_dicts)
>>> text = '岳灵珊在四川省水利局上班。'
>>> result = lexicon_ner(text)
>>> print(result)
# [{'type': 'Person', 'text': '岳灵珊', 'offset': [0, 3]},
# {'type': 'Organization', 'text': '四川省水利局', 'offset': [4, 10]}]
- 实体词典如上
entity_dicts
所示。 - 所有匹配找出的实体均为 entity 格式。
- 构建 trie 树时,当同一个实体出现在多个类别中,则打印警告信息,仅默认选取其中一个类别进行构建。
NER 模型在训练好后,并行预测阶段,可以有若干种方法对模型进行加速,提高并行处理能力。具体使用方法如下:
>>> import jionlp as jio
>>> # 1、并用样例:
>>> text_list = [list(line) for line in text_list]
>>> def func(token_lists, para=1):
>>> ... token_lists = [['S-' + chr(ord(token) + para) for token in token_list]
>>> ... for token_list in token_lists]
>>> ... return token_lists
>>> max_sen_len = 70
>>> token_batch_obj = jio.ner.TokenBatchBucket(func, max_sen_len=max_sen_len, batch_size=30)
>>> token_break_obj = jio.ner.TokenBreakLongSentence(token_batch_obj, max_sen_len=max_sen_len)
>>> token_split_obj = jio.ner.TokenSplitSentence(token_break_obj, max_sen_len=max_sen_len, combine_sentences=True)
>>> res = token_split_obj(text_list, para=1) # 补充 func 函数的参数
>>> # 其中,三个工具的 max_sen_len 必须保持一致。
>>> # 2、分用样例:
>>> # 允许 TokenSplitSentence, TokenBreakLongSentence 两者结合
>>> token_break_obj = jio.ner.TokenBreakLongSentence(token_batch_obj, max_sen_len=max_sen_len)
>>> token_split_obj = jio.ner.TokenSplitSentence(token_break_obj, max_sen_len=max_sen_len, combine_sentences=True)
>>> res = token_break_obj(text_list, para=1) # 补充 func 函数的参数
- 原理说明:
- 1、将短句进行拼接,至接近最大序列长度。 一般 NER 模型在输入模型前,须首先进行分句处理。但一般较短的句子,其上下文依赖 少,不利于挖掘上下文信息;另一方面,需要大量的 pad 操作,限制了模型效率。因此 须将较短的句子逐一拼接,至接近模型允许的序列最大长度。该方法主要由 TokenSplitSentence 实现。
- 2、将超长句子进行重叠拆解,并使用规则对其进行合并。 输入的文本有一部分,长度超过模型允许序列最大长度,且无标点符号。这类句子一旦 直接应用模型,一方面造成模型并行性能急剧下降,另一方面其模型效果也会下降。因此 须将超长句子进行重叠拆分,然后再次利用规则合并,达到高速并行的效果。该方法已申 请专利。由 TokenBreakLongSentence 实现。
- 3、将相近长度的句子拼接入一个 batch,提升模型的并行能力。 在 tensorflow 等框架中,动态处理 LSTM 等 RNN 序列,会以最长的序列为基准进行 计算。因此,若句子长度均相近,长句和长句放入一个 batch,短句和短句放入一个 batch,则会减少 pad 数量,提升模型的并行能力。由 TokenBatchBucket 实现。
- 其中,样例中的
func
指,输入模型规定最长序列max_sen_len
的一个batch_size
的句子序列集,输出其对应的 tag 标签。
NER 的标注数据,经过模型训练后,预测得到的实体结果,两者之间存在差异,该方法提供了比较两者之间差异的功能。
>>> import jionlp as jio
>>> text = '张三在西藏拉萨游玩!之后去新疆。'
>>> labeled_entities = [
{'text': '张三', 'offset': [0, 2], 'type': 'Person'},
{'text': '西藏拉萨', 'offset': [3, 7], 'type': 'Location'}]
>>> predicted_entities = [
{'text': '张三在', 'offset': [0, 3], 'type': 'Person'},
{'text': '西藏拉萨', 'offset': [3, 7], 'type': 'Location'},
{'text': '新疆', 'offset': [13, 15], 'type': 'Location'}]
>>> res = jio.ner.entity_compare(
text, labeled_entities, predicted_entities, content_pad=1)
>>> print(res)
# [
# {'context': '张三在西',
# 'labeled_entity': {'text': '张三', 'offset': [0, 2], 'type': 'Person'},
# 'predicted_entity': {'text': '张三在', 'offset': [0, 3], 'type': 'Person'}},
# {'context': '去新疆。',
# 'labeled_entity': None,
# 'predicted_entity': {'text': '新疆', 'offset': [13, 15], 'type': 'Location'}}
# ]
- 针对标注语料的实体,以及模型训练后预测得到的实体,往往存在不一致,找出这些标注不一致的数据,能够有效分析模型的预测能力,找出 bad case。
- 提供了参数
context_pad(int)
,用于给出不一致实体对的上下文信息。 - 输入的文本和实体,必须以字符级别为基准,不可以用词汇级别为基准。