前言
本文主要是对论文 [2302.06397] Type-Aware Decomposed Framework for Few-Shot Named Entity Recognition (arxiv.org) 中的代码进行简单的解析,只解析对 FEW-NERD 数据集的操作。具体原理部分请看论文。
源码:https://github.com/liyongqi2002/TadNER
代码流程
执行 run_fewnerd.sh 即可在 FEW-NERD 数据集上训练和测试模型。因此我们首先分析此脚本。
进入 run_fewnerd.sh 文件,对于每个我们设定的数据集,我们都会执行 main.py 函数在目标数据集上训练模型,然后根据设定的 n_way_k_shot 来验证模型的小样本学习效果。
进入 main.py 文件,首先设置了一些参数,因为每个参数都有一些说明,因此我们直接略过对这些参数的说明。接着,对一些参数进行验证,确保参数输入符合我们的要求。接着,通过 get_filepath 这个函数获取到输入的文件路径。
进入 utils.get_filepath 函数,通过这个函数我们可以获取到数据集的 train 文件,dev 文件,label 文件以及将要进行小样本学习的 test 文件的路径。
回到 main.py 文件,用一个字典来记录这些路径的名称,并将这些文件的路径加入到参数集中。接着,通过 read_labels_from_file 获取数据集的所有 label。
进入 utils.read_labels_from_file 函数,读取 label 文件中的所有 label。这里分别保存了 train,dev,test 文件中的 label,也将这三个文件的 label 整合在一起,并且加上一个非标签(O)保存起来。需要注意的是,train,dev,test 这三个文件中的 label 是互不相交的,proxy_train/dev/test 可以看作是 train/dev/test 文件中 label 的别名。
回到 main.py 文件,获取到这些 label 后,将其加入到参数集中,记录源数据集和目标数据集中的类别数目,选择使用的设备(GPU or CPU)、分词器,设置随机种子,如果选择训练模型,则调用 train 函数。
进入 train.train 函数,如果设置的是两个阶段都训练(正常情况是都训练),那么将会先后调用 train_stage1 训练一阶段模型,train_stage2 训练二阶段模型。
进入 train.train_stage1 函数,首先调用 read_conll2003_format_data_from_file 函数对源数据集的 train 文件进行处理,获取训练的句子和 label。
进入 utils.read_conll2003_format_data_from_file 函数,将句子中的每个单词和单词对应的 label 分别存储在数组中,然后将数据集中的所有数据存储在数组中,最后对获取到的句子数组和句子 label 数组进行过滤,去除掉空的数组。最终返回句子数组和句子 label 数组,具体形式为 [['When', 'reconstruction', 'of', 'the', 'building', 'was', 'complete', ',', 'the', 'rear', 'half', 'of', 'the', 'building', 'was', 'named', 'Budig', 'Hall', ',', 'for', 'then', 'KU', 'Chancellor', 'Gene', 'Budig', '.'], ['The', 'City', 'of', 'Bradenton', 'talked', 'A', "'s", 'owner', 'Charlie', 'Finley', 'into', 'staying', 'at', 'McKechnie', 'until', '.'],...] [['O', 'O', 'O', 'O', 'O', 'O', 'O', 'O', 'O', 'O', 'O', 'O', 'O', 'O', 'O', 'O', 'O', 'O', 'O', 'O', 'O', 'O', 'O', 'person-other', 'person-other', 'O'], ['O', 'O', 'O', 'O', 'O', 'O', 'O', 'O', 'person-other', 'person-other', 'O', 'O', 'O', 'O', 'O', 'O'],...]
回到 train.train_stage1 函数,调用 convert_label_to_id 函数将前面获取到的句子 label 转换为对应的 label_id(label 在前面获取到的 label2id 字典中对应的值)
进入到 utils.convert_label_to_id 函数中,将 label 转换为 id。例子:输入 [["B-art-film","I-art-film","O"],["O","O"],]
,输出 [[2,2,0],[0,0],]
。
回到 train.train_stage1 函数,调用 GetDataLoader 函数构造源训练数据集 dataloader。
进入 utils.GetDataLoader 函数,如果 ignore_o_sentence=True
,那么就要要忽略全是 O label 的句子。要想构造适合源训练数据集 dataloader,首先需要调用 convert_to_feature 函数将句子和 label 转换为 feature。
进入 utils.convert_to_feature 函数,此函数接收一个句子和句子对应的 label_id 数组。对于句子中的每个单词,首先通过分词器将单词分为一个个字母(apple -> a,p,p,l,e),然后将单词对应的 label_id 分给每个字母,单词的第一个字母对应的是 label_id,其它位置是填充,最后将这些字母和字母对应的 label_id 保存在数组中(示例:假设 apple 对应的 label_id 为 2,那么经过上面操作,最后结果为 ['a','p','p','l','e',...] ['2','-1','-1','-1','-1',...]
)。将句子和句子 label_id 转换为字母数组和字母 label_id,需要添加两个特殊字符,并通过分词器的 convert_tokens_to_ids 函数将字母 token 转换为对应的 token_id。然后将这些 token_id 填充到 max_len,使得这些 token_id 的长度保持一致。最后将这些输入构造为一个 InputFeature 对象。在这个函数中,有四个关键的变量。input_ids_padded,为句子中字母转换为 token_id 然后填充到 max_len 形成;token_type_ids,为 token 对应的句子 id,值为0或1(0表示对应的token属于第一句,1表示属于第二句);attention_mask,记录哪些是句子部分,哪些是填充部分;label_ids_tokens,记录了句子对应的 label_id,包括填充部分。
回到 utils.GetDataLoader 函数,将所有的句子都转换为 feature 后,调用 convert_features_to_dataset 将 feature 转换为 dataset,最后调用 DataLoader 构造源训练集 dataloader。
回到 train.train_stage1 函数,利用 BertModelStage1 初始化一个一阶段模型。
进入 model.BertModelStage1 这个类,这个模型部分不预测实体的类型,只预测哪些是实体,以及是实体的哪部分(根据选择的 IO_mode),具体如何实现,请看源码和论文,这里并不解析。
回到 train.train_stage1 函数,初始化模型后,选择优化方法,学习率调整策略,然后进行训练,训练完后保存权重。如此,一阶段的模型训练完成,接下来,进行二阶段模型的训练。
进入 train.train_stage2 函数,和一阶段一样的步骤训练模型,不同之处在于模型,在二阶段使用的模型是 BertModelStage2,这个模型会预测实体的类型,只预测一阶段预测的实体的类型。具体如何实现,请看源码和论文,这里并不解析。如此,二阶段的模型训练完成。
回到 main.py 文件,当要进行小样本学习并评估时,调用 evaluate 函数。
进入 evaluate.evaluate 函数,调用 evaluate_episodes 函数来进行小样本学习评估。
进入 evaluate.evaluate_episodes 函数,调用 read_episodes_data_from_file 函数读取小样本数据。
进入 utils.read_episodes_data_from_file 函数,根据设置的要读取的数据数量从 target_episodes 文件中读取数据。target_episodes 文件中每一行都是相对独立的数据,数据分为 support 和 query 数据,support 数据是用来对模型进行微调的,而 query 数据则是用来训练模型的,并且 support 和 query 数据都标有 label,这里的 label 属于 test 里面的 label。
回到 evaluate.evaluate_episodes 函数,获取到 episodes_data 数据后,让模型加载前面训练的两个阶段权重。遍历 episodes_data 中的数据(里面的数据都是相对独立的),如果采用过滤策略并且不止测试阶段一的模型,那么阶段二的模型权重需要重新加载。获取到当前 episode 的 support 的句子和 label,记录 label 中一共有几类(这就是我们拓展的新实体类),并且记录每个 label 在 label_types_id 中的索引。如果不止测试阶段二的模型,那么阶段一模型的权重也要重新加载。(因为每个 episode 数据都是独立的,所以模型的权重也要重新加载。不同 episode 中的类别并不一定是相交的,模型的预测也是按照 episode 来的,并不是叠加的)。接着,调用 adapt_predict_stage1_episode 来获取一阶段模型预测的结果。
进入 evaluate.adapt_predict_stage1_episode 函数,利用 episode 中的 support 句子和 label,构造一阶段模型的输入(bert 的输入),对模型进行训练,训练 100 次。训练完成后,利用 episode 中的 query 句子和 label,构造输入,让模型预测 query 中句子的实体位置。获取到预测结果后,对结果进行处理,获取句子的预测结果(预测结果根据选择的IO_mode会有不同的取值范围,以 IO 为例,预测的结果只有 0,1。1 代表 I),然后将预测结果转换为实体在句子中的起止索引。最后就是根据预测的和真正的实体位置计算一些指标。
回到 evaluate.evaluate_episodes 函数,获取到预测的实体位置和一些评价指标后,将评价指标保存在文件中。如果选择测试二阶段模型,同样获取到support 集中的 label。如果选择 filter,那么调用 adapt_stage2 函数对二阶段模型进行微调。
进入 evaluate.adapt_stage2 函数,调用 get_proxy_label_emb 获取 label 的 embedding 并将其放在线性层中,这时二阶段模型的输出就对应 support 集中的 label。过滤掉 label 全为 0 的句子,和阶段一的微调一样,利用 support 集构造模型的输入,然后对模型进行训练(只训练了 encoder 部分)。
回到 evaluate.evaluate_episodes 函数,利用 get_original_prototypes 获取所有句子标签对应的实体的归一化 embedding,形状为 [len(label_types_id),768]
,具体函数实现看代码注释。如果使用代理名称,那么获取proxy_label的embedding([len(label_types_id), 768]
),将 label 的embedding 和 实体的embedding 拼接在一起,最后利用 cal_span_threshold 函数计算实体识别模型中的阈值。利用 predict_stage2_episode 预测实体的 label(只预测前面一阶段模型预测的实体位置的 label)。
进入 evaluate.predict_stage2_episode 函数,利用 query 数据集构造模型输入,经过模型的 encoder 模块后获取到数据集句子的 embedding,用数组保存每个句子的 embedding。对于每个句子模型预测的实体位置和句子 embedding,对于句子预测的每个实体位置,获取实体的平均 embedding([768]
),如果使用代理名称,再拼接一个实体的平均 embedding,将 all_proto_emb 和实体平均 embedding 相乘,最后即可获取每个 label 的概率([len(label_types_id)]
),这样就可以预测出每个实体位置对应的 label。最终返回的是一个 list,list 中的每个元素是一个 list,这个 list 记录了每个句子的实体位置对应的 label。
回到 evaluate.evaluate_episodes 函数,对于每个句子,利用 extract_entity_span_label 函数将其 query_label_id 转换为实体起止和 label 的形式([{“start”:0,“end”:1,“label”:2},{“start”:3,“end”:3,“label”:7}]),再将预测的实体起止和 label 也转换为这样的形式,最后计算评价指标保存返回即可。
回到 main.py 文件,接收返回的评价指标。
如何利用自己的数据集进行训练测试
- 构造 train.json/txt,如果你的数据集和 FEW-NERD/intra/train.txt 一样,那么啥都不需要管。如果你的数据集不一样,那么就需要稍微修改。给一个示例:
我的 train.json 的数据格式为
{ "text": "第二个中信山语湖被南海区人民政府命名为休闲度假区,这个休闲度假区的揭牌仪式也是在这个时候。", "label": { "government": { "南海区人民政府": [ [ 9, 15 ] ] }, "scene": { "中信山语湖": [ [ 3, 7 ] ] } } }
那么我调用下面的函数将上面的格式转换为代码需要的格式。
def data_process_train(raw_filepath, saved_filepath, label_filepath):
"""
给句子的每个字进行标注,根据所给label将每个字分为label和O
"""
with open(label_filepath) as f:
labels_data = f.read()
json_labels = json.loads(labels_data)
label_train = json_labels["train"]
sentences = []
sentences_labels = []
with open(raw_filepath, 'r', encoding="utf-8") as f_in:
raw_data = f_in.readlines()
for json_data in raw_data:
data = json.loads(json_data)
text = data['text']
text_label = data['label']
sentence_list = list(text)
sentence_label = ['O'] * len(sentence_list)
for label in text_label:
if label in label_train:
label_content = text_label[label]
for l_c in label_content:
for label_idx in label_content[l_c]:
for idx in range(label_idx[0], label_idx[1] + 1):
sentence_label[idx] = label
sentences.append(sentence_list)
sentences_labels.append(sentence_label)
with open(saved_filepath, 'w', encoding="utf-8") as f_out:
for (sentence, sentence_label) in zip(sentences, sentences_labels):
sentence_dict = {'text': sentence, 'label': sentence_label}
f_out.write(json.dumps(sentence_dict, ensure_ascii=False) + '\n')
转换完后的数据样式为:
{"text": ["第", "二", "个", "中", "信", "山", "语", "湖", "被", "南", "海", "区", "人", "民", "政", "府", "命", "名", "为", "休", "闲", "度", "假", "区", ",", "这", "个", "休", "闲", "度", "假", "区", "的", "揭", "牌", "仪", "式", "也", "是", "在", "这", "个", "时", "候", "。"], "label": ["O", "O", "O", "scene", "scene", "scene", "scene", "scene", "O", "government", "government", "government", "government", "government", "government", "government", "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", "O", "O", "O", "O"]}
这样在读取数据进行训练时,就不能使用原来的读取数据方法,使用下面的方法即可:
def read_nerd_format_data_from_file(filepath):
with open(filepath, "r", encoding='UTF-8') as f:
data = f.readlines()
sentences_word = []
sentences_label = []
for line in data:
json_data = json.loads(line)
sentence = json_data['text']
sentence_label = json_data['label']
for label in sentence_label:
if label != 'O':
sentences_word.append(sentence)
sentences_label.append(sentence_label)
break
return sentences_word, sentences_label
-
构造 labels.jsonl 记录你数据集的 label(train 和 test 部分不相交,proxy 可以和正常的一样)
-
构造 test_n_way_k_shot.jsonl (例:test_2_3,表明 support 和 query 集中包含两个类,每个类有三个句子,这两个类不在 train 中,在 test 中)。一个例子:我原来的 test 文件为:
{ "text": "工行纽约分行的开业,经历了11年的历程。在今年中美战略经济对话前夕,", "label": { "company": { "工行纽约分行": [ [ 0, 5 ] ] } } } { "text": "年度中后期市场成交清淡,商品棉周转速度缓慢。”中国棉花协会常务副会长高芳指出。", "label": { "organization": { "中国棉花协会": [ [ 23, 28 ] ] }, "name": { "高芳": [ [ 34, 35 ] ] }, "position": { "常务副会长": [ [ 29, 33 ] ] } } } { "text": "4月20日,在郭菲成为白金卡的第10天,她高高兴兴地跑到深圳北大医院享受1000元的免费体检,“", "label": { "organization": { "深圳北大医院": [ [ 28, 33 ] ] }, "name": { "郭菲": [ [ 7, 8 ] ] } } } { "text": "通过花旗网上银行进行人民币同城或异地转账将免去全部手续费。这样一来,", "label": { "company": { "花旗网上银行": [ [ 2, 7 ] ] } } } { "text": "招行信用卡中心的一位工作人员表示,信用卡未激活并不代表没有成本,发卡行在核准发卡后,", "label": { "company": { "招行信用卡中心": [ [ 0, 6 ] ] } } } { "text": "只是门前创造机会的能力稍差。卡利亚里4战4败失8球仅入1球,积分垫底,", "label": { "organization": { "卡利亚里": [ [ 14, 17 ] ] } } } { "text": "(十)不来梅vs法兰克", "label": { "organization": { "不来梅": [ [ 3, 5 ] ], "法兰克": [ [ 8, 10 ] ] } } } { "text": "由中国摄影家协会、中国房地产业协会和新浪网举办的“我的城市我的家”在这里举行。", "label": { "organization": { "中国摄影家协会": [ [ 1, 7 ] ], "中国房地产业协会": [ [ 9, 16 ] ] }, "company": { "新浪网": [ [ 18, 20 ] ] } } } { "text": "消费者花了多少冤枉钱在里面?昨天,家具协会首次在企业推行红木家具“透明价”。将红木原料价格、工费、", "label": { "organization": { "家具协会": [ [ 17, 20 ] ] } } } { "text": "向雷曼兄弟公司转入3亿欧元。", "label": { "company": { "雷曼兄弟公司": [ [ 1, 6 ] ] } } } { "text": "需要关注其反弹动向。立博相同赔率下,上赛季至今意甲主队2平2负,其中包括罗马客场被利沃诺1比1逼平。", "label": { "organization": { "意甲": [ [ 23, 24 ] ], "罗马": [ [ 36, 37 ] ], "利沃诺": [ [ 41, 43 ] ] }, "company": { "立博": [ [ 10, 11 ] ] } } } { "text": "2009年11月26日,韩亚银行(中国)有限公司正式发行借记卡。", "label": { "company": { "韩亚银行(中国)有限公司": [ [ 12, 23 ] ] } } }
我使用下面的函数将其转换为程序需要的输入形式:
def data_process_test(raw_filepath, saved_filepath, label_filepath, mode="2_3"): with open(label_filepath) as f: labels_data = f.read() json_labels = json.loads(labels_data) label_train = json_labels["test"] sentences = [] sentences_labels = [] types = [] with open(raw_filepath, 'r', encoding="utf-8") as f_in: raw_data = f_in.readlines() for json_data in raw_data: data = json.loads(json_data) text = data['text'] text_label = data['label'] sentence_list = list(text) sentence_label = ['O'] * len(sentence_list) for label in text_label: if label in label_train: if label not in types: types.append(label) label_content = text_label[label] for l_c in label_content: for label_idx in label_content[l_c]: for idx in range(label_idx[0], label_idx[1] + 1): sentence_label[idx] = label sentences.append(sentence_list) sentences_labels.append(sentence_label) support_words = sentences[:len(sentences)//2] support_labels = sentences_labels[:len(sentences_labels)//2] query_words = sentences[len(sentences)//2:] query_labels = sentences_labels[len(sentences_labels)//2:] support_dict = {'word': support_words, 'label': support_labels} query_dict = {'word': query_words, 'label': query_labels} support_query = {'support': support_dict, 'query': query_dict, 'types': types} with open(saved_filepath, 'w', encoding='utf-8') as f_out: f_out.write(json.dumps(support_query, ensure_ascii=False) + '\n')
转换后的 test_2_3.jsonl 数据样式为:
{"support": {"word": [["工", "行", "纽", "约", "分", "行", "的", "开", "业", ",", "经", "历", "了", "1", "1", "年", "的", "历", "程", "。", "在", "今", "年", "中", "美", "战", "略", "经", "济", "对", "话", "前", "夕", ","], ["年", "度", "中", "后", "期", "市", "场", "成", "交", "清", "淡", ",", "商", "品", "棉", "周", "转", "速", "度", "缓", "慢", "。", "”", "中", "国", "棉", "花", "协", "会", "常", "务", "副", "会", "长", "高", "芳", "指", "出", "。"], ["4", "月", "2", "0", "日", ",", "在", "郭", "菲", "成", "为", "白", "金", "卡", "的", "第", "1", "0", "天", ",", "她", "高", "高", "兴", "兴", "地", "跑", "到", "深", "圳", "北", "大", "医", "院", "享", "受", "1", "0", "0", "0", "元", "的", "免", "费", "体", "检", ",", "“"], ["通", "过", "花", "旗", "网", "上", "银", "行", "进", "行", "人", "民", "币", "同", "城", "或", "异", "地", "转", "账", "将", "免", "去", "全", "部", "手", "续", "费", "。", "这", "样", "一", "来", ","], ["招", "行", "信", "用", "卡", "中", "心", "的", "一", "位", "工", "作", "人", "员", "表", "示", ",", "信", "用", "卡", "未", "激", "活", "并", "不", "代", "表", "没", "有", "成", "本", ",", "发", "卡", "行", "在", "核", "准", "发", "卡", "后", ","], ["只", "是", "门", "前", "创", "造", "机", "会", "的", "能", "力", "稍", "差", "。", "卡", "利", "亚", "里", "4", "战", "4", "败", "失", "8", "球", "仅", "入", "1", "球", ",", "积", "分", "垫", "底", ","]], "label": [["company", "company", "company", "company", "company", "company", "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", "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", "O", "organization", "organization", "organization", "organization", "organization", "organization", "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", "O", "O", "O", "O", "O", "O", "O", "O", "O", "O", "O", "O", "O", "organization", "organization", "organization", "organization", "organization", "organization", "O", "O", "O", "O", "O", "O", "O", "O", "O", "O", "O", "O", "O", "O"], ["O", "O", "company", "company", "company", "company", "company", "company", "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", "O"], ["company", "company", "company", "company", "company", "company", "company", "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", "O", "O", "O", "O", "O", "O", "O", "O", "O", "O"], ["O", "O", "O", "O", "O", "O", "O", "O", "O", "O", "O", "O", "O", "O", "organization", "organization", "organization", "organization", "O", "O", "O", "O", "O", "O", "O", "O", "O", "O", "O", "O", "O", "O", "O", "O", "O"]]}, "query": {"word": [["(", "十", ")", "不", "来", "梅", "v", "s", "法", "兰", "克"], ["由", "中", "国", "摄", "影", "家", "协", "会", "、", "中", "国", "房", "地", "产", "业", "协", "会", "和", "新", "浪", "网", "举", "办", "的", "“", "我", "的", "城", "市", "我", "的", "家", "”", "在", "这", "里", "举", "行", "。"], ["消", "费", "者", "花", "了", "多", "少", "冤", "枉", "钱", "在", "里", "面", "?", "昨", "天", ",", "家", "具", "协", "会", "首", "次", "在", "企", "业", "推", "行", "红", "木", "家", "具", "“", "透", "明", "价", "”", "。", "将", "红", "木", "原", "料", "价", "格", "、", "工", "费", "、"], ["向", "雷", "曼", "兄", "弟", "公", "司", "转", "入", "3", "亿", "欧", "元", "。"], ["需", "要", "关", "注", "其", "反", "弹", "动", "向", "。", "立", "博", "相", "同", "赔", "率", "下", ",", "上", "赛", "季", "至", "今", "意", "甲", "主", "队", "2", "平", "2", "负", ",", "其", "中", "包", "括", "罗", "马", "客", "场", "被", "利", "沃", "诺", "1", "比", "1", "逼", "平", "。"], ["2", "0", "0", "9", "年", "1", "1", "月", "2", "6", "日", ",", "韩", "亚", "银", "行", "(", "中", "国", ")", "有", "限", "公", "司", "正", "式", "发", "行", "借", "记", "卡", "。"]], "label": [["O", "O", "O", "organization", "organization", "organization", "O", "O", "organization", "organization", "organization"], ["O", "organization", "organization", "organization", "organization", "organization", "organization", "organization", "O", "organization", "organization", "organization", "organization", "organization", "organization", "organization", "organization", "O", "company", "company", "company", "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", "O", "O", "O", "O", "O", "O", "O", "O", "O", "O", "organization", "organization", "organization", "organization", "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", "O", "O", "O"], ["O", "company", "company", "company", "company", "company", "company", "O", "O", "O", "O", "O", "O", "O"], ["O", "O", "O", "O", "O", "O", "O", "O", "O", "O", "company", "company", "O", "O", "O", "O", "O", "O", "O", "O", "O", "O", "O", "organization", "organization", "O", "O", "O", "O", "O", "O", "O", "O", "O", "O", "O", "organization", "organization", "O", "O", "O", "organization", "organization", "organization", "O", "O", "O", "O", "O", "O"], ["O", "O", "O", "O", "O", "O", "O", "O", "O", "O", "O", "O", "company", "company", "company", "company", "company", "company", "company", "company", "company", "company", "company", "company", "O", "O", "O", "O", "O", "O", "O", "O"]]}, "types": ["company", "organization"]}
-
给自己的数据集取个名字,在判断 dataset_target 是否等于 ‘FEW-NERD-INTRA’ 地方把自己的数据集加进去,让我们的数据集和 FEW-NERD-INTRA 执行相同的函数(除了训练时读取数据的函数)。
-
在 utils.get_filepath 函数中仿照 FEW-NERD-INTER 的形式构造自己数据集文件的路径。
-
如果是中文数据集,那么 BertTokenizer 就需要加载中文 bert 模型。
-
最终在运行模型时,只需要将 run_fewnerd.sh 中的 datasets_target 和 n_way_k_shots 改为自己的即可。