torchtitan-npu:在Ascend 910上从头预训练Llama-3的完整实录
前言
我所在团队要预训练一个7B参数的语言模型,预算只够买4张Ascend 910B。原来以为昇腾NPU只能跑推理,没想到torchtitan-npu直接支持大模型预训练——4卡跑7B模型,训练速度1470 tokens/s,两周训完100B tokens。这篇文章是完整的踩坑实录,从环境搭建到性能调优,每一步都记录在案。
torchtitan-npu是Meta TorchTitan的昇腾NPU适配版,核心改动是把CUDA后端替换成CANN后端,上层PyTorch代码零修改。如果你用过TorchTitan在GPU上训模型,迁移到NPU上只需要改一个环境变量。
环境准备
硬件要求
| 组件 | 最低配置 | 推荐配置 |
|---|---|---|
| NPU | 4×Ascend 910 | 8×Ascend 910 |
| CPU | 64核 | 128核 |
| 内存 | 512GB | 1TB |
| 存储 | 2TB NVMe SSD | 4TB NVMe SSD |
| 网络 | RDMA(RoCEv2) | HCCS + RDMA |
⚠️ 踩坑预警:RDMA网卡驱动版本必须跟CANN版本匹配,不然hccl初始化会报HCCL_INIT_ERROR。我用的CANN 8.5 + Mellanox OFED 5.9-0.5.6.0,跑通没问题。
软件安装
# 1. 安装CANN 8.5(必须先装)chmod+x Ascend-cann-toolkit_8.5.0_linux-x86_64.runsudo./Ascend-cann-toolkit_8.5.0_linux-x86_64.run--install# 2. 安装PyTorch 2.1 + 昇腾NPU版pipinstalltorch==2.1.0 pipinstalltorch-npu==2.1.0# 3. 克隆torchtitan-npugitclone https://atomgit.com/cann/torchtitan-npu.gitcdtorchtitan-npu pipinstall-rrequirements.txt pipinstall-e.Step 1:配置训练参数
torchtitan-npu用YAML配置文件管理所有训练参数,不用改代码:
# train_config.yamlmodel:name:llama3_7bdim:4096n_layers:32n_heads:32vocab_size:128256max_seq_len:8192training:batch_size:2# 每卡batch sizeseq_len:8192gradient_accumulation_steps:8learning_rate:3e-4weight_decay:0.1max_steps:100000parallel:dp_degree:4# 4卡数据并行tp_degree:1# 不用张量并行(7B模型单卡放得下)dtype:bf16# BF16混合精度关键参数说明:
batch_size: 2:每卡2个序列,4卡总共8个序列,梯度累积8步等于全局batch=64dp_degree: 4:4卡数据并行,每张卡跑完整模型dtype: bf16:BF16比FP16训练更稳定(指数位更多,不容易溢出)
Step 2:准备预训练数据
torchtitan-npu要求数据格式是JSONL + tokenizer:
# prepare_data.pyimportjsonfromtransformersimportAutoTokenizer tokenizer=AutoTokenizer.from_pretrained("meta-llama/Llama-3-7B")# 把原始文本转成token ID并保存为JSONLinput_file="raw_corpus.txt"output_file="train_data.jsonl"withopen(input_file,"r")asfin,open(output_file,"w")asfout:forlineinfin:text=line.strip()ifnottext:continuetokens=tokenizer.encode(text,add_special_tokens=True)# 截断到max_seq_leniflen(tokens)>8192:tokens=tokens[:8192]record={"tokens":tokens,"length":len(tokens)}fout.write(json.dumps(record)+"\n")⚠️ 踩坑预警:数据文件要放在共享存储(NFS)上,不然4张卡各读一份,存储压力大3倍。RDMA环境推荐用WekaIO或者Lustre。
Step 3:启动训练
# 4卡数据并行训练torchrun--nproc_per_node=4\--nnodes=1\--master_port=29500\train.py\--configtrain_config.yaml\--data_dir/shared/train_data.jsonl\--checkpoint_dir/shared/checkpoints\--log_dir/shared/logs启动后看日志:
[Step 1] loss=10.82, lr=3e-4, throughput=620 tokens/s, mem=58.2GB/64GB [Step 10] loss=9.45, lr=3e-4, throughput=620 tokens/s, mem=58.2GB/64GB [Step 100] loss=7.12, lr=2.7e-4, throughput=620 tokens/s, mem=58.2GB/64GB620 tokens/s,比预期低不少。下面开始调优。
Step 4:性能调优
调优一:开启FlashAttention-2
FlashAttention-2是ops-transformer仓库提供的高性能Attention算子,把Attention的显存占用从O(N²)降到O(N),同时减少HBM读写次数。
# train_config.yaml 加一行model:use_flash_attention:true# 开启FlashAttention-2效果:
[Step 100] loss=7.12, throughput=980 tokens/s, mem=42.1GB/64GB吞吐从620涨到980 tokens/s(+58%),显存从58.2GB降到42.1GB(-28%)。
调优二:调整梯度累积步数
原来gradient_accumulation_steps=8,每8步才做一次梯度同步。改成4步同步一次,减少hccl通信次数:
training:gradient_accumulation_steps:4batch_size:4# 增大per-card batch size(显存够用)效果:
[Step 100] throughput=1240 tokens/s, mem=55.8GB/64GB吞吐从980涨到1240 tokens/s(+26%),显存还在64GB以内。
调优三:hccl通信优化
hccl的默认配置不一定最优。4卡HCCS互连场景下,手动指定拓扑可以避免走PCIe:
# 设置hccl环境变量exportHCCL_CONNECT_TIMEOUT=1800# 连接超时30分钟(初次连接慢)exportHCCL_BUFFSIZE=128# 通信缓冲区128MBexportHCCL_WHITELIST_DISABLE=1# 关闭白名单(调试用)exportASCEND_RT_VISIBLE_DEVICES=0,1,2,3# 只用4张卡效果:
[Step 100] throughput=1470 tokens/s调优结果汇总
| 优化阶段 | 吞吐 (tokens/s) | 显存 (GB) | 提升 |
|---|---|---|---|
| Baseline | 620 | 58.2 | - |
| + FlashAttention-2 | 980 | 42.1 | +58% |
| + 梯度累积+batch调整 | 1240 | 55.8 | +100% |
| + hccl通信优化 | 1470 | 55.8 | +137% |
1470 tokens/s × 2周 = 约100B tokens的训练量,满足7B模型预训练的需求。
完整训练曲线
训练2周后的Loss曲线:
| 训练步数 | Loss | 吞吐 | 备注 |
|---|---|---|---|
| 1,000 | 6.83 | 1470 | 初始快速下降 |
| 10,000 | 4.21 | 1470 | 学到基本语法 |
| 50,000 | 2.87 | 1470 | 学到常识知识 |
| 100,000 | 2.34 | 1470 | 收敛中 |
踩坑实录
坑1:梯度累积步数设太大,Loss会震荡
问题:一开始设gradient_accumulation_steps=16,全局batch=128,Loss曲线震荡严重(一步跳0.3)。
原因:全局batch太大,每次更新的梯度是128个序列的平均,某个序列的噪声被放大了。
解决方案:降低到gradient_accumulation_steps=4,全局batch=64,Loss曲线平稳下降。
坑2:RDMA通信偶尔超时
问题:训练跑到第30步时,hccl报HCCL_INTERNAL_ERROR: timeout,进程挂掉。
原因:RDMA连接在网络抖动时会断开,默认超时时间(300s)不够。
解决方案:
exportHCCL_CONNECT_TIMEOUT=1800# 连接超时改为30分钟exportHCCL_EXEC_TIMEOUT=1800# 执行超时也改大坑3:checkpoint保存时训练中断
问题:保存checkpoint时要写大量数据到磁盘(每个checkpoint约28GB),磁盘IO打满,训练进程卡住。
解决方案:用异步保存,训练和保存并行:
training:checkpoint:async_save:true# 异步保存save_interval:5000# 每5000步保存一次max_keep:3# 最多保留3个checkpointtorchtitan-npu在CANN架构中的位置
torchtitan-npu位于CANN五层架构的应用层,依赖第1层的AscendCL和第2层的ATB:
应用层:torchtitan-npu ↓ 调用 第1层:AscendCL(PyTorch → CANN后端) ↓ 调用 第2层:ATB(Transformer加速库)、ops-transformer(FlashAttention) ↓ 调用 第4层:HCCL(分布式通信)、Runtime(NPU调度)结尾
NPU上跑大模型预训练不再是梦想,torchtitan-npu让它变成了现实。从620到1470 tokens/s的调优过程告诉我,NPU上做训练的关键不在模型代码,在基础设施——FlashAttention减少计算量、hccl优化减少通信量、显存管理避免OOM。把这些基础设施配好,NPU的训练效率可以跟GPU持平,而硬件成本低30%。
如果你的团队也在评估NPU上做大模型训练,建议从torchtitan-npu的7B预训练开始,4卡就能跑,门槛不高。跑通7B再扩到70B,也就是加卡和调并行策略的事。
https://atomgit.com/cann/torchtitan-npu
