如何在 Llama-Factory 中自定义损失函数?高级用法指南
在大模型微调日益普及的今天,越来越多的实际任务开始暴露出标准训练流程的局限性。比如,你在训练一个金融客服机器人时发现,尽管整体准确率不错,但模型总是'忽略'那些关键却少见的问题——像'账户被冻结怎么办'这类高风险咨询,出现频率低、样本少,结果在交叉熵损失主导下被梯度淹没。这时候,你真正需要的不是更多数据,而是一种能表达业务优先级的损失函数。
这正是 Llama-Factory 作为现代微调框架的价值所在:它不仅让你'跑得起来',更允许你深入到底层训练逻辑,把领域知识、工程经验甚至产品目标,编码进模型的学习过程中。其中最关键的入口之一,就是自定义损失函数。
Llama-Factory 基于 Hugging Face Transformers 构建,底层使用 PyTorch,其训练流程遵循典型的因果语言建模范式。默认情况下,Trainer 类会调用内置的 CrossEntropyLoss 来计算 token 级别的预测误差。这个过程看似固定,实则留出了清晰的扩展点——只要你重写 compute_loss 方法,就能完全接管损失计算逻辑。
这种设计并非偶然。它的核心思想是:训练引擎负责调度和优化,而损失函数定义'什么是对的'。 换句话说,框架管'怎么学',你来决定'学成什么样'。
举个例子,标签平滑(Label Smoothing)是一种常见的正则化技术,用于防止模型对训练标签过度自信。虽然 Hugging Face 的 Trainer 支持通过参数启用,但在某些场景下你需要更细粒度的控制,比如动态调整平滑强度或结合其他监督信号。这时,直接定制 compute_loss 就成了最灵活的选择。
import torch
import torch.nn as nn
from transformers import Trainer
class CustomTrainer(Trainer):
def __init__(self, label_smoothing=0.0, **kwargs):
super().__init__(**kwargs)
self.label_smoothing = label_smoothing
self.ce_loss = nn.CrossEntropyLoss(reduction="none")
def compute_loss(self, model, inputs, return_outputs=False):
labels = inputs.get("labels")
outputs = model(**inputs)
logits = outputs.get("logits")
# Shift for causal language modeling
shift_logits = logits[..., :-1, :].contiguous()
shift_labels = labels[..., 1:].contiguous()
flat_logits = shift_logits.view(-, shift_logits.size(-))
flat_labels = shift_labels.view(-)
.label_smoothing > :
vocab_size = flat_logits.shape[-]
torch.no_grad():
true_probs = torch.full_like(flat_logits, .label_smoothing / (vocab_size - ))
true_probs.scatter_(, flat_labels.unsqueeze(), - .label_smoothing)
log_probs = torch.log_softmax(flat_logits, dim=-)
loss = -(true_probs * log_probs).(dim=-).mean()
:
loss = .ce_loss(flat_logits, flat_labels).mean()
(loss, outputs) return_outputs loss

