Gemma 4 开箱即用已经很强了,但如果你想让它说你们品牌的话、按你的格式输出、或者在某个垂直领域表现更好,微调就是必走的路。
好消息是:现在不需要一堆 A100 了。用 LoRA 加一个叫 Unsloth 的工具,一张消费级显卡就能在一小时内完成微调。
什么时候该微调?
先别急着动手,确认一下微调是不是真正需要的:
| 场景 | 建议方案 |
|---|---|
| 模型不懂你的行业术语 | 用行业数据微调 |
| 模型总是不按格式输出 | 用格式示例微调 |
| 模型需要最新信息 | 用 RAG,不需要微调 |
| 模型太啰嗦或太简短 | 先试试调 prompt |
| 模型偶尔回答错误 | 先试试 few-shot |
如果 prompt 工程和 RAG 都搞不定,再上微调。动手之前先试试最佳 Gemma 4 提示词,说不定调 prompt 就能解决。想先了解 Gemma 4 能干什么,可以看看使用场景指南。
LoRA 和 QLoRA 到底是什么?
LoRA(低秩适配)不改动原始模型的权重,而是在模型上面训练一组很小的适配器权重。打个比方就像给相机装个滤镜,而不是重新造一台相机。
- 基础模型:冻结,完全不动
- 适配器:很小,通常只有基础模型的 1-5%
- 效果:接近全参数微调,但成本低得多
QLoRA 更进一步——它把基础模型用 4-bit 量化加载,显存占用直接砍半。质量大概是 LoRA 的 90% 以上,但能在小得多的显卡上跑。
| 方法 | 显存需求 (E4B) | 显存需求 (26B) | 质量 |
|---|---|---|---|
| 全参数微调 | 32GB+ | 100GB+ | 最好 |
| LoRA | 16GB | 48GB | 约 98% |
| QLoRA | 8GB | 24GB | 约 95% |
安装 Unsloth
Unsloth 是微调 Gemma 4 最快的工具,比原版 HuggingFace 快 2 倍,省 60% 显存。
# 创建虚拟环境
python -m venv gemma4-finetune
source gemma4-finetune/bin/activate
# 安装 Unsloth(包含所有依赖)
pip install unsloth
# 如果要用 QLoRA,还需要:
pip install bitsandbytes检查环境是否就绪:
import torch
print(f"CUDA 可用: {torch.cuda.is_available()}")
print(f"GPU: {torch.cuda.get_device_name(0)}")
print(f"显存: {torch.cuda.get_device_properties(0).total_mem / 1e9:.1f} GB")准备训练数据
训练数据用 JSONL 格式,每行是一段对话:
{"messages": [{"role": "user", "content": "退货政策是什么?"}, {"role": "assistant", "content": "我们支持30天内凭原始收据退货。"}]}
{"messages": [{"role": "user", "content": "发国际快递吗?"}, {"role": "assistant", "content": "支持,覆盖45个国家,通常7-14个工作日到达。"}]}
{"messages": [{"role": "system", "content": "你是一个医疗编码助手。"}, {"role": "user", "content": "编码:患者出现急性支气管炎"}, {"role": "assistant", "content": "ICD-10: J20.9 - 急性支气管炎,未特指"}]}数据质量要点:
- 500-1000 条是大多数任务的甜区
- 质量比数量重要——500 条高质量数据碾压 5000 条水数据
- 要包含边界情况和反例
- 回答的格式和语气要保持一致
- 训练前先验证数据:
import json
def validate_jsonl(filepath):
valid = 0
errors = 0
with open(filepath, 'r') as f:
for i, line in enumerate(f, 1):
try:
data = json.loads(line)
assert "messages" in data, "缺少 'messages' 字段"
assert len(data["messages"]) >= 2, "至少需要2条消息"
valid += 1
except (json.JSONDecodeError, AssertionError) as e:
print(f"第 {i} 行: {e}")
errors += 1
print(f"\n有效: {valid}, 错误: {errors}")
validate_jsonl("training_data.jsonl")训练配置
完整的训练脚本:
from unsloth import FastLanguageModel
import torch
# 加载模型(QLoRA 4-bit)
model, tokenizer = FastLanguageModel.from_pretrained(
model_name="google/gemma-4-e4b",
max_seq_length=4096,
dtype=None, # 自动检测
load_in_4bit=True, # QLoRA
)
# 添加 LoRA 适配器
model = FastLanguageModel.get_peft_model(
model,
r=16, # 秩——越高容量越大,显存也越多
target_modules=[
"q_proj", "k_proj", "v_proj", "o_proj",
"gate_proj", "up_proj", "down_proj",
],
lora_alpha=16,
lora_dropout=0, # Unsloth 优化过,保持 0
bias="none",
use_gradient_checkpointing="unsloth",
)
# 加载训练数据
from datasets import load_dataset
dataset = load_dataset("json", data_files="training_data.jsonl", split="train")
# 训练参数
from trl import SFTTrainer
from transformers import TrainingArguments
trainer = SFTTrainer(
model=model,
tokenizer=tokenizer,
train_dataset=dataset,
max_seq_length=4096,
args=TrainingArguments(
per_device_train_batch_size=2,
gradient_accumulation_steps=4,
warmup_steps=10,
num_train_epochs=3,
learning_rate=2e-4,
fp16=not torch.cuda.is_bf16_supported(),
bf16=torch.cuda.is_bf16_supported(),
logging_steps=10,
output_dir="outputs",
optim="adamw_8bit",
seed=42,
),
)
# 开始训练
trainer.train()
# 保存 LoRA 适配器
model.save_pretrained("gemma4-finetuned-lora")
tokenizer.save_pretrained("gemma4-finetuned-lora")关键参数调整建议:
r=16:LoRA 秩。先用 16,效果不够再加到 32 或 64num_train_epochs=3:一般 2-5 就够。注意观察是否过拟合learning_rate=2e-4:默认值通常够用。训练不稳定就降到 1e-4per_device_train_batch_size=2:显存够的话可以加大
导出 GGUF 格式
训练好的适配器要跑在 Ollama 上,需要先转成 GGUF。各种量化选项的详细对比看 GGUF 量化指南。
# 合并适配器并导出 GGUF(Q4_K_M 量化)
model.save_pretrained_gguf(
"gemma4-finetuned-gguf",
tokenizer,
quantization_method="q4_k_m",
)
# 也可以导出多个量化等级
for method in ["q4_k_m", "q5_k_m", "q8_0"]:
model.save_pretrained_gguf(
f"gemma4-finetuned-{method}",
tokenizer,
quantization_method=method,
)| 量化方式 | 文件大小 (E4B) | 质量损失 | 适合场景 |
|---|---|---|---|
| Q4_K_M | ~2.5 GB | 很小 | 大多数用户 |
| Q5_K_M | ~3.2 GB | 更小 | 追求质量 |
| Q8_0 | ~4.8 GB | 几乎没有 | 显存充足时 |
用 Ollama 部署
创建一个 Modelfile 把微调好的模型导入 Ollama:
# Modelfile
FROM ./gemma4-finetuned-gguf/gemma4-finetuned-q4_k_m.gguf
PARAMETER temperature 0.7
PARAMETER top_p 0.9
SYSTEM "你是一个专门为客户服务优化过的助手。"然后构建并运行:
# 创建自定义模型
ollama create my-gemma4 -f Modelfile
# 测试
ollama run my-gemma4 "退货政策是什么?"
# 确认模型已创建
ollama list微调后的模型现在跟普通 Ollama 模型一样使用。可以通过 Ollama API 调用,在上面做开发,或者分享给团队。
常见问题
训练时显存不够: 把 per_device_train_batch_size 降到 1,打开 gradient_checkpointing,或者换更小的基础模型。
微调后模型输出乱码: 数据格式可能有问题,或者训练轮次太多了。减少 epochs,重新检查 JSONL 数据。
GGUF 导出失败: 确保磁盘空间充足(转换过程需要模型大小的 3-5 倍),Unsloth 版本也要是最新的。
训练 loss 不下降: 学习率可能太低,试试 5e-4。同时检查数据里是否真的包含了你想让模型学习的模式。
下一步
- 看看模型选择指南,选一个最适合微调的基础模型
- 搭建正式的生产部署流程
- 学习结构化 JSON 输出,微调 + 可靠输出格式组合使用
- 确认硬件配置够不够跑训练



