- Published on
SFT vs LoRA:大模型微调技术对比
SFT(Supervised Fine-Tuning,监督微调) 和 LoRA(Low-Rank Adaptation,低秩适配) 都是用于微调(fine-tuning)大语言模型(LLM)的方法,目的是让预训练模型更好地适配特定任务或数据集。它们都属于微调的范畴,但方法、效率和应用场景有显著差异。
1. 什么是微调?
微调是指在预训练模型(如 Qwen2-0.5B)的基础上,使用特定任务的数据集进一步训练,调整模型参数以提升在目标任务上的性能。SFT 和 LoRA 是两种不同的微调策略。
2. SFT(监督微调)
定义
- SFT:通过监督学习(Supervised Learning),在特定任务的数据集上调整模型的全部或部分参数,以优化模型在该任务上的表现。
- 过程:
- 使用标注好的数据集(如对话、问答数据)。
- 通过梯度下降优化模型权重,通常更新所有参数(全参数微调)。
- 示例:用中文对话数据集(如 Ultrachat)微调 Qwen2-0.5B,使其更擅长中文对话。
特点
- 参数更新:通常更新模型的所有权重(例如 Qwen2-0.5B 的 5 亿参数)。
- 计算需求:高,需要大量 GPU 显存(Qwen2-0.5B 约需 4-8 GB 显存)。
- 效果:对目标任务适配效果好,但可能导致灾难性遗忘(模型丢失通用能力)。
- 输出:完整的微调模型,文件大小与原始模型相当(未量化时 ~1 GB)。
示例
from transformers import AutoModelForCausalLM, AutoTokenizer, Trainer, TrainingArguments
from datasets import load_dataset
# 加载模型和数据集
model = AutoModelForCausalLM.from_pretrained("Qwen/Qwen2-0.5B-Instruct")
tokenizer = AutoTokenizer.from_pretrained("Qwen/Qwen2-0.5B-Instruct")
dataset = load_dataset("HuggingFaceH4/ultrachat", split="train")
# 数据预处理
def preprocess(examples):
return tokenizer([f"<|im_start|>user: {ex['prompt']} <|im_end|>\n<|im_start|>assistant: {ex['response']} <|im_end|>" for ex in examples], truncation=True, max_length=512)
tokenized_dataset = dataset.map(preprocess, batched=True)
# 训练参数
training_args = TrainingArguments(
output_dir="./sft_output",
per_device_train_batch_size=2,
num_train_epochs=3,
learning_rate=2e-5,
fp16=True
)
# 训练
trainer = Trainer(model=model, args=training_args, train_dataset=tokenized_dataset)
trainer.train()
# 保存
model.save_pretrained("./sft_qwen2-0.5b")
3. LoRA(低秩适配)
定义
- LoRA:一种参数高效微调(PEFT)方法,通过在预训练权重上添加低秩更新矩阵(( \Delta W = A \cdot B ))来适配任务,仅训练这些额外矩阵。
- 过程:
- 冻结原始模型权重 ( W )。
- 引入小规模矩阵 ( A ) 和 ( B ),训练它们以捕捉任务特定信息。
- 示例:用 LoRA 微调 Qwen2-0.5B,仅更新注意力层的部分参数。
特点
- 参数更新:仅训练低秩矩阵(例如 ( r=8 ),参数量仅几十万到几百万)。
- 计算需求:低,Qwen2-0.5B LoRA 微调只需 2-4 GB 显存。
- 效果:适配效果接近全参数微调,同时保留模型通用性,适合多任务场景。
- 输出:LoRA 适配器(几 MB),可与基础模型组合使用。
示例
from transformers import AutoModelForCausalLM, AutoTokenizer, Trainer, TrainingArguments
from peft import LoraConfig, get_peft_model
from datasets import load_dataset
# 加载模型和数据集
model = AutoModelForCausalLM.from_pretrained("Qwen/Qwen2-0.5B-Instruct")
tokenizer = AutoTokenizer.from_pretrained("Qwen/Qwen2-0.5B-Instruct")
dataset = load_dataset("HuggingFaceH4/ultrachat", split="train")
# 配置 LoRA
lora_config = LoraConfig(
r=8,
lora_alpha=16,
target_modules=["q_proj", "k_proj", "v_proj", "o_proj"],
lora_dropout=0.1,
task_type="CAUSAL_LM"
)
model = get_peft_model(model, lora_config)
# 数据预处理
def preprocess(examples):
return tokenizer([f"<|im_start|>user: {ex['prompt']} <|im_end|>\n<|im_start|>assistant: {ex['response']} <|im_end|>" for ex in examples], truncation=True, max_length=512)
tokenized_dataset = dataset.map(preprocess, batched=True)
# 训练参数
training_args = TrainingArguments(
output_dir="./lora_output",
per_device_train_batch_size=2,
gradient_accumulation_steps=8,
num_train_epochs=3,
learning_rate=2e-5,
fp16=True
)
# 训练
trainer = Trainer(model=model, args=training_args, train_dataset=tokenized_dataset)
trainer.train()
# 保存 LoRA 权重
model.save_pretrained("./lora_qwen2-0.5b")
4. SFT 和 LoRA 的对比
特性 | SFT | LoRA |
---|---|---|
微调类型 | 全参数微调 | 参数高效微调 |
更新参数 | 全部权重(例如 Qwen2-0.5B 的 5 亿参数) | 低秩矩阵(几十万到几百万参数) |
计算需求 | 高(4-8 GB 显存) | 低(2-4 GB 显存) |
存储需求 | 大(完整模型,~1 GB 未量化) | 小(LoRA 适配器,几 MB) |
训练速度 | 慢(更新所有参数) | 快(更新少量参数) |
模型通用性 | 可能损失(灾难性遗忘) | 保留(原始权重冻结) |
多任务支持 | 需为每个任务保存完整模型 | 可为每个任务保存小规模 LoRA 适配器 |
与 llama.cpp 兼容性 | 需转换为 GGUF,推理时加载完整模型 | 支持直接加载 LoRA 适配器(GGUF 格式) |
llama.cpp
中的应用
5. SFT 和 LoRA 在 SFT:
- SFT 微调后,生成完整的模型权重,需转换为 GGUF 格式:
python convert_hf_to_gguf.py --outfile ./sft_qwen2-0.5b.gguf ./sft_qwen2-0.5b ./build/bin/llama-quantize ./sft_qwen2-0.5b.gguf ./sft_qwen2-0.5b-q4_0.gguf q4_0
- 推理:
./build/bin/llama-cli -m ./sft_qwen2-0.5b-q4_0.gguf -p "你好!如何学习 AI?" -cnv
- 局限:GGUF 文件较大,部署和存储成本高。
- SFT 微调后,生成完整的模型权重,需转换为 GGUF 格式:
LoRA:
- LoRA 适配器可直接加载(需转换为 GGUF 格式):
./build/bin/llama-cli -m ~/.cache/llama.cpp/Qwen_Qwen2-0.5B-Instruct-GGUF_qwen2-0_5b-instruct-q2_k.gguf --lora ./lora_qwen2-0.5b.gguf -p "你好!如何学习 AI?" -cnv
- 或者合并 LoRA 权重到基础模型,再转换为 GGUF(参考前文)。
- 优势:LoRA 适配器小,适合多任务切换,推理效率高。
- LoRA 适配器可直接加载(需转换为 GGUF 格式):
6. 总结
- SFT 和 LoRA 都是微调方法:
- SFT:全参数微调,更新所有权重,计算成本高。
- LoRA:参数高效微调,仅更新低秩矩阵,适合低资源环境。
- 与 llama.cpp 的关系:
- SFT 生成完整模型,需转换为 GGUF。
- LoRA 生成小规模适配器,可直接加载或合并到 GGUF。
- 矩阵示例:SFT 直接修改权重,LoRA 添加低秩更新,参数量更少。
THE END