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 的对比

特性SFTLoRA
微调类型全参数微调参数高效微调
更新参数全部权重(例如 Qwen2-0.5B 的 5 亿参数)低秩矩阵(几十万到几百万参数)
计算需求高(4-8 GB 显存)低(2-4 GB 显存)
存储需求大(完整模型,~1 GB 未量化)小(LoRA 适配器,几 MB)
训练速度慢(更新所有参数)快(更新少量参数)
模型通用性可能损失(灾难性遗忘)保留(原始权重冻结)
多任务支持需为每个任务保存完整模型可为每个任务保存小规模 LoRA 适配器
与 llama.cpp 兼容性需转换为 GGUF,推理时加载完整模型支持直接加载 LoRA 适配器(GGUF 格式)

5. SFT 和 LoRA 在 llama.cpp 中的应用

  • 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 文件较大,部署和存储成本高。
  • 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 适配器小,适合多任务切换,推理效率高。

6. 总结

  • SFT 和 LoRA 都是微调方法
    • SFT:全参数微调,更新所有权重,计算成本高。
    • LoRA:参数高效微调,仅更新低秩矩阵,适合低资源环境。
  • 与 llama.cpp 的关系
    • SFT 生成完整模型,需转换为 GGUF。
    • LoRA 生成小规模适配器,可直接加载或合并到 GGUF。
  • 矩阵示例:SFT 直接修改权重,LoRA 添加低秩更新,参数量更少。

THE END