HuggingFace Tokenizer为何仅返回1组input_ids而非3组?
问题描述
参照HuggingFace教程对squad数据集执行分词操作,实现代码如下:
from datasets import load_dataset from transformers import RobertaTokenizer from transformers import logging logging.set_verbosity_error() dataset = load_dataset('squad') checkpoint = 'roberta-base' tokenizer = RobertaTokenizer.from_pretrained(checkpoint) def tokenize_function(example): return tokenizer(example['question'], example['context'], [d['text'][0] for d in example['answers']], truncation=True) tokenized_datasets = dataset['train'].map(tokenize_function, batched=True)
打印tokenized_datasets时,得到如下输出:
Dataset({ features: ['id', 'title', 'context', 'question', 'answers', 'input_ids', 'attention_mask'], num_rows: 87599 })
预期分词器返回3组input_ids,分别对应问题、上下文、答案三个文本,实际仅返回1组input_ids。
原因说明
核心问题是对PreTrainedTokenizer的调用参数逻辑理解有误:
- 分词器调用时仅支持2个文本类位置参数:第一个为主文本,第二个为配对文本。传入后会自动按照模型预设规则拼接成单个序列(RoBERTa的句子对拼接格式为
<s> 文本1 </s></s> 文本2 </s>),最终仅返回这组拼接序列的input_ids,不会为两段输入文本分别返回独立编码结果。 - 你传入的第三个参数(答案文本列表),没有被识别为待编码的第三段文本:按函数形参位置顺序,第三个位置参数对应
add_special_tokens配置项,你传入的非空列表会被判定为真值,等价于设置add_special_tokens=True,完全没有参与文本编码流程,自然不会生成对应的input_ids。
如果确实需要分别获取问题、上下文、答案三组文本的独立编码结果,需要对三类文本分别调用分词器,注意重命名返回的键避免冲突,参考实现:
def tokenize_function(example): question_enc = tokenizer(example['question'], truncation=True) context_enc = tokenizer(example['context'], truncation=True) answer_enc = tokenizer([d['text'][0] for d in example['answers']], truncation=True) return { "question_input_ids": question_enc["input_ids"], "question_attention_mask": question_enc["attention_mask"], "context_input_ids": context_enc["input_ids"], "context_attention_mask": context_enc["attention_mask"], "answer_input_ids": answer_enc["input_ids"], "answer_attention_mask": answer_enc["attention_mask"] }
额外说明:SQuAD属于抽取式问答任务,常规训练流程不需要单独编码答案,标准处理逻辑是拼接问题和上下文做编码,通过偏移量映射把答案在原上下文里的字符位置,转换为拼接后token序列的起止位置作为训练标签即可,单独编码答案的逻辑不符合这类任务的常规实现思路。
内容的提问来源于stack exchange,提问作者Penguin
相关产品推荐
相关产品推荐

