原文出处:Hands-On Exercises: Fine-Tuning SmolLM3 原作者:Hugging Face · 许可证:Apache-2.0 License 中文译本由诸葛AI学院翻译整理,仅供学习参考,版权归原作者与 Hugging Face 所有。
(接上篇)
练习 3:用 SFTTrainer 对 SmolLM3 做监督微调
目标:用 TRL 库的 SFTTrainer,在真实数据集上对 SmolLM3 做监督微调(supervised fine-tuning,简称 SFT)。
注意:你需要一块至少 8GB 显存(VRAM)的 GPU。
第 1 步:环境准备与加载模型
我们加载基座模型(base model)和分词器(tokenizer),设置填充(padding)行为,把模型放到合适的设备上,为微调做好准备。
```python
导入微调所需的库
from transformers import AutoModelForCausalLM, AutoTokenizer, TrainingArguments from trl import SFTTrainer, SFTConfig, DataCollatorForCompletionOnlyLM from datasets import load_dataset import torch import wandb # 可选:用于实验跟踪
初始化 Weights & Biases(可选)
wandb.init(project="smollm3-finetuning")
加载用于微调的 SmolLM3 基座模型
model_name = "HuggingFaceTB/SmolLM3-3B-Base" new_model_name = "SmolLM3-Custom-SFT"
print(f"Loading {model_name}...") model = AutoModelForCausalLM.from_pretrained( model_name, dtype=torch.bfloat16, device_map="auto", trust_remote_code=True )
tokenizer = AutoTokenizer.from_pretrained(model_name) tokenizer.pad_token = tokenizer.eos_token # 设置填充令牌 tokenizer.padding_side = "right" # 生成时在右侧填充
print(f"Model loaded! Parameters: {model.num_parameters():,}") ```
python output
Loading HuggingFaceTB/SmolLM3-3B-Base...
Model loaded! Parameters: 3,075,098,624
准备数据集
这里我们选一个规模可控的子集来加快训练,然后用聊天模板把每条样本映射成一个 text 字符串——训练器读的就是这个字段。
```python
加载并准备训练数据集
print("=== PREPARING DATASET ===\n")
方案一:用 SmolTalk2(新手推荐)
dataset = load_dataset("HuggingFaceTB/smoltalk2", "SFT") train_dataset = dataset["smoltalk_everyday_convs_reasoning_Qwen3_32B_think"].select(range(1000)) # 取子集,训练更快
方案二:用你在练习 2 里处理好的数据集
train_dataset = gsm8k_formatted.select(range(500))
print(f"Training examples: {len(train_dataset)}") print(f"Example: {train_dataset[0]}")
准备 SFT 用的数据集
def format_chat_template(example): """用聊天模板把 messages 格式化成文本""" if "messages" in example: # SmolTalk2 的格式 messages = example["messages"] else: # 自定义格式——按需调整 messages = [ {"role": "user", "content": example["instruction"]}, {"role": "assistant", "content": example["response"]} ]
# 套用聊天模板
text = instruct_tokenizer.apply_chat_template(
messages,
tokenize=False,
add_generation_prompt=False
)
return {"text": text}
应用格式化
formatted_dataset = train_dataset.map(format_chat_template) formatted_dataset = formatted_dataset.remove_columns( [col for col in formatted_dataset.column_names if col != "text"] ) print(f"Formatted example: {formatted_dataset[0]['text'][:200]}...") ```
输出
```python output === PREPARING DATASET === Training examples: 1000 Example: {'messages': [{'content': 'Solve the problem step by step.', 'role': 'system'}, {'content': 'Natalia sold clips to 48 of her friends in April, and then she sold half as many clips in May. How many clips did Natalia sell altogether in April and May?', 'role': 'user'}]} Formatted example: You are a math tutor. Solve problems step by step. Natalia sold clips to 48 of her friends in April, and then she sold half as many clips in May. How many clips did Natalia sell altogether in April and May?... ```训练配置
我们配置 SFT 的几个关键参数:批大小(batch size)、序列长度、学习率(learning rate)、日志与保存频率,同时打开可选的实验跟踪和 Hub 集成。
```python
配置训练参数
training_config = SFTConfig( # 模型与数据 output_dir=f"./{new_model_name}", dataset_text_field="text", max_length=2048,
# 训练超参数
per_device_train_batch_size=2, # 按你的显卡显存调整
gradient_accumulation_steps=2,
learning_rate=5e-5,
num_train_epochs=1, # 先从 1 轮(epoch)开始
max_steps=500, # 演示用,限制步数
# 优化
warmup_steps=50,
weight_decay=0.01,
optim="adamw_torch",
# 日志与保存
logging_steps=10,
save_steps=100,
eval_steps=100,
save_total_limit=2,
# 显存优化
dataloader_num_workers=0,
group_by_length=True, # 把长度相近的序列分到一组
# Hugging Face Hub 集成
push_to_hub=False, # 设为 True 则上传到 Hub
hub_model_id=f"your-username/{new_model_name}",
# 实验跟踪
report_to=["trackio"], # 用 trackio 做实验跟踪
run_name=f"{new_model_name}-training",
)
print("Training configuration set!") print(f"Effective batch size: {training_config.per_device_train_batch_size * training_config.gradient_accumulation_steps}") ```
python output
Training configuration set!
Effective batch size: 4
可选:用 LoRA/PEFT 训练(省显存)
如果显存有限,或者想让迭代更快,可以用 PEFT 库的 LoRA。它只训练一小部分适配器(adapter)权重,基座模型保持冻结。之后你可以继续带着适配器用,也可以在部署前把它们合并回模型。
```python
用 PEFT 配置 LoRA
from peft import LoraConfig
peft_config = LoraConfig( r=8, lora_alpha=16, lora_dropout=0.05, bias="none", task_type="CAUSAL_LM", )
创建启用 LoRA 的 SFTTrainer
from trl import SFTTrainer
lora_trainer = SFTTrainer( model=model, train_dataset=formatted_dataset, # 带 "text" 字段的数据集,或在配置里用 messages + dataset_text_field args=training_config, peft_config=peft_config, # << 启用 LoRA )
print("Starting LoRA training…") lora_trainer.train() ```
第 4 步:初始化 SFTTrainer 并训练
我们实例化训练器,先记录一条训练前的基线生成结果,然后启动 train(),把训练得到的检查点(checkpoint)保存到配置好的输出目录。
```python
trainer = SFTTrainer( model=model, train_dataset=formatted_dataset, args=config, ) ```
然后就可以开始训练模型。
python
trainer.train()
测试微调后的模型
最后,用同一个提示词(prompt)再生成一次,定性对比训练前后的输出;还可以把模型推送到 Hub 分享。
```python
测试微调后的模型
print("=== AFTER TRAINING ===") with torch.no_grad(): outputs = model.generate( **inputs, max_new_tokens=100, temperature=0.7, do_sample=True, pad_token_id=tokenizer.eos_token_id ) response = tokenizer.decode(outputs[0], skip_special_tokens=True) print(response[len(formatted_prompt):])
可选:推送到 Hugging Face Hub
if training_config.push_to_hub: trainer.push_to_hub( commit_message="Fine-tuned SmolLM3 with custom dataset", tags=["smol-course", "sft", "instruction-tuning"] ) print(f"Model pushed to Hub: {training_config.hub_model_id}") ```
练习 4:用 TRL CLI 走生产流程
前面的练习里,我们深入使用了 TRL 的 Python API 做微调,也研究了所用和所生成的数据。这一练习改用 TRL 的命令行工具(CLI)来微调模型,这也是生产环境里最常见的微调方式。
我们可以在 TRL CLI 里定义一条微调命令,之后用 trl sft 运行它。CLI 命令和 Python API 共用同一套配置项。
我们预先处理了 SmolTalk2 的 smoltalk_everyday_convs_reasoning_Qwen3_32B_think 子集,让它配合 TRL CLI 使用时更顺手。
```bash
用 TRL CLI 微调 SmolLM3
trl sft \ --model_name_or_path HuggingFaceTB/SmolLM3-3B-Base \ --dataset_name HuggingFaceTB/smoltalk2_everyday_convs_think \ --output_dir ./smollm3-sft-cli \ --per_device_train_batch_size 4 \ --gradient_accumulation_steps 2 \ --learning_rate 5e-5 \ --num_train_epochs 1 \ --max_length 2048 \ --logging_steps 10 \ --save_steps 500 \ --warmup_steps 100 \ --bf16 True \ --push_to_hub \ --hub_model_id your-username/smollm3-sft-cli ```
为了省事、也为了可复现,还可以把配置写进一个文件。比如建一个 sft_config.yaml,填入下面的内容:
```yaml
模型与数据集
model_name_or_path: HuggingFaceTB/SmolLM3-3B-Base dataset_name: HuggingFaceTB/smoltalk2_everyday_convs_think output_dir: ./smollm3-advanced-sft
训练超参数
per_device_train_batch_size: 2 gradient_accumulation_steps: 4 learning_rate: 3e-5 num_train_epochs: 2 max_length: 4096
优化
warmup_steps: 200 weight_decay: 0.01 optim: adamw_torch lr_scheduler_type: cosine
显存与性能
bf16: true dataloader_num_workers: 4 group_by_length: true remove_unused_columns: false
日志与评估
logging_steps: 25 eval_steps: 250 save_steps: 500 eval_strategy: steps load_best_model_at_end: true metric_for_best_model: eval_loss
Hub 集成
push_to_hub: true hub_model_id: your-username/smollm3-advanced hub_strategy: every_save ```
然后把这个文件提交进仓库,用 Git 管理起来。
```bash
用配置文件启动训练
trl sft --config sft_config.yaml ```
排错
遇到 GPU 显存不足(OOM)报错:
- 把 per_device_train_batch_size 降到 1
- 把 max_length 降到 1024 或 512
- 用 torch.cuda.empty_cache() 清理显存
模型加载失败:
- 检查网络连接
- 试试 device_map="cpu",改用 CPU 加载
- 测试时换小一点的模型,比如 HuggingFaceTB/SmolLM3-1.7B
训练失败: - 确认数据集格式正确 - 检查每条样本的长度是否合理(别太长) - 盯着训练损失(loss),它应该稳定下降
小结
恭喜!你完成了这一整套动手练习,内容覆盖:
- SmolLM3 的聊天模板体系与双模式推理
- 数据集的处理与准备技巧
- 用 Python API 做监督微调
- 用 CLI 工具走生产流程
- 分布式训练环境搭建
这些是构建高质量指令微调(instruction-tuned)模型的基本功。接下来的模块里,我们会学习偏好对齐(preference alignment)、参数高效微调(parameter-efficient fine-tuning)和进阶评估方法。
延伸阅读
- TRL 文档:完整参考手册
- SmolLM3 Model Card(模型卡):模型详情
- SmolTalk2 数据集:训练数据
- Hugging Face Hub:分享你的模型
- Discord 社区:获取帮助、参与讨论