Pull Request已成功合入, 合并人@CANN-robot
(感谢 gcw_gpcLb7J8 的贡献)变更摘要
本次 PR 在 contrib/tutorials/swan_lab_example/ 目录下新增一套面向昇腾 NPU 的 Qwen3 医学场景全参数微调与推理接入实战教程,覆盖从数据准备、BF16 全参数 SFT、SwanLab 训练过程记录,到 Transformer 原生推理验证,以及接入 cann-recipes-infer 统一执行器完成 NPU 推理部署的完整流程,并提供配置脚本和运行时 YAML 模板。
主要改动
- 新增
qwen3_medical_sft.ipynb交互式教程:以 Jupyter Notebook 形式完整演示 Qwen3-1.7B 在医学问答场景下的数据下载、监督标签构建(MedicalDataset)、BF16 全参数 SFT 微调、SwanLab 实验记录、Transformer 原生推理验证,以及cann-recipes-infer统一执行器部署与领域问题验证的全流程。 - 新增
prepare_config.py工具脚本:通过正则替换将训练产出的 checkpoint 绝对路径写入统一执行器运行时 YAML 文件的model_path字段,简化部署配置步骤。 - 新增
qwen3_medical_sft.yaml运行时配置文件:定义模型推理所需的模型配置(model_name、model_path、exe_mode等)、数据配置(dataset、input_truncated_len)、并行配置(world_size、各类 TP 大小)以及调度器配置(max_new_tokens、batch_size)。 - 新增
run_cann_infer.sh启动脚本:封装cann-recipes-infer仓库中统一执行器executor/scripts/infer.sh的调用逻辑,支持通过参数指定 YAML 文件名和仓库路径,规范部署执行入口。


代码审查
审查总结
本次审查覆盖了全部 6 个变更文件,共发现 5 个问题:
| 优先级 | 数量 | 说明 |
|---|---|---|
| P1 | 1 | notebook 中 sed 转义错误导致关键环境修正静默失效 |
| P2 | 2 | prepare_config.py 正则静默无匹配 + notebook str.replace 静默无匹配 |
| P3 | 2 | README/validation.md 中 max_steps 文档不准确 + YAML key 疑似拼写错误 |
各文件审查结果
| 文件 | 结论 |
|---|---|
README.md |
1 个 P3(max_steps 文档不准确) |
prepare_config.py |
1 个 P2(正则静默失败 + 路径含引号破坏 YAML) |
qwen3_medical_sft.ipynb |
2 个(P1 sed 转义错误 + P2 str.replace 静默失败) |
qwen3_medical_sft.yaml |
1 个 P3(moe_tp_size 疑似拼写错误) |
run_cann_infer.sh |
无问题,脚本编写规范(set -euo pipefail、${1:?}、变量引号保护、cd 前目录检查) |
validation.md |
无独立问题(与 README 共享 max_steps 不准确问题,已在 README 处报告) |
整体风险评估
中等风险。最值得关注的是 P1 问题:notebook 5.1 节中 sed 命令的 \$ 转义错误会导致 ASCEND_HOME_PATH 冲突修正静默失效,用户在按教程执行后可能遇到 torch_npu 导入失败的 RuntimeError。建议在合并前优先修复此项。两个 P2 问题(prepare_config.py 和 notebook 中的路径替换无匹配检测)在当前 cann-recipes-infer 版本下不会触发,但降低了教程对模板变更的鲁棒性。
| 类型 | 数量 |
|---|---|
| 🔴 阻塞 | 1 |
| 🟡 建议 | 4 |
⛔ 需要修改


Thanks for your pull-request.
The full list of commands accepted by me can be found at here。
You can get sig-info at here
PR Approval Progress
✅ Congratulations! All modules have met the lgtm and approve requirements.
Module Approval Details
| module | lgtm status | approve status |
|---|---|---|
| repo-cann/cann-learning-hub | ✅ mlewis, waimaidaole (2/2) | ✅ mlewis (1/1) |
💡 Tip:
- Committer can comment
/approveor/lgtm- Commenting
/approveimplies both code review (lgtm) and intent to merge (approve)
CLA Signature Pass
gcw_gpcLb7J8, thanks for your pull request. All authors of the commits have signed the CLA. 👍


🟡 Medium Priority
affected behavior/contract → 本教程的主题是"医学场景全参微调",读者阅读此验证文档后可能将 AI 生成的医学建议视为经过验证的医学指导。该输出示例中模型以"医生"角色自居,给出的饮食建议(如"简单碳水比如精制糖、白面包、白米应该限制""复杂碳水比如燕麦、糙米更适合糖尿病患者")虽属常识性内容,但在无任何免责声明的情况下呈现在教程文档中,存在被读者当作专业医疗建议的误导风险。
failure mode → 读者(尤其是非医学专业人士)阅读文档后,可能将模型输出的饮食建议当作可靠指导并应用于实际糖尿病管理中,而未咨询真正的医疗专业人员。这对于一个"医学场景"AI 教程而言是显著的文档安全风险。
建议:在输出示例上方添加醒目的免责声明,明确标注该内容为 AI 生成、不构成医疗建议。


