ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

海光DCU微调Qwen2实战:环境搭建与LoRA训练全记录

海光DCU微调Qwen2实战:环境搭建与LoRA训练全记录 第一次在海光 DCU 上跑微调任务我原以为最大的坎会是显存结果第一个坎就是环境。打开官网看到 DTK、HIP、ROCm 这一堆词再想想自己熟悉的那套 CUDA 工作流一时间不知道该装什么、从哪下载、装完怎么验证。这篇东西不是官方文档的复述而是我实际跑通“环境搭建 → 微调 Qwen2-0.5B → loss 下降”全过程的记录把当时每一步怎么做、为什么这么做、踩了什么坑都写清楚。正在折腾海光 DCU 且准备跑第一个微调任务的读者可以拿它当一份对照清单用。先说结论海光 DCU 的环境没有想象中那么吓人但版本匹配的坑非常密集。只要驱动、DTK、PyTorch 三者对齐后续训练流程和普通 GPU 基本一致。所以这篇文章会花不少篇幅在环境验证上——因为这一环是你和 loss 之间最大的一块拦路石。1. 认清 DCU 生态这波环境安装为什么不能照搬 CUDA 经验1.1 DCU 与 CUDA 的底层差异海光 DCU 的软件栈是基于 ROCm/HIP 的。传统 NVIDIA 显卡用 CUDA 生态编译好的 CUDA 代码不能直接拿到 DCU 上跑需要经过 HIP 转换或者直接使用社区里已经适配好的版本。好消息是PyTorch 这类主流框架已经被海光官方适配过你日常打交道的还是torch.cuda这一套 API很多代码不用改就能跑。坏消息是如果你习惯自己写 CUDA kernel或者依赖某个冷门的 CUDA 扩展库就极大概率在环境阶段卡住。打个比方CUDA 像是英语HIP 像是普通话海光 DCU 能听懂普通话。PyTorch 相当于一个同声传译大部分英文资料它能翻给你听但偶尔有个别方言词汇它会卡壳。理解这个底层关系很重要因为后续很多报错本质都是 翻译 层面的问题而不是模型代码的问题。1.2 硬件识别与版本配套清单拿到机器第一件事确认你手里是什么卡、装了什么系统。用lspci | grep -i display看设备条目通常显示 AMD 或者 Hygon 相关字样这是正常的因为海光 DCU 采用的架构和 AMD CDNA 有血缘关系。接着确认内核版本、发行版再去找对应的驱动和 DTK 版本。这一步最忌讳看着像就装。DCU 驱动和 DTK 版本不匹配后面 torch 就算装上了一跑张量运算也会崩溃。我当时整理了一份配套清单供参考组件我的选择注意事项操作系统Ubuntu 22.04 x86_64内核别选太新的官方支持列表有限DCU 驱动海光官网对应版本不建议拿 AMD 公版驱动替代DTK24.04 系列与驱动配套安装路径通常在 /opt/dtkPython3.10用 conda 管理别动系统 PythonPyTorch海光仓库提供的 2.1.0dtk 版本普通 PyPI 源里的 torch 没有 DCU 后端1.3 第一个任务选小不选大第一次跑别上来就全量微调 7B 模型。算力、显存、调试成本都不友好。我的建议是用 0.5B 或 1.5B 的小模型加上 LoRA跑通单卡训练 → loss 下降 → 生成结果的最小闭环。小模型迭代快一次实验几分钟到十几分钟方便你不断调整快速建立对这套环境的直觉。等你确认所有环节都正常再换大模型和更多数据那才是性价比最高的路线。2. 从裸机到 PyTorch驱动、DTK、conda 与最终验证2.1 驱动与 DTK 安装顺序这是最容易出问题的环节而且错误信息往往很迷惑。我建议的顺序是先装系统依赖再装 DCU 驱动重启并验证设备节点然后装 DTK最后配置环境变量。顺序不要倒过来否则你根本分不清是驱动问题还是 DTK 问题。实际命令大致如下具体包名以你拿到的手册为准sudo apt update sudo apt install -y build-essential dkms linux-headers-$(uname -r) # 安装驱动有的版本是运行安装脚本有的版本是 deb 包 sudo ./amdgpu_install.sh sudo reboot # 重启后确认设备节点已经出现 ls /dev/kfd /dev/dri lspci | grep -i display # 安装 DTK 并配置环境变量 cd /opt/dtk source env.sh echo source /opt/dtk/env.sh ~/.bashrc/dev/kfd存在说明 ROCm 层的设备节点起来了lspci能看到 DCU 条目说明系统识别到了卡。如果这两个检查没过后面装什么都白搭别急着往下走。提示驱动装完一定先重启。有些机器不重启内核模块不会加载这是我从第一次偷懒里学到的教训。2.2 conda 环境与 Python 版本DTK 对 Python 版本有兼容范围通常 3.8 到 3.10 比较稳。我用 conda 创建了一个独立环境隔离性比直接装在系统 Python 里好得多将来环境坏了删掉重建就行conda create -n dcu python3.10 -y conda activate dcu为什么要用 conda因为微调项目往往还要装 transformers、peft、datasets 这些包它们对 Python 版本、依赖版本都有要求。全部堆在系统环境里一旦某个依赖升级可能连带把 DTK 相关的库也搞坏。独立环境是成本最低的保险也是我强烈建议你养成的好习惯。2.3 安装带 DCU 支持的 PyTorch这里是一个大坑不能直接pip install torch从默认 PyPI 装因为普通 PyPI 的 torch 没有 DCU 后端。需要从海光提供的软件仓库安装 DCU 版 torch它会自带 HIP 支持。安装方式一般有两种一种是使用海光的 pip 源加-f参数另一种是直接下载对应 wheel 安装。版本要和 DTK 对应例如 DTK 24.04 对应的 torch 版本命名通常带dtk后缀。装完立刻验证不要直接上模型import torch print(torch.__version__) print(torch.cuda.is_available()) print(torch.cuda.device_count()) a torch.randn(1024, 1024, devicecuda) b torch.randn(1024, 1024, devicecuda) print((a b).sum().item())在 DCU 上torch.cuda.is_available()返回 True 是正常的因为 HIP 层把 CUDA API 做了兼容。如果你看到torch.cuda.device_count()等于卡数、矩阵乘法能算出数说明 torch 和 DCU 已经打通。如果版本号里没有dtk字样那大概率是装错源了。2.4 也可以用官方 Docker 镜像快速走通如果你不想在宿主机上折腾驱动和 DTK 的匹配问题可以先用海光官方提供的 Docker 镜像。镜像里通常已经装好驱动运行库和 DTK你只需要把项目代码挂进去跑对第一次试水非常友好。但上了生产环境或者需要长期调试我还是建议回到裸机安装因为容器多了一层抽象排查性能问题时反而更费劲。3. 设计第一个微调实验Qwen2-0.5B LoRA 小指令集3.1 三种微调方式为什么我选 LoRA大模型微调主要有三条路全量微调所有参数都更新、freeze 微调冻结大部分层只更新部分层、LoRA低秩适配在旁路插入少量可训练参数。方式可训练参数显存/算力需求适用场景全量微调100%最高数据多、算力充足、想逼近效果上限freeze 微调部分层中等下游任务和基座能力非常接近LoRA约 1% 左右低数据量不大、单卡常见、快速验证选 LoRA 的原因很直接第一个任务的首要目标是跑通链路不是刷榜。LoRA 需要的显存和训练时间都低出错后调试成本也低。加上 peft 库对 LoRA 的支持非常成熟和 transformers 配合得很顺代码量也不大。3.2 数据集准备不要贪多。我用了一个几百条规模的小型中文指令数据集格式大致如下{instruction: 什么是动量, input: , output: 动量是物体质量与速度的乘积是描述物体运动状态的物理量...}预处理阶段把 instruction 和 output 拼成带标记的文本用 tokenizer 编码最大长度设为 256 或 512。这里有个重要细节labels要和input_ids对齐但在 pad 的位置要置为-100否则模型会把 padding token 也当作预测目标来算 loss训练会变得非常奇怪。这一点很多新手不会注意但恰恰是影响 loss 形态的关键之一。3.3 微调脚本核心代码我用AutoModelForCausalLM peft的方式核心代码如下from transformers import AutoModelForCausalLM, AutoTokenizer from peft import LoraConfig, get_peft_model model_name /data/models/Qwen2-0.5B-Instruct tokenizer AutoTokenizer.from_pretrained(model_name, trust_remote_codeTrue) tokenizer.pad_token tokenizer.eos_token model AutoModelForCausalLM.from_pretrained(model_name, trust_remote_codeTrue) lora_config LoraConfig( r8, lora_alpha32, target_modules[q_proj, k_proj, v_proj, o_proj], lora_dropout0.05, biasnone, task_typeCAUSAL_LM, ) model get_peft_model(model, lora_config) model.print_trainable_parameters()训练部分直接交给 transformers 的 Trainer理由是不用自己写梯度更新、checkpoint 和日志先跑通再深入细节也不迟from transformers import Trainer, TrainingArguments training_args TrainingArguments( output_dir./qwen_lora, per_device_train_batch_size4, gradient_accumulation_steps4, num_train_epochs3, learning_rate2e-4, logging_steps10, save_steps500, remove_unused_columnsFalse, fp16False, bf16False, ) trainer Trainer( modelmodel, argstraining_args, train_datasetdataset, tokenizertokenizer, ) trainer.train()3.4 参数选择的理由这里每个参数都不是随便填的r8LoRA 的秩太小表达力不足太大参数多但收益递减8 是常见起步值。lora_alpha32缩放系数一般设为 r 的 2-4 倍决定 LoRA 更新的幅度。learning_rate2e-4LoRA 的常用学习率比全量微调大因为可训练参数比较少步子可以迈大一点。per_device_train_batch_size4搭配gradient_accumulation_steps4等效 batch size 16但显存压力小得多。先不开fp16/bf16DCU 上混合精度支持要看 DTK 版本和算子覆盖第一次跑先排除混合精度变量是最稳妥的。如果嫌手写麻烦也可以直接用 LlamaFactory 这类工具配置化地跑 LoRA。但我个人建议第一次还是手写一遍代码你才能理解每个按钮背后发生了什么后面再用工具才不会懵。4. 训练中真正让我卡住的地方loss 不降的完整排查4.1 怎么判断 loss 是不是正常的训练之前先算一个理论值交叉熵 loss 在模型输出接近均匀随机分布时约等于词表大小的自然对数。Qwen2 词表约 15 万ln(151936) ≈ 11.93。所以如果你的训练第一步 loss 不是 10 到 12 这个量级而是很低比如 0.5反而要警惕是不是数据标签泄露如果 loss 直接是 nan那就是数值稳定性问题通常和学习率或混合精度有关。这个参照系特别有用。它让你一眼就能看出训练是不是从一开始就不对而不是盲目地盯着曲线猜。4.2 我遇到的 loss 卡 11.5 的根因第一次把训练跑起来loss 一直在 11.5 附近抖动几百步都不往下走。第一反应是学习率太小调大之后还是不动。后来打印model.print_trainable_parameters()才发现可训练参数只有几万个明显不对。检查 LoRA 配置后发现target_modules里的模块名和 Qwen2 模型实际的模块名对不上LoRA 层没有挂到任何 attention 层上模型只靠原本就未冻结的 lm_head 在训练loss 当然下不去。修正方式是把target_modules改成target_modules[q_proj, k_proj, v_proj, o_proj, gate_proj, up_proj, down_proj]然后准备一个小数据集先跑 30 步确认 loss 开始明显下降再上完整数据。这个小步验证的习惯帮我省了很多时间强烈推荐。4.3 排查链路与对照表如果你也遇到 loss 不降建议按这个顺序排查先看model.print_trainable_parameters()的输出确认 LoRA 层挂载正常。看每个 step 的 grad norm 日志如果始终为 0说明参数根本没参与更新。检查数据预处理随便打印几条 tokenize 后的样本看 instruction、output、padding 是否对齐。检查 labelspadding 位置是否设成了 -100。调整学习率LoRA 建议从 1e-4 起步太小的 lr 会让曲线看起来像没动。关闭混合精度再试排除个别算子不兼容导致 loss 变成 nan。症状可能原因解决方式loss 在初值附近长期不动LoRA 目标模块没挂上 / lr 太小 / 可训练参数没进优化器打印 trainable params调大 lr检查 optimizer 参数loss 一开始就极低标签泄露 / 数据重复重新审视数据预处理loss 变 nan学习率过大 / 混合精度算子问题降低 lr关 AMP 或换 bf16loss 下降但生成效果差过拟合 / 数据分布和任务不匹配增加数据多样性加 dropout5. 实测记录从 OOM 到 loss 稳定下降5.1 第一次跑OOM 与显存优化我一开始用 batch size 8、max_length 512 启动结果直接torch.cuda.OutOfMemoryError。DCU 的显存和主流通用卡类似单卡 16GB 或 32GB 看起来很充裕但 Qwen2 的激活值、梯度、AdamW 优化器状态加起来很容易就把显存吃满。优化办法很简单把per_device_train_batch_size降到 2max_length降到 256开启gradient_checkpointingTrue再用gradient_accumulation_steps8凑回等效 batch size。改完显存占用大概在 60% 到 70%训练稳定了。这里不用纠结等效 batch size 是否足够大第一步任务只有一个把流程顺畅地跑起来。5.2 loss 下降后的曲线与停止时机修好 LoRA 挂载问题后重新跑。前 10 步loss 从 11.93 快速掉到 5 左右50 步左右到 2.5500 步时降到 1.6 附近。这是一个很标准的收敛曲线前面掉得快后面变慢最后趋于平缓。对于跑通这个目标看到 loss 从 11.9 一路降到 2 以下已经说明模型的权重在发生有意义的变化。什么时候停下来我的判断标准是loss 在几百步内没有明显下降趋势或者保存 checkpoint 后手动生成几条测试回答质量不再变好就可以停了。如果只看训练 loss很容易被过拟合骗了——训练 loss 继续降但生成质量可能反而变差最终还是要回到任务本身来评判。5.3 顺手验证模型效果训练结束后我用一个最简单的生成函数验证prompt 什么是动量 inputs tokenizer(prompt, return_tensorspt).to(cuda) output model.generate(**inputs, max_new_tokens64) print(tokenizer.decode(output[0], skip_special_tokensTrue))对比微调前和微调后的输出明显能感觉到模型开始按照指令-回答的格式组织语言。这个结果不需要多惊艳但足以证明整条链路是通的你已经成功地在海光 DCU 上完成了一次微调训练。6. 海光 DCU 的硬件注意事项功耗、多卡与混合精度6.1 power loss 自动关闭不是软件 bug训练跑得正欢服务器突然断电重启这种经历在 DCU 机器上并不少见。很多人第一反应是驱动问题于是反复重装环境结果浪费一整天。其实常见原因有两个整机功耗超过电源供电能力或者机房散热跟不上导致高温保护。8 张 DCU 同时满载加上 CPU、内存、硬盘整机峰值功耗能到数千瓦机房单路供电稍微弱一点就会触发电源保护。排查方法训练前看 BMC/IPMI 日志里有没有 power event训练中用类似 rocm-smi 的工具实时看功耗和温度确认是供电问题后限制单卡最大功耗、降低训练并发或者改善散热。别把这类硬件问题当成软件 bug 处理方向错了怎么折腾都没用。6.2 多卡训练环境变量与显存管理单卡跑通之后自然想上多卡。在 DCU 上CUDA_VISIBLE_DEVICES通常要换成HIP_VISIBLE_DEVICESDDP 训练时的通信后端在 ROCm 世界对应 RCCL。我第一次没用对环境变量结果只看到一张卡还以为是多卡驱动没装好。多卡训练时学习率和 batch size 要一起调整。比如 batch size 翻倍学习率也建议翻倍这是常见的线性缩放规则。显存碎片方面可以试试 PyTorch 2.x 的内存扩展配置实践下来对大 batch 训练有一定改善。场景变量/操作说明指定使用哪些 DCU 卡HIP_VISIBLE_DEVICES0,1等价于 CUDA_VISIBLE_DEVICESDDP 通信后端nccl/ RCCLtransformers 通常能自动适配每卡 batch 与 lrbatch 翻倍lr 翻倍线性缩放经验显存碎片PYTORCH_CUDA_ALLOC_CONFexpandable_segments:TruePyTorch 2.x 支持实测对大 batch 有改善6.3 混合精度和其他细节DCU 对混合精度的支持程度和 DTK 版本强相关。我第一次开fp16Trueloss 很容易变 nan后来改成bf16才稳定下来但这也取决于你的 DCU 型号和 DTK 版本是否支持。如果版本比较老直接全部用 fp32 跑牺牲一点速度换稳定完全值得。最后说一点日常习惯每次训练实验我都会在代码里记录 eval loss 和 grad norm并固定随机种子。这样即使某次训练跑飞了回看日志也能快速定位是数据问题、参数问题还是硬件问题而不是盲目地调参重跑。在海光 DCU 上跑微调最值钱的经验就是把环境验证和小步试跑做到位。每个环节装完都用最小脚本立刻验证确认没问题再进行下一步第一次训练务必选小模型、小数据、小 batch哪怕多跑几轮调试也要先把链路跑通。这套方法论不仅适用于 DCU也适用于任何你第一次接触的异构计算平台。希望这篇记录能帮你少走一些我走过的弯路。
返回列表