关于使用transformers的Trainer训练问题(... unexpected keyword argument 'labels')

1,891 阅读2分钟

踩坑 transformers Trainer forward unexpected labels
关于使用transformers的Trainer训练问题
TypeError: forward() got an unexpected keyword argument 'labels'

环境

transformers==4.15.0

代码

part1: train.py

trainer = Trainer(
    model=model,
    args=training_args,
    train_dataset=train_dataset,
    tokenizer=tokenizer,
    callbacks=[TensorBoardCallback],
    compute_metrics=EvalPrediction
)
trainer.train()

part2: model.py

def forward(self, input_ids, attention_mask, token_type_ids, label=None)

part3: data.py

datasets = DatasetDict(
    {
        "train": Dataset.from_pandas(pd.read_csv(train_file_path))
    }
)
# txt -> feature
train_dataset = datasets["train"].map(
    prepare_features,
    batched=True,
    remove_columns="sent0",
    num_proc=preprocessing_num_workers,
)
# train_dataset.column_names: 'attention_mask; input_ids; label; token_type_ids'

已知

  1. 从part3可以看到train_dataset里用的是label
  2. part2里用的形参名为也label
  3. transformers的Trainer逻辑为将train_dataset的columns名与forward的形参名对应(此条为假设,后文会说明)

然而报错

torch\nn\modules\module.py
return forward_call(*input, **kwargs)
TypeError: forward() got an unexpected keyword argument 'labels'

疑问

明明train_dataset和forward都是label,为什么说找不到labels呢
debug发现,传给forward的四个columns分别是'attention_mask; input_ids; labels; token_type_ids'
这就更离谱了,明明train_dataset里是label,怎么变成labels了呢

本着你凭什么把我自己定义的label改成labels的气头,开始寻根溯源

首先transformers\trainer.py", line 1332  |||   tr_loss_step = self.training_step(model, inputs)
发现报错中这里的inputs,label已经变为labels,继续溯源
epoch_iterator中label已经变为labels ==>train_dataloader中label已经变为labels ==>

return DataLoader(
    train_dataset,
    batch_size=self.args.train_batch_size,
    sampler=train_sampler,
    collate_fn=self.data_collator,
    drop_last=self.args.dataloader_drop_last,
    num_workers=self.args.dataloader_num_workers,
    pin_memory=self.args.dataloader_pin_memory,
)

这里的train_dataset中的label还是label,但是实例化成DataLoader类后,其iter方法取出的值中label变成了labels。
我们知道单纯的DataLoader(train_dataset)不会改变label这个名称。那就是某个参数导致,经过测试,collate_fn参数导致label变成了labels。
点开self.data_collator ==> 它的值为DataCollatorWithPadding(tokenizer),点开它的类:DataCollatorWithPadding,找到修改label为labels的代码。

transformers.data.data_collator.py

class DataCollatorWithPadding:
    def __call__(self, features: List[Dict[str, Any]]) -> Dict[str, Any]:
        .
        .
        .
        if "label" in batch:
            batch["labels"] = batch["label"]
            del batch["label"]
        if "label_ids" in batch:
            batch["labels"] = batch["label_ids"]
            del batch["label_ids"]
        .
        .
        .

补充

dataset 同时有label和labels,label依然会被读进去。如果label不能转换成tensor依然会报错

后记

我不明白,为什么DataCollator的时候知道label==labels,forward的时候不加这种规则呢,明显的不对称,反直觉。

本人知乎上传过zhuanlan.zhihu.com/p/454630549 ————————————————————