趋近智
LoRA等参数 (parameter)高效微调 (fine-tuning)(PEFT)方法相较于完全微调,能显著减少可训练参数的数量,但扩大PEFT训练规模,通常仍需要采用分布式计算方法。这主要有两方面原因:基础大型语言模型(LLM)本身仍然非常庞大,其正向和反向传播 (backpropagation)需要大量内存和计算资源,并且在大规模数据集上高效训练得益于并行处理,可以缩短实际运行时间。为PEFT调整标准分布式训练框架,需要了解PEFT如何与数据并行和模型并行技术配合使用。
最常用的分布式方法是数据分布式并行(DDP)。在标准DDP中(例如,使用PyTorch的DistributedDataParallel),模型会在多个设备(GPU)上进行复制。每个设备处理不同的mini-batch数据,本地计算梯度,然后梯度会在所有设备之间同步(通常通过AllReduce操作),之后优化器在每个副本上更新模型权重 (weight)。
当将DDP应用于PEFT时:
PEFT等库会自动将只有适配器参数标记 (token)为可训练。当使用DistributedDataParallel包装模型时,它能正确识别这些可训练参数,并且只同步它们的梯度。# 示例伪代码:PyTorch DDP 结合 PEFT
import torch
import torch.distributed as dist
from torch.nn.parallel import DistributedDataParallel as DDP
from transformers import AutoModelForCausalLM
from peft import get_peft_model, LoraConfig # 假设使用了PEFT库
# 初始化分布式环境(例如,使用torchrun)
dist.init_process_group(backend='nccl')
local_rank = dist.get_rank()
torch.cuda.set_device(local_rank)
# 加载基础模型
base_model = AutoModelForCausalLM.from_pretrained("meta-llama/Llama-2-7b-hf")
base_model.to(local_rank)
# 配置并应用PEFT(例如,LoRA)
peft_config = LoraConfig(
r=16,
lora_alpha=32,
target_modules=["q_proj", "v_proj"],
lora_dropout=0.05,
bias="none",
task_type="CAUSAL_LM"
)
model = get_peft_model(base_model, peft_config)
model.print_trainable_parameters() # 验证只有PEFT参数是可训练的
# 使用DDP包装模型
# DDP将自动处理可训练(PEFT)参数的梯度同步
ddp_model = DDP(model, device_ids=[local_rank])
# 优化器仅针对可训练参数
optimizer = torch.optim.AdamW(ddp_model.parameters(), lr=1e-4) # AdamW会隐式过滤requires_grad=True的参数
# --- 训练循环 ---
# for batch in dataloader:
# outputs = ddp_model(**batch)
# loss = outputs.loss
# loss.backward() # 只计算PEFT参数的梯度
# optimizer.step() # 梯度同步后更新PEFT参数
# optimizer.zero_grad()
# --- 训练循环结束 ---
dist.destroy_process_group()
即使梯度通信量减少,大型基础模型的正向和反向传播 (backpropagation)计算成本在DDP中每步训练时间中仍是主导因素。
尽管DDP通过分配数据批次有助于扩展计算能力,但它不减少每个GPU存储模型权重 (weight)、梯度和优化器状态所需的内存占用。DeepSpeed的ZeRO(零冗余优化器)提供将这些组件在数据并行工作器之间分区的技术,显著降低了每个GPU的内存需求。
ZeRO阶段与PEFT的配合:
为PEFT选择ZeRO阶段:
将PEFT与DeepSpeed结合通常涉及配置DeepSpeed JSON文件,并使用deepspeed.initialize函数初始化模型、优化器和数据加载器。请确保传递给DeepSpeed的优化器配置为只优化可训练的PEFT参数。
// 示例DeepSpeed配置文件片段 (ds_config.json) 用于PEFT的ZeRO Stage 2
{
"train_batch_size": 32,
"gradient_accumulation_steps": 1,
"optimizer": {
"type": "AdamW",
"params": {
"lr": 1e-4,
"betas": [0.9, 0.999],
"eps": 1e-8,
"weight_decay": 0.01
}
},
"scheduler": {
"type": "WarmupLR",
"params": {
"warmup_min_lr": 0,
"warmup_max_lr": 1e-4,
"warmup_num_steps": 100
}
},
"zero_optimization": {
"stage": 2,
"offload_optimizer": {
"device": "cpu", // 可选:将优化器状态卸载到CPU内存
"pin_memory": true
},
"contiguous_gradients": true,
"overlap_comm": true
},
"gradient_clipping": 1.0,
"fp16": {
"enabled": true // 或 bf16: { "enabled": true }
}
}
# 示例伪代码:DeepSpeed 结合 PEFT
import deepspeed
from transformers import AutoModelForCausalLM
from peft import get_peft_model, LoraConfig
# --- 设置PEFT模型(如DDP示例中所示)---
# model = get_peft_model(base_model, peft_config)
# 如果需要,可以显式过滤优化器参数,
# 尽管如果模型设置正确,DeepSpeed通常会处理此问题。
# optimizer_grouped_parameters = [
# {'params': [p for n, p in model.named_parameters() if p.requires_grad], 'weight_decay': 0.01}
# ]
# optimizer = torch.optim.AdamW(optimizer_grouped_parameters, lr=1e-4) # 或使用DeepSpeed优化器配置
# 初始化DeepSpeed
# model.parameters()调用应只返回可训练的PEFT参数
model_engine, optimizer, _, _ = deepspeed.initialize(
model=model,
# model_parameters=model.parameters(), # 或显式传递参数
# optimizer=optimizer, # 可以传递预配置的优化器
config_params='ds_config.json' # DeepSpeed配置文件的路径
)
# --- 使用model_engine的训练循环 ---
# for batch in dataloader:
# loss = model_engine(**batch).loss
# model_engine.backward(loss)
# model_engine.step() # 处理优化器步骤、梯度裁剪、调度器
# --- 训练循环结束 ---
分布式方法的选择显著影响每个GPU的内存使用。与完全微调 (fine-tuning)相比,PEFT本身就减少了梯度和优化器状态所需的内存。ZeRO进一步优化了这一点。
这是大型模型微调场景下,每个GPU的预估内存组成。完全微调DDP需要存储完整的权重 (weight)、梯度和优化器状态。PEFT DDP大幅减少了梯度和优化器状态的内存。ZeRO-2进一步减少了优化器状态的内存(可能进行卸载)。ZeRO-3分区所有组件,包括基础模型权重,提供最低的每个GPU占用空间,但可能带来更高的通信开销。激活内存很大程度上取决于批次大小和序列长度,这里为了比较假设为常数。
选择合适的PEFT分布式方法涉及考虑:
通过仔细考虑这些因素并善用PyTorch DDP和DeepSpeed ZeRO等框架,你可以有效地扩展PEFT微调 (fine-tuning),以处理大型模型、大型数据集和复杂的多适配器场景,同时高效管理硬件资源。
这部分内容有帮助吗?
© 2026 ApX Machine LearningAI伦理与透明度•