赣州市网站建设_网站建设公司_API接口_seo优化
2026/1/5 20:27:02 网站建设 项目流程

过程描述

按教程里的正常流程启动微调后,总会出现cuda out of memory的现象
于是不得不用两个gpu同时训练
这里的代码修改只涉及到设置多gpu进行微调,不涉及量化等

完整微调脚本

import torch
from datasets import load_dataset
from transformers import (AutoTokenizer,AutoModelForCausalLM,DataCollatorForSeq2Seq,TrainingArguments,Trainer
)
from peft import (LoraConfig,TaskType,get_peft_model,prepare_model_for_kbit_training
)
import osprint("=========================================")
print("Qwen1.5-7B LoRA微调 - 多GPU分布式训练")
print("=========================================")# ====================
# 1. 加载tokenizer和模型
# ====================
model_path = './qwen/Qwen1.5-7B-Chat/'print(f"1. 加载tokenizer: {model_path}")
tokenizer = AutoTokenizer.from_pretrained(model_path,use_fast=False,trust_remote_code=True
)# 确保pad token设置正确
if tokenizer.pad_token is None:tokenizer.pad_token = tokenizer.eos_tokenprint(f"  设置pad_token为eos_token: {tokenizer.pad_token}")print("2. 检查GPU信息...")
print(f"  GPU数量: {torch.cuda.device_count()}")
for i in range(torch.cuda.device_count()):print(f"  GPU {i}: {torch.cuda.get_device_name(i)}")print(f"    显存总量: {torch.cuda.get_device_properties(i).total_memory / 1024 ** 3:.2f} GB")print("3. 加载模型(半精度)...")
# 检查是否支持bfloat16,如果支持则使用,否则使用float16
if torch.cuda.is_bf16_supported():torch_dtype = torch.bfloat16print("  GPU支持bfloat16,使用bfloat16精度")
else:torch_dtype = torch.float16print("  GPU不支持bfloat16,使用float16精度")# 加载模型
model = AutoModelForCausalLM.from_pretrained(model_path,device_map="auto",  # 让transformers自动分配模型到多GPUtorch_dtype=torch_dtype,trust_remote_code=True,
)print(f"  模型以{torch_dtype}精度加载")
print(f"  当前显存占用: {torch.cuda.memory_allocated() / 1024 ** 3:.2f} GB")# ====================
# 2. 加载并处理数据集
# ====================
dataset_path = "/tmp/pycharm_project_514/dataset/huanhuan.json"
print(f"\n4. 加载数据集: {dataset_path}")try:dataset = load_dataset('json', data_files=dataset_path)['train']print(f"  数据集大小: {len(dataset)} 条")
except Exception as e:print(f"  数据集加载失败: {e}")# 创建测试数据from datasets import Datasettest_data = [{"instruction": "你是谁?","input": "","output": "家父是大理寺少卿甄远道。"} for _ in range(100)]dataset = Dataset.from_list(test_data)print(f"  使用测试数据,大小: {len(dataset)} 条")def process_func(example):MAX_LENGTH = 128  # 减小序列长度以节省显存# 构建对话格式system_msg = "现在你要扮演皇帝身边的女人--甄嬛"user_msg = example['instruction'] + example['input']# Qwen1.5的特殊格式prompt = f"<|im_start|>system\n{system_msg}<|im_end|>\n"prompt += f"<|im_start|>user\n{user_msg}<|im_end|>\n"prompt += f"<|im_start|>assistant\n"# 编码prompt_encoded = tokenizer(prompt, add_special_tokens=False)response = example['output'] + "<|im_end|>"response_encoded = tokenizer(response, add_special_tokens=False)# 拼接input_ids = prompt_encoded["input_ids"] + response_encoded["input_ids"]attention_mask = prompt_encoded["attention_mask"] + response_encoded["attention_mask"]labels = [-100] * len(prompt_encoded["input_ids"]) + response_encoded["input_ids"]# 截断if len(input_ids) > MAX_LENGTH:input_ids = input_ids[:MAX_LENGTH]attention_mask = attention_mask[:MAX_LENGTH]labels = labels[:MAX_LENGTH]return {"input_ids": input_ids,"attention_mask": attention_mask,"labels": labels}print("5. 处理数据集中...")
tokenized_dataset = dataset.map(process_func,remove_columns=dataset.column_names,desc="处理数据"
)
print(f"  数据预处理完成")# ====================
# 3. 配置Lora参数
# ====================
print("\n6. 配置LoRA...")
lora_config = LoraConfig(task_type=TaskType.CAUSAL_LM,target_modules=["q_proj", "v_proj", "k_proj", "o_proj"],  # 可调整训练层inference_mode=False,r=8,lora_alpha=32,lora_dropout=0.1,bias="none",
)# ====================
# 4. 准备模型用于训练
# ====================
print("7. 准备模型训练...")
model = get_peft_model(model, lora_config)# 打印可训练参数
model.print_trainable_parameters()# ====================
# 5. 配置训练参数(多GPU优化)
# ====================
print("\n8. 配置训练参数(多GPU)...")# 计算有效的总batch size
per_device_batch_size = 1  # 减小batch size,因为现在不使用量化
gradient_accumulation_steps = 16  # 增加梯度累积步数
num_gpus = max(1, torch.cuda.device_count())  # 可用GPU数量
effective_batch_size = per_device_batch_size * gradient_accumulation_steps * num_gpusprint(f"  可用GPU数量: {num_gpus}")
print(f"  每个GPU batch size: {per_device_batch_size}")
print(f"  梯度累积步数: {gradient_accumulation_steps}")
print(f"  有效总batch size: {effective_batch_size}")training_args = TrainingArguments(output_dir="./output/Qwen1.5-7B-Chat-multi-gpu",per_device_train_batch_size=per_device_batch_size,gradient_accumulation_steps=gradient_accumulation_steps,logging_steps=10,num_train_epochs=3,save_steps=100,learning_rate=1e-4,bf16=torch.cuda.is_bf16_supported(),fp16=not torch.cuda.is_bf16_supported(),gradient_checkpointing=True,gradient_checkpointing_kwargs={"use_reentrant": False},optim="adamw_torch",  # 使用标准优化器save_total_limit=3,report_to="none",remove_unused_columns=False,dataloader_pin_memory=False,group_by_length=True,lr_scheduler_type="cosine",warmup_ratio=0.03,# 多GPU相关设置dataloader_num_workers=4,  # 增加数据加载工作线程ddp_find_unused_parameters=False if num_gpus > 1 else None,  # 多GPU时需要ddp_timeout=1800,  # 分布式训练超时时间# 优化设置max_grad_norm=0.3,weight_decay=0.01,
)# ====================
# 6. 创建Trainer
# ====================
trainer = Trainer(model=model,args=training_args,train_dataset=tokenized_dataset,data_collator=DataCollatorForSeq2Seq(tokenizer=tokenizer,padding=True,pad_to_multiple_of=8,return_tensors="pt"),
)# ====================
# 7. 开始训练
# ====================
print("\n9. 开始训练...")
print("=" * 50)# 清空显存
torch.cuda.empty_cache()
print(f"训练前显存状态:")
for i in range(torch.cuda.device_count()):print(f"  GPU {i}: {torch.cuda.memory_allocated(i) / 1024 ** 3:.2f} GB / "f"{torch.cuda.get_device_properties(i).total_memory / 1024 ** 3:.2f} GB")try:print(f"开始分布式训练,使用 {num_gpus} 个GPU...")trainer.train()print("\n训练完成!")# 保存模型(只在主进程保存)output_dir = "./output/Qwen1.5-7B-Chat-lora-multi"trainer.save_model(output_dir)print(f"模型已保存到: {output_dir}")except torch.cuda.OutOfMemoryError as e:print(f"\n显存不足错误: {e}")print("尝试减小以下参数:")print(f"  1. per_device_train_batch_size (当前: {per_device_batch_size})")print(f"  2. MAX_LENGTH (当前: 128)")print(f"  3. gradient_accumulation_steps (当前: {gradient_accumulation_steps})")print("或尝试以下方法:")print("  4. 减小LoRA秩 (当前: r=8)")print("  5. 只训练更少的层 (当前: target_modules包含4个层)")except Exception as e:print(f"\n训练出错: {e}")import tracebacktraceback.print_exc()print("\n训练结束")
print("=" * 50)

