在深度学习领域,训练大型语言模型(Large Language Models)通常需要昂贵的计算资源和复杂的分布式训练环境。但借助Hugging Face生态系统的Trainer API和ModelHubMixin类,我们可以大幅简化这一过程。本文将手把手教你如何:
整个过程不需要复杂的训练脚本,甚至能自动获得分布式训练能力。我们以MNIST手写数字分类为例,但这个方法适用于任何自定义模型架构。
首先确保你的Python环境(建议3.8+)已安装以下包:
bash复制pip install torch torchvision
pip install datasets evaluate accelerate
pip install "huggingface_hub>=0.22"
注意:accelerate包是Hugging Face的分布式训练库,即使你目前只有单卡,也建议安装以便未来扩展。
在代码开头添加认证逻辑:
python复制from huggingface_hub import notebook_login
notebook_login()
这会弹出窗口让你输入Hugging Face的write token(在账号设置中生成)。如果你在脚本中运行而非notebook,可以使用huggingface-cli login命令。
我们使用经典的MNIST数据集作为示例:
python复制from datasets import load_dataset, Image
from torchvision import transforms
# 加载数据集
dataset = load_dataset("mnist")
# 将图像列转换为Pillow格式
dataset = dataset.cast_column("image", Image())
# 关键:标签列必须命名为"labels"
dataset = dataset.rename_column("label", "labels")
# 定义转换管道
transform = transforms.Compose([
transforms.ToTensor(),
transforms.Normalize((0.5,), (0.5,))
])
def to_pt(batch):
batch["image"] = [transform(image.convert("RGB")) for image in batch["image"]]
return batch
# 应用转换(延迟执行)
train = dataset["train"].with_transform(to_pt)
test = dataset["test"].with_transform(to_pt)
技术细节:
with_transform比map更高效,因为它只在数据被访问时应用转换。但要注意它不支持流式数据集(streaming),如果需要处理超大数据集,可以考虑自定义Dataset类。
让你的模型兼容Hugging Face Hub的关键是继承PyTorchModelHubMixin:
python复制import torch.nn as nn
from huggingface_hub import PyTorchModelHubMixin
class BasicNet(nn.Module, PyTorchModelHubMixin, tags=["image-classification"]):
def __init__(self, channels=3):
super().__init__()
self.criterion = nn.CrossEntropyLoss()
self.conv1 = nn.Conv2d(channels, 10, kernel_size=5)
self.conv2 = nn.Conv2d(10, 20, kernel_size=5)
self.conv2_drop = nn.Dropout2d(p=0.5)
self.fc1 = nn.Linear(320, 50)
self.fc2 = nn.Linear(50, 10)
def forward(self, image, labels=None):
# 第一卷积层
x = F.relu(F.max_pool2d(self.conv1(image), 2))
# 第二卷积层
x = F.relu(F.max_pool2d(self.conv2_drop(self.conv2(x)), 2))
# 展平
x = x.view(-1, 320)
# 全连接层
x = F.relu(self.fc1(x))
x = F.dropout(x, training=self.training)
x = self.fc2(x)
logits = F.log_softmax(x, dim=-1)
if labels is not None:
loss = self.criterion(logits, labels)
return {"loss": loss, "logits": logits}
return logits
这个Mixin类自动为你的模型添加了三个关键方法:
save_pretrained(): 本地保存模型权重和配置from_pretrained(): 从本地或Hub加载模型push_to_hub(): 上传模型到Hugging Face Hub要让模型与Trainer API协同工作,forward方法必须:
labels参数我们的示例已经满足这些要求。如果你的数据列名不同,可以通过TrainingArguments的label_names参数指定。
python复制from transformers import DefaultDataCollator
def collate_fn(examples):
images = []
labels = []
for example in examples:
images.append(example["image"])
labels.append(example["labels"])
return {
"image": torch.stack(images),
"labels": torch.tensor(labels)
}
调试技巧:在将collate_fn传给Trainer前,可以用
collate_fn(train.select(range(8)))测试它是否能正确处理小批量数据。
python复制import evaluate
import numpy as np
accuracy = evaluate.load("accuracy")
def compute_metrics(eval_pred):
predictions, labels = eval_pred
predictions = np.argmax(predictions, axis=1)
return accuracy.compute(predictions=predictions, references=labels)
python复制from transformers import TrainingArguments, Trainer
training_args = TrainingArguments(
output_dir="./mnist_checkpoints",
evaluation_strategy="steps",
save_strategy="epoch",
learning_rate=5e-4,
per_device_train_batch_size=64,
per_device_eval_batch_size=128,
num_train_epochs=3,
weight_decay=0.01,
logging_steps=100,
push_to_hub=True,
hub_model_id="your-username/mnist-model",
report_to="none" # 禁用wandb等记录器
)
关键参数解析:
evaluation_strategy="steps": 每N步评估一次(默认500)save_strategy="epoch": 每个epoch保存检查点push_to_hub=True: 训练完成后自动上传hub_model_id: 指定模型在Hub上的路径python复制model = BasicNet(channels=3)
trainer = Trainer(
model=model,
args=training_args,
train_dataset=train,
eval_dataset=test,
data_collator=collate_fn,
compute_metrics=compute_metrics,
)
trainer.train()
重要提示:Trainer默认只会上传模型权重,不包括配置文件。训练完成后需要手动调用:
python复制model.push_to_hub("your-username/mnist-model")
python复制from huggingface_hub import PyTorchModelHubMixin
# 最简单的方式
loaded_model = BasicNet.from_pretrained("your-username/mnist-model")
# 手动加载方式(了解底层)
from safetensors.torch import load_file
from huggingface_hub import snapshot_download
snapshot_download(repo_id="your-username/mnist-model", local_dir="temp_dir")
weights = load_file("temp_dir/model.safetensors")
manual_model = BasicNet(3)
manual_model.load_state_dict(weights)
python复制import torch
from PIL import Image
# 加载测试图像
img = Image.open("test_digit.png").convert("RGB")
img_tensor = transform(img).unsqueeze(0) # 添加batch维度
# 推理
with torch.no_grad():
logits = loaded_model(img_tensor)
prediction = torch.argmax(logits, dim=-1).item()
print(f"Predicted digit: {prediction}")
如果想在Trainer基础上添加自定义逻辑,可以子类化Trainer:
python复制class CustomTrainer(Trainer):
def compute_loss(self, model, inputs, return_outputs=False):
labels = inputs.pop("labels")
outputs = model(**inputs)
logits = outputs["logits"]
loss = F.cross_entropy(logits, labels)
return (loss, outputs) if return_outputs else loss
形状不匹配错误:
CUDA内存不足:
per_device_train_batch_sizegradient_accumulation_steps)上传Hub失败:
huggingface_hub版本≥0.22torch.compile()加速模型(PyTorch 2.0+)fp16=True)datasets的流式加载上传模型时,良好的文档能帮助他人使用你的模型。在push_to_hub时可以通过参数添加元数据:
python复制model.push_to_hub(
"mnist-model",
license="apache-2.0",
tags=["image-classification", "mnist"],
model_card="""
# MNIST分类模型
## 模型描述
这是一个基于CNN的MNIST手写数字分类器
## 使用方法
```python
from models import BasicNet
model = BasicNet.from_pretrained("your-username/mnist-model")
```
"""
)
在实际项目中,我发现这套工作流不仅能简化模型开发部署流程,还能促进团队协作和模型版本管理。特别是对于需要频繁迭代的实验性项目,能够快速保存和分享中间结果非常宝贵。