【DeepSpeed】使用 DeepSpeed 进行 LoRA 微调 示例
结合 DeepSpeed 和 LoRA,可以在分布式环境中高效地微调大型语言模型(如 LLaMA、BERT),尤其适用于资源受限的场景。本示例将展示如何使用 DeepSpeed 进行 LoRA 微调,基于 Hugging Face 的 Transformers 库和 PEFT(Parameter-Efficient Fine-Tuning)库,结合 DeepSpeed 的 ZeRO 优化(Stag
DeepSpeed 是一个强大的分布式深度学习优化库,广泛用于大规模模型的训练和微调。LoRA(Low-Rank Adaptation)是一种高效的微调方法,通过在预训练模型的权重矩阵中引入低秩分解矩阵(即 LoRA 矩阵),大幅减少需要训练的参数量,从而降低内存和计算需求。结合 DeepSpeed 和 LoRA,可以在分布式环境中高效地微调大型语言模型(如 LLaMA、BERT),尤其适用于资源受限的场景。
本示例将展示如何使用 DeepSpeed 进行 LoRA 微调,基于 Hugging Face 的 Transformers 库和 PEFT(Parameter-Efficient Fine-Tuning)库,结合 DeepSpeed 的 ZeRO 优化(Stage 3)和混合精度训练。
1. 环境准备
1.1 依赖安装
确保安装以下库:
pip install deepspeed==0.12.6
pip install transformers==4.36.2
pip install peft==0.7.1
pip install torch==2.1.0
pip install datasets
1.2 硬件要求
- GPU:至少 1 块 NVIDIA GPU(推荐 A100/V100,显存 ≥16GB)。
- 内存:CPU 内存 ≥32GB(ZeRO-Offload 需要高性能 CPU)。
- 存储:快速 SSD/NVMe(用于检查点保存)。
1.3 数据集
本示例使用 Hugging Face 的 IMDb 数据集(情感分类任务)进行微调。你可以替换为自己的数据集。
2. LoRA 微调配置
2.1 DeepSpeed 配置文件(ds_config.json)
DeepSpeed 的配置包括 ZeRO 优化、混合精度训练和检查点设置。以下是一个适合 LoRA 微调的配置文件,启用 ZeRO Stage 3 和 FP16。
{
"train_batch_size": 16,
"gradient_accumulation_steps": 4,
"train_micro_batch_size_per_gpu": 4,
"fp16": {
"enabled": true,
"loss_scale": 0,
"loss_scale_window": 1000,
"initial_scale_power": 16,
"hysteresis": 2,
"min_loss_scale": 1
},
"zero_optimization": {
"stage": 3,
"offload_optimizer": {
"device": "cpu",
"pin_memory": true
},
"offload_param": {
"device": "cpu",
"pin_memory": true
},
"overlap_comm": true,
"contiguous_gradients": true,
"allgather_partitions": true,
"allgather_bucket_size": 5e8,
"reduce_bucket_size": 5e8
},
"optimizer": {
"type": "AdamW",
"params": {
"lr": 5e-5,
"betas": [0.9, 0.999],
"eps": 1e-8,
"weight_decay": 0.01
}
},
"scheduler": {
"type": "WarmupLR",
"params": {
"warmup_min_lr": 0,
"warmup_max_lr": 5e-5,
"warmup_num_steps": 100
}
},
"gradient_clipping": 1.0,
"steps_per_print": 10,
"wall_clock_breakdown": false,
"zero_allow_untested_optimizer": true
}
关键配置说明
train_batch_size:全局批次大小(16)。gradient_accumulation_steps:梯度累积步数(4),实际每 GPU 微批次为train_batch_size / gradient_accumulation_steps / num_gpus。fp16:启用 FP16 混合精度,降低内存占用。zero_optimization:stage: 3:ZeRO Stage 3,分区参数、梯度和优化器状态。offload_optimizer和offload_param:将优化器状态和参数卸载到 CPU。overlap_comm:通信与计算重叠,减少延迟。
optimizer:使用 AdamW,适合 Transformer 模型。scheduler:Warmup 学习率调度,稳定训练。gradient_clipping:裁剪梯度,防止爆炸。
2.2 LoRA 配置
LoRA 配置通过 PEFT 库指定,定义目标模块(如 q_proj、v_proj)和 LoRA 参数(如秩 r)。
from peft import LoraConfig
lora_config = LoraConfig(
r=16, # LoRA 秩
lora_alpha=32, # 缩放因子
target_modules=["q_proj", "v_proj"], # 目标模块(Transformer 注意力层)
lora_dropout=0.1, # Dropout 率
bias="none", # 不调整偏置
task_type="CAUSAL_LM" # 任务类型(因果语言模型)
)
3. 代码示例
以下是一个完整的 LoRA 微调示例,使用 Hugging Face 的 DistilBERT 模型在 IMDb 数据集上进行情感分类,结合 DeepSpeed 的 ZeRO Stage 3。
import deepspeed
import torch
from transformers import AutoModelForSequenceClassification, AutoTokenizer, Trainer, TrainingArguments
from peft import get_peft_model, LoraConfig
from datasets import load_dataset
import os
# Environment setup
os.environ["MASTER_ADDR"] = "localhost"
os.environ["MASTER_PORT"] = "29500"
# Load model and tokenizer
model_name = "distilbert-base-uncased"
model = AutoModelForSequenceClassification.from_pretrained(model_name, num_labels=2)
tokenizer = AutoTokenizer.from_pretrained(model_name)
# Apply LoRA
lora_config = LoraConfig(
r=16,
lora_alpha=32,
target_modules=["q_lin", "v_lin"], # DistilBERT 的注意力模块
lora_dropout=0.1,
bias="none",
task_type="SEQ_CLS"
)
model = get_peft_model(model, lora_config)
# Load dataset
dataset = load_dataset("imdb")
def preprocess_function(examples):
return tokenizer(examples["text"], truncation=True, padding="max_length", max_length=512)
tokenized_dataset = dataset.map(preprocess_function, batched=True)
# DeepSpeed configuration
ds_config = {
"train_batch_size": 16,
"gradient_accumulation_steps": 4,
"train_micro_batch_size_per_gpu": 4,
"fp16": {"enabled": True},
"zero_optimization": {
"stage": 3,
"offload_optimizer": {"device": "cpu", "pin_memory": True},
"offload_param": {"device": "cpu", "pin_memory": True},
"overlap_comm": True,
"contiguous_gradients": True,
"allgather_partitions": True,
"allgather_bucket_size": 5e8,
"reduce_bucket_size": 5e8
},
"optimizer": {
"type": "AdamW",
"params": {
"lr": 5e-5,
"betas": [0.9, 0.999],
"eps": 1e-8,
"weight_decay": 0.01
}
},
"scheduler": {
"type": "WarmupLR",
"params": {
"warmup_min_lr": 0,
"warmup_max_lr": 5e-5,
"warmup_num_steps": 100
}
},
"gradient_clipping": 1.0,
"steps_per_print": 10,
"zero_allow_untested_optimizer": True
}
# Training arguments
training_args = TrainingArguments(
output_dir="./lora_output",
per_device_train_batch_size=4,
gradient_accumulation_steps=4,
num_train_epochs=1,
logging_steps=10,
save_steps=100,
save_total_limit=2,
deepspeed=ds_config
)
# Initialize Trainer
trainer = Trainer(
model=model,
args=training_args,
train_dataset=tokenized_dataset["train"],
eval_dataset=tokenized_dataset["test"]
)
# Train
trainer.train()
# Save model
model.save_pretrained("./lora_output/final_model")
tokenizer.save_pretrained("./lora_output/final_model")
4. 运行步骤
4.1 保存代码和配置
- 保存 DeepSpeed 配置文件为
ds_config.json。 - 保存上述代码为
train_lora.py。
4.2 运行命令
使用 deepspeed 命令运行:
deepspeed --num_gpus=4 train_lora.py
或使用 torch.distributed.launch:
python -m torch.distributed.launch --nproc_per_node=4 train_lora.py
4.3 输出
- 日志:显示训练进度、损失和 DeepSpeed 状态:
Step 10, Loss: 0.5432 Step 20, Loss: 0.5211 - 检查点:保存到
lora_output/checkpoint-*。 - 最终模型:保存到
lora_output/final_model。
5. 关键实现细节
5.1 LoRA 集成
- PEFT 库:通过
get_peft_model自动为指定模块(如q_lin、v_lin)添加 LoRA 矩阵。 - 参数效率:LoRA 只训练低秩矩阵(约占总参数的 0.1%-1%),冻结预训练权重。
- 目标模块:根据模型结构选择(如 DistilBERT 使用
q_lin、v_lin,LLaMA 使用q_proj、v_proj)。
5.2 DeepSpeed 优化
- ZeRO Stage 3:分区参数和优化器状态,支持大模型微调。
- CPU 卸载:将参数和优化器状态卸载到 CPU,节省 GPU 显存。
- FP16 训练:降低内存占用,加速计算。
- 通信优化:
overlap_comm和contiguous_gradients减少通信延迟。
5.3 Trainer 集成
- Hugging Face 的
Trainer与 DeepSpeed 无缝集成,通过deepspeed参数加载配置。 - 支持检查点保存、评估和日志记录。
6. 优化建议
6.1 内存优化
- 降低微批次大小:
{ "train_micro_batch_size_per_gpu": 2 } - 启用 NVMe 卸载(如果有快速存储):
{ "zero_optimization": { "offload_param": {"device": "nvme"} } } - 激活检查点:
{ "activation_checkpointing": { "partition_activations": True, "cpu_checkpointing": True } }
6.2 性能优化
- 通信压缩:
{ "communication_data_type": "fp16", "compress_communication": True } - NCCL 优化:
export NCCL_IB_DISABLE=0 export NCCL_ALGO=Tree - 优化算子:
pip install deepspeed[ops]
6.3 超参数调优
- LoRA 秩:尝试
r=8或r=32,平衡性能和内存。 - 学习率:调整
lr(如1e-5到1e-4)以优化收敛。 - Warmup 步数:根据数据集大小调整
warmup_num_steps。
6.4 自动调优
使用 DeepSpeed 自动调优优化批次大小和 ZeRO 参数:
{
"autotuning": {
"enabled": True,
"fast": True,
"arg_mappings": {
"train_micro_batch_size_per_gpu": ["2", "4", "8"],
"zero_optimization.allgather_bucket_size": ["1e8", "5e8"]
}
}
}
7. 调试与常见问题
7.1 内存溢出(OOM)
- 症状:训练中断,报
CUDA out of memory。 - 解决:
- 降低
train_micro_batch_size_per_gpu。 - 启用 ZeRO 卸载或激活检查点。
- 检查内存使用:
from deepspeed.utils import memory_status memory_status()
- 降低
7.2 通信错误
- 症状:NCCL 超时或进程挂起。
- 解决:
- 检查环境变量:
export MASTER_ADDR=localhost export MASTER_PORT=29500 - 启用 NCCL 日志:
export NCCL_DEBUG=INFO export NCCL_DEBUG_FILE=nccl_log.txt - 优化通信:
{ "communication_data_type": "fp16" }
- 检查环境变量:
7.3 收敛慢或损失不下降
- 症状:损失值高或波动。
- 解决:
- 调整学习率(
lr)或 Warmup 步数。 - 检查数据集预处理,确保输入正确。
- 验证 LoRA 模块是否正确应用:
model.print_trainable_parameters()
- 调整学习率(
7.4 检查点加载失败
- 症状:无法恢复训练。
- 解决:
- 确保检查点目录和
tag正确:trainer.model.load_checkpoint("lora_output", tag="checkpoint-100") - 检查进程数和并行配置是否与保存时一致。
- 确保检查点目录和
8. 扩展到其他模型
8.1 微调 LLaMA
对于更大的模型(如 LLaMA),需调整 LoRA 目标模块和 DeepSpeed 配置:
model_name = "meta-llama/Llama-2-7b-hf"
model = AutoModelForCausalLM.from_pretrained(model_name)
lora_config = LoraConfig(
r=16,
lora_alpha=32,
target_modules=["q_proj", "v_proj", "k_proj", "o_proj"],
task_type="CAUSAL_LM"
)
ds_config["train_micro_batch_size_per_gpu"] = 2 # 降低微批次
ds_config["zero_optimization"]["offload_param"]["device"] = "nvme" # NVMe 卸载
8.2 多节点训练
在多节点环境中运行:
deepspeed --num_nodes=2 --num_gpus=4 train_lora.py
- 确保
MASTER_ADDR和MASTER_PORT在所有节点一致。 - 调整
train_batch_size和gradient_accumulation_steps以适应节点数。
9. 保存和推理
9.1 保存 LoRA 模型
训练完成后,保存 LoRA 参数和 tokenizer:
model.save_pretrained("./lora_output/final_model")
tokenizer.save_pretrained("./lora_output/final_model")
9.2 推理
加载微调后的模型进行推理:
from transformers import AutoModelForSequenceClassification, AutoTokenizer
from peft import PeftModel
# Load base model and tokenizer
base_model = AutoModelForSequenceClassification.from_pretrained("distilbert-base-uncased")
tokenizer = AutoTokenizer.from_pretrained("distilbert-base-uncased")
# Load LoRA weights
model = PeftModel.from_pretrained(base_model, "./lora_output/final_model")
# Inference
text = "This movie is great!"
inputs = tokenizer(text, return_tensors="pt", padding=True, truncation=True, max_length=512)
outputs = model(**inputs)
logits = outputs.logits
pred = torch.argmax(logits, dim=-1)
print("Prediction:", "Positive" if pred.item() == 1 else "Negative")
10. 学习资源
- DeepSpeed 文档:https://www.deepspeed.ai/docs/
- Hugging Face PEFT:https://huggingface.co/docs/peft/
- Transformers 文档:https://huggingface.co/docs/transformers/
- DeepSpeed 示例:https://github.com/microsoft/DeepSpeedExamples
- LoRA 论文:https://arxiv.org/abs/2106.09685
魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。
更多推荐
所有评论(0)