已合并
[Doc] 量化算法、PTQ对应资料内容整体整改 #972
wanlongze123创建于 5 天前
[Doc] 量化算法、PTQ对应资料内容整体整改 #972
已合并
共 104 个文件变更+4107-5755
| @@ -58,7 +58,7 @@ evaluation: | |||
| 58 | | `standing_high_with_experience` | **`ModelSlimPipelineInterfaceV1`** + **`StandingHighWithExperienceInterface`**(`load_model`,离群值抑制能力探测) | | 58 | | `standing_high_with_experience` | **`ModelSlimPipelineInterfaceV1`** + **`StandingHighWithExperienceInterface`**(`load_model`,离群值抑制能力探测) | |
| 59 | | `binary_fallback` | **`ModelSlimPipelineInterfaceV1`** (实现V1架构) | | 59 | | `binary_fallback` | **`ModelSlimPipelineInterfaceV1`** (实现V1架构) | |
| 60 | 60 | ||
| 61 | -详见各策略文档「适用要求 / 模型适配」章节及 《[LLM 大模型接入指南](../../knowledge_base/model/integrating_models.md#自动调优与敏感层分析)》。 | 61 | +详见各策略文档「适用要求 / 模型适配」章节及 《[LLM 大模型接入指南](../../knowledge_base/ptq/llm/integration_guide_large_language_model_quantization.md)》。 |
| 62 | 62 | ||
| 63 | **策略特有配置字段** | 63 | **策略特有配置字段** |
| 64 | 64 | ||
| @@ -10,7 +10,7 @@ | |||
| 10 | 10 | ||
| 11 | **覆盖流程**:分析 `${MODEL_PATH}/config.json` 中的新模型结构 → 设计 `lab_practice/deepseek_v4/deepseek_v4_pro_w4a8.yaml` 量化方案 → 完成 `msmodelslim/model/deepseek_v4/` 模型适配器及 `lab_practice/deepseek_v4/` 量化 YAML 开发 → 环境准备与工具安装 → 执行一键量化命令 → 检查 `${SAVE_PATH}` 量化权重完整性 → 使用 `${SAVE_PATH}` 推理部署与精度评测 → 量化调优(可选)。 | 11 | **覆盖流程**:分析 `${MODEL_PATH}/config.json` 中的新模型结构 → 设计 `lab_practice/deepseek_v4/deepseek_v4_pro_w4a8.yaml` 量化方案 → 完成 `msmodelslim/model/deepseek_v4/` 模型适配器及 `lab_practice/deepseek_v4/` 量化 YAML 开发 → 环境准备与工具安装 → 执行一键量化命令 → 检查 `${SAVE_PATH}` 量化权重完整性 → 使用 `${SAVE_PATH}` 推理部署与精度评测 → 量化调优(可选)。 |
| 12 | 12 | ||
| 13 | -**关联流程**:[《权重量化使用指南》](../user_guide/usage_weight_quantization.md)、[《一键量化完整指南》](../user_guide/usage_quick_quantization.md)、[《LLM 大模型接入指南》](../knowledge_base/model/integrating_models.md)、[《量化算法总览》](../knowledge_base/quantization_algorithms/README.md) | 13 | +**关联流程**:[《权重量化使用指南》](../user_guide/usage_weight_quantization.md)、[《一键量化完整指南》](../user_guide/usage_quick_quantization.md)、[《LLM 大模型接入指南》](../knowledge_base/ptq/llm/integration_guide_large_language_model_quantization.md)、[《量化算法总览》](../knowledge_base/quantization_algorithms/README.md) |
| 14 | 14 | ||
| 15 | **模型相关信息** | 15 | **模型相关信息** |
| 16 | 16 | ||
| @@ -6,7 +6,7 @@ | |||
| 6 | 6 | ||
| 7 | **覆盖流程**:模型适配 → 量化方案设计 → 权重量化 → 精度测评。 | 7 | **覆盖流程**:模型适配 → 量化方案设计 → 权重量化 → 精度测评。 |
| 8 | 8 | ||
| 9 | -**关联流程**:《[多模态理解模型接入指南](../knowledge_base/model/integrating_multimodal_understanding_model.md)》、《[一键量化使用说明](../user_guide/usage_quick_quantization.md)》 | 9 | +**关联流程**:《[多模态理解模型接入指南](../knowledge_base/ptq/vlm/integration_guide_vision_transformer_quantization.md)》、《[一键量化使用说明](../user_guide/usage_quick_quantization.md)》 |
| 10 | 10 | ||
| 11 | ## 2. 环境与版本 | 11 | ## 2. 环境与版本 |
| 12 | 12 | ||
| @@ -61,7 +61,7 @@ | |||
| 61 | | `format` | `mxfp4-pack-quantized` | 原生权重中包含 MXFP4 量化权重,需要离线或在线完成权重反量化 | | 61 | | `format` | `mxfp4-pack-quantized` | 原生权重中包含 MXFP4 量化权重,需要离线或在线完成权重反量化 | |
| 62 | | `mm_projector_type` | `patchmergerv2` | 视觉特征的融合模块类,影响旋转量化算法适配 | | 62 | | `mm_projector_type` | `patchmergerv2` | 视觉特征的融合模块类,影响旋转量化算法适配 | |
| 63 | 63 | ||
| 64 | -2. 按照《[多模态理解模型接入指南](../knowledge_base/model/integrating_multimodal_understanding_model.md)》开发模型适配器([`msmodelslim/model/kimi_k3/model_adapter.py`](../../../msmodelslim/model/kimi_k3/model_adapter.py))。对于 Kimi-K3,即使采用逐层加载方式,单卡 64GB 显存仍会溢出,需参考《[专家并行使用指南](../knowledge_base/parallel/expert_parallelism/expert_parallelism_guide.md)》完成专家并行的适配([`ep_patches.py`](../../../msmodelslim/model/kimi_k3/ep_patches.py))。 | 64 | +2. 按照《[多模态理解模型接入指南](../knowledge_base/ptq/vlm/integration_guide_vision_transformer_quantization.md)》开发模型适配器([`msmodelslim/model/kimi_k3/model_adapter.py`](../../../msmodelslim/model/kimi_k3/model_adapter.py))。对于 Kimi-K3,即使采用逐层加载方式,单卡 64GB 显存仍会溢出,需参考《[专家并行使用指南](../knowledge_base/parallel/expert_parallelism/expert_parallelism_guide.md)》完成专家并行的适配([`ep_patches.py`](../../../msmodelslim/model/kimi_k3/ep_patches.py))。 |
| 65 | 65 | ||
| 66 | 3. 注册模型适配器([`config/config.ini`](../../../config/config.ini)): | 66 | 3. 注册模型适配器([`config/config.ini`](../../../config/config.ini)): |
| 67 | 67 | ||
| @@ -6,7 +6,7 @@ | |||
| 6 | 6 | ||
| 7 | **覆盖流程**:模型接入适配 → 量化方案设计 → 权重量化 → 精度评测 | 7 | **覆盖流程**:模型接入适配 → 量化方案设计 → 权重量化 → 精度评测 |
| 8 | 8 | ||
| 9 | -**关联流程**:《[多模态生成模型接入指南](../knowledge_base/model/integrating_multimodal_generation_model.md)》、《[一键量化使用说明](../user_guide/usage_quick_quantization.md)》 | 9 | +**关联流程**:《[多模态生成模型接入指南](../knowledge_base/ptq/dit/integration_guide_diffusion_transformer_quantization.md)》、《[一键量化使用说明](../user_guide/usage_quick_quantization.md)》 |
| 10 | 10 | ||
| 11 | ## 2. 模型相关信息 | 11 | ## 2. 模型相关信息 |
| 12 | 12 | ||
| @@ -36,7 +36,7 @@ | |||
| 36 | | PyTorch | 2.9.0 | | 36 | | PyTorch | 2.9.0 | |
| 37 | | TorchNPU | 2.9.0 | | 37 | | TorchNPU | 2.9.0 | |
| 38 | | MindIE-SD | 3.1.0 | | 38 | | MindIE-SD | 3.1.0 | |
| 39 | -| 测评工具 | [AISBench](https://github.com/AISBench/benchmark) | | 39 | +| 评测工具 | [AISBench](https://github.com/AISBench/benchmark) | |
| 40 | | 其他依赖 | Wan2.2-T2V-A14B 模型权重、[VBench-1.0-mini 评测数据集](https://modelers.cn/datasets/AISBench/VBench-1.0-mini) | | 40 | | 其他依赖 | Wan2.2-T2V-A14B 模型权重、[VBench-1.0-mini 评测数据集](https://modelers.cn/datasets/AISBench/VBench-1.0-mini) | |
| 41 | 41 | ||
| 42 | **本案例前置条件**: | 42 | **本案例前置条件**: |
| @@ -148,10 +148,10 @@ Wan2.2 适配器基类 `Wan2_2BaseModelAdapter` 实现公共逻辑,场景子 | |||
| 148 | > | 148 | > |
| 149 | > 完成模型适配与注册后,需在源代码目录执行 `bash install.sh` 重新安装 msmodelslim,使适配器代码生效;否则 `--model_type Wan2.2-T2V-A14B` 无法命中适配器。 | 149 | > 完成模型适配与注册后,需在源代码目录执行 `bash install.sh` 重新安装 msmodelslim,使适配器代码生效;否则 `--model_type Wan2.2-T2V-A14B` 无法命中适配器。 |
| 150 | 150 | ||
| 151 | -**输出**:适配器代码编写完成,模型名注册完成,msModelSlim 可识别 `Wan2.2-T2V-A14B`。 | 151 | +**输出**:适配器代码编写完成,模型名注册完成,msModelSlim 可识别 `Wan2.2-T2V-A14B`。 |
| 152 | -**记录**:适配器代码文件列表(`base_model_adapter.py`、`expert_sub_adapter.py`、`constants.py`、`t2v/model_adapter.py`、`t2v/loader.py`)、`config.ini` 注册配置。 | 152 | +**记录**:适配器代码文件列表(`base_model_adapter.py`、`expert_sub_adapter.py`、`constants.py`、`t2v/model_adapter.py`、`t2v/loader.py`)、`config.ini` 注册配置。 |
| 153 | 153 | ||
| 154 | -**参考**:《[多模态生成模型接入指南](../knowledge_base/model/integrating_multimodal_generation_model.md)》 | 154 | +**参考**:《[多模态生成模型接入指南](../knowledge_base/ptq/dit/integration_guide_diffusion_transformer_quantization.md)》 |
| 155 | 155 | ||
| 156 | ### 步骤 2:量化方案设计 | 156 | ### 步骤 2:量化方案设计 |
| 157 | 157 | ||
| @@ -164,7 +164,7 @@ Wan2.2 适配器基类 `Wan2_2BaseModelAdapter` 实现公共逻辑,场景子 | |||
| 164 | 1. 确定整体量化策略。 | 164 | 1. 确定整体量化策略。 |
| 165 | 165 | ||
| 166 | Wan2.2 双专家 DiT 主干采用 W4A4F4 混合量化方案,要点如下: | 166 | Wan2.2 双专家 DiT 主干采用 W4A4F4 混合量化方案,要点如下: |
| 167 | - | 167 | + |
| 168 | - 根据经验前五层一般比较敏感,因此将前5层(blocks.0 ~ blocks.4)回退为 W8A8 量化(激活/权重均 mxfp8),降低浅层特征量化误差对生成质量的冲击。 | 168 | - 根据经验前五层一般比较敏感,因此将前5层(blocks.0 ~ blocks.4)回退为 W8A8 量化(激活/权重均 mxfp8),降低浅层特征量化误差对生成质量的冲击。 |
| 169 | - 主干绝大多数层(`blocks.5` 及之后)进行 W4A4 量化:激活与权重均按 `per_block` 粒度对称量化为 mxfp4(激活 `minmax`、权重 `ceil_x` 并开启 `enable_search` 搜索),在保证生成质量的同时取得显存与带宽收益。 | 169 | - 主干绝大多数层(`blocks.5` 及之后)进行 W4A4 量化:激活与权重均按 `per_block` 粒度对称量化为 mxfp4(激活 `minmax`、权重 `ceil_x` 并开启 `enable_search` 搜索),在保证生成质量的同时取得显存与带宽收益。 |
| 170 | - 注意力(`self_attn`)使能在线 QuaRot(`online_quarot`):attention 激活通常存在离群值,直接低比特量化易产生精度损失,因此通过 Hadamard 旋转改善激活分布,降低量化的精度损失。 | 170 | - 注意力(`self_attn`)使能在线 QuaRot(`online_quarot`):attention 激活通常存在离群值,直接低比特量化易产生精度损失,因此通过 Hadamard 旋转改善激活分布,降低量化的精度损失。 |
| @@ -191,9 +191,9 @@ Wan2.2 适配器基类 `Wan2_2BaseModelAdapter` 实现公共逻辑,场景子 | |||
| 191 | 191 | ||
| 192 | ### 步骤 3:环境准备与路径配置 | 192 | ### 步骤 3:环境准备与路径配置 |
| 193 | 193 | ||
| 194 | -**目标**:设置环境变量,核对依赖版本,确认模型和数据路径正确。 | 194 | +**目标**:设置环境变量,核对依赖版本,确认模型和数据路径正确。 |
| 195 | -**输入**:模型路径、数据路径、输出路径。 | 195 | +**输入**:模型路径、数据路径、输出路径。 |
| 196 | -**操作**:配置环境变量,执行版本核对命令。 | 196 | +**操作**:配置环境变量,执行版本核对命令。 |
| 197 | 197 | ||
| 198 | ```bash | 198 | ```bash |
| 199 | # 设置环境变量(替换为实际路径) | 199 | # 设置环境变量(替换为实际路径) |
| @@ -211,14 +211,14 @@ python -c "import torch_npu; print('TorchNPU:', torch_npu.__version__)" | |||
| 211 | pip show msmodelslim mindiesd | 211 | pip show msmodelslim mindiesd |
| 212 | ``` | 212 | ``` |
| 213 | 213 | ||
| 214 | -**输出**:环境变量配置完成,所有依赖版本核对记录在案。 | 214 | +**输出**:环境变量配置完成,所有依赖版本核对记录在案。 |
| 215 | -**记录**:CANN版本、PyTorch/TorchNPU版本、msmodelslim版本、NPU型号与驱动。 | 215 | +**记录**:CANN版本、PyTorch/TorchNPU版本、msmodelslim版本、NPU型号与驱动。 |
| 216 | 216 | ||
| 217 | ### 步骤 4:执行Wan2.2 W4A4F4量化 | 217 | ### 步骤 4:执行Wan2.2 W4A4F4量化 |
| 218 | 218 | ||
| 219 | -**目标**:运行msModelSlim量化流程,完成双专家逐层量化,导出量化权重。 | 219 | +**目标**:运行msModelSlim量化流程,完成双专家逐层量化,导出量化权重。 |
| 220 | -**输入**:浮点模型、校准数据集、量化配置(`${YAML_PATH}`)。 | 220 | +**输入**:浮点模型、校准数据集、量化配置(`${YAML_PATH}`)。 |
| 221 | -**操作**:使用对应model_type执行W4A4F4量化。 | 221 | +**操作**:使用对应model_type执行W4A4F4量化。 |
| 222 | 222 | ||
| 223 | ```bash | 223 | ```bash |
| 224 | # 浮点模型权重路径:${MODEL_PATH} | 224 | # 浮点模型权重路径:${MODEL_PATH} |
| @@ -234,14 +234,14 @@ msmodelslim quant \ | |||
| 234 | --trust_remote_code true | 234 | --trust_remote_code true |
| 235 | ``` | 235 | ``` |
| 236 | 236 | ||
| 237 | -**输出**:量化权重保存至`${SAVE_PATH}`目录,包含双专家量化权重与描述文件。 | 237 | +**输出**:量化权重保存至`${SAVE_PATH}`目录,包含双专家量化权重与描述文件。 |
| 238 | -**记录**:量化过程完整日志、量化总时长、各层量化状态。 | 238 | +**记录**:量化过程完整日志、量化总时长、各层量化状态。 |
| 239 | 239 | ||
| 240 | ### 步骤 5:准备VBench-1.0-mini评测子集 | 240 | ### 步骤 5:准备VBench-1.0-mini评测子集 |
| 241 | 241 | ||
| 242 | -**目标**:从VBench-1.0-mini原始数据集中整理出0.01子集,供推理脚本使用。 | 242 | +**目标**:从VBench-1.0-mini原始数据集中整理出0.01子集,供推理脚本使用。 |
| 243 | -**输入**:已下载的VBench-1.0-mini原始数据集。 | 243 | +**输入**:已下载的VBench-1.0-mini原始数据集。 |
| 244 | -**操作**:目录结构调整与文件重命名。 | 244 | +**操作**:目录结构调整与文件重命名。 |
| 245 | 245 | ||
| 246 | VBench-1.0-mini原始目录结构如下: | 246 | VBench-1.0-mini原始目录结构如下: |
| 247 | 247 | ||
| @@ -275,14 +275,14 @@ cp VBench-1.0-mini/VBench_kmeans_info_0.01.json \ | |||
| 275 | 275 | ||
| 276 | 完成后推理传参 `--vbench_mini_root ./final_mini_dataset_0_01` 即可使用0.01子集。该子集包含11条prompt,覆盖11个维度;按每条prompt生成1个视频计算,共11个视频。 | 276 | 完成后推理传参 `--vbench_mini_root ./final_mini_dataset_0_01` 即可使用0.01子集。该子集包含11条prompt,覆盖11个维度;按每条prompt生成1个视频计算,共11个视频。 |
| 277 | 277 | ||
| 278 | -**输出**:`final_mini_dataset_0_01/` 目录准备完毕,可直接传入 `--vbench_mini_root`。 | 278 | +**输出**:`final_mini_dataset_0_01/` 目录准备完毕,可直接传入 `--vbench_mini_root`。 |
| 279 | -**记录**:整理后的目录结构、`VBench_kmeans_info.json` 文件内容(11条prompt列表)。 | 279 | +**记录**:整理后的目录结构、`VBench_kmeans_info.json` 文件内容(11条prompt列表)。 |
| 280 | 280 | ||
| 281 | ### 步骤 6:执行Wan2.2浮点模型VBench评测推理 | 281 | ### 步骤 6:执行Wan2.2浮点模型VBench评测推理 |
| 282 | 282 | ||
| 283 | -**目标**:运行浮点模型推理,在VBench-mini数据集上生成评测结果作为精度对比基线,该步骤只生成视频。 | 283 | +**目标**:运行浮点模型推理,在VBench-mini数据集上生成评测结果作为精度对比基线,该步骤只生成视频。 |
| 284 | -**输入**:浮点模型权重、VBench-mini评测数据集、推理超参配置。 | 284 | +**输入**:浮点模型权重、VBench-mini评测数据集、推理超参配置。 |
| 285 | -**操作**:本案例中具体取值为:`ulysses_size=2`、`dit_fsdp + t5_fsdp`、`cfg_size=2`、`vae_parallel` 启用。FP16 与 W4A4F4 两组使用完全相同的并行约定。其中 `ALGO` 为推理仓用于选择 FA 计算方式的环境变量,需按设备与推理方式取值(取值说明详见推理仓文档 [开始前必读-ALGO配置说明](https://modelers.cn/models/MindIE/wan2.2#31-%E5%BC%80%E5%A7%8B%E5%89%8D%E5%BF%85%E8%AF%BB))。本案例产品形态为Ascend 950PR&950DT 系列产品:浮点推理取 `ALGO=0`;量化推理因使能 attention FP8,取 `ALGO=3`。此为两侧唯一的非模型路径差异。 | 285 | +**操作**:本案例中具体取值为:`ulysses_size=2`、`dit_fsdp + t5_fsdp`、`cfg_size=2`、`vae_parallel` 启用。FP16 与 W4A4F4 两组使用完全相同的并行约定。其中 `ALGO` 为推理仓用于选择 FA 计算方式的环境变量,需按设备与推理方式取值(取值说明详见推理仓文档 [开始前必读-ALGO配置说明](https://modelers.cn/models/MindIE/wan2.2#31-%E5%BC%80%E5%A7%8B%E5%89%8D%E5%BF%85%E8%AF%BB))。本案例产品形态为 Ascend 950PR&950DT 系列产品:浮点推理取 `ALGO=0`;量化推理因使能 attention FP8,取 `ALGO=3`。此为两侧唯一的非模型路径差异。 |
| 286 | 286 | ||
| 287 | ```bash | 287 | ```bash |
| 288 | # Wan2.2浮点模型推理 | 288 | # Wan2.2浮点模型推理 |
| @@ -315,14 +315,14 @@ torchrun --nproc_per_node=4 --master_port=23459 vbench.py \ | |||
| 315 | --temporal_flickering_samples 1 | 315 | --temporal_flickering_samples 1 |
| 316 | ``` | 316 | ``` |
| 317 | 317 | ||
| 318 | -**输出**:推理正常完成,所有评测视频生成完毕,结果保存至`${OUTPUT_DIR}/vbench_fp_output/`目录,生成视频无画质崩坏。 | 318 | +**输出**:推理正常完成,所有评测视频生成完毕,结果保存至`${OUTPUT_DIR}/vbench_fp_output/`目录,生成视频无画质崩坏。 |
| 319 | -**记录**:推理完整运行日志。 | 319 | +**记录**:推理完整运行日志。 |
| 320 | 320 | ||
| 321 | ### 步骤 7:执行Wan2.2量化模型VBench评测推理 | 321 | ### 步骤 7:执行Wan2.2量化模型VBench评测推理 |
| 322 | 322 | ||
| 323 | -**目标**:运行 W4A4F4 量化模型推理,生成评测视频,用于与浮点基线对比。 | 323 | +**目标**:运行 W4A4F4 量化模型推理,生成评测视频,用于与浮点基线对比。 |
| 324 | -**输入**:W4A4F4量化权重、与浮点相同的VBench-mini评测数据集、W4A4F4的推理超参配置。 | 324 | +**输入**:W4A4F4量化权重、与浮点相同的VBench-mini评测数据集、W4A4F4的推理超参配置。 |
| 325 | -**操作**:仅将 `ALGO` 由 0 改为 3、增加 `--quant_dit_path` 参数指向量化权重路径,其余推理参数与浮点完全相同,执行推理生成评测视频。 | 325 | +**操作**:仅将 `ALGO` 由 0 改为 3、增加 `--quant_dit_path` 参数指向量化权重路径,其余推理参数与浮点完全相同,执行推理生成评测视频。 |
| 326 | 326 | ||
| 327 | ```bash | 327 | ```bash |
| 328 | # Wan2.2量化模型推理与VBench评测命令 | 328 | # Wan2.2量化模型推理与VBench评测命令 |
| @@ -357,8 +357,8 @@ torchrun --nproc_per_node=4 --master_port=23459 vbench.py \ | |||
| 357 | --temporal_flickering_samples 1 | 357 | --temporal_flickering_samples 1 |
| 358 | ``` | 358 | ``` |
| 359 | 359 | ||
| 360 | -**输出**:推理正常完成,所有评测视频生成完毕,结果保存至`${OUTPUT_DIR}/vbench_quant_output/`目录,生成视频无画质崩坏。 | 360 | +**输出**:推理正常完成,所有评测视频生成完毕,结果保存至`${OUTPUT_DIR}/vbench_quant_output/`目录,生成视频无画质崩坏。 |
| 361 | -**记录**:量化推理完整运行日志。 | 361 | +**记录**:量化推理完整运行日志。 |
| 362 | 362 | ||
| 363 | ## 6. 精度测试 | 363 | ## 6. 精度测试 |
| 364 | 364 | ||
| @@ -368,7 +368,7 @@ torchrun --nproc_per_node=4 --master_port=23459 vbench.py \ | |||
| 368 | 368 | ||
| 369 | | 项 | 内容 | | 369 | | 项 | 内容 | |
| 370 | | ---------- | ---------------------------- | | 370 | | ---------- | ---------------------------- | |
| 371 | -| 测评工具 | AISBench | | 371 | +| 评测工具 | AISBench | |
| 372 | | 对比对象 | Wan2.2 FP16浮点推理生成结果 vs Wan2.2 W4A4F4量化推理生成结果 | | 372 | | 对比对象 | Wan2.2 FP16浮点推理生成结果 vs Wan2.2 W4A4F4量化推理生成结果 | |
| 373 | | 数据集与任务 | Vbench-1.0-mini 1%子集 | | 373 | | 数据集与任务 | Vbench-1.0-mini 1%子集 | |
| 374 | | 样本数 / 子集策略 | VBench-1.0-mini 1%子集共11条prompt,每条prompt生成1个视频,共11个视频样本 | | 374 | | 样本数 / 子集策略 | VBench-1.0-mini 1%子集共11条prompt,每条prompt生成1个视频,共11个视频样本 | |
| @@ -27,7 +27,7 @@ | |||
| 27 | 27 | ||
| 28 | 遵循我们的 Pull Request《[模板与规范](../../../.gitcode/PULL_REQUEST_TEMPLATE.md)》。 | 28 | 遵循我们的 Pull Request《[模板与规范](../../../.gitcode/PULL_REQUEST_TEMPLATE.md)》。 |
| 29 | 29 | ||
| 30 | -参考开发者文档 《[LLM 大模型接入指南](../knowledge_base/model/integrating_models.md)》。 | 30 | +参考开发者文档 《[LLM 大模型接入指南](../knowledge_base/ptq/llm/integration_guide_large_language_model_quantization.md)》。 |
| 31 | 31 | ||
| 32 | 参考开发者文档 《[编码规范](development_guide/coding_standards.md)》。 | 32 | 参考开发者文档 《[编码规范](development_guide/coding_standards.md)》。 |
| 33 | 33 | ||
| @@ -1,184 +0,0 @@ | |||
| 1 | -# LLM大模型接入指南 | ||
| 2 | - | ||
| 3 | -## 简介 | ||
| 4 | - | ||
| 5 | -本文档面向需要将自有模型接入 msModelSlim 的开发者。 | ||
| 6 | -msModelSlim认识到量化机制和算法都有适用范围和局限性,而新的模型结构层出不穷,不存在一劳永逸的模型量化方法。 | ||
| 7 | -为了尽可能简化自有模型量化流程,msModelSlim将量化机制和算法生效所依赖的模型条件抽取出来,以接口形式描述。 | ||
| 8 | -模型则以模型适配器描述,模型适配器是接口实现的组合,通过实现各式各样的机制和算法等组件的接口打通自有模型的量化过程。 | ||
| 9 | - | ||
| 10 | -## 概念说明 | ||
| 11 | - | ||
| 12 | -### 接口 | ||
| 13 | - | ||
| 14 | -* 接口在量化机制和算法等组件中定义,描述了对应组件对模型的诉求,接口定义和使用请参考对应组件的文档和代码 | ||
| 15 | -* 仅当使用到对应组件时才需要实现对应接口 | ||
| 16 | -* 接口汇总:[`msmodelslim/model/interface_hub.py`](../../../../msmodelslim/model/interface_hub.py) | ||
| 17 | - | ||
| 18 | -### 模型适配器 | ||
| 19 | - | ||
| 20 | -* 模型适配器是组件接口实现的组合,描述了模型特性和行为,这些特性和行为服务于具体的组件接口实现 | ||
| 21 | -* 结构相似的一组模型可复用一个模型适配器,一个模型适配器可注册多个模型 | ||
| 22 | -* `msmodelslim`命令中常见参数`model_type`即对应模型适配器注册的模型名,用于匹配和创建模型适配器 | ||
| 23 | - | ||
| 24 | -## 模型接入 | ||
| 25 | - | ||
| 26 | -以下内容将以 [`Qwen3-32B`](../../../../msmodelslim/model/qwen3/model_adapter.py) W8A8动态量化场景(简称“场景示例”)的模型接入为例: | ||
| 27 | - | ||
| 28 | -### 新建模型适配器`py`文件 | ||
| 29 | - | ||
| 30 | -建议放在[`msmodelslim/model/`](../../../../msmodelslim/model) 下,命名如 `qwen3.py`。 | ||
| 31 | - | ||
| 32 | -### 理清量化过程涉及的组件,以组件接口组合定义适配器类 | ||
| 33 | - | ||
| 34 | -模型适配器类必须继承自[`BaseModelAdapter`](../../../../msmodelslim/model/base.py)。 | ||
| 35 | - | ||
| 36 | -根据经验,W8A8动态量化的精度损失很小,无需搭配离群值抑制算法,也很少需要回退;因此,在场景示例中,我们仅需支持量化调度,无需支持离群值量化、敏感层分析等额外功能。需要接入其他算法可以参考《[`算法总览`](../quantization_algorithms/README.md)》。 | ||
| 37 | - | ||
| 38 | -```python | ||
| 39 | -from msmodelslim.model.interface_hub import ModelSlimPipelineInterfaceV1 | ||
| 40 | -from msmodelslim.model.common.transformers import TransformersModel | ||
| 41 | -from msmodelslim.utils.logging import logger_setter | ||
| 42 | - | ||
| 43 | - | ||
| 44 | -@logger_setter() | ||
| 45 | -class Qwen3ModelAdapter(TransformersModel, # 继承自BaseModelAdapter,基于Transformers模型通用特性和行为简化接口实现 | ||
| 46 | - ModelSlimPipelineInterfaceV1, # 必要,服务于量化调度 | ||
| 47 | - ): | ||
| 48 | - pass | ||
| 49 | -``` | ||
| 50 | - | ||
| 51 | -### 实现组件接口方法 | ||
| 52 | - | ||
| 53 | -实现接口所需的方法,方法描述模型特性和行为,若使用IDE,可通过IDE功能快速创建接口方法,再填入功能代码。 | ||
| 54 | - | ||
| 55 | -```python | ||
| 56 | -from typing import List, Any, Generator | ||
| 57 | -from torch import nn | ||
| 58 | -from msmodelslim.core.const import DeviceType | ||
| 59 | -from msmodelslim.core.base.protocol import ProcessRequest | ||
| 60 | -from msmodelslim.model.interface_hub import ModelSlimPipelineInterfaceV1 | ||
| 61 | -from msmodelslim.model.common.transformers import TransformersModel | ||
| 62 | -from msmodelslim.model.common.layer_wise_forward import generated_decoder_layer_visit_func, \ | ||
| 63 | - transformers_generated_forward_func | ||
| 64 | -from msmodelslim.utils.logging import logger_setter | ||
| 65 | - | ||
| 66 | - | ||
| 67 | -@logger_setter() | ||
| 68 | -class Qwen3ModelAdapter(TransformersModel, | ||
| 69 | - ModelSlimPipelineInterfaceV1 | ||
| 70 | - ): | ||
| 71 | - def handle_dataset(self, dataset: Any, device: DeviceType = DeviceType.NPU) -> List[Any]: # 描述校准集转化为批量输入 | ||
| 72 | - return self._get_tokenized_data(dataset, device) # TransformersModel已基于Transformers模型特点给出默认实现 | ||
| 73 | - | ||
| 74 | - def init_model(self, device: DeviceType = DeviceType.NPU) -> nn.Module: # 描述如何初始化模型 | ||
| 75 | - return self._load_model(device) # TransformersModel已基于Transformers模型特点给出默认实现 | ||
| 76 | - | ||
| 77 | - def generate_model_visit(self, model: nn.Module) -> Generator[ProcessRequest, Any, None]: # 描述如何将模型分段,必须与模型结构匹配 | ||
| 78 | - # msmodelslim/model/common/layer_wise_forward.py已给出基于DecoderLayer类分段的默认实现 | ||
| 79 | - yield from generated_decoder_layer_visit_func(model) | ||
| 80 | - | ||
| 81 | - def generate_model_forward(self, model: nn.Module, inputs: Any, | ||
| 82 | - ) -> Generator[ProcessRequest, Any, None]: # 描述如何将模型前向过程分段,必须与模型前向过程匹配 | ||
| 83 | - # msmodelslim/model/common/layer_wise_forward.py已给出基于DecoderLayer类前向过程的默认实现 | ||
| 84 | - yield from transformers_generated_forward_func(model, | ||
| 85 | - inputs) | ||
| 86 | - | ||
| 87 | - def enable_kv_cache(self, model: nn.Module, need_kv_cache: bool) -> None: # 描述是否禁用 KVCache,可减少显存 | ||
| 88 | - return self._enable_kv_cache(model, need_kv_cache) # TransformersModel已基于Transformers模型特点给出默认实现 | ||
| 89 | -``` | ||
| 90 | - | ||
| 91 | -### 注册模型 | ||
| 92 | - | ||
| 93 | -在配置文件 [`config.ini`](../../../../config/config.ini) 中注册模型名称,便于同一系列的模型复用一个模型适配器。 | ||
| 94 | - | ||
| 95 | -```ini | ||
| 96 | -# 在ModelAdapter中的qwen3系列注册Qwen3-32B模型,qwen3对应下面的Qwen3ModelAdapter模型适配器 | ||
| 97 | -[ModelAdapter] | ||
| 98 | -default = default | ||
| 99 | -deepseek_v3 = DeepSeek-V3, DeepSeek-V3-0324, DeepSeek-R1, DeepSeek-R1-0528, DeepSeek-V3.1 | ||
| 100 | -deepseek_v3_2 = DeepSeek-V3.2-Exp | ||
| 101 | -qwen2_5 = Qwen2.5-7B-Instruct, Qwen2.5-32B-Instruct, Qwen2.5-72B-Instruct, Qwen2.5-Coder-7B-Instruct | ||
| 102 | -qwen3 = Qwen3-8B, Qwen3-14B, Qwen3-32B # 此处添加 | ||
| 103 | -qwen3_moe = Qwen3-30B, Qwen3-235B | ||
| 104 | -qwq = Qwen-QwQ-32B, QwQ-32B | ||
| 105 | -wan2_1 = Wan2_1, Wan2.1 | ||
| 106 | -qwen3_next = Qwen3-Next-80B-A3B-Instruct | ||
| 107 | -wan2_2 = Wan2_2, Wan2.2 | ||
| 108 | - | ||
| 109 | -# 如果添加新模型适配器,需要在ModelAdapterEntryPoints中添加,注意ModelAdapter与ModelAdapterEntryPoints中的key需要保持一致,不然无法生效 | ||
| 110 | -[ModelAdapterEntryPoints] | ||
| 111 | -default = msmodelslim.model.default.model_adapter:DefaultModelAdapter | ||
| 112 | -deepseek_v3 = msmodelslim.model.deepseek_v3.model_adapter:DeepSeekV3ModelAdapter | ||
| 113 | -deepseek_v3_2 = msmodelslim.model.deepseek_v3_2.model_adapter:DeepSeekV32ModelAdapter | ||
| 114 | -qwen2_5 = msmodelslim.model.qwen2_5.model_adapter:Qwen25ModelAdapter | ||
| 115 | -qwen3 = msmodelslim.model.qwen3.model_adapter:Qwen3ModelAdapter | ||
| 116 | -qwen3_moe = msmodelslim.model.qwen3_moe.model_adapter:Qwen3MoeModelAdapter | ||
| 117 | -qwq = msmodelslim.model.qwq.model_adapter:QwqModelAdapter | ||
| 118 | -wan2_1 = msmodelslim.model.wan2_1.model_adapter:Wan2Point1Adapter | ||
| 119 | -qwen3_next = msmodelslim.model.qwen3_next.model_adapter:Qwen3NextModelAdapter | ||
| 120 | -wan2_2 = msmodelslim.model.wan2_2.model_adapter:Wan2Point2Adapter | ||
| 121 | -``` | ||
| 122 | - | ||
| 123 | -## 自动调优与敏感层分析 | ||
| 124 | - | ||
| 125 | -使用 《[自动调优使用说明](../../user_guide/usage_auto_precision_tuning.md)》 且策略需**自动生成回退候选**时(`standing_high` 始终;`binary_fallback` 在未配置非空 `rollback_candidates` 时;`standing_high_with_experience` 委托 Standing High 执行),模型适配器须实现 **`ModelSlimPipelineInterfaceV1`**,执行脚本如下: | ||
| 126 | - | ||
| 127 | -```python | ||
| 128 | -from msmodelslim.model.interface_hub import ModelSlimPipelineInterfaceV1 | ||
| 129 | -``` | ||
| 130 | - | ||
| 131 | -该接口与 CLI **`msmodelslim analyze`** 及 `PipelineAnalysisService` 的模型协议相同,需实现 `init_model`、`handle_dataset`、`generate_model_visit`、`generate_model_forward` 等方法。调优策略内由 `PipelineAnalysisService` 调用上述方法,**不会在策略侧预先 `load_model`**。 | ||
| 132 | - | ||
| 133 | -| 调优策略 | 敏感层分析 | 额外接口 | | ||
| 134 | -|----------|------------|----------| | ||
| 135 | -| `standing_high` | 始终自动运行 | 无 | | ||
| 136 | -| `binary_fallback` | 配置了非空 `rollback_candidates` 时跳过 | 无 | | ||
| 137 | -| `standing_high_with_experience` | 委托 Standing High | **`StandingHighWithExperienceInterface`**(`load_model`,离群值抑制能力探测);**`ModelSlimPipelineInterfaceV1` 须单独继承** | | ||
| 138 | - | ||
| 139 | -详见 《[自动调优配置协议说明](../../user_guide/usage_auto_precision_tuning.md)》 及各策略算法文档。 | ||
| 140 | - | ||
| 141 | -## 量化自有模型 | ||
| 142 | - | ||
| 143 | -当完成模型适配器的编写与注册后,即可使用一键量化能力对自有模型进行量化。 | ||
| 144 | - | ||
| 145 | -### 创建W8A8动态量化Yaml配置文件 | ||
| 146 | - | ||
| 147 | -```yaml | ||
| 148 | -apiversion: modelslim_v1 | ||
| 149 | -spec: | ||
| 150 | - process: | ||
| 151 | - - type: "linear_quant" # 线性层量化 | ||
| 152 | - qconfig: | ||
| 153 | - act: # 激活值量化 | ||
| 154 | - scope: "per_token" # 动态量化 | ||
| 155 | - dtype: "int8" # 8比特整数量化 | ||
| 156 | - symmetric: True # 对称量化 | ||
| 157 | - method: "minmax" # 使用minmax算法 | ||
| 158 | - weight: # 权重量化 | ||
| 159 | - scope: "per_channel" # per_channel量化 | ||
| 160 | - dtype: "int8" # 8比特整数量化 | ||
| 161 | - symmetric: True # 对称量化 | ||
| 162 | - method: "minmax" # 使用minmax算法 | ||
| 163 | - include: [ "*" ] # 全局w8a8动态量化 | ||
| 164 | - exclude: [ "*down_proj*" ] # 回退down_proj层 | ||
| 165 | - | ||
| 166 | - save: | ||
| 167 | - - type: "ascendv1_saver" | ||
| 168 | - part_file_size: 4 # 每个safetensors权重文件最大4G | ||
| 169 | -``` | ||
| 170 | - | ||
| 171 | -### 运行一键量化 | ||
| 172 | - | ||
| 173 | -可通过如下命令完成自有模型量化,请注意`trust_remote_code`为`true`时可能执行浮点模型权重中的代码文件,请确保浮点模型来源安全可靠。其中 `${MODEL_PATH}` 为原始浮点权重路径,`${SAVE_PATH}` 为用户自定义的量化权重保存路径,`${MODEL_TYPE}` 为注册的模型名称,`${CONFIG_PATH}` 为YAML配置文件路径。 | ||
| 174 | - | ||
| 175 | -```bash | ||
| 176 | -msmodelslim quant --model_path ${MODEL_PATH} \ | ||
| 177 | - --save_path ${SAVE_PATH} \ | ||
| 178 | - --device npu \ | ||
| 179 | - --model_type ${MODEL_TYPE} \ | ||
| 180 | - --config ${CONFIG_PATH} \ | ||
| 181 | - --trust_remote_code false | ||
| 182 | -``` | ||
| 183 | - | ||
| 184 | -- 详细用法与参数说明请参阅:《[一键量化使用说明](../../user_guide/usage_quick_quantization.md)》 | ||
| @@ -1,1453 +0,0 @@ | |||
| 1 | -# 多模态生成模型接入指南 | ||
| 2 | - | ||
| 3 | -## 总体实现思路 | ||
| 4 | - | ||
| 5 | -### 核心目标 | ||
| 6 | - | ||
| 7 | -将自有文生视频/图生视频模型接入 msModelSlim 的量化管线,实现**校准数据 → 推理链路重放 → DiT 逐层量化**的端到端流程。 | ||
| 8 | - | ||
| 9 | -### 与传统 LLM 量化的关键差异 | ||
| 10 | - | ||
| 11 | -| 维度 | LLM 量化 | 多模态生成量化 | | ||
| 12 | -|------|---------|--------------| | ||
| 13 | -| 输入处理 | Token 序列 | 图文混合(图像路径 + Prompt) | | ||
| 14 | -| 主干网络 | Decoder-only Transformer | DiT(Diffusion Transformer) | | ||
| 15 | -| 网络结构 | 单一堆叠 | 单网络 / 双专家 / 多模块 | | ||
| 16 | -| 参数来源 | 直接可配 | 需桥接原推理仓复杂参数体系 | | ||
| 17 | -| 前向方式 | 自回归 | 扩散多步去噪(需重放完整链路) | | ||
| 18 | - | ||
| 19 | -### 接入原则 | ||
| 20 | - | ||
| 21 | -1. **配置优先于硬编码**:通过 `inference_config`(Pydantic 强校验)而非 `model_config`(字符串映射)定义参数。 | ||
| 22 | -2. **复用原仓逻辑**:桥接原推理仓的 `parse_args` + `_validate_args`,不重复实现参数校验。 | ||
| 23 | -3. **分层解耦**:基类管公共能力(参数桥接、缓存装配),子类管场景差异(样本校验、具体生成逻辑)。 | ||
| 24 | -4. **双路径兼容**:新模型实现 `MultimodalPipelineInterface`;主仓已接入模型保留 `LegacyMultimodalPipelineInterface`,由 `MultimodalSDModelslimV1QuantService` 按适配器类型自动分发。 | ||
| 25 | - | ||
| 26 | ---- | ||
| 27 | - | ||
| 28 | -## msModelSlim 架构与编排 | ||
| 29 | - | ||
| 30 | -### 量化服务双分支(必读) | ||
| 31 | - | ||
| 32 | -`MultimodalSDModelslimV1QuantService` 根据适配器实现的接口类型选择编排路径: | ||
| 33 | - | ||
| 34 | -| 路径 | 适配器接口 | 典型 `model_type` | 校准 dump | 量化调度 | | ||
| 35 | -|------|------------|-------------------|-----------|----------| | ||
| 36 | -| **重构** | `MultimodalPipelineInterface` | `Wan2.2-T2V-A14B`、`Wan2.2-I2V-A14B`、`Wan2.2-TI2V-5B`、`HunyuanVideo` | `prepare_calib_data` → `inference_dump_calib_data` | `get_expert_adapter` + `quantization_context` | | ||
| 37 | -| **Legacy** | `LegacyMultimodalPipelineInterface` | `Wan2_2` / `Wan2.2`(单体)、`wan2_1`、`flux1`、`qwen_image_edit` 等 | `run_calib_inference` | `apply_quantization` + 切换 `transformer` | | ||
| 38 | - | ||
| 39 | -重构路径下,`inference_config` 由 **`quant_config.validate_inference_config(adapter, sd_config)`** 统一校验(调用适配器的 `get_inference_config_class()`),适配器不再提供 `build_inference_config()`。 | ||
| 40 | - | ||
| 41 | -### 整体交互流程(重构路径) | ||
| 42 | - | ||
| 43 | -```mermaid | ||
| 44 | -sequenceDiagram | ||
| 45 | - participant User as 用户 YAML + Dataset | ||
| 46 | - participant QS as MultimodalSDModelslimV1QuantService | ||
| 47 | - participant Adapter as ModelAdapter | ||
| 48 | - participant Repo as 原推理仓 | ||
| 49 | - participant Runner as LayerWiseRunner | ||
| 50 | - | ||
| 51 | - User->>QS: 启动量化 (quant_config.yaml) | ||
| 52 | - QS->>QS: validate_inference_config(adapter, sd_config) | ||
| 53 | - Note over QS: adapter.get_inference_config_class().model_validate | ||
| 54 | - | ||
| 55 | - QS->>Adapter: configure_runtime(inference_config) | ||
| 56 | - Adapter->>Repo: _parse_args_from_* (sys.argv 桥接) | ||
| 57 | - Repo-->>Adapter: model_args | ||
| 58 | - | ||
| 59 | - QS->>Adapter: init_model(device) | ||
| 60 | - Adapter->>Repo: 加载 Pipeline / DiT | ||
| 61 | - Adapter->>Adapter: _setup_cache() [Hunyuan 等必需] | ||
| 62 | - | ||
| 63 | - QS->>Adapter: handle_dataset(raw_dataset) | ||
| 64 | - Adapter-->>QS: List[VlmCalibSample] | ||
| 65 | - | ||
| 66 | - QS->>Adapter: prepare_calib_data(models, dump_config, dataset, ...) | ||
| 67 | - Note over Adapter: 缓存 pth 或 inference_dump_calib_data | ||
| 68 | - Adapter-->>QS: calib_data per expert_name | ||
| 69 | - | ||
| 70 | - loop 每个 expert | ||
| 71 | - QS->>Adapter: get_expert_adapter(expert_name) | ||
| 72 | - QS->>Runner: LayerWiseRunner(expert_adapter) | ||
| 73 | - Runner->>Adapter: quantization_context + run(calib_data[expert]) | ||
| 74 | - end | ||
| 75 | -``` | ||
| 76 | - | ||
| 77 | -**多专家约束**:`init_model()` 返回的每个 expert 必须在 `calib_data` 中有对应 **key**;缺 key 时量化服务 **fail-fast** 抛 `SchemaValidateError`(`calib_data[expert]=None` 表示无 dump 数据的全动态量化,仍算有效 key)。**不支持**仅量化部分专家(例如只量化 `low_noise_model`)。 | ||
| 78 | - | ||
| 79 | -### Legacy 路径(主仓兼容) | ||
| 80 | - | ||
| 81 | -仍使用 `LegacyMultimodalPipelineInterface` 的模型(`wan2_1`、`flux1`、`qwen_image_edit`、`Wan2_2`/`Wan2.2` 单体入口等)走 `_quant_process_legacy`: | ||
| 82 | - | ||
| 83 | -1. `set_model_args`(`model_config` 字符串映射,非 Pydantic `inference_config`) | ||
| 84 | -2. `load_pipeline` → `run_calib_inference` | ||
| 85 | -3. `apply_quantization(quant_model_func)`,在回调内切换 `transformer` 并调用 LayerWise | ||
| 86 | - | ||
| 87 | -**新接入多模态生成模型应优先实现 `MultimodalPipelineInterface`**;Legacy 仅用于保持与主仓既有适配器行为一致。`wan2_2/model_adapter.py` + `config.ini` 中 `wan2_2 = Wan2_2, Wan2.2` 为旧入口,与场景化 `Wan2.2-T2V-A14B` 等重构入口并存。 | ||
| 88 | - | ||
| 89 | -**规划中的能力**:按专家独立 `process` 链(`expert_process`)尚未实现,当前所有专家共用 YAML `spec.process`。 | ||
| 90 | - | ||
| 91 | -### 核心类结构 | ||
| 92 | - | ||
| 93 | -```mermaid | ||
| 94 | -classDiagram | ||
| 95 | - direction TB | ||
| 96 | - | ||
| 97 | - class BaseModelAdapter { | ||
| 98 | - <<abstract>> | ||
| 99 | - +configure_runtime() | ||
| 100 | - +init_model() | ||
| 101 | - +generate_model_visit() | ||
| 102 | - +generate_model_forward() | ||
| 103 | - } | ||
| 104 | - | ||
| 105 | - class MultimodalPipelineInterface { | ||
| 106 | - <<interface>> | ||
| 107 | - +get_inference_config_class() | ||
| 108 | - +prepare_calib_data() | ||
| 109 | - +inference_dump_calib_data() | ||
| 110 | - +quantization_context() | ||
| 111 | - +get_expert_adapter() | ||
| 112 | - } | ||
| 113 | - | ||
| 114 | - class LegacyMultimodalPipelineInterface { | ||
| 115 | - <<interface>> | ||
| 116 | - +set_model_args() | ||
| 117 | - +load_pipeline() | ||
| 118 | - +run_calib_inference() | ||
| 119 | - +apply_quantization() | ||
| 120 | - } | ||
| 121 | - | ||
| 122 | - class ModelInfoInterface { | ||
| 123 | - <<interface>> | ||
| 124 | - +get_model_type() | ||
| 125 | - } | ||
| 126 | - | ||
| 127 | - class OnlineQuaRotInterface { | ||
| 128 | - <<interface>> | ||
| 129 | - +get_online_rotation_configs() | ||
| 130 | - } | ||
| 131 | - | ||
| 132 | - class FA3QuantAdapterInterface { | ||
| 133 | - <<interface>> | ||
| 134 | - +inject_fa3_placeholders() | ||
| 135 | - } | ||
| 136 | - | ||
| 137 | - BaseModelAdapter <|-- HunyuanVideoModelAdapter : extends | ||
| 138 | - BaseModelAdapter <|-- Wan2_2BaseModelAdapter : extends | ||
| 139 | - | ||
| 140 | - MultimodalPipelineInterface <|.. HunyuanVideoModelAdapter : implements | ||
| 141 | - ModelInfoInterface <|.. HunyuanVideoModelAdapter : implements | ||
| 142 | - OnlineQuaRotInterface <|.. HunyuanVideoModelAdapter : implements | ||
| 143 | - FA3QuantAdapterInterface <|.. HunyuanVideoModelAdapter : implements | ||
| 144 | - | ||
| 145 | - MultimodalPipelineInterface <|.. Wan2_2BaseModelAdapter : implements | ||
| 146 | - ModelInfoInterface <|.. Wan2_2BaseModelAdapter : implements | ||
| 147 | - OnlineQuaRotInterface <|.. Wan2_2BaseModelAdapter : implements | ||
| 148 | - FA3QuantAdapterInterface <|.. Wan2_2BaseModelAdapter : implements | ||
| 149 | - | ||
| 150 | - Wan2_2BaseModelAdapter <|-- Wan2_2T2VModelAdapter : extends | ||
| 151 | - Wan2_2BaseModelAdapter *-- Wan2_2ExpertSubAdapter : 组合 | ||
| 152 | - | ||
| 153 | - OnlineQuaRotInterface <|.. Wan2_2ExpertSubAdapter : implements | ||
| 154 | - FA3QuantAdapterInterface <|.. Wan2_2ExpertSubAdapter : implements | ||
| 155 | -``` | ||
| 156 | - | ||
| 157 | ->[!NOTE] | ||
| 158 | -> | ||
| 159 | ->- 两类场景的主适配器继承完全相同的 5 个基类/接口。 | ||
| 160 | ->- `Wan2_2ExpertSubAdapter` 是**组合关系**(非继承),由基类在分区 5 创建并持有。 | ||
| 161 | ->- 子适配器**独立实现** `OnlineQuaRotInterface` 和 `FA3QuantAdapterInterface`,供 `LayerWiseRunner` 按专家单独调度。 | ||
| 162 | - | ||
| 163 | -### 配置分层模型 | ||
| 164 | - | ||
| 165 | -```mermaid | ||
| 166 | -flowchart TD | ||
| 167 | - A[YAML spec.multimodal_sd_config] --> B[MultimodalSDConfig] | ||
| 168 | - B --> C{inference_config 与 model_extra.model_config} | ||
| 169 | - C -->|仅 inference_config| D[resolve_inference_raw 返回 dict] | ||
| 170 | - C -->|仅 model_config extra| E[告警后回退 Legacy dict] | ||
| 171 | - C -->|二者同时存在| F[SchemaValidateError] | ||
| 172 | - | ||
| 173 | - D --> G[validate_inference_config] | ||
| 174 | - G --> H[adapter.InferenceConfig Pydantic 实例] | ||
| 175 | - H --> I[configure_runtime] | ||
| 176 | - I --> J[argv 合并 + _parse_args_from_*] | ||
| 177 | - J --> K[model_args] | ||
| 178 | - K --> L[init_model / prepare_calib_data / 量化] | ||
| 179 | -``` | ||
| 180 | - | ||
| 181 | ->[!NOTE] | ||
| 182 | -> | ||
| 183 | ->`inference_config` 为 `MultimodalSDConfig` **声明字段**,由 Pydantic 解析到 `self.inference_config`,**不会**进入 `model_extra`;勿依赖「通过 extra 传 inference_config」。 | ||
| 184 | - | ||
| 185 | -### 代码分区规范(必读) | ||
| 186 | - | ||
| 187 | -两类场景的分区组织略有差异,必须严格遵循源码中的分区标记: | ||
| 188 | - | ||
| 189 | -**场景一:单网络 DiT 类型的多模态生成模型接入(以 HunyuanVideo 为例)—— 7 个分区** | ||
| 190 | - | ||
| 191 | -```text | ||
| 192 | -分区 1:公共流水线接口 # validate_calib_samples, handle_dataset, init_model, generate_model_visit/forward, enable_kv_cache | ||
| 193 | -分区 2:公共运行时配置 # get_inference_config_class, configure_runtime | ||
| 194 | -分区 3:公共校准执行 # prepare_calib_data, inference_dump_calib_data, quantization_context | ||
| 195 | -分区 4:运行时通用辅助 # _runtime_value(HunyuanVideo 无 _quantization_context_with_no_sync) | ||
| 196 | -分区 5:私有参数桥接 # _fixed_quant_runtime_overrides, _allowed_hyvideo_config_keys, _build_default_quant_cli, _namespace_to_argv, _parse_args_from_hyvideo | ||
| 197 | -分区 6:私有运行时与缓存装配 # _check_import_dependency, _setup_cache, _load_pipeline | ||
| 198 | -分区 7:量化扩展接口 # get_online_rotation_configs, inject_fa3_placeholders, _attach_attention_cache_to_blocks | ||
| 199 | -``` | ||
| 200 | - | ||
| 201 | -**场景二:双专家 DiT 类型的多模态生成模型接入(以 Wan2.2 为例)—— 8 个分区(基类)** | ||
| 202 | - | ||
| 203 | -```text | ||
| 204 | -分区 1:公共流水线接口 # validate_calib_samples, handle_dataset, init_model(抽象), generate_model_visit/forward, enable_kv_cache | ||
| 205 | -分区 2:公共运行时配置 # get_inference_config_class(子类), configure_runtime(基类) | ||
| 206 | -分区 3:公共校准执行 # prepare_calib_data, inference_dump_calib_data(抽象), quantization_context(抽象) | ||
| 207 | -分区 4:基类运行时通用辅助 # _runtime_value, _quantization_context_with_no_sync | ||
| 208 | -分区 5(不同点):私有专家子适配器装配 # _bind_expert_sub_adapters, _create_expert_sub_adapter | ||
| 209 | -分区 6:私有参数桥接 # _allowed_generate_config_keys, _build_default_generate_cli, _namespace_to_argv, _parse_args_from_generate | ||
| 210 | -分区 7:私有运行时与缓存装配 # _check_import_dependency, _init_logging, _load_pipeline, _setup_wan_dit_runtime, _setup_*_attention_cache | ||
| 211 | -分区 8:量化扩展接口 # get_online_rotation_configs, inject_fa3_placeholders, _attach_attention_cache_to_blocks | ||
| 212 | -``` | ||
| 213 | - | ||
| 214 | ->[!NOTE] | ||
| 215 | -> | ||
| 216 | ->- 分区 2 的 `configure_runtime` 在两场景中都位于**分区 2**;它调用的 argv/parse_args 辅助方法分别在 HunyuanVideo **分区 5**、Wan2.2 **分区 6**。 | ||
| 217 | ->- Wan2.2 **分区 5** 为双专家特有的子适配器装配,HunyuanVideo 无对应分区。 | ||
| 218 | ->- Wan2.2 基类 **分区 4** 额外提供 `_quantization_context_with_no_sync`,供子类 `quantization_context` 复用。 | ||
| 219 | - | ||
| 220 | ---- | ||
| 221 | - | ||
| 222 | -## 分步实现指南 | ||
| 223 | - | ||
| 224 | -### 前置准备 | ||
| 225 | - | ||
| 226 | -1. 确认原推理仓可正常运行浮点推理。 | ||
| 227 | -2. 准备校准数据集(图文对或纯文本列表)。 | ||
| 228 | -3. 确定 DiT 结构类型:**单网络** vs **双专家**。 | ||
| 229 | - | ||
| 230 | ---- | ||
| 231 | - | ||
| 232 | -### 场景一:单网络 DiT 类型的多模态生成模型接入(以 HunyuanVideo 为例) | ||
| 233 | - | ||
| 234 | -**适用特征**:单个 `transformer` 主干;`init_model` 返回 `{'': self.transformer}`。 | ||
| 235 | - | ||
| 236 | -**实现顺序**:按源码分区 1 → 7 依次实现(与上文「代码分区规范」一致)。 | ||
| 237 | - | ||
| 238 | -**接入新模型时的实现建议**: | ||
| 239 | - | ||
| 240 | -1. 先确认原推理仓 DiT block 的类名与 `forward` 签名(双流/单流是否都存在)。 | ||
| 241 | -2. QuaRot:只需在 attention 入口为 Q/K 提供可替换的 `q_rot`/`k_rot` 子模块路径。 | ||
| 242 | -3. FA3:在 Q/K/V 张量就绪后、进入 attention 算子前插入三个占位调用;勿改动扩散主循环逻辑。 | ||
| 243 | - | ||
| 244 | -**核心类**(示例代码均写在类体内): | ||
| 245 | - | ||
| 246 | -```python | ||
| 247 | -class HunyuanVideoModelAdapter( | ||
| 248 | - BaseModelAdapter, | ||
| 249 | - ModelInfoInterface, | ||
| 250 | - MultimodalPipelineInterface, | ||
| 251 | - FA3QuantAdapterInterface, | ||
| 252 | - OnlineQuaRotInterface, | ||
| 253 | -): | ||
| 254 | - ... | ||
| 255 | -``` | ||
| 256 | - | ||
| 257 | -#### Step 0:目录结构 | ||
| 258 | - | ||
| 259 | -```text | ||
| 260 | -msmodelslim/model/hunyuan_video/ | ||
| 261 | -├── __init__.py | ||
| 262 | -├── model_adapter.py # 主适配器(含分区 1~7) | ||
| 263 | -├── constants.py # DEFAULT_VIDEO_SIZE、HYVIDEO_CLI_LIST_FIELDS 等 | ||
| 264 | -└── loader.py # HunyuanVideoAdapterLoader | ||
| 265 | -``` | ||
| 266 | - | ||
| 267 | -#### Step 1:分区 1 —— 公共流水线接口 | ||
| 268 | - | ||
| 269 | -**职责**:数据集校验、模型加载入口、LayerWise 所需的 visit/forward 分段。 | ||
| 270 | - | ||
| 271 | -| 方法 | 说明 | | ||
| 272 | -|------|------| | ||
| 273 | -| `validate_calib_samples` | 当前仅 text-only,禁止 `image` | | ||
| 274 | -| `handle_dataset` | 转 `List[VlmCalibSample]` 并校验 | | ||
| 275 | -| `init_model` | 调用分区 6 的 `_load_pipeline` + `_setup_cache`,返回 `{'': transformer}` | | ||
| 276 | -| `generate_model_visit` | 按 `streamblock` 关键字逐层 visit | | ||
| 277 | -| `generate_model_forward` | 首层 hook 截获输入后逐层 yield | | ||
| 278 | - | ||
| 279 | -```python | ||
| 280 | -class HunyuanVideoModelAdapter( | ||
| 281 | - BaseModelAdapter, | ||
| 282 | - ModelInfoInterface, | ||
| 283 | - MultimodalPipelineInterface, | ||
| 284 | - FA3QuantAdapterInterface, | ||
| 285 | - OnlineQuaRotInterface, | ||
| 286 | -): | ||
| 287 | - """单网络 DiT 适配器(hunyuan_video/model_adapter.py)。""" | ||
| 288 | - | ||
| 289 | - _HYVIDEO_CONFIG_KEYS: ClassVar[Optional[frozenset[str]]] = None | ||
| 290 | - | ||
| 291 | - # ===== 分区 1:公共流水线接口 ===== | ||
| 292 | - | ||
| 293 | - def validate_calib_samples(self, samples: List[VlmCalibSample]) -> List[VlmCalibSample]: | ||
| 294 | - """ | ||
| 295 | - 校验校准样本列表,确保每个样本满足 HunyuanVideo T2V(文本转视频)的输入要求。 | ||
| 296 | - | ||
| 297 | - - 检查每个 sample 必须包含非空的 text 字段。 | ||
| 298 | - - 禁止包含 image 字段(HunyuanVideo 当前仅支持 text-only 输入)。 | ||
| 299 | - - 若不满足要求,则抛出 SchemaValidateError。 | ||
| 300 | - """ | ||
| 301 | - for idx, sample in enumerate(samples): | ||
| 302 | - # 检查文本字段必须为非空字符串 | ||
| 303 | - if not sample.text or not sample.text.strip(): | ||
| 304 | - raise SchemaValidateError( | ||
| 305 | - f"hunyuan_video sample[{idx}] requires non-empty text", | ||
| 306 | - action="Provide text in dataset entries (index.jsonl / VlmCalibSample.text)." | ||
| 307 | - ) | ||
| 308 | - # 校准阶段禁止传入 image 字段,HunyuanVideo 仅支持纯文本 | ||
| 309 | - if sample.image is not None: | ||
| 310 | - raise SchemaValidateError( | ||
| 311 | - f"hunyuan_video sample[{idx}] must not include image", | ||
| 312 | - action="HunyuanVideo T2V calibration is text-only; remove image from dataset." | ||
| 313 | - ) | ||
| 314 | - return samples | ||
| 315 | - | ||
| 316 | - def handle_dataset( | ||
| 317 | - self, | ||
| 318 | - dataset: Any, | ||
| 319 | - device: DeviceType = DeviceType.NPU, | ||
| 320 | - ) -> List[VlmCalibSample]: | ||
| 321 | - """ | ||
| 322 | - 统一将输入数据集转换为 List[VlmCalibSample] 并完成校验。 | ||
| 323 | - | ||
| 324 | - - 支持传入 None、单个 VlmCalibSample、或 VlmCalibSample 的列表/可迭代对象。 | ||
| 325 | - - 校验逻辑委托给 validate_calib_samples,确保所有样本字段符合要求。 | ||
| 326 | - - device 参数预留,当前未使用,便于接口一致性。 | ||
| 327 | - """ | ||
| 328 | - _ = device # 设备参数当前未用,仅作占位 | ||
| 329 | - if dataset is None: | ||
| 330 | - return [] # 若数据集为空,返回空列表 | ||
| 331 | - if isinstance(dataset, VlmCalibSample): | ||
| 332 | - # 若为单个校准样本,包装为单元素列表后校验 | ||
| 333 | - return self.validate_calib_samples([dataset]) | ||
| 334 | - # 否则假定为可迭代对象,转换为列表后校验 | ||
| 335 | - return self.validate_calib_samples(list(dataset)) | ||
| 336 | - | ||
| 337 | - def init_model(self, device: DeviceType = DeviceType.NPU) -> Dict[str, nn.Module]: | ||
| 338 | - """ | ||
| 339 | - 初始化模型,加载主推理 pipeline,并完成必要的 cache 设置。 | ||
| 340 | - - device 参数用于指定推理设备(默认为 NPU),目前主要用于接口一致性,内部未直接使用。 | ||
| 341 | - - 必须先调用 _load_pipeline(实现见分区 6),保证 transformer/pipeline 被正确加载。 | ||
| 342 | - - 随后必须调用 _setup_cache,这一步是因为推理仓要求每次加载模型后必须初始化 block 级 attention cache(并非 KV cache),否则部分推理流程无法正常运行,是 HunyuanVideo 推理框架的特殊要求。 | ||
| 343 | - - 返回值为 dict[str, nn.Module]。 | ||
| 344 | - """ | ||
| 345 | - self._load_pipeline() # 加载主推理 pipeline(见分区 6) | ||
| 346 | - self._setup_cache() # 初始化 cache 机制(见分区 6),此步骤为必要流程 | ||
| 347 | - return {'': self.transformer} # 主体模型 transformer,key 为''(空字符串) | ||
| 348 | - | ||
| 349 | - def generate_model_visit( | ||
| 350 | - self, | ||
| 351 | - model: torch.nn.Module, | ||
| 352 | - transformer_blocks: Optional[List[Tuple[str, torch.nn.Module]]] = None, | ||
| 353 | - ) -> Generator[ProcessRequest, Any, None]: | ||
| 354 | - """ | ||
| 355 | - 逐层遍历 transformer_blocks,按 LayerWise 语义按需 yield ProcessRequest, | ||
| 356 | - 用于外部流程可插桩(如量化、调试等)或定制处理。 | ||
| 357 | - - 内部默认按类名中包含 'streamblock' 筛选模块列表(与 generate_model_forward 内部一致) | ||
| 358 | - - 可支持外部自定义 transformer_blocks,暂无特殊需求时可直接使用默认参数 | ||
| 359 | - - 返回生成器,每层 yield ProcessRequest,封装当前块的信息与参数 | ||
| 360 | - """ | ||
| 361 | - return generated_decoder_layer_visit_func_with_keyword(model, keyword="streamblock") | ||
| 362 | - | ||
| 363 | - def generate_model_forward( | ||
| 364 | - self, | ||
| 365 | - model: torch.nn.Module, | ||
| 366 | - inputs: Any, | ||
| 367 | - ) -> Generator[ProcessRequest, Any, None]: | ||
| 368 | - """ | ||
| 369 | - 用于按 LayerWise 逐层截取输入与输出,支持推理链路重放、量化插桩等场景。 | ||
| 370 | - | ||
| 371 | - 通常的实现逻辑为: | ||
| 372 | - - 拦截模型的首层输入 | ||
| 373 | - - 按照 “streamblock” 关键字拆分 Transformer Block | ||
| 374 | - - 每经过一层,yield 出 ProcessRequest,交由上游控制是否中断、插桩或继续前向 | ||
| 375 | - - 直到所有层被遍历且前向结束 | ||
| 376 | - | ||
| 377 | - 返回的是一个生成器(Generator), | ||
| 378 | - 每次 yield 提供当前层的信息及中间输入/输出,便于外部流程逐层处理 | ||
| 379 | - """ | ||
| 380 | - pass | ||
| 381 | -``` | ||
| 382 | - | ||
| 383 | -#### Step 2:分区 2 —— 公共运行时配置 | ||
| 384 | - | ||
| 385 | -**职责**:声明 `HunyuanVideoInferenceConfig` 与 `get_inference_config_class()`;`configure_runtime` 将**已校验**的 `inference_config` 写入 `model_args`。 | ||
| 386 | -**校验入口**:`MultimodalSDModelslimV1QuantService` 在 `quant_process` 中调用 `validate_inference_config(adapter, sd_config)`(`quant_config.py`),内部执行 `get_inference_config_class().model_validate(raw_dict)`。 | ||
| 387 | -**注意**:`configure_runtime` 在分区 2,桥接方法在分区 5(`_build_default_quant_cli`、`_parse_args_from_hyvideo` 等)。 | ||
| 388 | - | ||
| 389 | -```python | ||
| 390 | -class HunyuanVideoModelAdapter( | ||
| 391 | - BaseModelAdapter, | ||
| 392 | - ModelInfoInterface, | ||
| 393 | - MultimodalPipelineInterface, | ||
| 394 | - FA3QuantAdapterInterface, | ||
| 395 | - OnlineQuaRotInterface, | ||
| 396 | -): | ||
| 397 | - # ===== 分区 2:公共运行时配置 ===== | ||
| 398 | - | ||
| 399 | - class HunyuanVideoInferenceConfig(BaseModel): | ||
| 400 | - model_config = ConfigDict(extra="forbid") | ||
| 401 | - model_resolution: Optional[Literal["540p", "720p"]] = "720p" | ||
| 402 | - video_size: Optional[Union[Tuple[int, int], List[int]]] = (720, 1280) | ||
| 403 | - infer_steps: Optional[int] = 50 | ||
| 404 | - # ... 与 hyvideo CLI 可对齐的字段 | ||
| 405 | - | ||
| 406 | - def get_inference_config_class(self): | ||
| 407 | - return self.HunyuanVideoInferenceConfig | ||
| 408 | - | ||
| 409 | - def configure_runtime(self, inference_config: HunyuanVideoInferenceConfig) -> None: | ||
| 410 | - """ | ||
| 411 | - 将 YAML/Dict 格式的 inference_config 校验并转为 argparse-compatible 的 model_args。 | ||
| 412 | - 步骤: | ||
| 413 | - 1. 对 inference_config 进行序列化(过滤 None 字段),仅允许受支持的字段(非法字段报错)。 | ||
| 414 | - 2. 构造基础 CLI argv(如模型路径、分辨率信息)。 | ||
| 415 | - 3. 将 config 字段和量化固定覆盖项加入 argv。 | ||
| 416 | - 4. 传递给 hyvideo 仓库的 parse_args,得到解析结果并存入 self.model_args。 | ||
| 417 | - """ | ||
| 418 | - override = inference_config.model_dump(exclude_none=True) | ||
| 419 | - allowed_attrs = self._allowed_hyvideo_config_keys() # 分区 5 | ||
| 420 | - # ... 非法字段校验 | ||
| 421 | - argv = self._build_default_quant_cli() # 分区 5 | ||
| 422 | - argv.extend(self._namespace_to_argv(override)) # 分区 5 | ||
| 423 | - argv.extend(self._namespace_to_argv(self._fixed_quant_runtime_overrides())) | ||
| 424 | - self.model_args = self._parse_args_from_hyvideo(argv) # 分区 5 | ||
| 425 | -``` | ||
| 426 | - | ||
| 427 | -#### Step 3:分区 3 —— 公共校准执行 | ||
| 428 | - | ||
| 429 | -**职责**:`prepare_calib_data` 负责 pth 缓存与 dump 调度;`inference_dump_calib_data` 做浮点推理重放;`quantization_context` 提供量化时的 `autocast/no_grad` 等上下文。 | ||
| 430 | - | ||
| 431 | -```python | ||
| 432 | -# ===== 分区 3:公共校准执行 ===== | ||
| 433 | - | ||
| 434 | -def prepare_calib_data(self, models, dump_config, save_path, dataset, inference_config): | ||
| 435 | - """ | ||
| 436 | - 按 expert_name 构造 calib_data_<task>_<expert>.pth 路径; | ||
| 437 | - enable_dump 时调用 inference_dump_calib_data 生成缓存,否则加载已有 pth。 | ||
| 438 | - 单 DiT 时 models 仅含 key ''。 | ||
| 439 | - """ | ||
| 440 | - ... | ||
| 441 | - | ||
| 442 | -def inference_dump_calib_data(self, dataset=None, inference_config=None): | ||
| 443 | - """ | ||
| 444 | - 执行浮点模型推理以导出校准数据,用于后续量化步骤。 | ||
| 445 | - Args: | ||
| 446 | - dataset: 可迭代的校准样本集合,每个样本应包含 text 等字段。 | ||
| 447 | - inference_config: 推理配置对象或字典,包含推理过程所需的参数。 | ||
| 448 | - 流程: | ||
| 449 | - - 遍历每个样本,统一通过 _runtime_value 动态获取配置参数(支持优先使用 inference_config, 回落到 model_args)。 | ||
| 450 | - - 按样本内容及推理参数调用 hunyuan_video_sampler.predict,生成用于量化的校准数据。 | ||
| 451 | - """ | ||
| 452 | - for sample in tqdm(dataset): | ||
| 453 | - seed = self._runtime_value(inference_config, "seed") # 分区 4 | ||
| 454 | - self.hunyuan_video_sampler.predict( | ||
| 455 | - prompt=sample.text, | ||
| 456 | - height=video_size[0], | ||
| 457 | - # ... 其余参数均经 _runtime_value 取值 | ||
| 458 | - ) | ||
| 459 | - | ||
| 460 | -def quantization_context(self): | ||
| 461 | - """ | ||
| 462 | - 量化相关上下文环境,通常组合如下特性: | ||
| 463 | - - amp.autocast 自动混合精度(节省显存与加速) | ||
| 464 | - - torch.no_grad 禁用梯度计算(节省内存和提升推理效率) | ||
| 465 | - - 部分模块 device 切换(如将 blocks on CPU, 其余 on NPU) | ||
| 466 | - 用于量化模型推理、校准数据收集等场景。 | ||
| 467 | - """ | ||
| 468 | - # amp.autocast + no_grad + 模块 device 切换 | ||
| 469 | - ... | ||
| 470 | -``` | ||
| 471 | - | ||
| 472 | -#### Step 4:分区 4 —— 运行时通用辅助 | ||
| 473 | - | ||
| 474 | -**职责**:校准/推理执行期统一取值,避免在多处重复 `getattr(inference_config, ...) or getattr(model_args, ...)`。 | ||
| 475 | - | ||
| 476 | -```python | ||
| 477 | -# ===== 分区 4:运行时通用辅助 ===== | ||
| 478 | - | ||
| 479 | -def _runtime_value( | ||
| 480 | - self, | ||
| 481 | - inference_config: Optional[Union[BaseModel, Dict[str, Any]]], | ||
| 482 | - name: str, | ||
| 483 | -) -> Any: | ||
| 484 | - """ | ||
| 485 | - 推理执行期统一取值:优先 inference_config(Pydantic 或 dict),否则 model_args。 | ||
| 486 | - dict 使用 .get(name);None 时仅回退 model_args。 | ||
| 487 | - """ | ||
| 488 | - if inference_config is not None: | ||
| 489 | - if isinstance(inference_config, dict): | ||
| 490 | - val = inference_config.get(name) | ||
| 491 | - else: | ||
| 492 | - val = getattr(inference_config, name, None) | ||
| 493 | - if val is not None: | ||
| 494 | - return val | ||
| 495 | - return getattr(self.model_args, name, None) | ||
| 496 | -``` | ||
| 497 | - | ||
| 498 | -#### Step 5:分区 5 —— 私有参数桥接(配置与解析) | ||
| 499 | - | ||
| 500 | -**职责**:仅包含「把 dict/InferenceConfig 转成 argv 并调用原仓 `parse_args`」的私有方法。 | ||
| 501 | - | ||
| 502 | -| 方法 | 说明 | | ||
| 503 | -|------|------| | ||
| 504 | -| `_fixed_quant_runtime_overrides` | 量化固定覆盖(关并行/cache 等) | | ||
| 505 | -| `_allowed_hyvideo_config_keys` | 懒探测 hyvideo 合法字段 | | ||
| 506 | -| `_build_default_quant_cli` | 满足 resolution/size 约束的最小 CLI | | ||
| 507 | -| `_namespace_to_argv` | dict → argv(bool/list 特殊处理) | | ||
| 508 | -| `_parse_args_from_hyvideo` | 临时改写 `sys.argv` 调 `hyvideo.config.parse_args` | | ||
| 509 | - | ||
| 510 | -```python | ||
| 511 | -# ===== 分区 5:私有参数桥接(配置与解析) ===== | ||
| 512 | -# 由分区 2 的 configure_runtime() 调用;勿把 configure_runtime 写在本分区。 | ||
| 513 | - | ||
| 514 | -@staticmethod | ||
| 515 | -def _fixed_quant_runtime_overrides() -> Dict[str, Any]: | ||
| 516 | - """ | ||
| 517 | - 量化校准时强制写入 parse_args 的覆盖项。 | ||
| 518 | - | ||
| 519 | - 关闭分布式并行、VAE 并行及各类 cache 优化,避免量化路径受训练/部署态配置干扰。 | ||
| 520 | - DiT block 级 cache 由适配器在分区 6 的 _setup_cache() 单独装配。 | ||
| 521 | - """ | ||
| 522 | - return { | ||
| 523 | - "ulysses_degree": 1, | ||
| 524 | - "ring_degree": 1, | ||
| 525 | - "vae_parallel": False, | ||
| 526 | - "use_cache": False, | ||
| 527 | - "use_cache_double": False, | ||
| 528 | - "use_attentioncache": False, | ||
| 529 | - } | ||
| 530 | - | ||
| 531 | - | ||
| 532 | -def _allowed_hyvideo_config_keys(self) -> frozenset[str]: | ||
| 533 | - """ | ||
| 534 | - 返回 hyvideo.config.parse_args 支持的 inference_config 字段名集合。 | ||
| 535 | - | ||
| 536 | - 进程内仅探测一次(类变量 _HYVIDEO_CONFIG_KEYS 缓存),避免每次量化重复 parse。 | ||
| 537 | - 用于 configure_runtime 中校验 YAML 是否包含非法字段。 | ||
| 538 | - """ | ||
| 539 | - cls = type(self) | ||
| 540 | - if cls._HYVIDEO_CONFIG_KEYS is None: | ||
| 541 | - probe = self._parse_args_from_hyvideo(self._build_default_quant_cli()) | ||
| 542 | - cls._HYVIDEO_CONFIG_KEYS = frozenset(vars(probe).keys()) | ||
| 543 | - return cls._HYVIDEO_CONFIG_KEYS | ||
| 544 | - | ||
| 545 | - | ||
| 546 | -def _build_default_quant_cli(self) -> List[str]: | ||
| 547 | - """ | ||
| 548 | - 构造 configure_runtime 合并 YAML 时使用的最小 argv。 | ||
| 549 | - | ||
| 550 | - 须满足 hyvideo 对 model_resolution / video_size 的断言(见 constants.py 默认值)。 | ||
| 551 | - 权重路径等必填项在此补齐,保证单次 parse_args 即可通过原仓校验。 | ||
| 552 | - """ | ||
| 553 | - model_base = str(self.model_path) | ||
| 554 | - h, w = DEFAULT_VIDEO_SIZE | ||
| 555 | - return [ | ||
| 556 | - "--model-base", model_base, | ||
| 557 | - "--prompt", PLACEHOLDER_PROMPT, | ||
| 558 | - "--model-resolution", DEFAULT_MODEL_RESOLUTION, | ||
| 559 | - "--video-size", str(h), str(w), | ||
| 560 | - "--dit-weight", str(Path(model_base).joinpath(*DIT_WEIGHT_REL)), | ||
| 561 | - "--vae-path", str(Path(model_base).joinpath(*VAE_PATH_REL)), | ||
| 562 | - "--text-encoder-path", str(Path(model_base).joinpath(*TEXT_ENCODER_PATH_REL)), | ||
| 563 | - "--text-encoder-2-path", str(Path(model_base).joinpath(*TEXT_ENCODER_2_PATH_REL)), | ||
| 564 | - ] | ||
| 565 | - | ||
| 566 | - | ||
| 567 | -@staticmethod | ||
| 568 | -def _namespace_to_argv(namespace_dict: Dict[str, Any]) -> List[str]: | ||
| 569 | - """ | ||
| 570 | - 将 Namespace 风格的 dict 转为 CLI 片段列表,供 _parse_args_from_hyvideo 使用。 | ||
| 571 | - | ||
| 572 | - 约定: | ||
| 573 | - - None:跳过(走 argparse 默认) | ||
| 574 | - - bool:仅 True 时追加 flag(store_true 语义;False 不传参) | ||
| 575 | - - list/tuple:仅 HYVIDEO_CLI_LIST_FIELDS(如 video_size)展开为 nargs="+" | ||
| 576 | - - dict:跳过 | ||
| 577 | - """ | ||
| 578 | - argv: List[str] = [] | ||
| 579 | - for key, val in namespace_dict.items(): | ||
| 580 | - if val is None: | ||
| 581 | - continue | ||
| 582 | - flag = "--" + key.replace("_", "-") | ||
| 583 | - if isinstance(val, dict): | ||
| 584 | - continue | ||
| 585 | - if isinstance(val, bool): | ||
| 586 | - if val: | ||
| 587 | - argv.append(flag) | ||
| 588 | - continue | ||
| 589 | - if isinstance(val, (list, tuple)): | ||
| 590 | - if key in HYVIDEO_CLI_LIST_FIELDS: | ||
| 591 | - argv.append(flag) | ||
| 592 | - argv.extend(str(v) for v in val) | ||
| 593 | - continue | ||
| 594 | - argv.extend([flag, str(val)]) | ||
| 595 | - return argv | ||
| 596 | - | ||
| 597 | - | ||
| 598 | -def _parse_args_from_hyvideo(self, cli_args: List[str]): | ||
| 599 | - """ | ||
| 600 | - 调用 hyvideo.config.parse_args(含 sanity_check 与任务相关 assert)。 | ||
| 601 | - | ||
| 602 | - 通过临时改写 sys.argv 模拟命令行;finally 中必须恢复,避免污染其他模块。 | ||
| 603 | - 注意:parse_args 的 namespace= 参数语义是“预填充对象”,不能直接传入 CLI 列表。 | ||
| 604 | - """ | ||
| 605 | - from hyvideo.config import parse_args | ||
| 606 | - | ||
| 607 | - original_argv = sys.argv | ||
| 608 | - try: | ||
| 609 | - sys.argv = ["sample_video.py", *cli_args] | ||
| 610 | - return parse_args() | ||
| 611 | - finally: | ||
| 612 | - sys.argv = original_argv | ||
| 613 | -``` | ||
| 614 | - | ||
| 615 | -#### Step 6:分区 6 —— 私有运行时与缓存装配 | ||
| 616 | - | ||
| 617 | -**职责**:加载原推理仓 Pipeline/Sampler;为 DiT block 注入 `CacheAgent`(原仓 `forward` 会调用 `self.cache.apply()`,与 `use_cache` 开关无关)。 | ||
| 618 | - | ||
| 619 | -```python | ||
| 620 | -# ===== 分区 6:私有运行时与缓存装配 ===== | ||
| 621 | - | ||
| 622 | -def _load_pipeline(self): | ||
| 623 | - """ | ||
| 624 | - 加载推理流水线,包括初始化 HunyuanVideoSampler 和 transformer。 | ||
| 625 | - """ | ||
| 626 | - self.hunyuan_video_sampler = HunyuanVideoSampler(...) | ||
| 627 | - self.transformer = self.hunyuan_video_sampler.pipeline.transformer | ||
| 628 | - | ||
| 629 | -def _setup_cache(self): | ||
| 630 | - """ | ||
| 631 | - 为每个 block 挂载 CacheAgent,使其与 sample_video.py 保持一致。 | ||
| 632 | - """ | ||
| 633 | - pass | ||
| 634 | -``` | ||
| 635 | - | ||
| 636 | -#### Step 7:分区 7 —— 量化扩展接口 | ||
| 637 | - | ||
| 638 | -**职责**:实现 `OnlineQuaRotInterface`、`FA3QuantAdapterInterface`;由量化服务在 LayerWise 流程中按配置调用。 | ||
| 639 | -**何时需要**:YAML `process` 中启用 Online QuaRot / FA3 相关算子时;未启用可暂不实现。 | ||
| 640 | - | ||
| 641 | -| 方法 | 接口 | 说明 | | ||
| 642 | -|------|------|------| | ||
| 643 | -| `get_online_rotation_configs` | `OnlineQuaRotInterface` | 为 DiT block 注册 `q_rot`/`k_rot` 并返回旋转配置 | | ||
| 644 | -| `inject_fa3_placeholders` | `FA3QuantAdapterInterface` | 注入 `fa3_q/k/v` 占位并包裹 block 的 `forward` | | ||
| 645 | - | ||
| 646 | -```python | ||
| 647 | -# ===== 分区 7:量化扩展接口 ===== | ||
| 648 | - | ||
| 649 | -# ----- OnlineQuaRotInterface ----- | ||
| 650 | - | ||
| 651 | -def get_online_rotation_configs(self, model: Optional[nn.Module] = None): | ||
| 652 | - """ | ||
| 653 | - 返回在线旋转配置:对每个目标 block 的 q_rot、k_rot 配置 Hadamard 旋转。 | ||
| 654 | - | ||
| 655 | - Args: | ||
| 656 | - model: 待量化的 DiT(通常为 self.transformer)。若提供,会先在 block 上 | ||
| 657 | - register_module('q_rot'/'k_rot', nn.Identity()) 作为旋转挂载点。 | ||
| 658 | - | ||
| 659 | - Returns: | ||
| 660 | - Dict[str, RotationConfig]: 键为模块路径(如 "blocks.0.q_rot"),值为旋转参数。 | ||
| 661 | - | ||
| 662 | - 目标 block 类型(与 hyvideo DiT 结构一致): | ||
| 663 | - - MMDoubleStreamBlock(双流:img + txt) | ||
| 664 | - - MMSingleStreamBlock(单流) | ||
| 665 | - """ | ||
| 666 | - pass | ||
| 667 | - | ||
| 668 | - | ||
| 669 | -# ----- FA3QuantAdapterInterface ----- | ||
| 670 | - | ||
| 671 | -def inject_fa3_placeholders( | ||
| 672 | - self, | ||
| 673 | - root_name: str, | ||
| 674 | - root_module: nn.Module, | ||
| 675 | - should_inject: Callable[[str], bool], | ||
| 676 | -) -> None: | ||
| 677 | - """ | ||
| 678 | - 为 HunyuanVideo DiT block 注入 FA3 量化占位,并包裹 forward 在 attention 前调用占位。 | ||
| 679 | - | ||
| 680 | - Args: | ||
| 681 | - root_name: 当前量化子树根路径(LayerWiseRunner 传入)。 | ||
| 682 | - root_module: 待处理的 nn.Module(通常为 transformer)。 | ||
| 683 | - should_inject: 按全路径名过滤是否注入(支持 include/exclude 策略)。 | ||
| 684 | - | ||
| 685 | - 流程概要: | ||
| 686 | - 1. 遍历 MMDoubleStreamBlock / MMSingleStreamBlock | ||
| 687 | - 2. set_submodule 注入 fa3_q、fa3_k、fa3_v(FA3QuantPlaceHolder) | ||
| 688 | - 3. 包裹 forward:在 Q/K/V cat 之后、attention 之前依次调用 | ||
| 689 | - q_rot/k_rot(若存在)→ fa3_q/fa3_k/fa3_v | ||
| 690 | - 4. forward 其余逻辑与原仓 block.forward 一致(需从原模块 import 辅助函数) | ||
| 691 | - | ||
| 692 | - 注意: | ||
| 693 | - - 使用 `module.forward = new_forward.__get__(module, module.__class__)` 绑定实例方法 | ||
| 694 | - - 完整 forward 体较长,实现时对照 hyvideo 对应 block 的 forward 复制并插入占位调用 | ||
| 695 | - - 推理仓 `parse_args` 暂不支持 `args=cli_list`,须通过临时改写 `sys.argv`(finally 恢复) | ||
| 696 | - """ | ||
| 697 | - pass | ||
| 698 | -``` | ||
| 699 | - | ||
| 700 | ---- | ||
| 701 | - | ||
| 702 | -### 场景二:双专家 DiT 类型的多模态生成模型接入(以 Wan2.2 为例) | ||
| 703 | - | ||
| 704 | -**适用特征**:`low_noise_model` + `high_noise_model` 两个 DiT 专家;`init_model` 返回 `{"low_noise_model": ..., "high_noise_model": ...}`。 | ||
| 705 | -**代码分工**:`base_model_adapter.py` 实现分区 1~8 的公共能力;`t2v/`、`i2v/`、`ti2v/` 各子目录下的 `model_adapter.py` 补充分区 1~3 的场景差异;`expert_sub_adapter.py` 供 LayerWiseRunner **按专家**调度量化。 | ||
| 706 | - | ||
| 707 | -**实现顺序**:基类按分区 1 → 8;子类在对应 Step 中覆盖标注的方法。 | ||
| 708 | - | ||
| 709 | -**接入新模型时的实现建议**: | ||
| 710 | - | ||
| 711 | -1. 先为每个 `scene_task` 建子类 + `config.ini` 的 `model_type`,勿在 YAML 用 `task` 切换场景。 | ||
| 712 | -2. `DEFAULT_SIZE` / `EXAMPLE_PROMPT` 须与原仓 `WAN_CONFIGS`、`SUPPORTED_SIZES` 一致,否则 `_validate_args` 失败。 | ||
| 713 | -3. QuaRot/FA3 在基类分区 8 实现一次;专家子适配器只做委托,并**显式继承**扩展接口。 | ||
| 714 | - | ||
| 715 | -**接入新模型时的差异要点**: | ||
| 716 | - | ||
| 717 | -1. `scene_task` 用子类 `ClassVar` 固定,与 `config.ini` 的 `model_type` 一一对应,勿在 YAML 里切换 task。 | ||
| 718 | -2. 双专家必须在 `init_model` 后调用 `_bind_expert_sub_adapters`,并保证 `get_expert_adapter` 能按名取到子适配器。 | ||
| 719 | -3. TI2V:`image` 可选;无图时 `_generate_video` 走 T2V 分支,有图走 I2V 分支(与推理仓默认行为一致)。 | ||
| 720 | - | ||
| 721 | -**核心类**: | ||
| 722 | - | ||
| 723 | -| 类 | 文件 | 说明 | | ||
| 724 | -|----|------|------| | ||
| 725 | -| `Wan2_2BaseModelAdapter` | `base_model_adapter.py` | 分区 1~8 公共逻辑 | | ||
| 726 | -| `Wan2_2T2VModelAdapter` 等 | `t2v/model_adapter.py` 等 | 场景子类,固定 `scene_task` | | ||
| 727 | -| `Wan2_2ExpertSubAdapter` | `expert_sub_adapter.py` | 单专家量化代理(非 BaseModelAdapter 子类) | | ||
| 728 | - | ||
| 729 | -#### Step 0:目录结构 | ||
| 730 | - | ||
| 731 | -```text | ||
| 732 | -msmodelslim/model/wan2_2/ | ||
| 733 | -├── base_model_adapter.py # 分区 1~8(基类,不可直接实例化) | ||
| 734 | -├── expert_sub_adapter.py # 专家子适配器(非 BaseModelAdapter 子类) | ||
| 735 | -├── constants.py # DEFAULT_SIZE、EXAMPLE_PROMPT、TASK_TYPES | ||
| 736 | -├── model_adapter.py # Legacy 单体适配器(LegacyMultimodalPipelineInterface,model_type=Wan2_2/Wan2.2) | ||
| 737 | -├── loader.py # Wan2_2AdapterLoader(Legacy 入口,config.ini wan2_2) | ||
| 738 | -├── t2v/ | ||
| 739 | -│ ├── model_adapter.py # 场景子类:T2V(scene_task=t2v-A14B) | ||
| 740 | -│ └── loader.py # Wan2_2T2VAdapterLoader | ||
| 741 | -├── i2v/ | ||
| 742 | -│ ├── model_adapter.py # 场景子类:I2V | ||
| 743 | -│ └── loader.py # Wan2_2I2VAdapterLoader | ||
| 744 | -└── ti2v/ | ||
| 745 | - ├── model_adapter.py # 场景子类:TI2V(image 可选,无图走 T2V) | ||
| 746 | - └── loader.py # Wan2_2TI2VAdapterLoader | ||
| 747 | -``` | ||
| 748 | - | ||
| 749 | -#### Step 1:分区 1 —— 公共流水线接口(基类 + 子类) | ||
| 750 | - | ||
| 751 | -**基类职责**:提供通用的 `handle_dataset`、`generate_model_visit/forward`(关键字 `attentionblock`)、`get_expert_adapter`、`prepare_calib_data`;`init_model` / `get_inference_config_class` 由子类实现。 | ||
| 752 | - | ||
| 753 | -**子类职责**:`validate_calib_samples`、`_build_wan_pipeline`、`init_model`、`_generate_video`(场景差异集中在此)。 | ||
| 754 | - | ||
| 755 | -| 方法 | 所在 | 说明 | | ||
| 756 | -|------|------|------| | ||
| 757 | -| `validate_calib_samples` | 子类 | T2V 禁图 / I2V 强制图 / TI2V 可选图 | | ||
| 758 | -| `handle_dataset` | 基类 | 转 `List[VlmCalibSample]` 并委托子类 `validate_calib_samples` | | ||
| 759 | -| `init_model` | 子类 | `_load_pipeline`(分区 7)→ `_bind_expert_sub_adapters`(分区 5) | | ||
| 760 | -| `generate_model_visit` | 基类 | 按类名含 `attentionblock` 的模块逐层 visit | | ||
| 761 | -| `generate_model_forward` | 基类 | 首层 pre_hook 截获输入后,按 attentionblock 逐层 yield | | ||
| 762 | -| `get_expert_adapter` | 基类 | 按专家名返回子适配器;T2V/I2V 未绑定则 `InvalidModelError`;TI2V 仅 `''` 未绑定时回退 `self` | | ||
| 763 | - | ||
| 764 | -```python | ||
| 765 | -class Wan2_2BaseModelAdapter( | ||
| 766 | - BaseModelAdapter, | ||
| 767 | - ModelInfoInterface, | ||
| 768 | - MultimodalPipelineInterface, | ||
| 769 | - FA3QuantAdapterInterface, | ||
| 770 | - OnlineQuaRotInterface, | ||
| 771 | -): | ||
| 772 | - """双专家 DiT 基类(wan2_2/base_model_adapter.py);须通过 T2V/I2V/TI2V 子类实例化。""" | ||
| 773 | - | ||
| 774 | - scene_task: ClassVar[str] = "" | ||
| 775 | - _GENERATE_CONFIG_KEYS: ClassVar[Optional[frozenset[str]]] = None | ||
| 776 | - | ||
| 777 | - # ===== 分区 1:公共流水线接口 ===== | ||
| 778 | - | ||
| 779 | - def validate_calib_samples(self, samples: List[VlmCalibSample]) -> List[VlmCalibSample]: | ||
| 780 | - """ | ||
| 781 | - 校验校准样本(基类默认透传;由 T2V/I2V/TI2V 子类覆盖)。 | ||
| 782 | - | ||
| 783 | - - T2V:必须 text,禁止 image | ||
| 784 | - - I2V:必须 text + image | ||
| 785 | - - TI2V:必须 text,image 可选(无图时走 T2V 推理分支) | ||
| 786 | - """ | ||
| 787 | - return samples | ||
| 788 | - | ||
| 789 | - def handle_dataset( | ||
| 790 | - self, | ||
| 791 | - dataset: Any, | ||
| 792 | - device: DeviceType = DeviceType.NPU, | ||
| 793 | - ) -> List[VlmCalibSample]: | ||
| 794 | - """ | ||
| 795 | - 统一将输入数据集转换为 List[VlmCalibSample] 并完成校验。 | ||
| 796 | - | ||
| 797 | - - dump 前仅做场景校验,不执行模型 forward。 | ||
| 798 | - - 支持传入 None、单个 VlmCalibSample、或可迭代对象。 | ||
| 799 | - - 校验逻辑委托给子类 validate_calib_samples(T2V/I2V/TI2V 规则不同)。 | ||
| 800 | - - device 参数预留,当前未使用,便于接口一致性。 | ||
| 801 | - """ | ||
| 802 | - _ = device | ||
| 803 | - if dataset is None: | ||
| 804 | - return [] | ||
| 805 | - if isinstance(dataset, VlmCalibSample): | ||
| 806 | - return self.validate_calib_samples([dataset]) | ||
| 807 | - return self.validate_calib_samples(list(dataset)) | ||
| 808 | - | ||
| 809 | - def init_model(self, device: DeviceType = DeviceType.NPU) -> Dict[str, nn.Module]: | ||
| 810 | - """基类抛出 NotImplementedError;由场景子类实现并返回 low/high 专家 dict。""" | ||
| 811 | - raise NotImplementedError( | ||
| 812 | - f"{type(self).__name__} must implement init_model() for its Wan2.2 task.", | ||
| 813 | - ) | ||
| 814 | - | ||
| 815 | - def generate_model_forward( | ||
| 816 | - self, | ||
| 817 | - model: torch.nn.Module, | ||
| 818 | - inputs: Any, | ||
| 819 | - ) -> Generator[ProcessRequest, Any, None]: | ||
| 820 | - """ | ||
| 821 | - 按 LayerWise 逐层截取输入与输出,供量化插桩与校准重放。 | ||
| 822 | - | ||
| 823 | - 实现要点(与 HunyuanVideo 类似,关键字不同): | ||
| 824 | - - 按类名含 attentionblock 筛选 Wan DiT block 列表 | ||
| 825 | - - 在首个 block 注册 forward_pre_hook,截获首层 (args, kwargs) 后抛出 | ||
| 826 | - TransformersForwardBreak,避免整网前向 | ||
| 827 | - - 将首层输入 to_device('cpu') 后,逐 block yield ProcessRequest(name, block, args, kwargs) | ||
| 828 | - - 每层用上一 block 的 hidden_states 作为下一层 args | ||
| 829 | - - 分布式场景下首层截获后 dist.barrier() 同步 | ||
| 830 | - | ||
| 831 | - 注意:LayerWiseRunner 对 low_noise_model / high_noise_model 分别调用时, | ||
| 832 | - model 为单个专家 DiT;关键字须与原仓 block 类名一致(非 streamblock)。 | ||
| 833 | - """ | ||
| 834 | - pass # 完整实现见 base_model_adapter.py | ||
| 835 | - | ||
| 836 | - def generate_model_visit( | ||
| 837 | - self, | ||
| 838 | - model: torch.nn.Module, | ||
| 839 | - transformer_blocks: Optional[List[Tuple[str, torch.nn.Module]]] = None, | ||
| 840 | - ) -> Generator[ProcessRequest, Any, None]: | ||
| 841 | - """ | ||
| 842 | - 逐层 visit DiT block,按 LayerWise 语义 yield ProcessRequest。 | ||
| 843 | - | ||
| 844 | - - 内部默认按类名含 attentionblock 筛选(与 generate_model_forward 一致) | ||
| 845 | - - 可传入自定义 transformer_blocks;一般使用默认即可 | ||
| 846 | - - 专家子适配器通过 __getattr__ 委托本方法,访问同一套 visit 逻辑 | ||
| 847 | - """ | ||
| 848 | - return generated_decoder_layer_visit_func_with_keyword(model, keyword="attentionblock") | ||
| 849 | - | ||
| 850 | - def get_expert_adapter(self, expert_name: str): | ||
| 851 | - """ | ||
| 852 | - LayerWiseRunner 按专家名(low_noise_model / high_noise_model)取子适配器。 | ||
| 853 | - | ||
| 854 | - init_model 中 _bind_expert_sub_adapters 写入的 key 须与 QuantService 传入名一致。 | ||
| 855 | - - T2V / I2V(双专家):未绑定 → InvalidModelError | ||
| 856 | - - TI2V(单 DiT):仅 expert_name=='' 且未绑定时回退 self | ||
| 857 | - """ | ||
| 858 | - ... | ||
| 859 | - | ||
| 860 | - | ||
| 861 | -# t2v/model_adapter.py —— 场景子类示例 | ||
| 862 | - | ||
| 863 | -class Wan2_2T2VModelAdapter(Wan2_2BaseModelAdapter): | ||
| 864 | - scene_task = "t2v-A14B" # 与 config.ini 的 model_type 绑定,不由 YAML 切换 | ||
| 865 | - | ||
| 866 | - def validate_calib_samples(self, samples: List[VlmCalibSample]) -> List[VlmCalibSample]: | ||
| 867 | - """ | ||
| 868 | - 校验 T2V 校准样本。 | ||
| 869 | - | ||
| 870 | - - 每个 sample 必须包含非空 text | ||
| 871 | - - 禁止包含 image(校准图不走 inference_config,T2V 亦不从 dataset 读图) | ||
| 872 | - """ | ||
| 873 | - for idx, sample in enumerate(samples): | ||
| 874 | - if not sample.text or not sample.text.strip(): | ||
| 875 | - raise SchemaValidateError( | ||
| 876 | - f"wan2_2 t2v sample[{idx}] requires non-empty text", | ||
| 877 | - action="Provide text in dataset entries (index.jsonl / VlmCalibSample.text).", | ||
| 878 | - ) | ||
| 879 | - if sample.image is not None: | ||
| 880 | - raise SchemaValidateError( | ||
| 881 | - f"wan2_2 t2v sample[{idx}] must not include image", | ||
| 882 | - action="Remove image field from dataset entries for T2V.", | ||
| 883 | - ) | ||
| 884 | - return samples | ||
| 885 | - | ||
| 886 | - def init_model(self, device: DeviceType = DeviceType.NPU) -> Dict[str, nn.Module]: | ||
| 887 | - """ | ||
| 888 | - 初始化双专家 DiT 并绑定子适配器。 | ||
| 889 | - | ||
| 890 | - - device 参数预留,当前未直接使用 | ||
| 891 | - - _load_pipeline(分区 7):创建 WanT2V,得到 low_noise_model / high_noise_model | ||
| 892 | - - _bind_expert_sub_adapters(分区 5):为每个专家创建 Wan2_2ExpertSubAdapter | ||
| 893 | - - 返回 dict key 须与 QuantService / get_expert_adapter 使用的专家名一致 | ||
| 894 | - """ | ||
| 895 | - _ = device | ||
| 896 | - self._load_pipeline() | ||
| 897 | - experts = { | ||
| 898 | - "low_noise_model": self.low_noise_model, | ||
| 899 | - "high_noise_model": self.high_noise_model, | ||
| 900 | - } | ||
| 901 | - self._bind_expert_sub_adapters(experts) | ||
| 902 | - return experts | ||
| 903 | - | ||
| 904 | - def _build_wan_pipeline(self, args, cfg, device, rank) -> None: | ||
| 905 | - """创建 WanT2V,挂载 attention_cache 到双专家 DiT。""" | ||
| 906 | - self.wan_t2v = wan.WanT2V(config=cfg, checkpoint_dir=args.ckpt_dir, ...) | ||
| 907 | - self.low_noise_model = self.wan_t2v.low_noise_model | ||
| 908 | - self.high_noise_model = self.wan_t2v.high_noise_model | ||
| 909 | - self._setup_wan_dit_runtime(args, self.low_noise_model, self.high_noise_model) | ||
| 910 | -``` | ||
| 911 | - | ||
| 912 | -#### Step 2:分区 2 —— 公共运行时配置(子类 InferenceConfig + 基类 configure_runtime) | ||
| 913 | - | ||
| 914 | -**职责**:子类定义 `*InferenceConfig` 与 `get_inference_config_class()`;基类 `configure_runtime` 合并 argv 并调用 `generate._parse_args`。 | ||
| 915 | -**校验**:由 `validate_inference_config` 统一执行;子类可在 `InferenceConfig` 中约束 `task` 与 `scene_task` 一致。 | ||
| 916 | -**注意**:`configure_runtime` 在**分区 2**,桥接辅助方法在**分区 6**。 | ||
| 917 | - | ||
| 918 | -```python | ||
| 919 | -class Wan2_2T2VModelAdapter(Wan2_2BaseModelAdapter): | ||
| 920 | - scene_task = "t2v-A14B" | ||
| 921 | - | ||
| 922 | - # ===== 分区 2:公共运行时配置(子类) ===== | ||
| 923 | - | ||
| 924 | - class Wan2_2T2VInferenceConfig(BaseModel): | ||
| 925 | - model_config = ConfigDict(extra="forbid") | ||
| 926 | - size: Optional[str] = "1280*720" | ||
| 927 | - frame_num: Optional[int] = 81 | ||
| 928 | - sample_steps: Optional[int] = 40 | ||
| 929 | - sample_guide_scale: Optional[float] = None # 省略则 generate._validate_args 用 WAN_CONFIGS 双专家默认 | ||
| 930 | - base_seed: Optional[int] = None | ||
| 931 | - task: Optional[str] = "t2v-A14B" # 若写则须与 scene_task 一致 | ||
| 932 | - # ... 与 generate.py CLI 可对齐的字段 | ||
| 933 | - | ||
| 934 | - def get_inference_config_class(self): | ||
| 935 | - return self.Wan2_2T2VInferenceConfig | ||
| 936 | - | ||
| 937 | - | ||
| 938 | -class Wan2_2BaseModelAdapter( | ||
| 939 | - BaseModelAdapter, | ||
| 940 | - ModelInfoInterface, | ||
| 941 | - MultimodalPipelineInterface, | ||
| 942 | - FA3QuantAdapterInterface, | ||
| 943 | - OnlineQuaRotInterface, | ||
| 944 | -): | ||
| 945 | - # ===== 分区 2:公共运行时配置(基类) ===== | ||
| 946 | - | ||
| 947 | - def configure_runtime(self, inference_config: Any) -> None: | ||
| 948 | - """ | ||
| 949 | - 将 InferenceConfig 落到 model_args(仅一次 generate._parse_args)。 | ||
| 950 | - argv:最小 CLI → YAML → 量化覆盖 → 强制 --task/--ckpt_dir。 | ||
| 951 | - """ | ||
| 952 | - from wan.configs import WAN_CONFIGS | ||
| 953 | - | ||
| 954 | - override = inference_config.model_dump(exclude_none=True) | ||
| 955 | - allowed_attrs = self._allowed_generate_config_keys() # 分区 6 | ||
| 956 | - # ... 非法字段校验 | ||
| 957 | - quant_overrides = {...} | ||
| 958 | - argv = self._build_default_generate_cli() # 分区 6 | ||
| 959 | - argv.extend(self._namespace_to_argv(override)) | ||
| 960 | - argv.extend(self._namespace_to_argv(quant_overrides)) | ||
| 961 | - argv.extend(["--task", self.scene_task, "--ckpt_dir", str(self.model_path)]) | ||
| 962 | - self.model_args = self._parse_args_from_generate(argv) # 分区 6 | ||
| 963 | - self.model_args.task_config = TASK_TYPES[self.scene_task] | ||
| 964 | - self.model_args.param_dtype = WAN_CONFIGS[self.scene_task].param_dtype | ||
| 965 | -``` | ||
| 966 | - | ||
| 967 | -#### Step 3:分区 3 —— 公共校准执行(基类 dump + 子类 quantization_context) | ||
| 968 | - | ||
| 969 | -**职责**:基类 `prepare_calib_data` 按专家生成/加载 `calib_data_<task>_<expert>.pth`;`inference_dump_calib_data` 遍历 dataset 调用子类 `_generate_video`;子类实现 `quantization_context`。 | ||
| 970 | - | ||
| 971 | -```python | ||
| 972 | -class Wan2_2BaseModelAdapter( | ||
| 973 | - BaseModelAdapter, | ||
| 974 | - ModelInfoInterface, | ||
| 975 | - MultimodalPipelineInterface, | ||
| 976 | - FA3QuantAdapterInterface, | ||
| 977 | - OnlineQuaRotInterface, | ||
| 978 | -): | ||
| 979 | - # ===== 分区 3:公共校准执行(基类) ===== | ||
| 980 | - | ||
| 981 | - def prepare_calib_data(self, models, dump_config, save_path, dataset, inference_config): | ||
| 982 | - """双专家各一条 pth;dump 一次 inference_dump_calib_data 写满所有专家缓存。""" | ||
| 983 | - ... | ||
| 984 | - | ||
| 985 | - def inference_dump_calib_data(self, dataset=None, inference_config: Any = None): | ||
| 986 | - """逐条样本调用子类 _generate_video dump 校准数据。""" | ||
| 987 | - stream = torch.npu.Stream() | ||
| 988 | - for sample in tqdm(dataset, desc="Dump calib data by float model inference"): | ||
| 989 | - seed = self._runtime_value(inference_config, "base_seed") # 分区 4 | ||
| 990 | - torch.manual_seed(seed) | ||
| 991 | - torch.npu.manual_seed_all(seed) | ||
| 992 | - self._generate_video(sample.text, sample.image, inference_config) # 子类 | ||
| 993 | - stream.synchronize() | ||
| 994 | - | ||
| 995 | - | ||
| 996 | -class Wan2_2T2VModelAdapter(Wan2_2BaseModelAdapter): | ||
| 997 | - # ===== 分区 3:公共校准执行(子类) ===== | ||
| 998 | - | ||
| 999 | - def quantization_context(self): | ||
| 1000 | - """双专家同时进入 autocast + no_sync 上下文。""" | ||
| 1001 | - return self._quantization_context_with_no_sync( | ||
| 1002 | - self.low_noise_model, self.high_noise_model, | ||
| 1003 | - ) | ||
| 1004 | - | ||
| 1005 | - def _generate_video(self, prompt, image_path, inference_config) -> None: | ||
| 1006 | - """T2V:调用 wan_t2v.generate,参数经 _runtime_value 取值。""" | ||
| 1007 | - self.wan_t2v.generate( | ||
| 1008 | - prompt, | ||
| 1009 | - size=SIZE_CONFIGS[self._runtime_value(inference_config, "size")], | ||
| 1010 | - # ... | ||
| 1011 | - ) | ||
| 1012 | -``` | ||
| 1013 | - | ||
| 1014 | -#### Step 4:分区 4 —— 基类运行时通用辅助(基类模型适配器实现的、各子任务场景公用的辅助方法) | ||
| 1015 | - | ||
| 1016 | -```python | ||
| 1017 | -class Wan2_2BaseModelAdapter( | ||
| 1018 | - BaseModelAdapter, | ||
| 1019 | - ModelInfoInterface, | ||
| 1020 | - MultimodalPipelineInterface, | ||
| 1021 | - FA3QuantAdapterInterface, | ||
| 1022 | - OnlineQuaRotInterface, | ||
| 1023 | -): | ||
| 1024 | - # ===== 分区 4:基类运行时通用辅助 ===== | ||
| 1025 | - | ||
| 1026 | - def _runtime_value( | ||
| 1027 | - self, | ||
| 1028 | - inference_config: Optional[Union[BaseModel, Dict[str, Any]]], | ||
| 1029 | - name: str, | ||
| 1030 | - ) -> Any: | ||
| 1031 | - """与 HunyuanVideo 相同:优先 inference_config(Pydantic/dict),否则 model_args。""" | ||
| 1032 | - ... | ||
| 1033 | - | ||
| 1034 | - @contextmanager | ||
| 1035 | - def _quantization_context_with_no_sync(self, *dit_models: nn.Module): | ||
| 1036 | - """autocast + no_grad + 各 DiT no_sync(ExitStack)。""" | ||
| 1037 | - import torch.cuda.amp as amp | ||
| 1038 | - with amp.autocast(dtype=self.model_args.param_dtype), torch.no_grad(), ExitStack() as stack: | ||
| 1039 | - for m in dit_models: | ||
| 1040 | - if m is not None: | ||
| 1041 | - stack.enter_context(getattr(m, "no_sync", nullcontext)()) | ||
| 1042 | - yield | ||
| 1043 | -``` | ||
| 1044 | - | ||
| 1045 | -#### Step 5:分区 5 —— 私有专家子适配器装配(对多个专家 DiT 分别实现适配器, 可实现各专家 DiT 的自定义扩展) | ||
| 1046 | - | ||
| 1047 | -**职责**:为 `low_noise_model` / `high_noise_model` 各创建一个 `Wan2_2ExpertSubAdapter`,供 LayerWiseRunner 按专家名调度。 | ||
| 1048 | - | ||
| 1049 | -| 方法 | 说明 | | ||
| 1050 | -|------|------| | ||
| 1051 | -| `_bind_expert_sub_adapters` | 遍历 expert_modules,创建并 bind 子适配器 | | ||
| 1052 | -| `_create_expert_sub_adapter` | 工厂:low → LowNoiseSubAdapter,high → HighNoiseSubAdapter | | ||
| 1053 | - | ||
| 1054 | -```python | ||
| 1055 | -class Wan2_2BaseModelAdapter( | ||
| 1056 | - BaseModelAdapter, | ||
| 1057 | - ModelInfoInterface, | ||
| 1058 | - MultimodalPipelineInterface, | ||
| 1059 | - FA3QuantAdapterInterface, | ||
| 1060 | - OnlineQuaRotInterface, | ||
| 1061 | -): | ||
| 1062 | - # ===== 分区 5:私有专家子适配器装配 ===== | ||
| 1063 | - | ||
| 1064 | - def _bind_expert_sub_adapters(self, expert_modules: Dict[str, nn.Module]) -> None: | ||
| 1065 | - """为每个专家创建并 bind Wan2_2ExpertSubAdapter。""" | ||
| 1066 | - adapters: Dict[str, Wan2_2ExpertSubAdapter] = {} | ||
| 1067 | - for expert_name, module in expert_modules.items(): | ||
| 1068 | - sub = self._create_expert_sub_adapter(expert_name) | ||
| 1069 | - sub.bind_module(module) | ||
| 1070 | - adapters[expert_name] = sub | ||
| 1071 | - self._expert_adapters = adapters | ||
| 1072 | - | ||
| 1073 | - def _create_expert_sub_adapter(self, expert_name: str) -> Wan2_2ExpertSubAdapter: | ||
| 1074 | - """ | ||
| 1075 | - 工厂方法:按 expert_name 返回子适配器实例。 | ||
| 1076 | - 子类可覆盖,返回自定义的 low/high 子适配器实现。 | ||
| 1077 | - """ | ||
| 1078 | - if expert_name == "low_noise_model": | ||
| 1079 | - return Wan2_2LowNoiseSubAdapter(self, expert_name) | ||
| 1080 | - if expert_name == "high_noise_model": | ||
| 1081 | - return Wan2_2HighNoiseSubAdapter(self, expert_name) | ||
| 1082 | - return Wan2_2ExpertSubAdapter(self, expert_name) | ||
| 1083 | -``` | ||
| 1084 | - | ||
| 1085 | -expert_sub_adapter.py(独立文件): | ||
| 1086 | - | ||
| 1087 | -```python | ||
| 1088 | -class Wan2_2ExpertSubAdapter(OnlineQuaRotInterface, FA3QuantAdapterInterface): | ||
| 1089 | - """ | ||
| 1090 | - 单专家量化代理。必须显式继承扩展接口(不能仅靠 __getattr__), | ||
| 1091 | - 否则 LayerWiseRunner 的 isinstance 检查失败。 | ||
| 1092 | - | ||
| 1093 | - forward/visit 委托 parent;quantization_context 仅包裹当前绑定的单个 DiT。 | ||
| 1094 | - """ | ||
| 1095 | - def quantization_context(self): | ||
| 1096 | - return self._parent._quantization_context_with_no_sync(self._module) | ||
| 1097 | - | ||
| 1098 | - def get_online_rotation_configs(self, model=None): | ||
| 1099 | - return self._parent.get_online_rotation_configs(model if model is not None else self._module) | ||
| 1100 | - | ||
| 1101 | - def inject_fa3_placeholders(self, root_name, root_module, should_inject): | ||
| 1102 | - """FA3 注入委托父适配器,保证 per-expert LayerWise 与主适配器逻辑一致。""" | ||
| 1103 | - return self._parent.inject_fa3_placeholders(root_name, root_module, should_inject) | ||
| 1104 | - | ||
| 1105 | - | ||
| 1106 | -class Wan2_2LowNoiseSubAdapter(Wan2_2ExpertSubAdapter): | ||
| 1107 | - """low_noise_model 默认子适配器(可按需重写 forward/visit/context/process)。""" | ||
| 1108 | - | ||
| 1109 | - | ||
| 1110 | -class Wan2_2HighNoiseSubAdapter(Wan2_2ExpertSubAdapter): | ||
| 1111 | - """high_noise_model 默认子适配器。""" | ||
| 1112 | -``` | ||
| 1113 | - | ||
| 1114 | -#### Step 6:分区 6 —— 私有参数桥接(配置与解析) | ||
| 1115 | - | ||
| 1116 | -**职责**:与 HunyuanVideo 分区 5 相同;**不含** `configure_runtime`(在分区 2)。 | ||
| 1117 | - | ||
| 1118 | -| 方法 | 说明 | | ||
| 1119 | -|------|------| | ||
| 1120 | -| `_allowed_generate_config_keys` | 懒探测 generate 合法字段 | | ||
| 1121 | -| `_build_default_generate_cli` | 含 `--size` 等,满足 `generate._validate_args` 对 scene_task 的约束 | | ||
| 1122 | -| `_namespace_to_argv` | 与 generate.py CLI 一致:标量走 argv;tuple/list/dict 跳过(T2V/I2V 双专家 `sample_guide_scale` 省略时由 WAN_CONFIGS 回填) | | ||
| 1123 | -| `_parse_args_from_generate` | `sys.argv` 模拟调用 `generate._parse_args` | | ||
| 1124 | - | ||
| 1125 | -```python | ||
| 1126 | -class Wan2_2BaseModelAdapter( | ||
| 1127 | - BaseModelAdapter, | ||
| 1128 | - ModelInfoInterface, | ||
| 1129 | - MultimodalPipelineInterface, | ||
| 1130 | - FA3QuantAdapterInterface, | ||
| 1131 | - OnlineQuaRotInterface, | ||
| 1132 | -): | ||
| 1133 | - # ===== 分区 6:私有参数桥接(配置与解析) ===== | ||
| 1134 | - # 由分区 2 的 configure_runtime() 调用;勿把 configure_runtime 写在本分区。 | ||
| 1135 | - | ||
| 1136 | - def _allowed_generate_config_keys(self) -> frozenset[str]: | ||
| 1137 | - """懒探测 generate._parse_args 合法字段(_GENERATE_CONFIG_KEYS 缓存)。""" | ||
| 1138 | - ... | ||
| 1139 | - | ||
| 1140 | - def _build_default_generate_cli(self) -> List[str]: | ||
| 1141 | - """最小 argv;size 须用 DEFAULT_SIZE[scene_task]。""" | ||
| 1142 | - ... | ||
| 1143 | - | ||
| 1144 | - @staticmethod | ||
| 1145 | - def _namespace_to_argv(namespace_dict: Dict[str, Any]) -> List[str]: | ||
| 1146 | - """dict → argv;与 generate.py 一致,sample_guide_scale 仅支持标量 float。""" | ||
| 1147 | - ... | ||
| 1148 | - | ||
| 1149 | - def _parse_args_from_generate(self, cli_args: List[str]): | ||
| 1150 | - """临时改写 sys.argv 调用 generate._parse_args。""" | ||
| 1151 | - ... | ||
| 1152 | -``` | ||
| 1153 | - | ||
| 1154 | -#### Step 7:分区 7 —— 私有运行时与缓存装配 | ||
| 1155 | - | ||
| 1156 | -**职责**:加载 Wan pipeline;为 DiT block 注入 `CacheAgent`(与 generate.py 一致,**始终挂载**,与 `use_attentioncache` 开关无关)。 | ||
| 1157 | - | ||
| 1158 | -| 方法 | 说明 | | ||
| 1159 | -|------|------| | ||
| 1160 | -| `_load_pipeline` | 分布式 rank/device、可选 prompt_extend、调用子类 `_build_wan_pipeline` | | ||
| 1161 | -| `_setup_wan_dit_runtime` | 1 或 2 个 DiT 上挂 attention_cache | | ||
| 1162 | -| `_build_wan_pipeline` | **子类实现**(WanT2V / WanI2V / WanTI2V) | | ||
| 1163 | - | ||
| 1164 | -```python | ||
| 1165 | -class Wan2_2BaseModelAdapter( | ||
| 1166 | - BaseModelAdapter, | ||
| 1167 | - ModelInfoInterface, | ||
| 1168 | - MultimodalPipelineInterface, | ||
| 1169 | - FA3QuantAdapterInterface, | ||
| 1170 | - OnlineQuaRotInterface, | ||
| 1171 | -): | ||
| 1172 | - # ===== 分区 7:私有运行时与缓存装配 ===== | ||
| 1173 | - | ||
| 1174 | - def _load_pipeline(self): | ||
| 1175 | - """加载 Wan pipeline(configure_runtime 之后);调用子类 _build_wan_pipeline。""" | ||
| 1176 | - ... | ||
| 1177 | - | ||
| 1178 | - def _setup_wan_dit_runtime(self, args, *transformers: nn.Module, dual_i2v: bool = False): | ||
| 1179 | - """为 1 或 2 个 DiT 注入 MindIE attention_cache。""" | ||
| 1180 | - if len(transformers) == 2: | ||
| 1181 | - self._setup_dual_expert_attention_cache(...) | ||
| 1182 | - elif len(transformers) == 1: | ||
| 1183 | - self._setup_single_transformer_attention_cache(...) | ||
| 1184 | -``` | ||
| 1185 | - | ||
| 1186 | -#### Step 8:分区 8 —— 量化扩展接口(基类实现) | ||
| 1187 | - | ||
| 1188 | -**职责**:与 HunyuanVideo 分区 7 类似,目标模块为 `WanSelfAttention` / `WanCrossAttention`;子适配器委托父类实现。 | ||
| 1189 | - | ||
| 1190 | -| 方法 | 接口 | 说明 | | ||
| 1191 | -|------|------|------| | ||
| 1192 | -| `get_online_rotation_configs` | `OnlineQuaRotInterface` | 注册 q_rot/k_rot,返回 Hadamard 配置 | | ||
| 1193 | -| `inject_fa3_placeholders` | `FA3QuantAdapterInterface` | 注入 fa3_q/k/v 并包裹 attention forward | | ||
| 1194 | - | ||
| 1195 | -```python | ||
| 1196 | -class Wan2_2BaseModelAdapter( | ||
| 1197 | - BaseModelAdapter, | ||
| 1198 | - ModelInfoInterface, | ||
| 1199 | - MultimodalPipelineInterface, | ||
| 1200 | - FA3QuantAdapterInterface, | ||
| 1201 | - OnlineQuaRotInterface, | ||
| 1202 | -): | ||
| 1203 | - # ===== 分区 8:量化扩展接口 ===== | ||
| 1204 | - | ||
| 1205 | - def get_online_rotation_configs(self, model: Optional[nn.Module] = None): | ||
| 1206 | - """ | ||
| 1207 | - OnlineQuaRotInterface:为 WanSelfAttention / WanCrossAttention 配置 q_rot、k_rot。 | ||
| 1208 | - 目标模块与 HunyuanVideo 不同;未传 model 时回退 low/high_noise_model。 | ||
| 1209 | - """ | ||
| 1210 | - pass | ||
| 1211 | - | ||
| 1212 | - def inject_fa3_placeholders( | ||
| 1213 | - self, | ||
| 1214 | - root_name: str, | ||
| 1215 | - root_module: nn.Module, | ||
| 1216 | - should_inject: Callable[[str], bool], | ||
| 1217 | - ) -> None: | ||
| 1218 | - """ | ||
| 1219 | - FA3QuantAdapterInterface:注入 fa3_q/k/v 并包裹 attention forward。 | ||
| 1220 | - forward 绑定使用 new_forward.__get__(module, module.__class__)(非 MethodType 裸绑定)。 | ||
| 1221 | - """ | ||
| 1222 | - pass | ||
| 1223 | -``` | ||
| 1224 | - | ||
| 1225 | ---- | ||
| 1226 | - | ||
| 1227 | -## 量化自有模型 | ||
| 1228 | - | ||
| 1229 | -完成模型适配器编写、注册、YAML 配置与校准数据准备后,即可对自有文生视频 / 图生视频模型执行量化。本节以 **Wan2.2-T2V-A14B** 为例说明;HunyuanVideo 等单 DiT 模型流程相同,仅 `model_type`、YAML 与 `dataset` 名称不同。 | ||
| 1230 | - | ||
| 1231 | -### 注册模型名 | ||
| 1232 | - | ||
| 1233 | -在 [`config/config.ini`](../../../../config/config.ini) 中注册模型。多模态生成建议 **按场景拆分为独立 `model_type`**,与适配器子类的 `scene_task` 一一对应,**不要**在 YAML 里用 `task` 切换 T2V / I2V / TI2V。 | ||
| 1234 | - | ||
| 1235 | -```ini | ||
| 1236 | -[ModelAdapter] | ||
| 1237 | -# ...其他模型... | ||
| 1238 | -wan2_2 = Wan2_2, Wan2.2 # Legacy 单体入口(LegacyMultimodalPipelineInterface) | ||
| 1239 | -wan2_2_t2v = Wan2.2-T2V-A14B | ||
| 1240 | -wan2_2_i2v = Wan2.2-I2V-A14B | ||
| 1241 | -wan2_2_ti2v = Wan2.2-TI2V-5B | ||
| 1242 | -hunyuan_video = HunyuanVideo | ||
| 1243 | - | ||
| 1244 | -[ModelAdapterEntryPoints] | ||
| 1245 | -# ...其他模型... | ||
| 1246 | -wan2_2 = msmodelslim.model.wan2_2.loader:Wan2_2AdapterLoader | ||
| 1247 | -wan2_2_t2v = msmodelslim.model.wan2_2.t2v.loader:Wan2_2T2VAdapterLoader | ||
| 1248 | -wan2_2_i2v = msmodelslim.model.wan2_2.i2v.loader:Wan2_2I2VAdapterLoader | ||
| 1249 | -wan2_2_ti2v = msmodelslim.model.wan2_2.ti2v.loader:Wan2_2TI2VAdapterLoader | ||
| 1250 | -hunyuan_video = msmodelslim.model.hunyuan_video.loader:HunyuanVideoAdapterLoader | ||
| 1251 | -``` | ||
| 1252 | - | ||
| 1253 | -| `model_type`(CLI `--model_type`) | 场景 | 适配器入口 | 编排 | | ||
| 1254 | -| :--- | :--- | :--- | :--- | | ||
| 1255 | -| `Wan2.2-T2V-A14B` | 文本生视频 | `Wan2_2T2VAdapterLoader` | 重构 | | ||
| 1256 | -| `Wan2.2-I2V-A14B` | 图像生视频 | `Wan2_2I2VAdapterLoader` | 重构 | | ||
| 1257 | -| `Wan2.2-TI2V-5B` | 文本+图像生视频 | `Wan2_2TI2VAdapterLoader` | 重构 | | ||
| 1258 | -| `HunyuanVideo` | 单 DiT 文生视频 | `HunyuanVideoAdapterLoader` | 重构 | | ||
| 1259 | -| `Wan2_2` / `Wan2.2` | 旧版 Wan2.2 单体 | `Wan2_2AdapterLoader` | Legacy | | ||
| 1260 | - | ||
| 1261 | -### 校准数据准备 | ||
| 1262 | - | ||
| 1263 | -校准数据由 YAML 的 `dataset` 字段指定,可写为: | ||
| 1264 | - | ||
| 1265 | -- **短名称**:在 [`lab_calib`](../../../../lab_calib) 下查找对应目录或文件; | ||
| 1266 | -- **绝对路径 / 相对路径**:指向自定义校准集。 | ||
| 1267 | - | ||
| 1268 | -多模态生成复用 `VlmCalibSample` 加载逻辑,常见为 **`index.json` / `index.jsonl`**,每条样本至少包含非空 **`text`**(Prompt)。字段约定与理解模型类似,详见[一键量化使用说明 — dataset 校准数据路径配置](../../user_guide/usage_quick_quantization.md#dataset---校准数据路径配置)。 | ||
| 1269 | - | ||
| 1270 | -**各场景对样本的要求**(与适配器 `validate_calib_samples` 一致): | ||
| 1271 | - | ||
| 1272 | -| 场景 | `model_type` | 样本要求 | | ||
| 1273 | -| :--- | :--- | :--- | | ||
| 1274 | -| T2V | `Wan2.2-T2V-A14B` | 必须有 `text`,**不得**带 `image` | | ||
| 1275 | -| I2V | `Wan2.2-I2V-A14B` | 必须有 `text` 与可访问的 `image` | | ||
| 1276 | -| TI2V | `Wan2.2-TI2V-5B` | 必须有 `text`;`image` 可选(无图时走 T2V 分支) | | ||
| 1277 | - | ||
| 1278 | -Wan2.2 T2V 示例配置中可使用: | ||
| 1279 | - | ||
| 1280 | -```yaml | ||
| 1281 | -dataset: wan2_2_t2v # 对应 lab_calib 下的校准集短名称 | ||
| 1282 | -``` | ||
| 1283 | - | ||
| 1284 | -重构路径下,`prepare_calib_data` 按专家写入/加载 `calib_data_<task_config>_<expert_name>.pth`(如双专家为 `calib_data_t2v-A14B_low_noise_model.pth`)。`enable_dump: True` 时执行浮点推理 dump;已有 pth 则复用。`enable_dump: False` 时各专家 `calib_data[expert]` 可为 `None`(全动态量化),但 **dict 仍须包含每个 expert 的 key**。详见 [multimodal_sd_config — dump_config](../../user_guide/usage_quick_quantization.md#dump_config---校准数据捕获配置)。 | ||
| 1285 | - | ||
| 1286 | -### 准备量化配置 | ||
| 1287 | - | ||
| 1288 | -创建量化配置文件(YAML)。Wan2.2 T2V 官方示例见 [`wan2_2_w8a8f8_mxfp_t2v.yaml`](../../../../lab_practice/wan2_2/wan2_2_w8a8f8_mxfp_t2v.yaml);HunyuanVideo 可参考 [`hunyuan_video_w8a8f8_mxfp.yaml`](../../../../lab_practice/hunyuan_video/hunyuan_video_w8a8f8_mxfp.yaml)。 | ||
| 1289 | - | ||
| 1290 | -```yaml | ||
| 1291 | -# 量化配置(Wan2.2-T2V-A14B,W8A8 MXFP8 + QuaRot + FA3) | ||
| 1292 | -apiversion: multimodal_sd_modelslim_v1 | ||
| 1293 | -metadata: | ||
| 1294 | - config_id: wan2_2_w8a8f8_mxfp_t2v | ||
| 1295 | - label: | ||
| 1296 | - w_bit: 8 | ||
| 1297 | - a_bit: 8 | ||
| 1298 | - fa_quant: True | ||
| 1299 | - | ||
| 1300 | -spec: | ||
| 1301 | - process: | ||
| 1302 | - # ========== 线性层 W8A8(MXFP8 per-block)========== | ||
| 1303 | - - type: "linear_quant" | ||
| 1304 | - qconfig: | ||
| 1305 | - act: | ||
| 1306 | - scope: "per_block" | ||
| 1307 | - dtype: "mxfp8" | ||
| 1308 | - symmetric: True | ||
| 1309 | - method: "minmax" | ||
| 1310 | - weight: | ||
| 1311 | - scope: "per_block" | ||
| 1312 | - dtype: "mxfp8" | ||
| 1313 | - symmetric: True | ||
| 1314 | - method: "minmax" | ||
| 1315 | - include: | ||
| 1316 | - - "*" | ||
| 1317 | - # ========== QuaRot(跳过首层 self_attn,与原仓对齐)========== | ||
| 1318 | - - type: "online_quarot" | ||
| 1319 | - include: | ||
| 1320 | - - "*.self_attn.*" | ||
| 1321 | - exclude: | ||
| 1322 | - - "*blocks.0.self_attn*" | ||
| 1323 | - # ========== FA3 注意力 FP8 动态量化 ========== | ||
| 1324 | - - type: "fa3_quant" | ||
| 1325 | - qconfig: | ||
| 1326 | - dtype: "fp8_e4m3" | ||
| 1327 | - scope: "per_token" | ||
| 1328 | - symmetric: True | ||
| 1329 | - method: "minmax" | ||
| 1330 | - include: | ||
| 1331 | - - "*self_attn" | ||
| 1332 | - exclude: | ||
| 1333 | - - "*blocks.0.self_attn*" | ||
| 1334 | - | ||
| 1335 | - dataset: wan2_2_t2v | ||
| 1336 | - | ||
| 1337 | - save: | ||
| 1338 | - - type: "mindie_format_saver" | ||
| 1339 | - part_file_size: 0 | ||
| 1340 | - | ||
| 1341 | - multimodal_sd_config: | ||
| 1342 | - dump_config: | ||
| 1343 | - enable_dump: False | ||
| 1344 | - capture_mode: "args" | ||
| 1345 | - dump_data_dir: "" | ||
| 1346 | - inference_config: | ||
| 1347 | - size: "1280*720" | ||
| 1348 | - frame_num: 81 | ||
| 1349 | - sample_steps: 40 | ||
| 1350 | - convert_model_dtype: True | ||
| 1351 | - task: "t2v-A14B" | ||
| 1352 | -``` | ||
| 1353 | - | ||
| 1354 | -**配置要点**: | ||
| 1355 | - | ||
| 1356 | -| 区块 | 说明 | | ||
| 1357 | -| :--- | :--- | | ||
| 1358 | -| `apiversion` | 固定为 `multimodal_sd_modelslim_v1`,走多模态生成 QuantService | | ||
| 1359 | -| `process` | 量化处理器链:`linear_quant` / `online_quarot` / `fa3_quant` 等,字段与 modelslim_v1 一致 | | ||
| 1360 | -| `per_expert`(可选) | 按专家名整链覆盖 `process` | | ||
| 1361 | -| `dataset` | 校准集;Wan2.2 各场景使用不同短名称(如 `wan2_2_i2v`、`wan2_2_ti2v`) | | ||
| 1362 | -| `save` | 多模态生成默认 `mindie_format_saver`,输出 MindIE-SD 格式 | | ||
| 1363 | -| `multimodal_sd_config.inference_config` | **推理参数桥接**(Pydantic 校验),字段须与原 Wan2.2 推理仓 CLI 一致;`task` 须与当前 `model_type` 对应(T2V 为 `t2v-A14B`) | | ||
| 1364 | - | ||
| 1365 | -`process`、`save`、`multimodal_sd_config` 的完整说明见 [multimodal_sd_modelslim_v1 配置详解](../../user_guide/usage_quick_quantization.md#53-multimodal_sd_modelslim_v1-配置详解)。I2V / TI2V 请改用 [`wan2_2_w8a8f8_mxfp_i2v.yaml`](../../../../lab_practice/wan2_2/wan2_2_w8a8f8_mxfp_i2v.yaml)、[`wan2_2_w8a8f8_mxfp_ti2v.yaml`](../../../../lab_practice/wan2_2/wan2_2_w8a8f8_mxfp_ti2v.yaml),并匹配对应的 `model_type` 与 `dataset`。 | ||
| 1366 | - | ||
| 1367 | -### 执行量化 | ||
| 1368 | - | ||
| 1369 | -**方式一:使用官方 `quant_type` 一键量化**(推荐,无需手写 YAML): | ||
| 1370 | - | ||
| 1371 | -```bash | ||
| 1372 | -msmodelslim quant \ | ||
| 1373 | - --model_path ${MODEL_PATH} \ | ||
| 1374 | - --save_path ${SAVE_PATH} \ | ||
| 1375 | - --device npu \ | ||
| 1376 | - --model_type Wan2.2-T2V-A14B \ | ||
| 1377 | - --quant_type w8a8f8 \ | ||
| 1378 | - --trust_remote_code true | ||
| 1379 | -``` | ||
| 1380 | - | ||
| 1381 | -**方式二:使用自定义 YAML**: | ||
| 1382 | - | ||
| 1383 | -```bash | ||
| 1384 | -msmodelslim quant \ | ||
| 1385 | - --model_path ${MODEL_PATH} \ | ||
| 1386 | - --save_path ${SAVE_PATH} \ | ||
| 1387 | - --device npu \ | ||
| 1388 | - --model_type Wan2.2-T2V-A14B \ | ||
| 1389 | - --config ${CONFIG_PATH} \ | ||
| 1390 | - --trust_remote_code true | ||
| 1391 | -``` | ||
| 1392 | - | ||
| 1393 | -请注意`trust_remote_code`为`true`时可能执行浮点模型权重中的代码文件,请确保浮点模型来源安全可靠。其中 `${MODEL_PATH}` 为原始浮点权重路径,`${SAVE_PATH}` 为用户自定义的量化权重保存路径,model_type对应配置为注册的模型名称,`${CONFIG_PATH}` 为YAML配置文件路径。 | ||
| 1394 | - | ||
| 1395 | -- 更多命令示例见《[Wan2.2 量化使用说明](../../../../example/multimodal_sd/Wan2_2/README.md)》。 | ||
| 1396 | - | ||
| 1397 | -## 常见问题与排障 | ||
| 1398 | - | ||
| 1399 | -### Q1: 配置报错 `SchemaValidateError: illegal config attributes` | ||
| 1400 | - | ||
| 1401 | -**原因**:`inference_config` 中写了原推理仓不支持的字段。 | ||
| 1402 | - | ||
| 1403 | -**排查**: | ||
| 1404 | - | ||
| 1405 | -1. 确认字段名与 CLI 参数对应。 | ||
| 1406 | -2. 使用 `_allowed_*_config_keys()` 探测合法字段。 | ||
| 1407 | -3. 检查 `extra="forbid"` 是否误删必要字段。 | ||
| 1408 | - | ||
| 1409 | -### Q2: 原仓 parse_args 断言失败(如 `Unsupported size for task`) | ||
| 1410 | - | ||
| 1411 | -**原因**:`_build_default_quant_cli` 提供的默认值与原仓该任务的校验冲突。 | ||
| 1412 | - | ||
| 1413 | -**排查**: | ||
| 1414 | - | ||
| 1415 | -1. 查看原仓 `_validate_args` 或 `WAN_CONFIGS` 中该任务的约束。 | ||
| 1416 | -2. 确保 `scene_task` 与默认值匹配(如 ti2v-5B 只支持 704x1280 或 1280x704)。 | ||
| 1417 | - | ||
| 1418 | -### Q3: 运行时报 `AttributeError: 'NoneType' object has no attribute 'apply'` | ||
| 1419 | - | ||
| 1420 | -**原因**:DiT block 未设置 cache。 | ||
| 1421 | - | ||
| 1422 | -**解决**:必须在 `init_model` 中调用 `_setup_cache()`,无论 `use_cache` 配置如何。 | ||
| 1423 | - | ||
| 1424 | -### Q4: 子适配器未触发 QuaRot/FA3 | ||
| 1425 | - | ||
| 1426 | -**原因**:`LayerWiseRunner` 通过 `isinstance(adapter, OnlineQuaRotInterface)` 判断,仅通过 `__getattr__` 代理不够。 | ||
| 1427 | - | ||
| 1428 | -**解决**:`Wan2_2ExpertSubAdapter` 必须显式继承 `OnlineQuaRotInterface` 和 `FA3QuantAdapterInterface`。 | ||
| 1429 | - | ||
| 1430 | -### Q5: `calib data missing for expert 'low_noise_model'` | ||
| 1431 | - | ||
| 1432 | -**原因**:`init_model()` 返回的专家名与 `prepare_calib_data` / dump 产出的 `calib_data` key 不一致,或 dump 未成功却缺少对应 key。 | ||
| 1433 | - | ||
| 1434 | -**排查**: | ||
| 1435 | - | ||
| 1436 | -1. 确认 `init_model` 返回的 dict key 与 `get_expert_adapter`、pth 文件名中的 `expert_name` 一致(双专家为 `low_noise_model`、`high_noise_model`)。 | ||
| 1437 | -2. 检查 `dump_config.enable_dump`、`dump_data_dir` 下 pth 是否齐全。 | ||
| 1438 | -3. 全动态量化仍需为每个 expert 提供 key(值可为 `None`),不能只配置单个专家。 | ||
| 1439 | - | ||
| 1440 | -### Q6: 能否只量化 `low_noise_model`? | ||
| 1441 | - | ||
| 1442 | -**不能**。量化服务对每个 `init_model` 专家循环量化,无跳过配置;需保证双专家均有合法 `calib_data` key。 | ||
| 1443 | - | ||
| 1444 | ---- | ||
| 1445 | - | ||
| 1446 | -## 参考实现 | ||
| 1447 | - | ||
| 1448 | -- **多模态生成量化服务**:[quant_service.py](../../../../msmodelslim/core/quant_service/multimodal_sd_v1/quant_service.py) | ||
| 1449 | -- **Pipeline 接口**:[pipeline_interface.py](../../../../msmodelslim/core/quant_service/multimodal_sd_v1/pipeline_interface.py)、[legacy_pipeline_interface.py](../../../../msmodelslim/core/quant_service/multimodal_sd_v1/legacy_pipeline_interface.py) | ||
| 1450 | -- **配置校验**:[quant_config.py](../../../../msmodelslim/core/quant_service/multimodal_sd_v1/quant_config.py)(`validate_inference_config`、`resolve_inference_raw`) | ||
| 1451 | -- **单网络 DiT**:[msmodelslim/model/hunyuan_video](../../../../msmodelslim/model/hunyuan_video) | ||
| 1452 | -- **双专家 DiT**:[msmodelslim/model/wan2_2](../../../../msmodelslim/model/wan2_2) | ||
| 1453 | -- **YAML 示例**:[hunyuan_video_w8a8f8_mxfp.yaml](../../../../lab_practice/hunyuan_video/hunyuan_video_w8a8f8_mxfp.yaml)、[wan2_2_w8a8f8_mxfp_t2v.yaml](../../../../lab_practice/wan2_2/wan2_2_w8a8f8_mxfp_t2v.yaml) | ||
| @@ -1,693 +0,0 @@ | |||
| 1 | -# 多模态理解模型接入指南 | ||
| 2 | - | ||
| 3 | -## 简介 | ||
| 4 | - | ||
| 5 | -本文档面向需要将自有多模态理解模型(Vision-Language Model, VLM)接入 msModelSlim 的开发者。 | ||
| 6 | - | ||
| 7 | -多模态理解模型通常由视觉编码器、视觉特征投影层和语言模型组成,能够同时处理图像和文本输入。相比纯语言模型,多模态理解模型的量化接入需要额外考虑: | ||
| 8 | - | ||
| 9 | -- **多模态校准数据的准备**:支持图像和文本prompt结合等模态融合的校准数据类型 | ||
| 10 | -- **视觉特征与语言特征的融合**:如Merger(视觉特征维度压缩与融合模块)、DeepStack(跨层视觉特征注入机制)等特殊架构的适配 | ||
| 11 | -- **视觉部分完整处理**:视觉部分一次性加载并处理,简化多模态融合逻辑 | ||
| 12 | -- **语言模型逐层加载**:语言模型逐层加载和量化,避免对内存和显存的占用过大 | ||
| 13 | - | ||
| 14 | -## 概念说明 | ||
| 15 | - | ||
| 16 | -### 前置知识 | ||
| 17 | - | ||
| 18 | -在开始之前,建议先阅读[《模型接入指南》](integrating_models.md),了解基础的接口概念和模型适配器设计。 | ||
| 19 | - | ||
| 20 | -### 多模态模型架构 | ||
| 21 | - | ||
| 22 | -典型的多模态理解模型包含以下组件: | ||
| 23 | - | ||
| 24 | -```mermaid | ||
| 25 | -flowchart TD | ||
| 26 | - A[图像输入] --> B[视觉编码器] | ||
| 27 | - B --> C[视觉特征投影] | ||
| 28 | - D[文本输入] --> E[文本嵌入] | ||
| 29 | - C & E --> F[语言模型] | ||
| 30 | - F --> G[输出] | ||
| 31 | -``` | ||
| 32 | - | ||
| 33 | -- **视觉编码器**:将图像转换为视觉特征(如ViT、CLIP等) | ||
| 34 | -- **视觉特征投影**:将视觉特征映射到语言模型的隐藏空间(如PatchMerger,即基于patch压缩的视觉特征合并模块) | ||
| 35 | -- **语言模型**:处理融合后的多模态特征(如Qwen、GLM系列等) | ||
| 36 | - | ||
| 37 | -### 多模态模型适配器 | ||
| 38 | - | ||
| 39 | -多模态模型适配器继承自 `VlmBaseModelAdapter`,提供了一些通用的多模态处理能力: | ||
| 40 | - | ||
| 41 | -- `_load_config`:模型config加载 | ||
| 42 | -- `_collect_inputs_to_device`:批量收集预处理过的多模态数据并移动输入到目标设备 | ||
| 43 | - | ||
| 44 | -同时需要实现 `ModelSlimPipelineInterfaceV1` 接口,与纯语言模型的主要区别在于: | ||
| 45 | - | ||
| 46 | -- **`init_model`**:需要完整加载视觉部分,仅加载语言部分首层 | ||
| 47 | -- **`generate_model_visit`**:先处理整个视觉部分,再逐层处理语言部分 | ||
| 48 | -- **`generate_model_forward`**:需要实现视觉部分完整前向、特征融合、语言部分逐层前向的流程 | ||
| 49 | -- **`handle_dataset`**:需要处理多模态校准数据,将图像路径和文本转换为模型输入 | ||
| 50 | - | ||
| 51 | -## 多模态模型接入 | ||
| 52 | - | ||
| 53 | -以下内容将以 [Qwen3-VL-MoE](../../../../msmodelslim/model/qwen3_vl_moe/model_adapter.py)(MoE: Mixture of Experts,混合专家架构)W8A8混合量化场景(简称"场景示例")的模型接入为例。 | ||
| 54 | - | ||
| 55 | -**Qwen3-VL-MoE的加载策略**: | ||
| 56 | - | ||
| 57 | -- **视觉部分**:完整加载(包含所有blocks、mergers等),作为一个整体进行处理和量化 | ||
| 58 | -- **语言部分**:逐层加载和处理,节省内存 | ||
| 59 | - | ||
| 60 | -**注**:不同模型可根据实际情况选择不同策略。例如,如果视觉部分需要更细粒度的控制,也可以采用逐层方案。如果需要接入其他算法可以见[附录-可用算法接口适配指导](#可用算法接口适配指导)。 | ||
| 61 | - | ||
| 62 | -### 新建模型适配器目录和文件 | ||
| 63 | - | ||
| 64 | -建议在 [`msmodelslim/model/`](../../../../msmodelslim/model) 下创建独立目录,如 `qwen3_vl_moe/`,包含以下文件: | ||
| 65 | - | ||
| 66 | -- [`model_adapter.py`](../../../../msmodelslim/model/qwen3_vl_moe/model_adapter.py):模型适配器主文件 | ||
| 67 | -- [`__init__.py`](../../../../msmodelslim/model/qwen3_vl_moe/__init__.py):导出适配器类 | ||
| 68 | -- [`moe_utils.py`](../../../../msmodelslim/model/qwen3_vl_moe/moe_utils.py)(可选):MoE融合权重等特殊结构的辅助转换工具 | ||
| 69 | - | ||
| 70 | -### 定义适配器类并继承必要接口 | ||
| 71 | - | ||
| 72 | -```python | ||
| 73 | -from msmodelslim.model.vlm_base import VlmBaseModelAdapter | ||
| 74 | -from msmodelslim.model.interface_hub import ModelSlimPipelineInterfaceV1 | ||
| 75 | -from msmodelslim.utils.logging import logger_setter | ||
| 76 | - | ||
| 77 | -@logger_setter() | ||
| 78 | -class Qwen3VLMoeModelAdapter(VlmBaseModelAdapter, # 提供多模态通用能力 | ||
| 79 | - ModelSlimPipelineInterfaceV1): # 必要,服务于量化调度 | ||
| 80 | - """ | ||
| 81 | - Qwen3-VL-MoE multimodal model adapter. | ||
| 82 | - Key features: | ||
| 83 | - - Layer-wise loading for text decoder | ||
| 84 | - - Vision encoder processed as a whole | ||
| 85 | - - Automatic MoE fusion layer conversion via MoeConverterProcessor | ||
| 86 | - - Multimodal calibration dataset support | ||
| 87 | - """ | ||
| 88 | - pass | ||
| 89 | -``` | ||
| 90 | - | ||
| 91 | -### 实现接口方法 | ||
| 92 | - | ||
| 93 | -#### `handle_dataset`:处理多模态校准数据 | ||
| 94 | - | ||
| 95 | -将校准数据(`VlmCalibSample`)转换为多模态理解模型支持的输入,`VlmCalibSample`的定义可参考[`vlm_dataset_loader.py`](../../../../msmodelslim/infra/dataset_loader/vlm_dataset_loader.py): | ||
| 96 | - | ||
| 97 | -**关键点**: | ||
| 98 | - | ||
| 99 | -- 使用 `VlmCalibSample` 结构体统一数据格式,校准数据支持的格式参考:[校准数据准备](#校准数据准备) | ||
| 100 | -- **加载processor或tokenizer**:主流多模态理解模型(如Qwen3-VL)一般使用processor对数据做预处理,但以InternVL2-8B为例的模型则使用tokenizer对数据做预处理,需要根据模型官方给出的推理示例进行设置 | ||
| 101 | -- **构建messages**:使用processor对数据做预处理的多模态理解模型,一般有自己特定的messages形式,需要参考模型官方给出的推理示例实现定义 | ||
| 102 | -- 使用 `_collect_inputs_to_device` 批量移动tensor到目标设备 | ||
| 103 | - | ||
| 104 | -```python | ||
| 105 | -def handle_dataset(self, dataset: Any, device: DeviceType = DeviceType.NPU) -> List[Any]: | ||
| 106 | - """ | ||
| 107 | - Convert calibration samples to model inputs. | ||
| 108 | - Args: | ||
| 109 | - dataset: List of VlmCalibSample: | ||
| 110 | - - VlmCalibSample(image="/path/to/img.jpg", text="Describe.") # 图文 | ||
| 111 | - Returns: | ||
| 112 | - List of model inputs (dict with input_ids, pixel_values, etc.) | ||
| 113 | - """ | ||
| 114 | - from msmodelslim.infra.dataset_loader.vlm_dataset_loader import VlmCalibSample | ||
| 115 | - from transformers import AutoProcessor | ||
| 116 | - self._processor = AutoProcessor.from_pretrained(self.model_path, trust_remote_code=True, local_files_only=True) | ||
| 117 | - | ||
| 118 | - model_inputs = [] | ||
| 119 | - for sample in dataset: | ||
| 120 | - image_path = sample.image | ||
| 121 | - text = sample.text | ||
| 122 | - # 构建messages | ||
| 123 | - messages = [ | ||
| 124 | - { | ||
| 125 | - "role": "user", | ||
| 126 | - "content": [ | ||
| 127 | - {"type": "image", "image": image_path}, | ||
| 128 | - {"type": "text", "text": text} | ||
| 129 | - ] | ||
| 130 | - } | ||
| 131 | - ] | ||
| 132 | - # 使用processor转换 | ||
| 133 | - inputs = self._processor.apply_chat_template( | ||
| 134 | - messages, | ||
| 135 | - tokenize=True, | ||
| 136 | - add_generation_prompt=True, | ||
| 137 | - return_dict=True, | ||
| 138 | - return_tensors="pt" | ||
| 139 | - ) | ||
| 140 | - | ||
| 141 | - # 移动tensor到目标设备 | ||
| 142 | - # 此处的keys和defaults需要参考原始的模型定义文件 | ||
| 143 | - # 以Qwen3-VL-235B-A22B为例,需要查看transformers.models.qwen3_vl_moe.modeling_qwen3_vl_moe.py | ||
| 144 | - # 确定Qwen3VLMoeForConditionalGeneration类对应的forward参数 | ||
| 145 | - inputs = self._collect_inputs_to_device( | ||
| 146 | - inputs, | ||
| 147 | - device, | ||
| 148 | - keys=[ | ||
| 149 | - 'input_ids', | ||
| 150 | - 'attention_mask', | ||
| 151 | - 'position_ids', | ||
| 152 | - 'past_key_values', | ||
| 153 | - 'inputs_embeds', | ||
| 154 | - 'labels', | ||
| 155 | - 'pixel_values', | ||
| 156 | - 'pixel_values_videos', | ||
| 157 | - 'image_grid_thw', | ||
| 158 | - 'video_grid_thw', | ||
| 159 | - 'cache_position', | ||
| 160 | - 'logits_to_keep', | ||
| 161 | - ], | ||
| 162 | - defaults={'logits_to_keep': 0} | ||
| 163 | - ) | ||
| 164 | - | ||
| 165 | - model_inputs.append(inputs) | ||
| 166 | - | ||
| 167 | - return model_inputs | ||
| 168 | -``` | ||
| 169 | - | ||
| 170 | -#### `init_model`:初始化模型(视觉部分完整加载+语言部分首层加载) | ||
| 171 | - | ||
| 172 | -多模态理解模型的初始化需要注意: | ||
| 173 | - | ||
| 174 | -- **视觉部分完整加载**:一次性加载整个视觉部分 | ||
| 175 | -- **语言部分仅加载首层**:仅加载第一个文本解码层,其余层按需动态加载 | ||
| 176 | -- **设置推理模式**:设置`_attn_implementation='eager'`等 | ||
| 177 | - | ||
| 178 | -**关键点**: | ||
| 179 | - | ||
| 180 | -- 通过临时设置 `num_hidden_layers=1` 来控制仅加载一个语言部分解码层 | ||
| 181 | -- 视觉部分会被完整加载(所有blocks、patch_embed、merger、deepstack_merger_list等) | ||
| 182 | -- 使用 `from_pretrained` 而非手动加载权重,更简洁可靠 | ||
| 183 | -- 如果首层是MoE层,需要进行3D权重转换(参考 [`moe_utils.py`](../../../../msmodelslim/model/qwen3_vl_moe/moe_utils.py)) | ||
| 184 | - | ||
| 185 | -```python | ||
| 186 | -def init_model(self, device: DeviceType = DeviceType.NPU) -> nn.Module: | ||
| 187 | - """ | ||
| 188 | - Initialize model with vision encoder fully loaded and only first text layer. | ||
| 189 | - Returns: | ||
| 190 | - nn.Module: Model with: | ||
| 191 | - - model.visual (完整加载:所有blocks、mergers等) | ||
| 192 | - - language_model.layers[0] (loaded) | ||
| 193 | - - language_model.layers[1..N] (待动态加载) | ||
| 194 | - """ | ||
| 195 | - # 1. 加载config | ||
| 196 | - from transformers import Qwen3VLMoeForConditionalGeneration | ||
| 197 | - | ||
| 198 | - # 2. 不使用Cache以节省显存 | ||
| 199 | - self.config.use_cache = False | ||
| 200 | - | ||
| 201 | - # 3. 保存原始语言部分层数,临时设置为1用于只加载首层 | ||
| 202 | - origin_layers = self.config.text_config.num_hidden_layers | ||
| 203 | - self.config.text_config.num_hidden_layers = 1 | ||
| 204 | - | ||
| 205 | - # 4. 使用from_pretrained加载模型 | ||
| 206 | - # 视觉部分会完整加载,语言部分只加载1层 | ||
| 207 | - model = Qwen3VLMoeForConditionalGeneration.from_pretrained( | ||
| 208 | - self.model_path, | ||
| 209 | - config=self.config, | ||
| 210 | - trust_remote_code=self.trust_remote_code, | ||
| 211 | - torch_dtype="auto", | ||
| 212 | - local_files_only=True, | ||
| 213 | - device_map="cpu", | ||
| 214 | - attn_implementation='eager' | ||
| 215 | - ).eval() | ||
| 216 | - | ||
| 217 | - # 5. 恢复原始层数配置和attention模式 | ||
| 218 | - self.config.text_config.num_hidden_layers = origin_layers | ||
| 219 | - self.config.text_config._attn_implementation = 'eager' | ||
| 220 | - | ||
| 221 | - # 6.加载完整state_dict | ||
| 222 | - state_dict = self._get_state_dict(model) | ||
| 223 | - model.load_state_dict(state_dict) | ||
| 224 | - | ||
| 225 | - # 7. 如果语言部分首层是MoE层,执行权重转换 | ||
| 226 | - if self._is_moe_layer(0): | ||
| 227 | - self._convert_single_moe_layer(model.model.language_model.layers[0], 0) | ||
| 228 | - | ||
| 229 | - return model | ||
| 230 | -``` | ||
| 231 | - | ||
| 232 | -#### `generate_model_visit`:生成模型访问序列 | ||
| 233 | - | ||
| 234 | -按照模型结构的拓扑顺序,依次yield各个需要量化的模块。**顺序非常重要**,必须与前向传播顺序一致。 | ||
| 235 | - | ||
| 236 | -**关键点**: | ||
| 237 | - | ||
| 238 | -- **视觉部分作为整体**:`model.visual` 一次性yield,包含所有子模块 | ||
| 239 | -- **语言部分逐层yield**:使用 `generated_decoder_layer_visit_func` 标准函数 | ||
| 240 | -- 使用 `generate_decoder_layer` 生成器动态加载文本层 | ||
| 241 | -- 对于MoE层,在 `_load_decoder_if_not_exist` 中自动进行权重转换 | ||
| 242 | - | ||
| 243 | -```python | ||
| 244 | -def generate_model_visit(self, model: nn.Module) -> Generator[ProcessRequest, Any, None]: | ||
| 245 | - """ | ||
| 246 | - Generate model visit sequence for layer-wise processing. | ||
| 247 | - | ||
| 248 | - Order (critical): | ||
| 249 | - 1. model.visual (整个视觉部分作为一个整体) | ||
| 250 | - 2. language_model.layers[0], language_model.layers[1], ..., language_model.layers[L-1] | ||
| 251 | - """ | ||
| 252 | - # 1. 处理整个视觉部分(作为一个整体) | ||
| 253 | - yield ProcessRequest( | ||
| 254 | - name="model.visual", | ||
| 255 | - module=model.model.visual, | ||
| 256 | - args=(), | ||
| 257 | - kwargs={} | ||
| 258 | - ) | ||
| 259 | - | ||
| 260 | - # 2. 逐层处理语言部分 | ||
| 261 | - yield from generated_decoder_layer_visit_func( | ||
| 262 | - model, | ||
| 263 | - transformer_blocks=self.generate_decoder_layer(model) | ||
| 264 | - ) | ||
| 265 | - | ||
| 266 | -def generate_decoder_layer(self, model: nn.Module) -> Generator[Tuple[str, nn.Module], None, None]: | ||
| 267 | - """ | ||
| 268 | - Generate decoder layers, loading them on-demand. | ||
| 269 | - | ||
| 270 | - Yields: | ||
| 271 | - (layer_name, layer_module) tuples | ||
| 272 | - """ | ||
| 273 | - num_layers = self.config.text_config.num_hidden_layers | ||
| 274 | - | ||
| 275 | - for layer_idx in range(num_layers): | ||
| 276 | - name = f"model.language_model.layers.{layer_idx}" | ||
| 277 | - | ||
| 278 | - # 动态加载layer(如果尚未加载) | ||
| 279 | - layer = self._load_decoder_if_not_exist(model, name, layer_idx) | ||
| 280 | - | ||
| 281 | - yield name, layer | ||
| 282 | -``` | ||
| 283 | - | ||
| 284 | -#### 辅助方法:动态加载语言部分的权重 | ||
| 285 | - | ||
| 286 | -由于视觉部分已在 `init_model` 中完整加载,只需实现语言部分文本解码器的动态加载逻辑。 | ||
| 287 | - | ||
| 288 | -```python | ||
| 289 | -def _load_decoder_if_not_exist(self, model: nn.Module, name: str, layer_idx: int) -> nn.Module: | ||
| 290 | - """ | ||
| 291 | - Dynamically load a text decoder layer if not already loaded. | ||
| 292 | - Args: | ||
| 293 | - model: The model instance | ||
| 294 | - name: Full layer name (e.g., "model.language_model.layers.1") | ||
| 295 | - layer_idx: Layer index | ||
| 296 | - Returns: | ||
| 297 | - Loaded decoder layer module | ||
| 298 | - """ | ||
| 299 | - try: | ||
| 300 | - # 尝试访问该层 | ||
| 301 | - decoder = model.get_submodule(name) | ||
| 302 | - # 检查是否真正加载(不在meta device上) | ||
| 303 | - try: | ||
| 304 | - _ = decoder.input_layernorm.weight.device | ||
| 305 | - return decoder | ||
| 306 | - except RuntimeError: | ||
| 307 | - pass # 在meta device上,需要加载 | ||
| 308 | - except AttributeError: | ||
| 309 | - pass # 层不存在,需要创建并加载 | ||
| 310 | - | ||
| 311 | - # 禁用reset_parameters避免不必要的初始化 | ||
| 312 | - from unittest.mock import patch | ||
| 313 | - with patch.object(nn.Linear, 'reset_parameters', lambda _self: None): | ||
| 314 | - # 创建层结构 | ||
| 315 | - from transformers.models.qwen3_vl_moe.modeling_qwen3_vl_moe import Qwen3VLMoeTextDecoderLayer | ||
| 316 | - decoder = Qwen3VLMoeTextDecoderLayer( | ||
| 317 | - self.config.text_config, | ||
| 318 | - layer_idx=layer_idx | ||
| 319 | - ) | ||
| 320 | - | ||
| 321 | - # 从safetensors加载权重 | ||
| 322 | - state_dict = self._get_state_dict(decoder, prefix=name) | ||
| 323 | - decoder.load_state_dict(state_dict) | ||
| 324 | - decoder.eval() | ||
| 325 | - | ||
| 326 | - # 添加到模型的layer列表 | ||
| 327 | - module_list = model.model.language_model.layers | ||
| 328 | - if len(module_list) <= layer_idx: | ||
| 329 | - module_list.append(decoder) | ||
| 330 | - else: | ||
| 331 | - module_list[layer_idx] = decoder | ||
| 332 | - | ||
| 333 | - # (仅MoE结构存在3D融合权重的模型需要)如果是MoE层,执行权重转换 | ||
| 334 | - if self._is_moe_layer(layer_idx): | ||
| 335 | - self._convert_single_moe_layer(decoder, layer_idx) | ||
| 336 | - | ||
| 337 | - return decoder | ||
| 338 | - | ||
| 339 | -def _is_moe_layer(self, layer_idx: int) -> bool: | ||
| 340 | - """Check if a layer is a MoE layer""" | ||
| 341 | - # 3D融合权重的MoE层判断 | ||
| 342 | - pass | ||
| 343 | - | ||
| 344 | -def _convert_single_moe_layer(self, layer: nn.Module, layer_idx: int): | ||
| 345 | - """ | ||
| 346 | - Convert MoE layer's 3D fused weights to standard nn.Linear layers. | ||
| 347 | - | ||
| 348 | - Args: | ||
| 349 | - layer: The decoder layer module | ||
| 350 | - layer_idx: Layer index (for logging) | ||
| 351 | - """ | ||
| 352 | - # 需要单独在moe_utils.py中实现标准nn.Linear层的等价替换对应的底层逻辑 | ||
| 353 | - # 以Qwen3-VL-235B-A22B为例,需要查看transformers.models.qwen3_vl_moe.modeling_qwen3_vl_moe.py | ||
| 354 | - # 确定MoE层Qwen3VLMoeTextSparseMoeBlock的定义并进行等价替换设计 | ||
| 355 | - from .moe_utils import UnstackedQwen3VLMoeSparseMoeBlock | ||
| 356 | - """ | ||
| 357 | - 等价替换操作 | ||
| 358 | - """ | ||
| 359 | - pass | ||
| 360 | -``` | ||
| 361 | - | ||
| 362 | -#### `generate_model_forward`:生成模型前向传播序列 | ||
| 363 | - | ||
| 364 | -实现模型的完整前向传播,同时yield每一层的处理请求。视觉部分一次性运行,语言部分逐层运行。 | ||
| 365 | - | ||
| 366 | -**关键点**: | ||
| 367 | - | ||
| 368 | -- **前向逻辑梳理**:相比大语言模型,多模态理解模型涉及模态融合等操作,其逐层前向传播序列处理更为复杂。所以必须对原始模型文件中的前向传播定义进行充分了解,以Qwen3-VL-235B-A22B为例,需要查看transformers.models.qwen3_vl_moe.modeling_qwen3_vl_moe.py获取原模型定义 | ||
| 369 | -- **视觉编码器一次性运行**:调用 `model.model.visual()` 获取所有输出 | ||
| 370 | -- **视觉特征融合**:使用 `masked_scatter` 将图像特征替换到文本序列中,每个模型的具体操作需要参考原模型定义 | ||
| 371 | -- **DeepStack注入**:在特定文本层后注入对应的deepstack视觉特征,每个模型是否涉及需要参考原模型定义 | ||
| 372 | -- **逐层yield文本解码器层**:通过 `generate_decoder_layer` 动态加载并处理每一层 | ||
| 373 | -- **device管理**:通过 `LoadProcessor` 自动管理,无需手动 `.to(device)` | ||
| 374 | - | ||
| 375 | -```python | ||
| 376 | -def generate_model_forward(self, model: nn.Module, inputs: Any) -> Generator[ProcessRequest, Any, None]: | ||
| 377 | - """ | ||
| 378 | - Generate model forward sequence. | ||
| 379 | - | ||
| 380 | - Flow: | ||
| 381 | - 1. Run vision encoder as a whole -> get image features | ||
| 382 | - 2. Merge image features into text embeddings | ||
| 383 | - 3. Run each text decoder layer with proper inputs | ||
| 384 | - """ | ||
| 385 | - # 1. 提取校准样本 | ||
| 386 | - if isinstance(inputs, list): | ||
| 387 | - sample = inputs[0] | ||
| 388 | - else: | ||
| 389 | - sample = inputs | ||
| 390 | - | ||
| 391 | - # ========== 阶段1: 视觉编码器完整前向传播 ========== | ||
| 392 | - # 根据inputs中不为None的字段,明确视觉部分的输入参数 | ||
| 393 | - # 具体的实现逻辑,请参考原模型定义 | ||
| 394 | - pixel_values = sample['pixel_values'] | ||
| 395 | - image_grid_thw = sample['image_grid_thw'] | ||
| 396 | - | ||
| 397 | - with torch.no_grad(): | ||
| 398 | - # 一次性运行整个视觉部分 | ||
| 399 | - image_embeds, deepstack_image_embeds = yield ProcessRequest( | ||
| 400 | - name="model.visual", | ||
| 401 | - module=model.model.visual, | ||
| 402 | - args=(pixel_values, image_grid_thw), | ||
| 403 | - kwargs={}, | ||
| 404 | - ) | ||
| 405 | - | ||
| 406 | - # ========== 阶段2: 构建语言模型输入(融合视觉特征) ========== | ||
| 407 | - # 根据inputs中不为None的字段,明确语言部分的输入参数 | ||
| 408 | - # 具体的实现逻辑,请参考原模型定义 | ||
| 409 | - input_ids = sample['input_ids'] | ||
| 410 | - attention_mask = sample['attention_mask'] | ||
| 411 | - | ||
| 412 | - # 获取文本嵌入 | ||
| 413 | - inputs_embeds = model.model.language_model.embed_tokens(input_ids) | ||
| 414 | - | ||
| 415 | - # 融合视觉特征:用image_embeds替换<image>占位符位置 | ||
| 416 | - """ | ||
| 417 | - 融合视觉特征的实现 | ||
| 418 | - """ | ||
| 419 | - | ||
| 420 | - # ========== 阶段3: 文本解码器逐层前向传播 ========== | ||
| 421 | - # 具体的实现逻辑,请参考原模型定义 | ||
| 422 | - """ | ||
| 423 | - position_ids和attention_mask的实现 | ||
| 424 | - position_ids = ... | ||
| 425 | - attention_mask = ... | ||
| 426 | - text_position_ids = ... | ||
| 427 | - cache_position = ... | ||
| 428 | - position_embeddings = ... | ||
| 429 | - """ | ||
| 430 | - | ||
| 431 | - # 逐层处理文本解码器 | ||
| 432 | - hidden_states = inputs_embeds | ||
| 433 | - for layer_idx, (name, layer) in enumerate(self.generate_decoder_layer(model)): | ||
| 434 | - with torch.no_grad(): | ||
| 435 | - # 前向传播 | ||
| 436 | - hidden_states = yield ProcessRequest( | ||
| 437 | - name=name, | ||
| 438 | - module=layer, | ||
| 439 | - args=(hidden_states,), | ||
| 440 | - kwargs={ | ||
| 441 | - 'attention_mask': attention_mask, | ||
| 442 | - 'position_ids': text_position_ids, | ||
| 443 | - 'cache_position': cache_position, | ||
| 444 | - 'position_embeddings': position_embeddings, | ||
| 445 | - 'past_key_values': None, | ||
| 446 | - 'use_cache': False, | ||
| 447 | - }, | ||
| 448 | - ) | ||
| 449 | - | ||
| 450 | - # DeepStack注入(如果该层需要) | ||
| 451 | - """ | ||
| 452 | - DeepStack注入的实现 | ||
| 453 | - """ | ||
| 454 | - | ||
| 455 | -``` | ||
| 456 | - | ||
| 457 | -### 注册模型名 | ||
| 458 | - | ||
| 459 | -在 [`config/config.ini`](../../../../config/config.ini) 中注册模型: | ||
| 460 | - | ||
| 461 | -```ini | ||
| 462 | -[ModelAdapter] | ||
| 463 | -# ...其他模型... | ||
| 464 | -qwen3_vl_moe = Qwen3-VL-30B-A3B, Qwen3-VL-235B-A22B | ||
| 465 | - | ||
| 466 | -[ModelAdapterEntryPoints] | ||
| 467 | -# ...其他模型... | ||
| 468 | -qwen3_vl_moe = msmodelslim.model.qwen3_vl_moe.model_adapter:Qwen3VLMoeModelAdapter | ||
| 469 | -``` | ||
| 470 | - | ||
| 471 | -### 校准数据准备 | ||
| 472 | - | ||
| 473 | -校准数据由 YAML 的 `dataset` 字段指定。`dataset` 可配置为短名称(在 [`lab_calib`](../../../../lab_calib) 下查找)、绝对路径或相对路径。支持三种使用方式(index.json/index.jsonl、纯图像目录、图像目录+单个 json/jsonl),详见[一键量化使用说明 — dataset 校准数据路径配置](../../user_guide/usage_quick_quantization.md#dataset---校准数据路径配置)。 | ||
| 474 | - | ||
| 475 | -### 准备量化配置 | ||
| 476 | - | ||
| 477 | -创建量化配置文件(YAML),例如 [`qwen3_vl_moe_w8a8.yaml`](../../../../lab_practice/qwen3_vl_moe/qwen3_vl_moe_w8a8.yaml): | ||
| 478 | - | ||
| 479 | -```yaml | ||
| 480 | -apiversion: multimodal_vlm_modelslim_v1 | ||
| 481 | -# ========== W8A8动态量化配置 ========== | ||
| 482 | -default_w8a8_dynamic: &default_w8a8_dynamic | ||
| 483 | - act: | ||
| 484 | - scope: "per_token" | ||
| 485 | - dtype: "int8" | ||
| 486 | - symmetric: True | ||
| 487 | - method: "minmax" | ||
| 488 | - weight: | ||
| 489 | - scope: "per_channel" | ||
| 490 | - dtype: "int8" | ||
| 491 | - symmetric: True | ||
| 492 | - method: "minmax" | ||
| 493 | -# ========== W8A8静态量化配置 ========== | ||
| 494 | -default_w8a8: &default_w8a8 | ||
| 495 | - act: | ||
| 496 | - scope: "per_tensor" | ||
| 497 | - dtype: "int8" | ||
| 498 | - symmetric: False | ||
| 499 | - method: "minmax" | ||
| 500 | - weight: | ||
| 501 | - scope: "per_channel" | ||
| 502 | - dtype: "int8" | ||
| 503 | - symmetric: True | ||
| 504 | - method: "minmax" | ||
| 505 | - | ||
| 506 | -spec: | ||
| 507 | - # ========== 量化配置 ========== | ||
| 508 | - process: | ||
| 509 | - - type: "linear_quant" | ||
| 510 | - qconfig: *default_w8a8 | ||
| 511 | - include: | ||
| 512 | - - "*" | ||
| 513 | - exclude: | ||
| 514 | - - "*experts*" # Exclude MoE experts for dynamic quantization | ||
| 515 | - - "*linear_fc2" | ||
| 516 | - - "*merger*" | ||
| 517 | - - "*deepstack_merger_list*" | ||
| 518 | - - "*mlp.gate" | ||
| 519 | - - type: "linear_quant" | ||
| 520 | - qconfig: *default_w8a8_dynamic | ||
| 521 | - include: | ||
| 522 | - - "*experts*" # MoE experts use dynamic quantization | ||
| 523 | - exclude: | ||
| 524 | - - "*linear_fc2" | ||
| 525 | - - "*merger*" | ||
| 526 | - - "*deepstack_merger_list*" | ||
| 527 | - - "*mlp.gate" | ||
| 528 | - | ||
| 529 | - # ========== 保存配置 ========== | ||
| 530 | - save: | ||
| 531 | - - type: "ascendv1_saver" | ||
| 532 | - part_file_size: 4 | ||
| 533 | - # ========== 数据集配置 ========== | ||
| 534 | - dataset: calibImages # 校准图片目录 | ||
| 535 | - default_text: "Describe this image in detail." # 图片默认的文本prompt | ||
| 536 | -``` | ||
| 537 | - | ||
| 538 | -配置中各字段说明详见[量化配置协议详解](../../user_guide/usage_quick_quantization.md#54-multimodal_vlm_modelslim_v1-配置详解)。 | ||
| 539 | - | ||
| 540 | -## 量化自有模型 | ||
| 541 | - | ||
| 542 | -完成模型适配器编写、注册、配置文件和校准数据准备后,即可执行量化: | ||
| 543 | - | ||
| 544 | -```bash | ||
| 545 | -msmodelslim quant --model_path ${MODEL_PATH} \ | ||
| 546 | - --save_path ${SAVE_PATH} \ | ||
| 547 | - --device npu \ | ||
| 548 | - --model_type ${MODEL_TYPE} \ | ||
| 549 | - --config ${CONFIG_PATH} \ | ||
| 550 | - --trust_remote_code true | ||
| 551 | -``` | ||
| 552 | - | ||
| 553 | -**参数说明**: | ||
| 554 | -请注意`trust_remote_code`为`true`时可能执行浮点模型权重中的代码文件,请确保浮点模型来源安全可靠。其中 `${MODEL_PATH}` 为原始浮点权重路径,`${SAVE_PATH}` 为用户自定义的量化权重保存路径,`${MODEL_TYPE}` 为注册的模型名称,`${CONFIG_PATH}` 为YAML配置文件路径。 | ||
| 555 | - | ||
| 556 | -## FAQ | ||
| 557 | - | ||
| 558 | -### 量化过程报错 Out Of Memory (OOM) 问题 | ||
| 559 | - | ||
| 560 | -**症状**:量化时显存溢出 | ||
| 561 | - | ||
| 562 | -**原因和解决方案**: | ||
| 563 | - | ||
| 564 | -- 对于**视觉部分**:当前Qwen3-VL-MoE采用完整加载方案,如遇OOM,可考虑: | ||
| 565 | - - 减少校准数据中的图片数量 | ||
| 566 | - - 使用较小分辨率的图片进行校准 | ||
| 567 | - - 使用显存更大的NPU | ||
| 568 | - - 参考语言部分逐层量化的实现自行适配视觉部分的逐层处理 | ||
| 569 | -- 对于**文本部分**:确保正确实现了逐层处理 | ||
| 570 | -- 确保 `init_model` 中 `num_hidden_layers` 临时设为1,只加载首个文本解码器层 | ||
| 571 | -- 在 `generate_decoder_layer` 中使用 `_load_decoder_if_not_exist` 按需加载文本解码器层 | ||
| 572 | - | ||
| 573 | -### MoE权重转换问题 | ||
| 574 | - | ||
| 575 | -**症状**:MoE层3D权重无法直接加载为标准Linear层 | ||
| 576 | - | ||
| 577 | -**原因**:原始模型使用3D参数存储所有专家权重 | ||
| 578 | - | ||
| 579 | -**解决**: | ||
| 580 | - | ||
| 581 | -- 参考[辅助方法动态加载语言部分的权重](#辅助方法动态加载语言部分的权重)中 `_convert_single_moe_layer` 方法,实现3D权重切分为多个Linear层 | ||
| 582 | -- 参考 [`moe_utils.py`](../../../../msmodelslim/model/qwen3_vl_moe/moe_utils.py) 的等价替换底层逻辑实现 | ||
| 583 | - | ||
| 584 | -### 校准数据格式错误 | ||
| 585 | - | ||
| 586 | -**症状**:运行中报错`InvalidDatasetError` | ||
| 587 | - | ||
| 588 | -**原因**:校准数据格式不符合要求或模型不支持该类型 | ||
| 589 | - | ||
| 590 | -**解决**: | ||
| 591 | - | ||
| 592 | -- 检查数据格式是否符合: [校准数据准备](#校准数据准备) | ||
| 593 | -- 确保图片路径可访问、格式正确(.jpg/.png/.jpeg) | ||
| 594 | - | ||
| 595 | -## 附录 | ||
| 596 | - | ||
| 597 | -### 可用算法接口适配指导 | ||
| 598 | - | ||
| 599 | -#### 支持IterSmooth离群值抑制算法 | ||
| 600 | - | ||
| 601 | -如果需要支持IterSmooth算法(Iterative Smooth,迭代平滑算法,通过对激活值的离群值进行迭代平滑抑制来提升量化精度),需要实现 `IterSmoothInterface`: | ||
| 602 | - | ||
| 603 | -```python | ||
| 604 | -from msmodelslim.model.interface_hub import IterSmoothInterface | ||
| 605 | - | ||
| 606 | -class Qwen3VLMoeModelAdapter(VlmBaseModelAdapter, | ||
| 607 | - ModelSlimPipelineInterfaceV1, | ||
| 608 | - IterSmoothInterface): | ||
| 609 | - | ||
| 610 | - def get_adapter_config_for_subgraph(self,) -> List[AdapterConfig]: | ||
| 611 | - """ | ||
| 612 | - Get adapter config for subgraph-based anti-outlier processing (iter_smooth). | ||
| 613 | - | ||
| 614 | - Defines the subgraph structure for norm-linear, ov, and other fusions. | ||
| 615 | - """ | ||
| 616 | - adapter_config = [] | ||
| 617 | - | ||
| 618 | - # 针对语言部分的文本解码器添加离群值抑制映射配置 | ||
| 619 | - for layer_idx in range(self.config.text_config.num_hidden_layers): | ||
| 620 | - # Norm-Linear: input_layernorm -> QKV | ||
| 621 | - """ | ||
| 622 | - 定义norm_linear_mapping_config | ||
| 623 | - """ | ||
| 624 | - # OV fusion: V -> O | ||
| 625 | - """ | ||
| 626 | - 定义ov_mapping_config | ||
| 627 | - """ | ||
| 628 | - # 定义其他类型映射配置 | ||
| 629 | - """ | ||
| 630 | - 定义linear_linear_mapping_config | ||
| 631 | - 定义up_down_mapping_config | ||
| 632 | - """ | ||
| 633 | - | ||
| 634 | - # 添加到适配器配置 | ||
| 635 | - adapter_config.extend([ | ||
| 636 | - AdapterConfig( | ||
| 637 | - subgraph_type="norm-linear", | ||
| 638 | - mapping=norm_linear_mapping_config | ||
| 639 | - ), | ||
| 640 | - AdapterConfig( | ||
| 641 | - subgraph_type="ov", | ||
| 642 | - mapping=ov_mapping_config, | ||
| 643 | - extra_config={} | ||
| 644 | - ), | ||
| 645 | - ]) | ||
| 646 | - return adapter_config | ||
| 647 | -``` | ||
| 648 | - | ||
| 649 | -详见:[Iterative Smooth 算法词条](../quantization_algorithms/iterative_smooth/term_iterative_smooth.md) | ||
| 650 | - | ||
| 651 | -#### 支持QuaRot旋转离群值抑制算法 | ||
| 652 | - | ||
| 653 | -如果需要支持QuaRot算法(Quantization with Rotation,基于正交旋转变换将激活值离群值均匀分散到各个维度,从而显著平滑数据分布、提升量化精度),需要实现旋转矩阵的初始化和应用: | ||
| 654 | - | ||
| 655 | -```python | ||
| 656 | -from msmodelslim.model.interface_hub import QuaRotInterface | ||
| 657 | - | ||
| 658 | -class Qwen3VLMoeModelAdapter(VlmBaseModelAdapter, | ||
| 659 | - ModelSlimPipelineInterfaceV1, | ||
| 660 | - QuaRotInterface): | ||
| 661 | - | ||
| 662 | - def get_ln_fuse_map(self) -> Tuple[Dict[str, List[str]], Dict[str, List[str]]]: | ||
| 663 | - """ | ||
| 664 | - 获取LayerNorm与Linear层的融合映射 | ||
| 665 | - """ | ||
| 666 | - # 实现细节参考QuaRot文档 | ||
| 667 | - pass | ||
| 668 | - | ||
| 669 | - def get_bake_names(self) -> Tuple[List[str], List[str]]: | ||
| 670 | - """ | ||
| 671 | - 获取需要mean融合的Linear层名称列表 | ||
| 672 | - """ | ||
| 673 | - # 实现细节参考QuaRot文档 | ||
| 674 | - pass | ||
| 675 | - | ||
| 676 | - def get_rotate_map(self, block_size: int) -> Tuple[List[RotatePair], List[RotatePair]]: | ||
| 677 | - """ | ||
| 678 | - 获取旋转映射,包括左旋转和右旋转的配置 | ||
| 679 | - """ | ||
| 680 | - # 实现细节参考QuaRot文档 | ||
| 681 | - pass | ||
| 682 | -``` | ||
| 683 | - | ||
| 684 | -详见:[QuaRot 算法词条](../quantization_algorithms/quarot/term_quarot.md) | ||
| 685 | - | ||
| 686 | -### 参考资料 | ||
| 687 | - | ||
| 688 | -- [模型接入指南](integrating_models.md):大模型基础接入指导 | ||
| 689 | -- [Qwen2.5-Omni模型适配器](../../../../msmodelslim/model/qwen2_5_omni_thinker/model_adapter.py):端到端多模态(文本/图像/音频/视频)适配示例 | ||
| 690 | -- [Qwen3-VL-MoE模型适配器](../../../../msmodelslim/model/qwen3_vl_moe/model_adapter.py):完整实现示例 | ||
| 691 | -- [VLM数据集加载器](../../../../msmodelslim/infra/dataset_loader/vlm_dataset_loader.py)与[dataset_loader](../../../../msmodelslim/infra/dataset_loader):校准数据加载处理 | ||
| 692 | -- [多模态VLM量化服务](../../../../msmodelslim/core/quant_service/multimodal_vlm_v1):服务层实现 | ||
| 693 | -- [一键量化使用说明](../../user_guide/usage_quick_quantization.md):命令行参数详解 | ||
| @@ -25,9 +25,9 @@ | |||
| 25 | 25 | ||
| 26 | | 分类 | 说明 | 入口 | | 26 | | 分类 | 说明 | 入口 | |
| 27 | |------|------|------| | 27 | |------|------|------| |
| 28 | -| LLM 量化 | 大语言模型一键量化流程 | [《LLM 量化使用指南》](llm/usage_large_language_model_quantization.md) | | 28 | +| LLM 量化 | 大语言模型一键量化流程 | [《大语言模型(LLM)量化使用指南》](llm/usage_large_language_model_quantization.md) | |
| 29 | -| VLM 量化 | 多模态理解模型量化流程 | [《VLM 量化使用指南》](vlm/usage_vision_transformer_quantization.md) | | 29 | +| VLM 量化 | 多模态理解模型量化流程 | [《多模态理解模型(VLM)量化使用指南》](vlm/usage_vision_transformer_quantization.md) | |
| 30 | -| DiT 量化 | 多模态生成模型量化流程 | [《DiT 量化使用指南》](dit/usage_diffusion_transformer_quantization.md) | | 30 | +| DiT 量化 | 多模态生成模型量化流程 | [《多模态生成模型(DiT)量化使用指南》](dit/usage_diffusion_transformer_quantization.md) | |
| 31 | | 权重转换 | 已有权重的格式 / 精度转换流程 | [《权重转换使用指南》](convert/usage_weight_conversion.md) | | 31 | | 权重转换 | 已有权重的格式 / 精度转换流程 | [《权重转换使用指南》](convert/usage_weight_conversion.md) | |
| 32 | 32 | ||
| 33 | ## 模型接入指南 | 33 | ## 模型接入指南 |