问题
KeyError: “The metric_for_best_model training argument is set to ‘eval_loss’, which is not found in the evaluation metrics. The available evaluation metrics are: [‘eval_runtime’, ‘eval_samples_per_second’, ‘eval_steps_per_second’, ‘epoch’]. Consider changing the metric_for_best_model via the TrainingArguments.”
在Transformer的Trainer中添加的compute_metrics没起作用。
参考:
***eval_loss not found when training a peft model using trainer.py #33420
It just doesn’t calculate the metrics #782
当前transformers
版本为4.46.0
。
def compute_metrics(eval_preds, **kwargs):
preds, labels, *inputs_losses = eval_preds
# preprocess labels
# take label which not equal to -100
preds = preds[labels != -100]
labels = labels[labels != -100]
labels = np.where(labels > 0.5, 1, 0)
accuracy = accuracy_score(labels, preds)
f1 = f1_score(labels, preds)
auc = roc_auc_score(labels, preds)
precision = precision_score(labels, preds)
recall = recall_score(labels, preds)
return dict(
accuracy=accuracy,
f1=f1,
auc=auc,
precision=precision,
recall=recall
)
trainer = Trainer(
model=model,
processing_class=tokenizer,
args=training_args,
train_dataset=train_dataset,
eval_dataset=valid_dataset,
data_collator=DataCollatorWithPadding(tokenizer),
preprocess_logits_for_metrics=preprocess_logits_for_metrics,
compute_metrics=compute_metrics,
compute_loss_func=compute_loss_func,
)
尝试过在training_args
中添加include_for_metrics = ['loss']
,但是不起作用。compute_metrics
中的内容没有返回。
解决
本质上由于使用的是PeftModel
导致self.label_names
为[]
。
所以,在trainer.py
的732
行附近,修改为如下代码。
原代码:
default_label_names = find_labels(self.model.__class__)
self.label_names = default_label_names if self.args.label_names is None else self.args.label_names
self.can_return_loss = can_return_loss(self.model.__class__)
修改为如下:
if _is_peft_model(self.model):
if hasattr(self.model, "get_base_model"):
model_to_inspect = self.model.get_base_model()
default_label_names = find_labels(model_to_inspect.__class__)
self.can_return_loss = can_return_loss(model_to_inspect.__class__)
else:
default_label_names = find_labels(self.model.__class__)
self.can_return_loss = can_return_loss(self.model.__class__)
self.label_names = default_label_names if self.args.label_names is None else self.args.label_names
分析
经过trainer.py
的源码分析,发现是all_labels
竟然是空的。导致compute_metrics
未执行。
向上回溯,发现prediction_step
返回的labels
也是空的。
进入看看,理论上has_labels
应该为True,因为len(self.label_names) != 0
,但是has_labels == False
。
看看self.label_names
的定义,发生了什么?理论上default_label_names
将找到默认的标签labels
,然后赋值给self.label_names
。
看看find_labels
,它将forward
函数的参数签名拿到,然后将里面包含label
的参数签名返回。
重点来了,因为我们使用的是PeftModel
,它的forward
函数的没有包含label
的参数签名。
找到问题后,发现已经有人提出PR了,但是最新版本的transformers
还没有更新上。