已合并
fix qwen3_8b chapter 5&6 issues #632
liuyufan创建于 8月10日
fix qwen3_8b chapter 5&6 issues #632
已合并
共 3 个文件变更+38-6
| @@ -144,7 +144,8 @@ | |||
| 144 | "source": [ | 144 | "source": [ |
| 145 | "from inference_scripts.recipe_workflow import resolve_model_path\n", | 145 | "from inference_scripts.recipe_workflow import resolve_model_path\n", |
| 146 | "#下载非量化模型权重\n", | 146 | "#下载非量化模型权重\n", |
| 147 | - "resolved_model_path, model_source = resolve_model_path('Qwen/Qwen3-8B', quiet_model_io=False)" | 147 | + "MODEL_ID = os.environ.get('QWEN3_8B_MODEL_PATH') or 'Qwen/Qwen3-8B'\n", |
| 148 | + "resolved_model_path, model_source = resolve_model_path(str(MODEL_ID), quiet_model_io=False)" | ||
| 148 | ] | 149 | ] |
| 149 | }, | 150 | }, |
| 150 | { | 151 | { |
| @@ -524,7 +525,7 @@ | |||
| 524 | "\n", | 525 | "\n", |
| 525 | "- **Shape 随推理阶段和模型层级变化**:Prefill 阶段 M 维度较大(如 50),Decode 阶段 M=1;K 和 N 取值取决于具体层的隐藏维度与投影维度。\n", | 526 | "- **Shape 随推理阶段和模型层级变化**:Prefill 阶段 M 维度较大(如 50),Decode 阶段 M=1;K 和 N 取值取决于具体层的隐藏维度与投影维度。\n", |
| 526 | "\n", | 527 | "\n", |
| 527 | - "现在让我们模拟一个实际的需求分析场景:假设我们正在部署一个 W8A8 量化的大语言模型,推理过程中需要调用 INT8 量化矩阵乘法算子。通过 Profiling 分析,我们发现该模型对底层算子的调用规格恰好如上表所示——即需要一种能同时支持\"纯 INT8 输出\"和\"perTokenScale 反量化输出\"两种模式的量化 Matmul 算子。\n", | 528 | + "现在让我们模拟一个实际的需求分析场景:假设我们正在部署一个 W8A8 量化的大语言模型,推理过程中需要调用 INT8 量化矩阵乘法算子。通过 Profiling 分析,我们发现该模型对底层算子的调用规格恰好如上表所示——即需要一种能同时支持\"INT32 输出\"和\"perTokenScale 反量化输出\"两种模式的量化 Matmul 算子。\n", |
| 528 | "\n", | 529 | "\n", |
| 529 | "然而,现有框架中可能没有完全匹配该规格的算子实现,或者我们需要针对特定硬件平台进行深度优化。此时,就需要开发一个自定义算子 `QmmCustom` 来满足这一需求。从 Profiling 数据中归纳出的输入输出规格,自然就成为 `QmmCustom` 的原型定义:\n", | 530 | "然而,现有框架中可能没有完全匹配该规格的算子实现,或者我们需要针对特定硬件平台进行深度优化。此时,就需要开发一个自定义算子 `QmmCustom` 来满足这一需求。从 Profiling 数据中归纳出的输入输出规格,自然就成为 `QmmCustom` 的原型定义:\n", |
| 530 | "\n", | 531 | "\n", |
| @@ -11,11 +11,11 @@ | |||
| 11 | "\n", | 11 | "\n", |
| 12 | "实践的环境准备、提交方式可参考[启航营课程实践说明](https://gitcode.com/cann/cann-launch-camp/blob/master/2026/University/HIT/First-session/README.md)。\n", | 12 | "实践的环境准备、提交方式可参考[启航营课程实践说明](https://gitcode.com/cann/cann-launch-camp/blob/master/2026/University/HIT/First-session/README.md)。\n", |
| 13 | "\n", | 13 | "\n", |
| 14 | - "### 本章定位\n", | 14 | + "## 本章定位\n", |
| 15 | "\n", | 15 | "\n", |
| 16 | "本章将开发一个自定义的量化 matmul 算子 `QmmCustom`,通过 A8W8 量化(Activation INT8 + Weight INT8)减少计算量和内存占用,从而提升推理性能。这是本次启航营中综合性最强的一章——涵盖了从算子概念理解、Tiling 设计、Kernel 实现到算子编译、接入网络与功能性能测试的全链路。\n", | 16 | "本章将开发一个自定义的量化 matmul 算子 `QmmCustom`,通过 A8W8 量化(Activation INT8 + Weight INT8)减少计算量和内存占用,从而提升推理性能。这是本次启航营中综合性最强的一章——涵盖了从算子概念理解、Tiling 设计、Kernel 实现到算子编译、接入网络与功能性能测试的全链路。\n", |
| 17 | "\n", | 17 | "\n", |
| 18 | - "### 通过实践达成的学习目标\n", | 18 | + "## 通过实践达成的学习目标\n", |
| 19 | "\n", | 19 | "\n", |
| 20 | "- **理解量化推理原理**:掌握 A8W8 量化方案(INT8 激活 + INT8 权重)的数学原理与 Scale 反量化机制,理解 perChannelScale 与 perTokenScale 的作用。\n", | 20 | "- **理解量化推理原理**:掌握 A8W8 量化方案(INT8 激活 + INT8 权重)的数学原理与 Scale 反量化机制,理解 perChannelScale 与 perTokenScale 的作用。\n", |
| 21 | "- **设计 Tiling 数据结构与函数**:根据算子原型定义自行设计 TilingData 结构体,实现多核分块的 Tiling 函数,理解 Cube 计算单元的分块策略。\n", | 21 | "- **设计 Tiling 数据结构与函数**:根据算子原型定义自行设计 TilingData 结构体,实现多核分块的 Tiling 函数,理解 Cube 计算单元的分块策略。\n", |
| @@ -23,7 +23,7 @@ | |||
| 23 | "- **完成算子编译与 Torch 接入**:编译自定义算子并通过 Torch 接口接入,打通“算子开发 → 框架调用”的链路。\n", | 23 | "- **完成算子编译与 Torch 接入**:编译自定义算子并通过 Torch 接口接入,打通“算子开发 → 框架调用”的链路。\n", |
| 24 | "- **测试算子功能与性能,尝试优化迭代**:验证自定义算子的精度正确性,测试单算子与网络性能,并根据测试结果对 Tiling 或 Kernel 进行优化迭代。\n", | 24 | "- **测试算子功能与性能,尝试优化迭代**:验证自定义算子的精度正确性,测试单算子与网络性能,并根据测试结果对 Tiling 或 Kernel 进行优化迭代。\n", |
| 25 | "\n", | 25 | "\n", |
| 26 | - "### 本章大纲\n", | 26 | + "## 本章大纲\n", |
| 27 | "\n", | 27 | "\n", |
| 28 | "- 环境准备\n", | 28 | "- 环境准备\n", |
| 29 | "- A8W8 量化 matmul 算子概念\n", | 29 | "- A8W8 量化 matmul 算子概念\n", |
| @@ -154,6 +154,8 @@ | |||
| 154 | "- **W(Weight)**:将权重从 BF16/FP16 量化为 **INT8**\n", | 154 | "- **W(Weight)**:将权重从 BF16/FP16 量化为 **INT8**\n", |
| 155 | "- **反量化**:计算结果 `INT8 × INT8 → INT32` 后,通过 Scale 反量化回浮点精度\n", | 155 | "- **反量化**:计算结果 `INT8 × INT8 → INT32` 后,通过 Scale 反量化回浮点精度\n", |
| 156 | "\n", | 156 | "\n", |
| 157 | + "> **术语说明**:A8W8 与第 5 章使用的 W8A8 是同一种 INT8 激活 + INT8 权重量化方案的不同写法,本章沿用 A8W8 命名。仓库中 YAML 配置文件名(如 `qwen3_8b_a8w8_1tp.yaml`)和类名(`CompressedTensorsW8A8Int8LinearMethod`)也分别使用了这两种写法。\n", | ||
| 158 | + "\n", | ||
| 157 | "### 2.2 算子规格\n", | 159 | "### 2.2 算子规格\n", |
| 158 | "\n", | 160 | "\n", |
| 159 | "在上一章中,通过分析量化模型的profiling,归纳出了自定义算子的输入输出规格:\n", | 161 | "在上一章中,通过分析量化模型的profiling,归纳出了自定义算子的输入输出规格:\n", |
| @@ -994,6 +996,10 @@ | |||
| 994 | "W8A8_FILE = RECIPE_ROOT / 'module/quantization/compressed_tensors/compressed_tensors_w8a8_int8.py'\n", | 996 | "W8A8_FILE = RECIPE_ROOT / 'module/quantization/compressed_tensors/compressed_tensors_w8a8_int8.py'\n", |
| 995 | "print('[替换] 目标文件:', W8A8_FILE)\n", | 997 | "print('[替换] 目标文件:', W8A8_FILE)\n", |
| 996 | "\n", | 998 | "\n", |
| 999 | + "# 备份原始内容,仅在首次执行时保存,避免重复执行时覆盖为已修改的代码\n", | ||
| 1000 | + "if 'W8A8_ORIGINAL_CODE' not in dir():\n", | ||
| 1001 | + " W8A8_ORIGINAL_CODE = W8A8_FILE.read_text(encoding='utf-8')\n", | ||
| 1002 | + "\n", | ||
| 997 | "ct_code = W8A8_FILE.read_text(encoding='utf-8')\n", | 1003 | "ct_code = W8A8_FILE.read_text(encoding='utf-8')\n", |
| 998 | "_lib_path = str(QMM_CUSTOM_DIR / \"build/libascendc_ops.so\")\n", | 1004 | "_lib_path = str(QMM_CUSTOM_DIR / \"build/libascendc_ops.so\")\n", |
| 999 | "_load_lib_stmt = 'torch.ops.load_library(r\"' + _lib_path + '\")'\n", | 1005 | "_load_lib_stmt = 'torch.ops.load_library(r\"' + _lib_path + '\")'\n", |
| @@ -1198,6 +1204,28 @@ | |||
| 1198 | "display(combined.style.set_caption('QmmCustom 各 Shape 平均耗时 (us)'))" | 1204 | "display(combined.style.set_caption('QmmCustom 各 Shape 平均耗时 (us)'))" |
| 1199 | ] | 1205 | ] |
| 1200 | }, | 1206 | }, |
| 1207 | + { | ||
| 1208 | + "cell_type": "markdown", | ||
| 1209 | + "id": "restore_w8a8_md", | ||
| 1210 | + "metadata": {}, | ||
| 1211 | + "source": [ | ||
| 1212 | + "### 7.4 恢复原始 W8A8 源码\n", | ||
| 1213 | + "\n", | ||
| 1214 | + "自定义算子推理与 Profiling 已完成,下方单元格会从备份恢复 `compressed_tensors_w8a8_int8.py` 原始内容,避免修改后的源码污染 baseline 对比或产生 git 工作区变更。" | ||
| 1215 | + ] | ||
| 1216 | + }, | ||
| 1217 | + { | ||
| 1218 | + "cell_type": "code", | ||
| 1219 | + "execution_count": null, | ||
| 1220 | + "id": "restore_w8a8_code", | ||
| 1221 | + "metadata": {}, | ||
| 1222 | + "outputs": [], | ||
| 1223 | + "source": [ | ||
| 1224 | + "W8A8_FILE = RECIPE_ROOT / 'module/quantization/compressed_tensors/compressed_tensors_w8a8_int8.py'\n", | ||
| 1225 | + "W8A8_FILE.write_text(W8A8_ORIGINAL_CODE, encoding='utf-8')\n", | ||
| 1226 | + "print('[恢复] 已恢复原始 W8A8 源码,baseline 对比不受污染')" | ||
| 1227 | + ] | ||
| 1228 | + }, | ||
| 1201 | { | 1229 | { |
| 1202 | "cell_type": "markdown", | 1230 | "cell_type": "markdown", |
| 1203 | "id": "9eca2376", | 1231 | "id": "9eca2376", |
| @@ -1,5 +1,5 @@ | |||
| 1 | # Qwen3-8B 推理优化实践 | 1 | # Qwen3-8B 推理优化实践 |
| 2 | -本教程以 `Qwen3-8B` 为例,展示如何在昇腾 NPU 上使用 cann-recipes-infer 离线推理框架完成 Baseline 推理、Profiling 分析,并验证 Dense RMSNorm NPU 融合路径的性能收益。课程主流程采用 recipes 的典型用法:查看并修改 `models/qwen/config/` 下的 YAML 配置,通过 `executor/scripts/infer.sh` 拉起离线推理,再从 `res/` 与 `prof/` 目录读取日志和性能产物。 | 2 | +本教程以 `Qwen3-8B` 为例,展示如何在昇腾 NPU 上使用 cann-recipes-infer 离线推理框架完成 Baseline 推理、Profiling 分析,验证 Dense RMSNorm NPU 融合路径的性能收益,并通过 AMCT 工具完成 W8A8 量化导出与推理,以及自定义量化 matmul 算子的开发与接入。课程主流程采用 recipes 的典型用法:查看并修改 `models/qwen/config/` 下的 YAML 配置,通过 `executor/scripts/infer.sh` 拉起离线推理,再从 `res/` 与 `prof/` 目录读取日志和性能产物。 |
| 3 | 3 | ||
| 4 | 教程包含以下内容: | 4 | 教程包含以下内容: |
| 5 | - Notebooks:包含环境准备、YAML 修改、`infer.sh` 启动、Profiling 分析和 Dense RMSNorm NPU 融合路径 A/B 验证步骤,可在 GitCode 提供的轻量级 Notebook 上运行,也可在本地 Jupyter 环境中执行。 | 5 | - Notebooks:包含环境准备、YAML 修改、`infer.sh` 启动、Profiling 分析和 Dense RMSNorm NPU 融合路径 A/B 验证步骤,可在 GitCode 提供的轻量级 Notebook 上运行,也可在本地 Jupyter 环境中执行。 |
| @@ -35,6 +35,7 @@ Notebook 环境中按顺序打开并 Run All: | |||
| 35 | 2. `02_baseline_inference.ipynb` | 35 | 2. `02_baseline_inference.ipynb` |
| 36 | 3. `03_profiling_analysis.ipynb` | 36 | 3. `03_profiling_analysis.ipynb` |
| 37 | 4. `04_npu_optimization.ipynb` | 37 | 4. `04_npu_optimization.ipynb` |
| 38 | +5. `05_quantization_qwen3_8b.ipynb` | ||
| 38 | 39 | ||
| 39 | 首次执行 `02_baseline_inference.ipynb` 的 Baseline 推理 cell 会开始下载并缓存 `Qwen/Qwen3-8B` 权重;如果已设置 `QWEN3_8B_MODEL_PATH`,则直接使用本地权重目录。 | 40 | 首次执行 `02_baseline_inference.ipynb` 的 Baseline 推理 cell 会开始下载并缓存 `Qwen/Qwen3-8B` 权重;如果已设置 `QWEN3_8B_MODEL_PATH`,则直接使用本地权重目录。 |
| 40 | 41 | ||
| @@ -49,6 +50,8 @@ recipes 日志保存在 `src/inference_scripts/recipe_qwen3_8b/models/qwen/res/< | |||
| 49 | 50 | ||
| 50 | 第 3 章会在 YAML 中打开 `model_config.enable_profiler=true`。运行后进入 recipes 结果目录下的 `prof/`,查看 `kernel_details`、trace、`op_statistic` 或 `op_summary` 等性能产物,再基于真实算子耗时选择第 4 章的优化点。 | 51 | 第 3 章会在 YAML 中打开 `model_config.enable_profiler=true`。运行后进入 recipes 结果目录下的 `prof/`,查看 `kernel_details`、trace、`op_statistic` 或 `op_summary` 等性能产物,再基于真实算子耗时选择第 4 章的优化点。 |
| 51 | 52 | ||
| 53 | +第 5 章使用 AMCT 工具将 BF16 权重导出为 W8A8 INT8 量化权重,并通过量化 YAML 配置(`qwen3_8b_a8w8_1tp.yaml`)执行量化模型推理与 Profiling 分析,定位耗时最高的量化 matmul 算子。第 6 章基于第 5 章 Profiling 归纳的算子规格,需要使用 Ascend C 开发自定义量化 matmul 算子 `QmmCustom`,编译后接入 Qwen3-8B 量化模型验证功能与性能。 | ||
| 54 | + | ||
| 52 | 本地终端运行前先准备 CANN 和可见 NPU。 | 55 | 本地终端运行前先准备 CANN 和可见 NPU。 |
| 53 | 56 | ||
| 54 | ```bash | 57 | ```bash |