关于多gpu的部分:

per_device_batch_size = 1  # 减小batch size,因为现在不使用量化
gradient_accumulation_steps = 16  # 增加梯度累积步数
num_gpus = max(1, torch.cuda.device_count())  # 可用GPU数量
effective_batch_size = per_device_batch_size * gradient_accumulation_steps * num_gpusprint(f"  可用GPU数量: {num_gpus}")
print(f"  每个GPU batch size: {per_device_batch_size}")
print(f"  梯度累积步数: {gradient_accumulation_steps}")
print(f"  有效总batch size: {effective_batch_size}")training_args = TrainingArguments(output_dir="./output/Qwen1.5-7B-Chat-multi-gpu",per_device_train_batch_size=per_device_batch_size,gradient_accumulation_steps=gradient_accumulation_steps,logging_steps=10,num_train_epochs=3,save_steps=100,learning_rate=1e-4,bf16=torch.cuda.is_bf16_supported(),fp16=not torch.cuda.is_bf16_supported(),gradient_checkpointing=True,gradient_checkpointing_kwargs={"use_reentrant": False},optim="adamw_torch",  # 使用标准优化器save_total_limit=3,report_to="none",remove_unused_columns=False,dataloader_pin_memory=False,group_by_length=True,lr_scheduler_type="cosine",warmup_ratio=0.03,# 多GPU相关设置dataloader_num_workers=4,  # 增加数据加载工作线程ddp_find_unused_parameters=False if num_gpus > 1 else None,  # 多GPU时需要ddp_timeout=1800,  # 分布式训练超时时间# 优化设置max_grad_norm=0.3,weight_decay=0.01,
)

