torchtune
在单卡昇腾 NPU 上跑通 torchtune 的最小 LoRA 微调链路:
安装:
uv pip install torchtune(二进制)与源码安装两条路径,拿到tuneCLI + 内置 recipes / configs。LoRA 微调:从 HuggingFace Hub 拉
Qwen/Qwen2.5-0.5B-Instruct作为底座,用tune run lora_finetune_single_device配qwen2_5/0.5B_lora_single_device配置跑 3 步 LoRA 微调。产物验证:检查 LoRA 适配器(
adapter_config.json+adapter_model.pt)落盘与 LoRA 矩阵配置。
前置条件
硬件
Atlas 900 A2 / A3 训练系列产品或者 Ascend 950 系列产品,并按需完成物理机或容器内的设备挂载(/dev/davinci* 等)。
基础软件
在跑本文档之前,你的机器上需要已经装好并可用:
可用的 Python 环境
可用的 CANN(参考快速安装昇腾环境)
与上面 CANN 匹配的
torch+torch_npu,且torch能正常import并torch.npu.is_available() == True(参考 Ascend PyTorch 安装文档,按 torch ↔ torch_npu ↔ CANN 三方兼容矩阵选择版本)
本文档示例使用的版本
配套机器:
机器类型:Atlas 900 A2 PODc(Ascend 910B4,64 GB × 1)
操作系统:Ubuntu 22.04
配套镜像:
swr.cn-south-1.myhuaweicloud.com/ascendhub/cann:9.1.0-910b-ubuntu22.04-py3.12
软件版本:
组件 |
版本 |
|---|---|
Python |
3.12 |
CANN |
9.1.0 |
torch |
2.11.0+cpu |
torch_npu |
2.11.0 |
torchtune |
最新 release 的源码/二进制 |
huggingface_hub |
最新稳定版 |
torchao |
|
模型 |
|
数据集 |
本文自带的 50 条 alpaca 格式样例(写入 |
前置安装
确认能看到 NPU 设备:
npu-smi info
输出类似:
+------------------------------------------------------------------------------------------------+
| npu-smi 25.5.2 Version: 25.5.2 |
+---------------------------+---------------+----------------------------------------------------+
| NPU Name | Health | Power(W) Temp(C) Hugepages-Usage(page)|
| Chip | Bus-Id | AICore(%) Memory-Usage(MB) HBM-Usage(MB) |
+===========================+===============+====================================================+
| 0 910B4 | OK | 89.9 39 0 / 0 |
| 0 | 0000:41:00.0 | 0 0 / 0 2922 / 32768 |
+===========================+===============+====================================================+
+---------------------------+---------------+----------------------------------------------------+
| NPU Chip | Process id | Process name | Process memory(MB) |
+===========================+===============+====================================================+
| No running processes found in NPU 0 |
+===========================+===============+====================================================+
Note
如果 npu-smi 不存在,请回到 Ascend 官方快速安装指南 补装驱动。
检查 Python 版本:
python --version
输出结果如下:
Python 3.12.xxx
对齐上游 pin 装 torch / torch_npu:
uv pip install -f https://mirrors.aliyun.com/pytorch-wheels/cpu torch==2.11.0
uv pip install --extra-index-url https://repo.huaweicloud.com/ascend/repos/pypi torch_npu==2.11.0
检查 torch / torch_npu 是否装好且 NPU 设备可用(下面的命令用 Python 执行):
import torch, torch_npu
print('torch=', torch.__version__)
print('torch_npu=', torch_npu.__version__)
print('is_available:', torch.npu.is_available())
print('count:', torch.npu.device_count())
输出结果如下:
torch= 2.11.0+cpu
torch_npu= 2.11.0
is_available: True
count: 1
Note
如果 import torch_npu 失败,回到 Ascend PyTorch 安装文档 检查 torch / torch_npu / CANN 三方兼容矩阵。
安装 huggingface_hub(用于从 HuggingFace Hub 下载底座模型)+ torchao:
uv pip install huggingface_hub
uv pip install 'torchao<0.16'
打印安装版本(下面的命令用 Python 执行):
import huggingface_hub, torchao
print('huggingface_hub', huggingface_hub.__version__)
print('torchao', torchao.__version__)
输出结果如下:
huggingface_hub xxx
torchao xxx
Note
xxx 表示实际安装的版本号。
安装 torchtune
使用 uv 进行安装
通过 PyPI 镜像直接装最新 release 的二进制 wheel:
uv pip install --index-url https://mirrors.aliyun.com/pypi/simple torchtune
python -c "import torchtune; print('torchtune', torchtune.__version__)"
输出结果类似如下:
torchtune xxx
Note
xxx 表示最新的版本号。
从源码安装
克隆上游仓库并 checkout 到当前 torchtune 的最新 release tag,安装并且验证:
git clone --depth 1 --branch <ref> https://github.com/meta-pytorch/torchtune.git
cd torchtune
uv pip install .
python -c "import torchtune; print('torchtune', torchtune.__version__)"
Note
<ref> 替换为 torchtune 当前的最新 release 版本。
输出结果类似如下:
torchtune xxx
Note
xxx 表示最新的版本号。
CLI 自检
tune --help 列出 torchtune 的子命令:
tune --help
输出结果类似如下:
usage: tune [-h] {download,ls,cp,run,validate,cat} ...
Welcome to the torchtune CLI!
options:
-h, --help show this help message and exit
...
使用样例:单卡 LoRA 微调 Qwen2.5-0.5B
对应上游 First Finetune Tutorial,在单卡昇腾 NPU 上跑通 3 步 LoRA 微调。
下载基础模型
默认使用 HuggingFace Hub 进行模型下载:
python -c "from huggingface_hub import snapshot_download; print(snapshot_download('Qwen/Qwen2.5-0.5B-Instruct'))" | tail -n 1
输出类似:
/root/.cache/huggingface/hub/models--Qwen--Qwen2.5-0.5B-Instruct/snapshots/xxxxxxxx
准备本地样例数据
本文档用 50 条 alpaca 格式的样例写到 data.json,再通过 dataset=torchtune.datasets.alpaca_dataset 切到本地 JSON 路径:
cat > data.json <<'JSON'
[
{"instruction": "Briefly explain why the sky looks blue.", "input": "", "output": "The sky appears blue because shorter-wavelength sunlight is scattered in all directions by the gases in Earth's atmosphere."},
{"instruction": "Name a prime number below ten.", "input": "", "output": "7"},
{"instruction": "Translate 'hello' into Spanish.", "input": "", "output": "Hola"},
{"instruction": "What is the capital of France?", "input": "", "output": "Paris"},
{"instruction": "Compute 12 times 13.", "input": "", "output": "156"},
{"instruction": "Who wrote 'Pride and Prejudice'?", "input": "", "output": "Jane Austen"},
{"instruction": "Give a synonym for 'happy'.", "input": "", "output": "Joyful"},
{"instruction": "What is the boiling point of water in Celsius?", "input": "", "output": "100"},
{"instruction": "List three primary colors.", "input": "", "output": "Red, yellow, blue"},
{"instruction": "Define photosynthesis in one sentence.", "input": "", "output": "Photosynthesis is the process by which plants convert light energy into chemical energy stored as glucose."},
{"instruction": "What's the chemical symbol for gold?", "input": "", "output": "Au"},
{"instruction": "Name a planet with rings.", "input": "", "output": "Saturn"},
{"instruction": "How many continents are there?", "input": "", "output": "Seven"},
{"instruction": "Translate 'thank you' into Japanese.", "input": "", "output": "ありがとう (arigatou)"},
{"instruction": "What is the largest ocean on Earth?", "input": "", "output": "The Pacific Ocean"},
{"instruction": "Define 'algorithm'.", "input": "", "output": "A step-by-step procedure for solving a problem or accomplishing a task."},
{"instruction": "Name the first president of the United States.", "input": "", "output": "George Washington"},
{"instruction": "What's 25 percent of 200?", "input": "", "output": "50"},
{"instruction": "Translate 'goodbye' into German.", "input": "", "output": "Auf Wiedersehen"},
{"instruction": "List the four fundamental forces of nature.", "input": "", "output": "Gravitational, electromagnetic, strong nuclear, and weak nuclear forces."},
{"instruction": "What's the square root of 64?", "input": "", "output": "8"},
{"instruction": "Name the author of '1984'.", "input": "", "output": "George Orwell"},
{"instruction": "What is the speed of light in vacuum (m/s, approximate)?", "input": "", "output": "About 3 x 10^8 meters per second."},
{"instruction": "Translate 'cat' into Italian.", "input": "", "output": "Gatto"},
{"instruction": "Define 'gravity'.", "input": "", "output": "Gravity is the force by which a planet or other body draws objects toward its center."},
{"instruction": "List three even numbers.", "input": "", "output": "2, 4, 6"},
{"instruction": "What's H2O commonly known as?", "input": "", "output": "Water"},
{"instruction": "Name the longest river in the world.", "input": "", "output": "The Nile (commonly cited) or the Amazon (by discharge volume)."},
{"instruction": "Translate 'yes' into Mandarin Chinese (pinyin).", "input": "", "output": "Shi (是)"},
{"instruction": "What is the periodic table?", "input": "", "output": "A tabular arrangement of chemical elements organized by atomic number."},
{"instruction": "Compute 7 squared.", "input": "", "output": "49"},
{"instruction": "Define 'democracy'.", "input": "", "output": "A system of government in which power is vested in the people, who exercise it directly or through elected representatives."},
{"instruction": "Name the largest mammal on Earth.", "input": "", "output": "The blue whale"},
{"instruction": "Translate 'red' into French.", "input": "", "output": "Rouge"},
{"instruction": "What is the smallest unit of life?", "input": "", "output": "The cell"},
{"instruction": "Define 'ecosystem'.", "input": "", "output": "A community of living organisms together with the nonliving components of their environment, interacting as a system."},
{"instruction": "List three noble gases.", "input": "", "output": "Helium, neon, argon"},
{"instruction": "What's the tallest mountain on Earth?", "input": "", "output": "Mount Everest"},
{"instruction": "Translate 'house' into Korean (romanized).", "input": "", "output": "Jip (집)"},
{"instruction": "What year did World War II end?", "input": "", "output": "1945"},
{"instruction": "Define 'metabolism'.", "input": "", "output": "The chemical processes by which an organism maintains life, including converting food to energy."},
{"instruction": "Name the gas plants take in for photosynthesis.", "input": "", "output": "Carbon dioxide (CO2)"},
{"instruction": "Compute 144 divided by 12.", "input": "", "output": "12"},
{"instruction": "Translate 'book' into Portuguese.", "input": "", "output": "Livro"},
{"instruction": "What is the hardest natural substance?", "input": "", "output": "Diamond"},
{"instruction": "Define 'protein'.", "input": "", "output": "A large biomolecule composed of amino acids, essential for the structure and function of cells."},
{"instruction": "List three common programming languages.", "input": "", "output": "Python, JavaScript, C++"},
{"instruction": "What's the currency of Japan?", "input": "", "output": "Japanese yen (JPY)"},
{"instruction": "Name the process by which liquid becomes gas.", "input": "", "output": "Evaporation (or vaporization)"},
{"instruction": "Translate 'sun' into Russian (transliterated).", "input": "", "output": "Solntse (солнце)"}
]
JSON
echo "${PWD}/data.json"
输出结果类似:
/path/to/ascend_docs/data.json
跑 3 步 LoRA 微调
ASCEND_RT_VISIBLE_DEVICES=0 tune run lora_finetune_single_device \
--config qwen2_5/0.5B_lora_single_device \
device=npu \
checkpointer.checkpoint_dir="<model_path>" \
tokenizer.path="<model_path>/vocab.json" \
tokenizer.merges_file="<model_path>/merges.txt" \
dataset=torchtune.datasets.alpaca_dataset \
dataset.source=json \
dataset.data_files="<data_path>" \
metric_logger=torchtune.training.metric_logging.StdoutLogger \
~metric_logger.log_dir \
log_peak_memory_stats=False \
output_dir="${PWD}/output" \
max_steps_per_epoch=3 \
epochs=1 \
log_every_n_steps=1
Note
<model_path> 在运行时指向「下载基础模型」一节 snapshot_download 命令打印的模型缓存路径;<data_path> 在运行时指向「准备本地样例数据」一节生成的 data.json 路径。
输出结果如下:
Step 1 | loss:xxx lr:xxx tokens_per_second_per_gpu:xxx
Step 2 | loss:xxx lr:xxx tokens_per_second_per_gpu:xxx
Step 3 | loss:xxx lr:xxx tokens_per_second_per_gpu:xxx
...
捕获 LoRA checkpoint 目录路径供下一步验证:
ls -dt ${PWD}/output/*/ | head -n 1
输出类似:
/path/to/ascend_docs/output/epoch_0/
验证 LoRA 适配器落盘
torchtune 默认落 adapter_config.json + adapter_model.pt(底座权重不动):
test -f "<ckpt>/adapter_config.json" && test -f "<ckpt>/adapter_model.pt" && echo "adapter files present"
输出结果如下:
adapter files present
Note
<ckpt> 在运行时替换为「跑 3 步 LoRA 微调」小节落盘的 LoRA checkpoint 目录。
捕获 checkpoint 里 LoRA 矩阵的统计信息(下面的命令用 Python 执行):
import json
cfg = json.load(open('<ckpt>/adapter_config.json'))
print('rank', cfg['r'], 'lora_alpha', cfg['lora_alpha'], 'targets', cfg['target_modules'])
输出结果如下:
rank ... lora_alpha ... targets ...
外部链接
GitHub:meta-pytorch/torchtune
文档中心:torchtune Docs