踩坑 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'
已知
- 从part3可以看到train_dataset里用的是label
- part2里用的形参名为也label
- 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 ————————————————————