Introduction
在自然语言处理(Natural Language Processing)中,任务很多种,大体可以分为以下几种:
- 句子级别分类任务,例如情感分类任务,检测电子邮件是否为垃圾邮件任务等;
- 单词级别的分类任务,例如命名实体识别(Named Entity Recognition, NER),词性标注(Part-of-Speech tagging, POS);
- 文本生成任务,包括根据提示prompt生成内容,以及完形填空等;
- 抽取式问答任务,给定一个问题和上下文,根据上下文中提供的信息提取问题的答案;
- 基于给定句子生成新句子任务,例如翻译任务,summary任务;
而基于HuggingFace Transformer的问答形式分为两种:
抽取式问答: 采用纯Encoder架构(例如BERT),适用于处理事实性问题,例如“谁发明了Transformer架构?”,这些问题的答案通常包含在上下文中;
生成式问答: 采用Encoder-Decoder架构(例如T5、BART),适用于处理开放式问题,例如“天空为什么是蓝色的?”,这些问题的答案通常结合上下文语义再进行抽象表达;
注:项目代码在脚注里的超链接中均可找到,有包含PretrainedModelForQuestionAnswering + Trainer的代码,也有Bert + 自定义模型的代码
Dataset
网上相关的公开数据集有很多,中文的数据集有CMRC2018等,英文的数据集有SQUAD等,数据集形式如下:
id: 用于标记文本唯一性
title: 文本所属的标题
context: 上下文句
question: 问题句
answer: 答案,其中包括:
answer_text: 答案文本内容
answer_start: 答案在context中的起始下标(char级别)
Preprocess train data
对于输入数据,采用的是“问题 + 上下文”对的输入形式,两端和中间用特殊符号进行连接,例如:
[CLS]question[SEP]context[SEP]
处理train数据集的最终目标是根据给定的answer text和answer start返回answer token span,并把start和end token级别的下标分别保存在start_positions以及end_positions中。
一般模型可接受的最长输入为384,即question和context加起来最长不能超过384,这就会出现一个问题,即:如果长度超过最长长度怎么办?方法是在做tokenize的时候多加两个参数:truncation="only_second"和return_overflowing_tokens=True,完整的tokenize的函数如下:
batch_inputs = tokenizer(
batch_question,
batch_context,
max_length=384,
padding='max_length',
truncation="only_second",
stride=128,
return_overflowing_tokens=True,
return_offsets_mapping=True,
return_tensors="pt"
)
若输入的句子过长,例如question = 100,context = 500,则切完之后变为(其中stride表明每两句sample之间重合的token有多少个):
- 100 + [0:284],
- 100 + [156:440],
- 100 + [312:596] (最后96个token是加了padding的)
其中return_overflowing_tokens会多返回一个键值对,即overflow_to_sample_mapping,该key表明当前第[j]个sample是来自原句的第[i]句example,
例如overflow_to_sample_mapping = [0,0,1,2,3,3],即第[0]和[1]子句是来自原句的第[0]句,第[2]子句是来自原句第[1]句,第[3]子句是原句第[2]句,第[4]和[5]子句是来自原句的第[3]句,这么处理了之后会产生一个问题,原来的句子答案的下标answer_start已经不准了,不准在于两点:
- 当把context拼到question后面时,原来的answer_start位置就不对了,需要加上len(question)的长度;
- 若把一句example切成两句sample后,答案只可能出现在其中一个sample里了,另外一句中就没有答案了;
针对以上两点问题,我们要做以下处理,在做处理之前,要把question和context以batch的形式放入tokenizer中,这样得到最终的结果的个数要比之前多,因为有一些example被切成了更多的sample了,同时,若某一句的offset_mapping被切开了,则第二句是接续编号的,例如,
offset_1 [(0,0),(0,3),(3,5),(5,10),(10,12),(12,20),(0,0),(0,6),(6,9),(9,13),(13,15),(15,18),(18,20),(20,23),(23,25),(25,30),(30,34),(34,35),(35.38),(38.40)]
offset_2 [(0,0),(0,3),(3,5),(5,10),(10,12),(12,20),(0,0),(40,42),(42,45),(45,50),(50,51),(51,54),(54,60),(60,62),(62,64),(64,68),(68,70),(0,0),(0,0),(0,0),(0,0)]
故针对上面这种原句被切分成多个samples的情况,已知在原句的answer_char_start为[index]的情况,返回answer_token_start和answer_token_end,操作如下:
- 根据answer char start以及answer text计算出answer char end的位置,方法为先根据sample_id以及sample mapping找到当前sample属于原句第几句,并拿到对应的answer,根据answer char start以及answer text计算出answer char end;
sample_mapping = batch_inputs.pop("overflow_to_sample_mapping")
example_id = sample_mapping[sample_idx]
answer_char_start = batch_answers[example_id]['answer_start'][0]
answer_char_end = answer_c

文章介绍了自然语言处理中的多种任务,如句子分类、词级分类和文本生成,并重点讨论了基于HuggingFaceTransformers的问答系统。它区分了抽取式和生成式问答,前者适用于事实性问题,后者用于开放式问题。文章详细阐述了数据预处理过程,包括处理过长输入的方法,以及如何确定答案的token范围。此外,还提到了模型的构建和预测阶段,以及如何从模型输出中提取最终的答案文本。
6195

被折叠的 条评论
为什么被折叠?