解析

多GPU微调训练知识点总结

这段代码展示了在多GPU环境下进行LoRA微调的关键配置,以下是对其中多GPU微调知识点的详细总结:

1. 批量大小计算与配置

关键参数关系

# 核心计算公式
effective_batch_size = per_device_batch_size * gradient_accumulation_steps * num_gpus

参数说明:

  • per_device_train_batch_size=1:每个GPU每次处理的样本数量

    • 由于不使用量化,模型占用显存较大,设置为1确保单GPU能容纳
    • 在多GPU环境中,这是每个GPU独立的batch size
  • gradient_accumulation_steps=16:梯度累积步数

    • 每累积16个批次的梯度才更新一次模型参数
    • 作用:模拟更大的batch size,但不增加显存占用
  • effective_batch_size:有效总批量大小

    • 计算方式:1 × 16 × GPU数量
    • 假设有2个GPU:有效batch size = 1 × 16 × 2 = 32
    • 这是参数更新时实际使用的batch size

2. 多GPU特定配置

分布式数据并行参数

ddp_find_unused_parameters=False if num_gpus > 1 else None
  • 作用:在分布式训练中处理未被使用的参数
  • 设置为False:当存在未使用的参数时不报错
  • 条件设置:只在多GPU(num_gpus > 1)时启用
ddp_timeout=1800
  • 作用:设置分布式训练的超时时间(秒)
  • 值1800:30分钟超时
  • 重要性:防止因网络延迟或负载不均衡导致的训练中断

3. 数据加载优化

dataloader_num_workers=4
  • 作用:设置数据加载的工作进程数
  • 推荐值:通常设置为GPU数量的2-4倍
  • 效果:并行加载数据,减少CPU到GPU的数据传输瓶颈
