模型微调(Fine-tuning)的具体操作步骤
模型微调(Fine-tuning)是指在一个预训练好的大模型基础上,使用特定领域的数据继续训练,以调整模型参数,使其更好地适应特定任务。下面以目前最常用的 LoRA(Low-Rank Adaptation) 微调方法为例,详细说明从环境准备到模型部署的完整操作流程。
一、微调前的准备
1. 明确微调目标
在开始之前,需要明确你希望模型完成什么任务,例如:
- 学会特定领域的术语和表达方式(如医学、法律、金融);
- 学会特定的输出格式(如 JSON、SQL、Markdown 表格);
- 学会特定的语气或风格(如客服语气、正式报告);
- 提升特定任务的能力(如文本分类、信息抽取、摘要)。
明确目标后,才能设计合适的数据集和评估方式。
2. 选择基座模型
选择一个合适的预训练模型作为起点。常见选择:
| 模型系列 | 参数量选项 | 特点 |
|---|---|---|
| Llama 3 / Llama 3.1 | 8B, 70B | 开源,社区活跃,支持多语言 |
| Qwen 2 / Qwen 2.5 | 0.5B~72B | 中文能力强,适合中文场景 |
| Mistral / Mixtral | 7B, 8x7B | 性能优秀,推理速度快 |
| DeepSeek | 7B, 67B | 中文和代码能力好 |
| Phi-3 | 3.8B, 7B | 小模型,适合资源受限场景 |
选择时需考虑:模型大小(显存限制)、语言支持、许可证(是否允许商用)、社区生态。
3. 准备硬件环境
微调对显存要求较高,以下是使用 LoRA 方法时的大致显存需求(以 7B 模型为例):
| 微调方式 | 显存需求(大约) | 适合硬件 |
|---|---|---|
| 全参数微调 | 60~80GB | A100 80GB / 多卡 |
| LoRA (fp16) | 16~24GB | RTX 3090/4090 24GB |
| QLoRA (4-bit) | 8~12GB | RTX 3060 12GB / T4 16GB |
如果本地硬件不足,可使用云 GPU 服务(如 AutoDL、Colab Pro、AWS SageMaker、阿里云 PAI)。
二、数据准备
1. 数据格式
微调数据通常是一个 JSON Lines(.jsonl) 文件,每行是一条样本,格式取决于任务类型。
(1)指令微调(Instruction Tuning)格式(最常用):
{"instruction": "将以下句子翻译成英文:", "input": "今天天气很好", "output": "The weather is nice today."}
{"instruction": "请提取文本中的日期", "input": "会议定于2025年3月15日举行", "output": "2025-03-15"}
(2)对话格式(用于多轮对话):
{"conversations": [
{"role": "user", "content": "帮我写一封请假邮件"},
{"role": "assistant", "content": "当然可以,请问请假原因和时间?"},
{"role": "user", "content": "感冒,明天一天"},
{"role": "assistant", "content": "好的,邮件如下:..."}
]}
(3)纯文本补全格式:
{"text": "问题:什么是光合作用?答案:光合作用是植物利用光能..."}
2. 数据量
- 风格/格式微调:几百到几千条高质量样本即可。
- 领域知识注入:可能需要几万条甚至更多。
- 任务能力提升:与任务复杂度相关,几千到几万条。
质量比数量更重要。数据需要经过清洗、去重、格式统一。
3. 数据清洗
- 去除重复样本;
- 检查并修正格式错误(如 JSON 解析失败);
- 过滤过短或过长文本;
- 去除包含敏感信息或不合适内容的样本;
- 确保 instruction 和 output 的对应关系正确。
三、环境搭建
1. 安装依赖
推荐使用 Python 3.10+,并安装以下核心库:
pip install torch transformers datasets accelerate peft trl bitsandbytes
transformers:Hugging Face 模型库;datasets:数据处理;peft:LoRA 等参数高效微调;trl:提供 SFTTrainer,简化指令微调;bitsandbytes:用于 QLoRA 量化加载;accelerate:分布式训练支持。
2. 验证 GPU 可用性
import torch
print(torch.cuda.is_available())
print(torch.cuda.get_device_name(0))
四、加载模型与 Tokenizer
1. 加载基座模型(以 QLoRA 为例,4-bit 量化)
from transformers import AutoTokenizer, AutoModelForCausalLM, BitsAndBytesConfig
import torch
model_name = "Qwen/Qwen2-7B-Instruct"
# 配置 4-bit 量化
bnb_config = BitsAndBytesConfig(
load_in_4bit=True,
bnb_4bit_quant_type="nf4",
bnb_4bit_compute_dtype=torch.bfloat16,
bnb_4bit_use_double_quant=True,
)
# 加载模型
model = AutoModelForCausalLM.from_pretrained(
model_name,
quantization_config=bnb_config,
device_map="auto",
trust_remote_code=True,
)
# 加载 tokenizer
tokenizer = AutoTokenizer.from_pretrained(model_name, trust_remote_code=True)
tokenizer.pad_token = tokenizer.eos_token # 设置 pad token
五、配置 LoRA
from peft import LoraConfig, get_peft_model, prepare_model_for_kbit_training
# 准备模型用于 k-bit 训练
model = prepare_model_for_kbit_training(model)
# 配置 LoRA
lora_config = LoraConfig(
r=16, # LoRA 秩,越大可训练参数越多
lora_alpha=32, # 缩放参数
target_modules=["q_proj", "k_proj", "v_proj", "o_proj", "gate_proj", "up_proj", "down_proj"],
lora_dropout=0.05,
bias="none",
task_type="CAUSAL_LM",
)
# 将 LoRA 应用到模型
model = get_peft_model(model, lora_config)
model.print_trainable_parameters()
输出示例:
trainable params: 41,943,040 || all params: 7,615,541,248 || trainable%: 0.5507
六、准备数据集
from datasets import load_dataset
# 加载本地 jsonl 文件
dataset = load_dataset("json", data_files="train.jsonl", split="train")
# 划分训练集和验证集(可选)
dataset = dataset.train_test_split(test_size=0.05)
train_dataset = dataset["train"]
eval_dataset = dataset["test"]
格式化函数
将数据转换为模型训练所需的格式(如 chat template):
def format_chat(example):
# 对于 instruction/input/output 格式
messages = [
{"role": "user", "content": example["instruction"] + "\n" + example["input"]},
{"role": "assistant", "content": example["output"]}
]
# 使用 tokenizer 的聊天模板
text = tokenizer.apply_chat_template(messages, tokenize=False, add_generation_prompt=False)
return {"text": text}
train_dataset = train_dataset.map(format_chat)
七、配置训练参数并开始训练
使用 trl 库的 SFTTrainer 简化指令微调流程。
from transformers import TrainingArguments
from trl import SFTTrainer
training_args = TrainingArguments(
output_dir="./outputs", # 输出目录
per_device_train_batch_size=4, # 每 GPU 训练批次大小
gradient_accumulation_steps=4, # 梯度累积步数
num_train_epochs=3, # 训练轮数
learning_rate=2e-4, # 学习率
fp16=True, # 使用混合精度(如果支持)
logging_steps=10, # 日志记录频率
save_steps=200, # 保存 checkpoint 频率
evaluation_strategy="steps", # 评估策略
eval_steps=200, # 评估频率
save_total_limit=2, # 最多保留 checkpoint 数
report_to="tensorboard", # 日志工具
)
trainer = SFTTrainer(
model=model,
args=training_args,
train_dataset=train_dataset,
eval_dataset=eval_dataset,
tokenizer=tokenizer,
max_seq_length=2048, # 最大序列长度
dataset_text_field="text", # 数据集中的文本字段
)
trainer.train()
参数说明
- per_device_train_batch_size:根据显存调整,OOM 时可减小;
- gradient_accumulation_steps:模拟更大的批次;
- learning_rate:LoRA 微调常用 1e-4 到 5e-4;
- num_train_epochs:一般 1~3 轮,过多容易过拟合;
- max_seq_length:根据数据长度设置,过长会占用更多显存。
八、保存模型
训练完成后,保存 LoRA 适配器:
model.save_pretrained("./lora_adapter")
tokenizer.save_pretrained("./lora_adapter")
如果希望将 LoRA 权重合并回基座模型,生成一个完整的模型文件:
from peft import PeftModel
base_model = AutoModelForCausalLM.from_pretrained(model_name)
merged_model = PeftModel.from_pretrained(base_model, "./lora_adapter")
merged_model = merged_model.merge_and_unload()
merged_model.save_pretrained("./merged_model")
tokenizer.save_pretrained("./merged_model")
九、评估微调效果
1. 自动化评估
- 准备一个留出的测试集,使用微调前后的模型分别生成答案,计算指标(如 BLEU、ROUGE、BERTScore);
- 对于分类任务,计算准确率、F1 等;
- 使用 LLM-as-Judge 对生成质量打分。
2. 人工评估
- 邀请领域专家对回答质量、相关性、风格符合度进行评分;
- 对比微调前后的输出,检查是否有明显改善。
3. 示例对比
def generate_response(prompt, model, tokenizer):
inputs = tokenizer(prompt, return_tensors="pt").to("cuda")
outputs = model.generate(**inputs, max_new_tokens=256)
return tokenizer.decode(outputs[0], skip_special_tokens=True)
prompt = "请写一封正式的请假邮件"
print(generate_response(prompt, merged_model, tokenizer))
十、部署微调后的模型
1. 使用 Hugging Face Transformers 直接推理
from transformers import AutoModelForCausalLM, AutoTokenizer
model = AutoModelForCausalLM.from_pretrained("./merged_model")
tokenizer = AutoTokenizer.from_pretrained("./merged_model")
# 推理代码同上
2. 使用推理服务器
- vLLM:高性能推理,支持 PagedAttention;
- Text Generation Inference(TGI):Hugging Face 官方;
- Ollama:适合本地部署,支持 GGUF 格式(需要转换)。
3. 转换为 GGUF 格式(用于 Ollama / llama.cpp)
# 使用 llama.cpp 转换
python convert-hf-to-gguf.py ./merged_model --outfile model.gguf
# 然后创建 Modelfile 导入 Ollama
十一、常见问题与调优建议
1. 训练过程中 loss 不下降
- 检查学习率是否过大/过小;
- 检查数据格式是否正确(是否包含大量噪声);
- 尝试增加训练轮数或数据量;
- 确认 tokenizer 的 pad token 设置正确。
2. 显存不足(OOM)
- 减小
per_device_train_batch_size; - 增加
gradient_accumulation_steps; - 减小
max_seq_length; - 使用更小的 LoRA 秩
r; - 使用 4-bit 量化(QLoRA)而非 8-bit。
3. 微调后模型失去通用能力(灾难性遗忘)
- 降低学习率;
- 减少训练轮数;
- 混合少量通用数据;
- 使用 LoRA 而非全参数微调(本身就有缓解作用)。
4. 过拟合
- 增加数据量;
- 使用正则化(如 dropout);
- 减少训练轮数;
- 使用早停(early stopping)。
十二、总结
模型微调的核心步骤可以概括为:
- 明确任务目标,选择合适的基座模型;
- 准备高质量数据集,格式统一、清洗干净;
- 配置环境,安装必要库;
- 加载模型并应用 LoRA,减少可训练参数;
- 使用 SFTTrainer 训练,监控 loss;
- 保存适配器并合并,得到完整模型;
- 全面评估,确保效果提升;
- 部署推理,提供服务。
通过以上流程,即使是在消费级显卡上,你也可以微调一个 7B 左右的模型,使其在特定领域表现更好。如果数据量很大或模型很大,可以考虑使用云端多卡训练。
评论(0)
还没有评论,来抢沙发吧 🛋️
发表评论