Deepseek-V3微调(Colossal-AI)

Deepseek-V3微调(Colossal-AI)

1. 环境准备

基本要求说明
  • DeepSeek V3 是 6710 亿参数的模型,开源项目直接提供的权重文件采用 FP8 格式,并通过E4M3量化方法进行存储,但训练过程中通过混合精度策略结合了 BF16、FP32 和定制化 E5M6 格式,其 FP8 格式下参数存储约需 700GB 显存。
  • 通常激活参数内存约为参数存储的 10 - 20%,即 700GB 的 10 - 20%,大约是 70 - 140GB,取较大值 140GB。
  • 运行过程中还需考虑其他因素如系统开销、缓存、临时数据存储等,这部分额外需求估计在 200 - 300GB 左右。
  • 综合上述分析,671B模型基础内存要求在1.1TB以上。
环境准备

首先需要准备好微调所需的环境,建议使用conda创建虚拟环境:

conda create -n colossalai python=3.10
conda activate colossalai

# 安装PyTorch(根据你的CUDA版本选择合适的安装命令)
pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu118
# 验证
python -c "import torch; print(torch.__version__); print(torch.cuda.is_available())"

##安装PyTorch CPU版本
##pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cpu
# python -c "import torch; print(torch.__version__); print(torch.cpu.is_available())"

# 安装Colossal-AI及其依赖
pip install colossalai deepspeed transformers datasets peft accelerate bitsandbytes
## 手动移除 flash-attn
cd /work/home/ac5ixifol4/soft/ColossalAI/applications/ColossalChat
pip install -e .
# 验证 coati
python -c "from coati.dataset.loader import RawConversationDataset; print('Success')"

git clone https://mirror.ghproxy.com/https://github.com/hpcaitech/Coati.git
cd coati
pip install .

2. 获取Colossal-AI微调脚本

从Colossal-AI官方仓库获取微调示例脚本:

git clone https://github.com/hpcaitech/ColossalAI.git
# 安装依赖
pip install -r /home/ColossalAI/requirements/requirements-test.txt
pip install -r /home/ColossalAI/requirements/requirements.txt
# 切换脚本目录-或变更后续脚本文件地址examples/training_scripts/lora_finetune.py
cd ColossalAI/applications/ColossalChat

3. 准备数据集

将你的数据集转换为JSONL格式,每行包含一个对话列表。示例:

[{"role": "system", "content": "你是一个智能助手。"},
{"role": "user", "content": "如何学习Python?"},
{"role": "assistant", "content": "学习Python可以从以下几个步骤开始:1. 学习基础语法;2. 练习项目;3. 学习数据结构和算法..."}]

将数据集保存为train.jsonlval.jsonl(可选)。

模型权重转换,将原BF8转换为BF16.

  1. 获取转换脚本

    git clone https://github.com/deepseek-ai/DeepSeek-V3.git
    cd DeepSeek-V3/inference
    ##  下载脚本
    wget https://github.com/deepseek-ai/DeepSeek-V3/blob/main/inference/fp8_cast_bf16.py
  2. 转换模型
python fp8_cast_bf16.py \
  --model_path /path/to/fp8_weights \  # FP8格式的原始权重路径
  --output_path /path/to/bf16_weights \  # 转换后的BF16权重保存路径
  --num_gpus 4  # 使用的GPU数量,根据实际情况调整

参数说明:

  • model_path: FP8 格式的 DeepSeek V3 原始权重路径
  • output_path: 转换后的 BF16 权重保存路径
  • num_gpus: 并行处理的 GPU 数量,可根据服务器配置调整

  • BF16 和 BF8:指的是模型参数的数据存储格式。BF16 是 16 位的脑浮点格式,能在保持一定精度的同时,降低存储和计算成本;BF8 是 8 位的脑浮点格式,进一步降低了精度和存储需求,模型占用空间更小,理论上能在相同硬件条件下提高计算速度,但可能会带来一定的精度损失2。

4. 配置微调参数

创建或修改配置文件,指定模型、数据集和微调参数。以下是一个示例配置:

# configs/finetune_deepseek_v3.py
from colossalai.nn.lr_scheduler import CosineAnnealingLR

# CPU专属
import torch
torch.set_num_threads(3)  # 根据 CPU 核心数调整(如 16 核设为 8,避免与分布式进程冲突)

# 模型配置
model = dict(
    type="llama",  # 基于Llama架构的模型
    pretrained_path="/public/home/scno0z94vb/SothisAI/model/Aihub/DeepSeek-V3-0324/main/DeepSeek-V3-0324",  # BF16格式的DeepSeek V3权重模型
    lora_rank=8,  # LoRA低秩适应矩阵的秩,控制参数量与表达能力的平衡
    lora_alpha=32, # LoRA缩放系数,影响微调时参数更新的幅度
    lora_dropout=0.1,  # LoRA层的dropout率,防止过拟合
     # 应用LoRA的注意力模块中的目标投影层
    lora_target_modules=["q_proj", "v_proj"], 
    bias="none", # 不训练偏置参数
    task_type="CAUSAL_LM",  # 任务类型为因果语言模型
)

