使用BertForQuestionAnswering构建问答机器人遇两类报错求助
解决Hugging Face QA机器人的两类报错问题
先搞懂为啥报错
- 用
DataCollatorForLanguageModeling报错:这个工具是给掩码语言建模(MLM)设计的,会自动生成labels参数,但BertForQuestionAnswering的前向传播只认start_positions和end_positions这俩标签,根本不接受labels,所以直接触发报错。 - 换
DefaultDataCollator后没返回loss:这个工具仅做基础的数据拼接,不会把你的标签转换成QA模型需要的格式,模型拿不到计算损失的必要参数,自然返回不了loss。
具体解决步骤
1. 修正预处理函数(preprocess_func)
必须让预处理后的数据带上start_positions和end_positions,这是QA模型计算损失的核心。参考实现如下:
def preprocess_func(examples): tokenizer = AutoTokenizer.from_pretrained("bert-base-chinese") # 先把intents.json的数据转成标准QA格式:问题、上下文、答案 questions = [item["question"] for item in examples["intents"]] contexts = [item["context"] for item in examples["intents"]] answers = [item["answer"] for item in examples["intents"]] # Tokenize时要返回offset_mapping,用来定位答案在token序列中的位置 tokenized = tokenizer( questions, contexts, truncation="only_second", max_length=512, return_offsets_mapping=True, padding="max_length" ) start_pos = [] end_pos = [] for i, offset in enumerate(tokenized["offset_mapping"]): ans = answers[i] start_char = ans["start"] end_char = start_char + len(ans["text"]) # 定位答案对应的token起始位置 token_start = 0 while token_start < len(offset) and offset[token_start][0] <= start_char: token_start += 1 token_start -= 1 # 定位答案对应的token结束位置 token_end = len(offset) - 1 while token_end >= 0 and offset[token_end][1] >= end_char: token_end -= 1 token_end += 1 start_pos.append(token_start) end_pos.append(token_end) # 添加模型需要的标签字段,删除用不上的offset_mapping tokenized["start_positions"] = start_pos tokenized["end_positions"] = end_pos del tokenized["offset_mapping"] return tokenized
注意:你的intents.json需要先转换为标准QA结构,每条数据需包含
question(问题)、context(上下文,若原数据无此字段,可将该意图下的所有回答拼接作为上下文)、answer(包含start字符起始位置和text答案内容)。
2. 使用正确的数据收集器
QA任务不能用MLM专属的收集器,推荐用DataCollatorForTokenClassification,它能正确处理start_positions和end_positions:
from transformers import DataCollatorForTokenClassification data_collator = DataCollatorForTokenClassification(tokenizer=tokenizer)
也可以自定义一个极简收集器,只要能将所有字段拼接成batch即可:
def qa_collator(features): batch = {} for key in features[0].keys(): if isinstance(features[0][key], torch.Tensor): batch[key] = torch.stack([f[key] for f in features]) return batch
3. 验证修复效果
改完后可以做两步验证:
- 运行预处理函数,检查输出是否包含
start_positions和end_positions字段; - 用收集器生成batch,喂给模型后查看是否能输出损失:
model = BertForQuestionAnswering.from_pretrained("bert-base-chinese") outputs = model(**batch) print(outputs.loss) # 能打印出具体数值即为修复成功
内容的提问来源于stack exchange,提问作者George
相关产品推荐
相关产品推荐

