在人工智能领域,大语言模型(LLM)的预训练需要消耗海量计算资源,但直接使用预训练模型往往难以满足特定场景的需求。这就好比买了一台高性能电脑,但如果不安装专业软件,依然无法完成视频剪辑或3D渲染工作。微调(Fine-tuning)和蒸馏(Distillation)正是解决这一问题的两把钥匙。
微调的本质是在预训练模型的基础上,通过特定领域的数据进行二次训练,调整模型参数使其适应新任务。这就像让一位通才学者通过专项培训成为某个领域的专家。而蒸馏则是将大模型(教师模型)的知识传递给小模型(学生模型),类似于名师将毕生所学提炼后传授给年轻学生。
在实际应用中,这两种技术通常结合使用:
关键认知:微调关注模型能力的扩展,蒸馏关注模型效率的提升。两者不是对立关系,而是互补的技术手段。
大模型训练对硬件的要求可以用"内存墙"来形容——显存容量直接决定了你能操作的模型规模。以下是三种典型配置方案:
| 硬件组合 | 适用场景 | 可操作模型规模 | 成本估算 |
|---|---|---|---|
| 单卡A100 80GB | 全量微调7B模型 | 7B-13B | 约15万元 |
| 4×RTX 4090 | LoRA微调13B模型 | 13B-30B | 约6万元 |
| 8×A100集群 | 生产级微调 | 70B+ | 百万元以上 |
显存占用的简易计算公式:
code复制总显存 ≈ (模型参数量 × 20字节) + (batch_size × 序列长度 × 1024字节)
其中20字节包含:
推荐使用Conda创建隔离环境,避免依赖冲突:
bash复制conda create -n llmft python=3.10
conda activate llmft
核心依赖安装:
bash复制# 基础框架
pip install torch==2.1.0 transformers==4.33.0 accelerate==0.22.0
# 高效训练组件
pip install flash-attn==2.3.2 bitsandbytes==0.41.1
# 参数高效微调
pip install peft==0.5.0
# 实验管理
pip install wandb==0.15.8
验证环境是否正常:
python复制import torch
print(torch.cuda.is_available()) # 应输出True
print(torch.cuda.get_device_name(0)) # 显示GPU型号
微调数据的质量直接影响模型效果,常见有两种标准格式:
json复制{
"instruction": "将以下句子翻译成英文",
"input": "今天的天气真好",
"output": "The weather is nice today"
}
json复制[
{"from": "human", "value": "如何用Python读取CSV文件?"},
{"from": "gpt", "value": "可以使用pandas库:\n```python\nimport pandas as pd\ndata = pd.read_csv('file.csv')\n```"}
]
python复制from simhash import Simhash
def get_hash(text):
return Simhash(text.split()).value
hashes = set()
unique_data = []
for item in raw_data:
h = get_hash(item['instruction'] + item['output'])
if h not in hashes:
hashes.add(h)
unique_data.append(item)
python复制import matplotlib.pyplot as plt
lengths = [len(tokenizer.encode(item['instruction'] + item['output']))
for item in dataset]
plt.hist(lengths, bins=50)
plt.xlabel('Token Length')
plt.ylabel('Count')
plt.show()
以LLaMA-2 7B模型为例,完整训练脚本包含以下关键组件:
python复制from transformers import (
AutoModelForCausalLM,
AutoTokenizer,
TrainingArguments,
Trainer
)
# 模型加载
model = AutoModelForCausalLM.from_pretrained(
"meta-llama/Llama-2-7b-hf",
torch_dtype=torch.bfloat16,
use_cache=False # 必须关闭以启用梯度检查点
)
model.gradient_checkpointing_enable() # 减少30%显存占用
# 数据预处理
tokenizer = AutoTokenizer.from_pretrained("meta-llama/Llama-2-7b-hf")
tokenizer.pad_token = tokenizer.eos_token # 关键设置
def tokenize_function(examples):
texts = [f"指令:{inst}\n输入:{inp}\n输出:{out}"
for inst, inp, out in zip(examples["instruction"],
examples["input"],
examples["output"])]
return tokenizer(texts, truncation=True, max_length=2048, padding="max_length")
python复制training_args = TrainingArguments(
per_device_train_batch_size=1, # 物理batch_size
gradient_accumulation_steps=8, # 等效batch_size=8
...
)
python复制training_args = TrainingArguments(
fp16=False, # NVIDIA显卡使用
bf16=True, # AMD显卡或A100+
...
)
json复制{
"train_batch_size": 8,
"gradient_accumulation_steps": 8,
"optimizer": {
"type": "AdamW",
"params": {
"lr": 2e-5,
"weight_decay": 0.01
}
},
"zero_optimization": {
"stage": 3,
"offload_optimizer": {
"device": "cpu"
}
}
}
LoRA(Low-Rank Adaptation)的核心思想是通过低秩分解来模拟参数更新:
code复制ΔW = BA
其中 W ∈ R^{d×k}, B ∈ R^{d×r}, A ∈ R^{r×k}, r ≪ min(d,k)
典型设置中,r=8时,可训练参数仅为原模型的0.1%。
python复制from peft import LoraConfig, get_peft_model
lora_config = LoraConfig(
r=8,
lora_alpha=32,
target_modules=["q_proj", "v_proj"],
lora_dropout=0.05,
bias="none",
task_type="CAUSAL_LM"
)
model = get_peft_model(model, lora_config)
model.print_trainable_parameters() # 查看可训练参数占比
4位量化配置示例:
python复制from transformers import BitsAndBytesConfig
bnb_config = BitsAndBytesConfig(
load_in_4bit=True,
bnb_4bit_use_double_quant=True,
bnb_4bit_quant_type="nf4",
bnb_4bit_compute_dtype=torch.bfloat16
)
model = AutoModelForCausalLM.from_pretrained(
"meta-llama/Llama-2-13b-hf",
quantization_config=bnb_config,
device_map="auto"
)
python复制teacher_model.generate(
input_ids,
max_length=512,
temperature=0.7,
top_p=0.9,
do_sample=True
)
python复制student_trainer = Trainer(
model=student_model,
args=TrainingArguments(per_device_train_batch_size=4),
train_dataset=distillation_dataset
)
student_trainer.train()
自定义损失函数:
python复制def distill_loss(student_output, teacher_output, labels, temp=3.0, alpha=0.3):
# Soft targets
soft_loss = F.kl_div(
F.log_softmax(student_output.logits / temp, dim=-1),
F.softmax(teacher_output.logits / temp, dim=-1),
reduction="batchmean"
) * (temp ** 2)
# Hard targets
hard_loss = F.cross_entropy(
student_output.logits.view(-1, student_output.logits.size(-1)),
labels.view(-1)
)
return alpha * hard_loss + (1 - alpha) * soft_loss
| 指标类型 | 计算方法 | 适用场景 | 优缺点 |
|---|---|---|---|
| 困惑度(Perplexity) | exp(−1/N ∑ logP(wi)) | 通用评估 | 计算简单但不够直观 |
| BLEU | n-gram匹配精度 | 机器翻译 | 忽略语义,对同义替换不敏感 |
| ROUGE | 召回率导向 | 文本摘要 | 偏向长文本匹配 |
| GPT-4评分 | 人工评估替代 | 开放对话 | 成本高但最接近人类判断 |
问题:训练loss波动大
问题:生成结果重复
问题:显存不足
LoRA权重合并:
python复制from peft import PeftModel
base_model = AutoModelForCausalLM.from_pretrained("base_model")
lora_model = PeftModel.from_pretrained(base_model, "./lora_checkpoint")
merged_model = lora_model.merge_and_unload()
merged_model.save_pretrained("./merged_model")
启动API服务:
bash复制python -m vLLM.entrypoints.api_server \
--model ./merged_model \
--tensor-parallel-size 2 \
--gpu-memory-utilization 0.9 \
--max-num-seqs 256
性能调优参数:
--tensor-parallel-size: 多卡并行数--block-size: KV缓存块大小(影响吞吐量)--max-num-seqs: 最大并发请求数在实际项目中,建议采用渐进式技术路线: