原帖把包含七个张量字段的 TensorDataset 直接交给 Transformers Trainer;它返回 tuple,而训练端的数据整理器按另一种对象结构处理:
TypeError: vars() argument must have dict attribute
样本格式与模型签名要同时匹配
原题准备了三个输入和三个 mask,却把 TensorDataset 的 tuple 直接交给 Trainer。旧整理器尝试 vars() 时要求对象带属性字典;问题发生在整理 batch 的阶段,不是 RoBERTa 的注意力计算。修成字典之后,还必须保证每个键真是 forward 能接收的参数。
先检查传给 Trainer 的数据集样本和模型签名。自己创建的普通 DataLoader 并没有被传给 Trainer,单独修改它的 collate_fn 不会影响 Trainer。
import inspect
print("josh050608")
print("sample:", type(train_data[0]), [t.shape for t in train_data[0]])
print("forward:", inspect.signature(model.forward))
原帖的每项顺序是三份 token ID、三份 mask、一个标签。可以显式提供 data_collator,把 tuple 变成模型能接收的字典。以下字段名是自定义三输入模型的示例,必须按刚打印的 forward 参数名修改,顺序保持与 TensorDataset 一致:
import torch
fields = ("input_ids_a", "input_ids_b", "input_ids_c",
"attention_mask_a", "attention_mask_b", "attention_mask_c", "labels")
def collate_batch(samples):
assert all(len(s) == len(fields) for s in samples)
return {
name: torch.stack([s[i] for s in samples])
for i, name in enumerate(fields)}
trainer = Trainer(model=model, args=training_args,
train_dataset=train_data, eval_dataset=validation_data,
data_collator=collate_batch)
若直接使用标准 Roberta 分类模型,它没有上述三组独立参数;应先定义文本合并/编码方案,形成其支持的 input_ids、attention_mask 与 labels,或编写真正处理三路输入的自定义 forward。不能只给任意 tuple 加名字就声称标准模型支持三路。
在正式 train 前先执行 batch = collate_batch([train_data[0], train_data[1]]),查看各键形状,再执行 model(**batch)。返回值应包含 Trainer 所需的 loss;类别索引标签也要符合损失要求。
不下载模型,先验证七字段怎样组批
下面仅使用 PyTorch,构造两个样本、每路长度 5 的 token ID 和 mask,检查 collator 输出。它验证三路字段的对应关系,不代表标准 RoBERTa 原生接受这些名字:
import torch
from torch.utils.data import TensorDataset
fields = ("input_ids_a", "input_ids_b", "input_ids_c",
"attention_mask_a", "attention_mask_b", "attention_mask_c", "labels")
ids = [torch.arange(10).reshape(2, 5) + offset for offset in (0, 10, 20)]
masks = [torch.ones(2, 5, dtype=torch.long) for _ in range(3)]
dataset = TensorDataset(*ids, *masks, torch.tensor([0, 1]))
def collate(samples):
return {
key: torch.stack([sample[i] for sample in samples])
for i, key in enumerate(fields)}
batch = collate([dataset[0], dataset[1]])
assert set(batch) == set(fields)
assert batch["input_ids_b"].shape == (2, 5)
assert torch.equal(batch["input_ids_b"], ids[1])
assert batch["labels"].shape == (2,)
print({
key: (tuple(value.shape), str(value.dtype)) for key, value in batch.items()})
真实项目先用 inspect.signature(model.forward) 决定字段,再选择三路编码方案。如果只是将三段文本作为一个分类输入,应在 tokenizer 阶段按模型支持的方式组织;如果要分别编码再融合,需要自定义模型真的实现三路 forward 和 loss。
各字段若变长,直接 stack 也会失败,应在 collator 中按每一路 tokenizer 的 pad 规则处理。labels 的形状由任务决定,不能把 token mask 与类别标签混起来。现代 Trainer 还可能按模型签名移除不用的列,升级版本时要结合 remove_unused_columns 等设置核对实际收到的 batch。
正式训练前,顺序验证一条样本、两条样本的 collator、model(**batch) 返回 loss,以及一次 backward。先停在这四步,能比直接启动一轮 Trainer 更快区分格式与模型定义问题。
参考:原始求助帖;技术依据:Transformers Trainer(4.46)、Transformers Data Collator(4.46)、Transformers 3.5.1 整理器源码。