# 数据配置
data = dict(
    train=dict(
        type="csv",  # 训练数据格式为JSONL CSV
        path="/public/home/scno0z94vb/SothisAI/dataset/Console/data/main/data/训练数据.csv",   #/path/to/train.jsonl
        max_length=2048,  # 输入序列的最大长度
        pad_to_max_length=False,  # 不强制填充到最大长度,采用动态填充策略
    ),
    val=dict(
        type="jsonl",
        path="/path/to/val.jsonl",
        max_length=2048,
        pad_to_max_length=False,
    ),
)

# 训练配置
train = dict(
    optimizer=dict(
        type="AdamW",  # 优化器类型为AdamW,结合了Adam和权重衰减
        lr=3e-2,       # 学习率,控制参数更新的步长 ,3e-4过高(大模型微调学习率通常1e-5~3e-5)
        weight_decay=0.01,  # 权重衰减系数,防止过拟合
        betas=(0.9, 0.95),  # AdamW优化器的指数移动平均参数
    ),
    lr_scheduler=dict(
        type=CosineAnnealingLR,      # 学习率调度器使用余弦退火策略
        # 总步数 = (样本数 / (batch_size * dp_size)) * max_epochs * gradient_accumulation_steps
        total_steps="${(len(train_dataset) // (train.dataloader.batch_size * distributed.dp_size)) * train.max_epochs * train.gradient_accumulation_steps}", 
        warmup_steps=0.05,   # 预热步数比例,初始阶段逐渐增加学习率
    ),
    max_epochs=3,    # 最大训练轮数
    dataloader=dict(
        batch_size=4,  # 每个GPU的批次大小,CPU内存通常小于GPU,易出现内存不足,可适当调小
        num_workers=3, # 数据加载的工作线程数,若为CPU减少为CPU核数的一半
        pin_memory=True,  # 是否将数据页锁定到GPU内存,加速数据传输,若为CPU建议关闭
    ),
    gradient_accumulation_steps=4,  # 梯度累积步数,等效于将批次大小扩大4倍
    clip_grad_norm=1.0,             # 梯度裁剪的范数阈值,防止梯度爆炸
)

# 分布式训练配置
distributed = dict(
    tp_size=1,  # 张量并行大小,将模型张量分割到多个设备,若为CPU,设为1
    pp_size=1,  # 流水线并行大小,将模型层分割到多个设备,若为CPU,设为1
    dp_size=3,  # 数据并行大小,将数据分割到多个设备,若为CPU减少为CPU核数的一半
    zero_stage=0,  # ZeRO优化阶段,GPU优化内存使用的高级技术(3表示完全优化),若为CPU,设为0
    cpu_offload=False,  # 是否启用CPU offload (注意注释:671B模型在BF16精度下需要1543GB内存),存CPU则保持False
)

5. 启动微调

使用Colossal-AI提供的启动脚本进行微调:

# 单节点多GPU微调  nproc_per_node设置为CPU核数的一半,且和脚本中dp_size保持一致
torchrun --standalone --nproc_per_node=3 examples/training_scripts/lora_finetune.py \
    --config /home/finetune_deepseek_v3.py \
    --output_dir /public/home/scno0z94vb/SothisAI/model/test/

参数说明:

  • --config: 指定配置文件路径
  • --output_dir: 微调后模型的保存路径
  • --nproc_per_node: 指定使用的GPU数量

6. 多节点分布式微调

如果需要在多节点集群上进行微调,使用以下命令:

# 节点1(主节点)
torchrun --nnodes=2 --node_rank=0 --nproc_per_node=8 --master_addr="node1_ip" --master_port=29500 \
    examples/training_scripts/lora_finetune.py \
    --config configs/finetune_deepseek_v3.py \
    --output_dir /path/to/save/finetuned_model

# 节点2
torchrun --nnodes=2 --node_rank=1 --nproc_per_node=8 --master_addr="node1_ip" --master_port=29500 \
    examples/training_scripts/lora_finetune.py \
    --config configs/finetune_deepseek_v3.py \
    --output_dir /path/to/save/finetuned_model

7. 使用CPU Offload降低硬件要求

如果GPU内存有限,可以启用CPU offload:

# 在配置文件中修改distributed部分
distributed = dict(
    tp_size=1,
    pp_size=1,
    dp_size=8,
    zero_stage=3,
    cpu_offload=True,  # 启用CPU offload
)

8. 监控训练过程

训练过程中可以查看日志文件监控训练进度:

tail -f logs/finetune.log

训练完成后,微调的模型会保存在指定的output_dir目录中。

9. 模型推理

微调完成后,可以使用以下代码进行推理:

import torch
from transformers import AutoModelForCausalLM, AutoTokenizer
from peft import PeftModel

# 加载基础模型
base_model = AutoModelForCausalLM.from_pretrained(
    "/path/to/deepseek-v3-bf16",
    torch_dtype=torch.bfloat16,
    device_map="auto",
)

# 加载微调权重
model = PeftModel.from_pretrained(
    base_model,
    "/path/to/save/finetuned_model",
)