🟡 Medium Priority
变更行: notebook 第 5.2 节(qwen3_medical_sft.ipynb 第 444–452 行):
content = content.replace(
'model_path: "/data/models/origin/Qwen3-8B"',
f'model_path: "{checkpoint_path}"'
)
...
print(f"已将 model_path 写入: {checkpoint_path}")
影响的行为/契约: 与 prepare_config.py 功能相同——将训练产物路径写入推理 YAML 的 model_path 字段。
失效模式: str.replace() 搜索一个硬编码的精确字符串 model_path: "/data/models/origin/Qwen3-8B"。如果 cann-recipes-infer 模板中的路径格式有任何差异(不同路径、不同空白、或模板之前已被修改过),replace 不会匹配,content 保持不变。但第 452 行的 print 无条件输出"已将 model_path 写入",给用户以操作成功的假象。这与 prepare_config.py 构成同类问题——两份代码(notebook cell 与独立脚本)都存在静默失败的风险,且使用的匹配方式不同(精确字符串 vs 正则),进一步增加了维护不一致的可能性。
建议:替换后检查原占位符路径是否仍存在于 content 中,若存在则报错提示用户模板可能已变更。


🟠 High Priority
变更行: notebook 第 5.1 节 (qwen3_medical_sft.ipynb 第 403 行) 的 bash cell 中:
影响的行为/契约: 该 sed 命令的意图是删除 set_env.sh 中 export ASCEND_HOME_PATH=$cann_path 这行(validation.md 第 88 行记录的修正方案),以解决 cann_path 变量身兼两职导致的 ASCEND_RUNTIME_PATH 目录不存在错误。
失效模式: 在 bash 单引号内,\$ 是两个字面字符:反斜杠 \ 后跟美元符 $。sed 接收到的正则模式是 export ASCEND_HOME_PATH=\$cann_path(匹配一个字面反斜杠)。但 set_env.sh 中的实际行是 export ASCEND_HOME_PATH=$cann_path(无反斜杠),因此 sed 永远不会匹配,删除操作静默失败。用户按 notebook 执行后,ASCEND_HOME_PATH 冲突问题未被修复,后续 import torch_npu 仍会触发 validation.md 第 88 行描述的 RuntimeError。
建议:在 JSON 中将 \\$ 改为 $(即 \$ → $),使 sed 模式匹配实际的无反斜杠行。
| 403
| - "sed -i '/export ASCEND_HOME_PATH= |
|
403 | + "sed -i '/export ASCEND_HOME_PATH=$cann_path/d' \\\n", |


🟡 Medium Priority
变更行: prepare_config.py 第 37–41 行:
new_content = re.sub(
r'model_path:\s*".*"',
f'model_path: "{checkpoint_path}"',
content,
)
影响的行为/契约: 该函数的目标是将 YAML 中的 model_path 字段替换为训练产出的 checkpoint 路径。
失效模式 1 — 静默无匹配: 正则 model_path:\s*".*" 要求值必须用双引号包裹。如果 cann-recipes-infer 的模板 YAML 使用了单引号、无引号、或不同的空白格式,re.sub 不会匹配任何内容,new_content 等于 content。脚本随后在第 46 行打印"已将...model_path 写入",给用户以操作成功的假象,但实际 YAML 未被修改。
失效模式 2 — YAML 注入: checkpoint_path 来自 sys.argv[1],通过 f-string 直接拼入 f'model_path: "{checkpoint_path}"'。如果路径中包含双引号字符 ",生成的 YAML 行将语法错误,导致推理框架解析配置失败。虽然路径含双引号在实际中罕见,但作为命令行参数未做任何校验是隐患。
- 对
checkpoint_path做基本校验(如禁止包含双引号),或使用 YAML 库安全写入。
建议:增加替换结果校验:若 new_content == content 则警告用户 pattern 未匹配;对 checkpoint_path 过滤或转义双引号。


🟡 Medium Priority
changed line → line 87: 该行指导用户直接修改 torch_npu/npu/utils.py(系统安装的 Python 包文件),并注明"该文件路径通常需要 sudo 权限修改"。
affected behavior/contract → 用户按照此修复操作时:(1) 需要以 root 权限修改系统级 Python 包文件,存在误操作破坏系统环境的风险;(2) 该补丁在 pip install --upgrade torch_npu 后会被静默覆盖丢失,修复不具备持久性;(3) 若补丁代码有误,可能导致 torch_npu 模块彻底无法加载,排查困难。
failure mode → 用户以 sudo 修改系统文件时误操作(例如编辑错误、保存不完整),导致 torch_npu 不可用;或后续升级包后问题复现但用户未意识到补丁已丢失,再次陷入同样的 UnicodeDecodeError 故障。
suggested fix → 将修复方式改为运行时 monkey-patch(在 import torch_npu 之后动态替换 get_cann_version 函数),无需修改系统文件也无需 sudo;同时建议向 torch_npu 上游提交 issue/bug report 以从根本上解决该编码兼容性问题。
建议:将修复方式改为运行时 monkey-patch(无需 sudo),而非直接修改系统安装的 Python 包文件;同时建议向上游报告该问题。


/lgtm
/approve


描述
新增
contrib/tutorials/swanlab_example/教程:在昇腾 NPU 上参考 SwanLab 官方 Qwen3 医学模型微调教程,完成 Qwen3-1.7B 的 BF16 全参数 SFT 微调(医学问答场景),并将微调产出的权重接入昇腾官方推理框架 cann-recipes-infer 完成 NPU 推理验证。教程覆盖数据准备、模型加载与监督标签构建、全参微调、SwanLab 训练过程记录、Transformer 原生推理验证、以及 cann-recipes-infer 统一执行器部署与性能实测的完整流程。关联的Issue
无
测试
qwen3_medical_sft.ipynb在昇腾 Atlas A3环境完整跑通训练和推理全流程文档更新
无
类型标签