You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

微调BERT模型作为聊天机器人时训练报错求助

问题:微调BERT生成角色风格回复时训练报错

我尝试微调BERT模型,让它根据输入语句生成类角色风格的回复,但训练时总是出现异常错误。其中source_texts是上下文语句列表,target_texts是对应回复语句列表,代码如下:

from transformers import AutoModel, AutoTokenizer

model = AutoModel.from_pretrained("bert-base-cased").to(device)
tokenizer = AutoTokenizer.from_pretrained("bert-base-cased")

input_ids = []
output_ids = []
for i in range (0 , len(source_text)):
    input_ids.append(tokenizer.encode(source_texts[i], return_tensors="pt"))
    output_ids.append(tokenizer.encode(target_texts[i], return_tensors="pt"))

import torch
device = torch.device("cuda")

from transformers import BertForMaskedLM, AdamW

model = BertForMaskedLM.from_pretrained("bert-base-cased")
optimizer = AdamW(model.parameters(), lr=1e-5)
loss_fn = torch.nn.CrossEntropyLoss()

def train(input_id, output_id):
    input_id = input_id.to(device)
    output_id = output_id.to(device)

    model.zero_grad()
    
    logits, _ = model(input_id, labels=output_id)
    
    # Compute the loss
    loss = loss_fn(logits.view(-1, logits.size(-1)), output_id.view(-1))
    
    loss.backward()
    optimizer.step()
    return loss.item()

for epoch in range(50):
    # Train the model on the training dataset
    train_loss = 0.0
    for input_sequences, output_sequences in zip(input_ids, output_ids):
        input_sequences = input_sequences.to(device)
        output_sequences = output_sequences.to(device)
        train_loss += train(input_sequences, output_sequences)

报错信息:ValueError: too many values to unpack (expected 2)


错误原因及修复方案

1. 核心错误:模型返回值处理错误

BertForMaskedLM的forward方法返回的是**MaskedLMOutput对象**,而非可解构的元组,因此logits, _ = model(...)会触发解包错误。正确获取logits的方式是访问对象的logits属性:

outputs = model(input_id, labels=output_id)
logits = outputs.logits
# 同时可直接调用outputs.loss获取预计算的损失,无需手动定义CrossEntropyLoss

2. 其他关键问题修复

  • 变量名错误:循环中使用len(source_text),但实际变量为source_texts,会触发NameError,需改为len(source_texts)。
  • 设备定义顺序错误:在定义device前就执行了model.to(device),会触发NameError,需将import torch和device定义移至代码最开头。
  • 重复加载模型:先加载AutoModel后又重新加载BertForMaskedLM,前者无实际作用,直接删除即可。
  • 数据未做对齐:每个样本的input_ids和output_ids长度不一致,训练时会报错,需用tokenizer.pad统一长度,或在encode时指定padding='max_length'和truncation=True。
  • 模型选型偏差:BertForMaskedLM是掩码语言模型,适用于完形填空类任务,不适合直接做上下文到回复的生成任务。若要实现对话生成,更推荐使用Seq2Seq模型(如BartForConditionalGeneration、T5ForConditionalGeneration)或专门的对话模型(如BlenderBot)。若坚持用BERT,需将任务改造为掩码预测形式(拼接上下文与回复,掩码回复部分token训练)。

修复后代码示例(改用Seq2Seq模型T5)

针对对话生成任务,推荐使用T5模型,示例代码如下:

import torch
from transformers import T5Tokenizer, T5ForConditionalGeneration, AdamW

# 初始化设备
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")

# 加载模型与分词器
model_name = "t5-small"
tokenizer = T5Tokenizer.from_pretrained(model_name)
model = T5ForConditionalGeneration.from_pretrained(model_name).to(device)

# 示例数据集
source_texts = ["你好,今天天气不错", "最近有什么好看的电影?"]
target_texts = ["是啊,适合出门散步", "推荐你看《流浪地球2》,特效很棒"]

# 数据预处理:添加任务前缀(T5要求),统一长度
inputs = tokenizer(
    ["generate response: " + text for text in source_texts],
    return_tensors="pt",
    padding=True,
    truncation=True,
    max_length=512
).to(device)
labels = tokenizer(
    target_texts,
    return_tensors="pt",
    padding=True,
    truncation=True,
    max_length=512
).to(device)

# 优化器
optimizer = AdamW(model.parameters(), lr=1e-5)

# 训练函数
def train_epoch():
    model.train()
    total_loss = 0.0
    optimizer.zero_grad()
    
    outputs = model(**inputs, labels=labels["input_ids"])
    loss = outputs.loss
    loss.backward()
    optimizer.step()
    
    total_loss += loss.item()
    return total_loss / len(source_texts)

# 训练循环
for epoch in range(50):
    epoch_loss = train_epoch()
    print(f"Epoch {epoch+1}, Loss: {epoch_loss:.4f}")

坚持用BERT的掩码训练方案

若一定要基于BERT实现,可将上下文与回复拼接后掩码回复部分token,示例代码如下:

import torch
from transformers import BertTokenizer, BertForMaskedLM, AdamW

device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
tokenizer = BertTokenizer.from_pretrained("bert-base-cased")
model = BertForMaskedLM.from_pretrained("bert-base-cased").to(device)

# 示例数据集
source_texts = ["Hello, how are you?", "What's your favorite hobby?"]
target_texts = ["I'm fine, thanks!", "I like reading books."]

# 预处理:拼接文本并生成掩码标签
input_ids = []
mask_labels = []
for src, tgt in zip(source_texts, target_texts):
    # 拼接上下文与回复
    combined = tokenizer.encode_plus(src, tgt, return_tensors="pt")
    input_id = combined["input_ids"].squeeze()
    # 定位回复部分起始位置(第一个[SEP]之后)
    sep_indices = (input_id == tokenizer.sep_token_id).nonzero().squeeze()
    tgt_start_idx = sep_indices[0] + 1
    # 生成掩码标签:回复部分保留原token,其余设为-100(CrossEntropyLoss会忽略)
    mask_label = torch.full_like(input_id, -100)
    mask_label[tgt_start_idx:] = input_id[tgt_start_idx:]
    # 随机掩码回复部分token
    mask_prob = 0.15
    mask_indices = torch.rand(len(input_id)) < mask_prob
    mask_indices &= (input_id != tokenizer.cls_token_id) & (input_id != tokenizer.sep_token_id)
    input_id[mask_indices] = tokenizer.mask_token_id
    
    input_ids.append(input_id)
    mask_labels.append(mask_label)

# 统一padding长度
max_len = max(len(x) for x in input_ids)
input_ids = torch.stack([torch.nn.functional.pad(x, (0, max_len - len(x)), value=tokenizer.pad_token_id) for x in input_ids]).to(device)
mask_labels = torch.stack([torch.nn.functional.pad(x, (0, max_len - len(x)), value=-100) for x in mask_labels]).to(device)

optimizer = AdamW(model.parameters(), lr=1e-5)

# 训练函数
def train_epoch():
    model.train()
    total_loss = 0.0
    optimizer.zero_grad()
    
    outputs = model(input_ids, labels=mask_labels)
    loss = outputs.loss
    loss.backward()
    optimizer.step()
    
    total_loss += loss.item()
    return total_loss / len(source_texts)

# 训练循环
for epoch in range(50):
    epoch_loss = train_epoch()
    print(f"Epoch {epoch+1}, Loss: {epoch_loss:.4f}")

内容的提问来源于stack exchange,提问作者Prarabdha Srivastava

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.07 05:50:20