# 加载tokenizer
tokenizer = AutoTokenizer.from_pretrained("/path/to/deepseek-v3-bf16")
tokenizer.pad_token = tokenizer.eos_token

# 准备输入
prompt = "如何学习Python?"
inputs = tokenizer(prompt, return_tensors="pt").to(model.device)

# 生成回答
with torch.no_grad():
    outputs = model.generate(
        **inputs,
        max_length=512,
        temperature=0.7,
        top_p=0.9,
        do_sample=True,
    )

# 解码输出
response = tokenizer.decode(outputs[0], skip_special_tokens=True)
print(response)

10. 优化建议

  1. 内存优化

    • 减小train.dataloader.batch_size
    • 增加train.gradient_accumulation_steps
    • 启用distributed.cpu_offload
  2. 性能优化

    • 使用更大的GPU(如A100、H100)
    • 增加数据并行度distributed.dp_size
    • 启用张量并行distributed.tp_size(需要模型支持)
  3. 精度调整
    • 使用BF16精度进行训练
    • 对于内存非常有限的情况,可以考虑使用INT8量化

名词解释

BF8与FP8

BF8(Brain Floating Point 8)和FP8(Floating Point 8)都是用于表示浮点数的8位数据格式,它们的关系和差异如下:

关系

  • 两者都是为了在深度学习等领域中实现更高效的计算和存储而设计的低精度浮点数格式。它们试图在保持一定计算精度的同时,减少数据存储和计算的开销,以提高计算效率和降低硬件成本。

差异

  • 数据表示范围和精度
    • FP8:通常有多种标准变体,如E4M3(4位指数,3位尾数)和E5M2(5位指数,2位尾数)等。E4M3格式的指数范围较大,能表示较大和较小的数,但尾数位数较少,精度相对较低;E5M2则指数范围相对较小,但尾数精度稍高。
    • BF8:采用了独特的指数和尾数分配方式,旨在更好地适应神经网络计算的特点。它可能在某些情况下对常见的数值范围有更优的表示,能在一定程度上平衡表示范围和精度,以满足深度学习模型训练和推理的需求。
  • 硬件支持
    • FP8:一些主流的深度学习硬件加速器,如NVIDIA的GPU,对FP8提供了专门的支持,通过硬件指令集和架构优化,能够高效地处理FP8数据,提高计算性能和能效比。
    • BF8:硬件支持相对较少,可能需要特定的硬件架构或对现有硬件进行定制化改造才能充分发挥其优势。不过,随着BF8的应用逐渐增多,一些硬件厂商也开始考虑提供对BF8的支持。
  • 应用场景
    • FP8:在各种深度学习任务中都有广泛应用,特别是在图像识别、自然语言处理等领域的模型训练和推理中表现出色。由于其硬件支持广泛,能够充分利用现有硬件资源,因此在实际应用中较为常见。
    • BF8:主要应用于一些对精度要求较为特殊的深度学习场景,例如某些对模型精度有特定要求的科研项目,或者在一些资源受限但对计算精度有一定要求的边缘计算设备上,BF8可能会因其独特的精度特性而得到应用。

lora_target_modules含义

假设你有一台功能强大的咖啡机(大模型),它能做出各种咖啡,但你想让它专门做 “草莓拿铁”。直接改造整台机器(训练整个大模型)成本太高,于是你决定:只给机器的 “关键零件” 装一个 “小调节器”(LoRA 的低秩矩阵),通过调节这个小装置,让机器能适应做新咖啡。

模型架构 注意力模块参数名(合并 QKV) 注意力模块参数名(分离 Q/K/V) 说明
Llama/LLaMA query_key_value(部分实现) q_proj, k_proj, v_proj 原始 Llama 使用分离的q_proj/k_proj/v_proj,部分优化实现(如 DeepSeek)可能合并为query_key_value
GPT 系列 c_attn(如 GPT-2/3) - QKV 合并为单个投影层c_attn,输出维度为 3*hidden_size
T5/MT5 q_proj, k_proj, v_proj q_proj, k_proj, v_proj 分离的 Q/K/V 投影层,名称固定。
BERT query, key, value(部分) query, key, value(部分) 不同实现可能有差异,需通过模型结构确认。

参数场景

合并 QKV 投影层的情况

当模型将查询(Q)、键(K)、值(V)的线性变换合并为一个单一矩阵时(如多头注意力中常见操作),参数名通常为:

  • query_key_value(Llama 变种、部分自定义实现)
  • c_attn(GPT 系列)

此时,LoRA 会同时作用于 Q、K、V 的投影参数,相当于对合并后的矩阵进行低秩分解。

分离 Q/K/V 投影层的情况

若模型使用分离的 Q、K、V 投影层(如原始 Llama),常见参数名为:

  • q_proj(查询投影)
  • k_proj(键投影)
  • v_proj(值投影)

此时,通常选择对 q_projv_proj 应用 LoRA(K 投影层通常可不调整,以减少计算量),如: lora_target_modules=["q_proj", "v_proj"]