dataloader_pin_memory=False
  • 作用:是否将数据固定到内存中
  • 设置为False:在多GPU环境中通常关闭,避免内存碎片化
  • 替代方案:让PyTorch自动管理内存

4. 训练过程优化

序列长度分组

group_by_length=True
  • 作用:将长度相近的样本分组到一起
  • 优势
    1. 减少padding,提高计算效率
    2. 在分布式环境中减少通信开销
    3. 提高GPU利用率

梯度相关设置

gradient_checkpointing=True
gradient_checkpointing_kwargs={"use_reentrant": False}
max_grad_norm=0.3
  1. 梯度检查点:用计算时间换显存空间
  2. use_reentrant=False:使用非重入检查点,更高效
  3. max_grad_norm=0.3:梯度裁剪阈值,防止梯度爆炸

5. 精度与优化器配置

混合精度训练

bf16=torch.cuda.is_bf16_supported()
fp16=not torch.cuda.is_bf16_supported()
  • 自动检测:根据GPU能力选择最佳精度
  • bf16优先:如果GPU支持bfloat16则使用,否则使用float16
  • 优势:减少显存占用,加速计算

优化器选择

optim="adamw_torch"
  • 不使用8bit优化器:因为没有使用bitsandbytes量化
  • AdamW:标准优化器,适合多GPU环境
  • weight_decay=0.01:L2正则化,防止过拟合

6. 学习率调度策略

lr_scheduler_type="cosine"
warmup_ratio=0.03
  • 余弦退火:学习率从初始值按余弦曲线下降
  • 热身阶段:前3%的训练步数线性增加学习率
  • 分布式优势:在多GPU中确保所有进程同步学习率变化

7. GPU资源管理与监控

资源分配逻辑

  1. 自动检测GPU数量torch.cuda.device_count()
  2. 设备映射:通过device_map="auto"自动分配模型到各GPU
  3. 负载均衡:Transformers自动将模型层分配到可用GPU

显存使用策略

  1. 保守的batch size:从1开始,确保不OOM
  2. 梯度累积:用时间换空间
  3. 梯度检查点:进一步减少显存峰值

8. 分布式训练中的批次处理流程

单GPU处理流程:
输入样本 → 前向传播 → 计算损失 → 反向传播 → 累积梯度多GPU处理流程(数据并行):
批次分割 → 各GPU独立处理 → 梯度同步 → 参数更新↓           ↓           ↓         ↓
per_device  每个GPU    all_reduce   effective
batch_size  独立计算   通信汇总     batch_size

9. 多GPU训练性能权衡

优势:

  1. 更大有效batch size:通过多GPU累积实现
  2. 更快训练速度:并行处理数据
  3. 解决显存限制:模型可以分布在多个GPU上

挑战:

  1. 通信开销:梯度同步增加时间成本
  2. 负载不均衡:需要合理分配计算任务
  3. 调试复杂性:多进程环境更难调试

10. 关键配置原则总结

配置项 单GPU 多GPU 说明
batch_size 较大值 较小值 多GPU时每个GPU的batch size应减小
梯度累积 可选 必须 多GPU时用梯度累积实现大batch
数据加载 1-2 workers 4+ workers 多GPU需要更多数据加载进程
设备映射 "auto"或"cuda" "auto" 多GPU时必须用auto自动分配
DDP参数 无需 必须设置 处理分布式训练中的未用参数

核心要点

  1. 有效批量大小是关键:通过per_device_batch_size × gradient_accumulation × num_gpus计算
  2. 梯度同步是瓶颈:合理设置gradient_accumulation_steps平衡通信开销
  3. 自动设备映射简化配置device_map="auto"让框架自动处理模型分布
  4. 数据加载优化不可忽视:足够的num_workers确保GPU不空闲
  5. 超时设置很重要ddp_timeout防止因网络问题导致训练失败

通过上述配置,可以在多GPU环境中高效地进行LoRA微调,充分利用硬件资源,同时避免显存不足的问题。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询