已合并
【feature】model_type支持transformers #891
【feature】model_type支持transformers #891
已合并
zhangz200102创建于 9 天前
14 个文件变更+781-8
@@ -4,6 +4,7 @@ question_and_answer_url = https://gitcode.com/Ascend/msmodelslim/blob/master/doc
4 4 
5[ModelAdapter]5[ModelAdapter]
6default = default6default = default
7+transformers = transformers
7deepseek_v3 = DeepSeek-V3, DeepSeek-V3-0324, DeepSeek-R1, DeepSeek-R1-0528, DeepSeek-V3.1, DeepSeek-V3.1-Terminus8deepseek_v3 = DeepSeek-V3, DeepSeek-V3-0324, DeepSeek-R1, DeepSeek-R1-0528, DeepSeek-V3.1, DeepSeek-V3.1-Terminus
8deepseek_v3_2 = DeepSeek-V3.2-Exp, DeepSeek-V3.29deepseek_v3_2 = DeepSeek-V3.2-Exp, DeepSeek-V3.2
9deepseek_v4 = DeepSeek-V4-Flash, DeepSeek-V4-Pro10deepseek_v4 = DeepSeek-V4-Flash, DeepSeek-V4-Pro
@@ -51,6 +52,7 @@ mimo_v2_flash = MiMo-V2-Flash
51 52 
52[ModelAdapterEntryPoints]53[ModelAdapterEntryPoints]
53default = msmodelslim.model.default.loader:DefaultAdapterLoader54default = msmodelslim.model.default.loader:DefaultAdapterLoader
55+transformers = msmodelslim.model.transformers.loader:TransformersAdapterLoader
54deepseek_v3 = msmodelslim.model.deepseek_v3.loader:DeepseekV3AdapterLoader56deepseek_v3 = msmodelslim.model.deepseek_v3.loader:DeepseekV3AdapterLoader
55deepseek_v3_2 = msmodelslim.model.deepseek_v3_2.loader:DeepseekV3_2AdapterLoader57deepseek_v3_2 = msmodelslim.model.deepseek_v3_2.loader:DeepseekV3_2AdapterLoader
56deepseek_v4 = msmodelslim.model.deepseek_v4.loader:DeepseekV4AdapterLoader58deepseek_v4 = msmodelslim.model.deepseek_v4.loader:DeepseekV4AdapterLoader
@@ -25,7 +25,7 @@ msmodelslim quant [--model_type <model_type>] --model_path <model_path> --save_p
25 25 
26| 参数 | 别名 | 类型 | 传入形式 | 必选/可选 | 默认值 | 取值范围或格式 | 含义 |26| 参数 | 别名 | 类型 | 传入形式 | 必选/可选 | 默认值 | 取值范围或格式 | 含义 |
27|------|------|------|----------|-----------|--------|----------------|------|27|------|------|------|----------|-----------|--------|----------------|------|
28-| `--model_type` | 无 | `string` | 单值 | 条件必选(普通量化路径必选;`--config` 指向 `apiversion: modelslim_convert` 的配置时可省略) | 无 | 模型类型名称,如 `Qwen2.5-7B-Instruct` | 指定待量化模型类型,用于加载对应模型适配器并匹配最佳实践;仅当 `--config` 指向 `apiversion: modelslim_convert` 的配置时可省略。 |28+| `--model_type` | 无 | `string` | 单值 | 条件必选(普通量化路径必选;`--config` 指向 `apiversion: modelslim_convert` 的配置时可省略) | 无 | 模型类型名称,如 `Qwen2.5-7B-Instruct`、`transformers` | 指定待量化模型类型,用于加载对应模型适配器并匹配最佳实践;`transformers`支持加载基于 transformers 的通用基础模型适配器,当前适用于大语言模型量化;仅当 `--config` 指向 `apiversion: modelslim_convert` 的配置时可省略。 |
29| `--model_path` | 无 | `string` | 单值 | 必选 | 无 | 原始模型权重目录(需存在且可读) | 待量化模型的权重目录。 |29| `--model_path` | 无 | `string` | 单值 | 必选 | 无 | 原始模型权重目录(需存在且可读) | 待量化模型的权重目录。 |
30| `--save_path` | 无 | `string` | 单值 | 必选 | 无 | 输出目录(需可写) | 量化权重与描述文件的保存目录。 |30| `--save_path` | 无 | `string` | 单值 | 必选 | 无 | 输出目录(需可写) | 量化权重与描述文件的保存目录。 |
31| `--device` | 无 | `string` | 单值 | 可选 | `npu` | `npu``cpu` | 运行设备类型;多卡索引请用 `--device_id` 指定。 |31| `--device` | 无 | `string` | 单值 | 可选 | `npu` | `npu``cpu` | 运行设备类型;多卡索引请用 `--device_id` 指定。 |
@@ -46,6 +46,7 @@ msmodelslim quant [--model_type <model_type>] --model_path <model_path> --save_p
46- 两者都不传时,按默认量化类型 `w8a8` 匹配最佳实践;未匹配到最佳实践时会给出提示并等待确认(输入 `y` 继续,否则退出)。46- 两者都不传时,按默认量化类型 `w8a8` 匹配最佳实践;未匹配到最佳实践时会给出提示并等待确认(输入 `y` 继续,否则退出)。
47- 指定 `--config` 后直接采用该配置,`--quant_type``--tags` 的最佳实践匹配均被忽略。47- 指定 `--config` 后直接采用该配置,`--quant_type``--tags` 的最佳实践匹配均被忽略。
48- `--model_type` 在普通量化路径下必须提供;仅当 `--config` 指向 `apiversion: modelslim_convert` 的配置时可省略。48- `--model_type` 在普通量化路径下必须提供;仅当 `--config` 指向 `apiversion: modelslim_convert` 的配置时可省略。
49+- `--model_type` 指定 `transformers` 时,若使用 `--quant_type`, 仅支持 `w8a16`/`w8a8`;若使用 `--config`,当前仅支持大语言模型量化配置(`apiversion: modelslim_v1`)。
49- `--tags` 指定多个值时须同时出现在同一已验证场景;未提供硬件类型标签时自动匹配当前设备类型。50- `--tags` 指定多个值时须同时出现在同一已验证场景;未提供硬件类型标签时自动匹配当前设备类型。
50- `--debug` 启用后量化上下文写入 `${SAVE_PATH}/debug_info/`51- `--debug` 启用后量化上下文写入 `${SAVE_PATH}/debug_info/`
51 52 
@@ -81,7 +82,17 @@ msmodelslim quant \
81 82 
82`${MODEL_TYPE}` 为模型类型名称(如 `Qwen2.5-7B-Instruct`),`${MODEL_PATH}` 为浮点权重目录,`${SAVE_PATH}` 为量化输出目录。未指定 `--quant_type``--config` 时,默认按量化类型 `w8a8` 匹配最佳实践并执行量化;若模型加载需要模型目录内的自定义代码,再补充 `--trust_remote_code true`83`${MODEL_TYPE}` 为模型类型名称(如 `Qwen2.5-7B-Instruct`),`${MODEL_PATH}` 为浮点权重目录,`${SAVE_PATH}` 为量化输出目录。未指定 `--quant_type``--config` 时,默认按量化类型 `w8a8` 匹配最佳实践并执行量化;若模型加载需要模型目录内的自定义代码,再补充 `--trust_remote_code true`
83 84 
84-### 7.2 显式指定量化与设备85+### 7.2 使用 transformers 通用基础模型适配器量化(仅适用于大语言模
C

【review】序号未更新,和下方序号重复。需要更新下方及之后的序号

likedislike
zhangz200102
7 天前 评论:
86+ 
87+```bash
88+msmodelslim quant \
89+ --model_type transformers \
90+ --model_path "${MODEL_PATH}" \
91+ --save_path "${SAVE_PATH}" \
92+ --quant_type w8a16
93+```
94+ 
95+### 7.3 显式指定量化类型与设备
85 96 
86```bash97```bash
87msmodelslim quant \98msmodelslim quant \
@@ -95,7 +106,7 @@ msmodelslim quant \
95 106 
96`--quant_type w8a8c8` 表示权重8bit、激活8bit、KVCache 8bit 量化;`--device_id 0 1 2 3` 使用4个 NPU 设备(索引列表形式在 `apiversion: modelslim_v1` 配置下受支持)。107`--quant_type w8a8c8` 表示权重8bit、激活8bit、KVCache 8bit 量化;`--device_id 0 1 2 3` 使用4个 NPU 设备(索引列表形式在 `apiversion: modelslim_v1` 配置下受支持)。
97 108 
98-### 7.3 使用自定义配置文件109+### 7.4 使用自定义配置文件
99 110 
100```bash111```bash
101msmodelslim quant \112msmodelslim quant \
@@ -107,7 +118,7 @@ msmodelslim quant \
107 118 
108`${CONFIG_PATH}` 指向符合 V1 等协议的量化 YAML;指定后直接采用该配置,不再做最佳实践匹配。字段说明见引用的配置文档。119`${CONFIG_PATH}` 指向符合 V1 等协议的量化 YAML;指定后直接采用该配置,不再做最佳实践匹配。字段说明见引用的配置文档。
109 120 
110-### 7.4 使用场景标签匹配最佳实践121+### 7.5 使用场景标签匹配最佳实践
111 122 
112```bash123```bash
113msmodelslim quant \124msmodelslim quant \
@@ -103,10 +103,17 @@ msmodelslim quant \
103 103 
104**操作**104**操作**
105 105 
106-- **尚未接入的模型**:先完成适配器开发与注册,再进入步骤 3。106+- **尚未接入的模型**:通常需要自主接入模型,完成适配器开发与注册,再进入步骤 3。特例:对于基于 transformers 的大语言模型,使用 `--model_type transformers` 可匹配通用基础模型适配器,无需自行适配。
107- **支持矩阵中已接入的模型**:可跳过本步骤,直接进入步骤 3 编写量化配置;确认所用 `--model_type`(即 `${MODEL_TYPE}`)名称即可。107- **支持矩阵中已接入的模型**:可跳过本步骤,直接进入步骤 3 编写量化配置;确认所用 `--model_type`(即 `${MODEL_TYPE}`)名称即可。
108 108 
109-尚未接入时按下列操作执行109+`--model_type transformers` 使用限制
110+ 
111+- 该模型适配器仅具备基础量化能力,未实现任何算法。
112+- 若使用 `--quant_type`,当前仅支持 `w8a16``w8a8(MXFP8)`;若使用 `--config`,仅支持大语言模型量化配置(`apiversion: modelslim_v1`)。
113+- 仅推荐用于一键量化,若用于敏感层分析、自动调优等其他功能,用户需自行确保相关接口行为符合预期。
114+- 仅支持 `runner: model_wise` ,因而显存要求需足以容纳完整模型。
115+ 
116+自主接入时按下列操作执行:
110 117 
1111. 按模型类别阅读对应接入指南,并按其要求完成适配器实现与注册(细节以接入指南为准):1181. 按模型类别阅读对应接入指南,并按其要求完成适配器实现与注册(细节以接入指南为准):
112 119 
@@ -0,0 +1,28 @@
1+apiversion: modelslim_v1
2+ 
3+metadata:
4+ config_id: llm_transformers_w8a16
5+ score: 50
6+ verified_model_types:
7+ - transformers
8+ label:
9+ w_bit: 8
10+ a_bit: 16
11+ is_sparse: False
12+ kv_cache: False
13+ 
14+spec:
15+ runner: "model_wise"
16+ process:
17+ - type: "linear_quant"
18+ qconfig:
19+ weight:
20+ scope: "per_channel"
21+ dtype: "int8"
22+ symmetric: True
23+ method: "minmax"
24+ include: [ "*" ]
25+ 
26+ save:
27+ - type: "ascendv1_saver"
28+ part_file_size: 4
@@ -0,0 +1,35 @@
1+apiversion: modelslim_v1
2+ 
3+metadata:
4+ config_id: llm_transformers_w8a8_mxfp8
5+ score: 50
6+ verified_model_types:
7+ - transformers
8+ label:
9+ w_bit: 8
10+ a_bit: 8
11+ is_sparse: False
12+ kv_cache: False
13+ 
14+default_w8a8_dynamic: &default_w8a8_mxfp8_dynamic
15+ act:
16+ scope: "per_block"
17+ dtype: "mxfp8"
18+ symmetric: True
19+ method: "minmax"
20+ weight:
21+ scope: "per_block"
22+ dtype: "mxfp8"
23+ symmetric: True
24+ method: "minmax"
25+ 
26+spec:
27+ runner: "model_wise"
28+ process:
29+ - type: "linear_quant"
30+ qconfig: *default_w8a8_mxfp8_dynamic
31+ include: [ "*" ]
32+ 
33+ save:
34+ - type: "ascendv1_saver"
35+ part_file_size: 4
@@ -156,6 +156,20 @@ def validate_device_index(device_index: Optional[List[int]], device_type: Device
156 )156 )
157 157 
158 158 
159+def check_model_type_transformers(model_adapter: IModel, config: PracticeConfig):
160+ model_type = model_adapter.get_model_type()
161+ if model_type == "transformers" and config.apiversion == "multimodal_vlm_modelslim_v1":
162+ raise UnsupportedError(
163+ "VLM quantization is not supported for model_type transformers",
164+ action="Please use a dedicated model adapter for multimodal models",
165+ )
166+ if model_type == "transformers" and config.apiversion == "multimodal_sd_modelslim_v1":
167+ raise UnsupportedError(
168+ "DiT quantization is not supported for model_type transformers",
169+ action="Please use a dedicated model adapter for multimodal models",
170+ )
171+ 
172+ 
159@logger_setter('msmodelslim.app.naive_quantization')173@logger_setter('msmodelslim.app.naive_quantization')
160class NaiveQuantizationApplication:174class NaiveQuantizationApplication:
161 def __init__(175 def __init__(
@@ -235,6 +249,7 @@ class NaiveQuantizationApplication:
235 if config_path is not None:249 if config_path is not None:
236 config = PracticeConfig.model_validate(yaml_safe_load(str(config_path)))250 config = PracticeConfig.model_validate(yaml_safe_load(str(config_path)))
237 get_logger().info("Naive Quant apply config_path: %s", config_path)251 get_logger().info("Naive Quant apply config_path: %s", config_path)
252+ check_model_type_transformers(model_adapter, config)
238 return config253 return config
239 254 
240 if not isinstance(model_adapter, ModelInfoInterface):255 if not isinstance(model_adapter, ModelInfoInterface):
@@ -0,0 +1,20 @@
1+#!/usr/bin/env python
2+# -*- coding: UTF-8 -*-
3+ 
4+"""
5+-------------------------------------------------------------------------
6+This file is part of the MindStudio project.
7+Copyright (c) 2026 Huawei Technologies Co.,Ltd.
8+ 
9+MindStudio is licensed under Mulan PSL v2.
10+You can use this software according to the terms and conditions of the Mulan PSL v2.
11+You may obtain a copy of Mulan PSL v2 at:
12+ 
13+ http://license.coscl.org.cn/MulanPSL2
14+ 
15+THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
16+EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
17+MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
18+See the Mulan PSL v2 for more details.
19+-------------------------------------------------------------------------
20+"""
@@ -0,0 +1,96 @@
1+#!/usr/bin/env python
2+# -*- coding: UTF-8 -*-
3+ 
4+"""
5+-------------------------------------------------------------------------
6+This file is part of the MindStudio project.
7+Copyright (c) 2026 Huawei Technologies Co.,Ltd.
8+ 
9+MindStudio is licensed under Mulan PSL v2.
10+You can use this software according to the terms and conditions of the Mulan PSL v2.
11+You may obtain a copy of Mulan PSL v2 at:
12+ 
13+ http://license.coscl.org.cn/MulanPSL2
14+ 
15+THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
16+EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
17+MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
18+See the Mulan PSL v2 for more details.
19+-------------------------------------------------------------------------
20+"""
21+ 
22+from enum import Enum
23+from pathlib import Path
24+from typing import Any, Dict
25+ 
26+from msmodelslim.utils.exception import UnsupportedError, SecurityError
27+from msmodelslim.utils.security import json_safe_load
28+ 
29+ 
30+class ModelClass(str, Enum):
31+ LLM = "llm"
32+ VLM = "vlm"
33+ DIT = "dit"
34+ 
35+ 
36+LLM_ADAPTER_CLASS_PATH = "msmodelslim.model.transformers.model_adapter:LLMTransformersModel"
37+ 
38+_VLM_CONFIG_KEYS = ("vision_config",)
39+ 
40+_DIT_CONFIG_KEYS = ("_diffusers_version",)
41+ 
42+ 
43+def _is_vlm_config(config: Dict[str, Any]) -> bool:
44+ if any(key in config for key in _VLM_CONFIG_KEYS):
45+ return True
46+ return False
47+ 
48+ 
49+def _is_dit_config(config: Dict[str, Any]) -> bool:
50+ if any(key in config for key in _DIT_CONFIG_KEYS):
51+ return True
52+ return False
53+ 
54+ 
55+def detect_transformers_kind(model_path: Path) -> ModelClass:
56+ """Classify a HuggingFace / diffusers directory as llm, vlm, or dit."""
57+ config_path = model_path / "config.json"
58+ try:
59+ config = json_safe_load(str(config_path))
60+ except SecurityError:
61+ raise UnsupportedError(
62+ f"The path {config_path} doesn't exist or isn't a file. "
63+ "Only standard transformers-based LLM model is supported currently "
64+ "when using model_type transformers.",
65+ action=(f"Please check (1) files in {model_path} are complete. (2) the model is a LLM model."),
66+ )
67+ 
68+ if _is_vlm_config(config):
69+ return ModelClass.VLM
70+ 
71+ if _is_dit_config(config):
72+ return ModelClass.DIT
73+ 
74+ return ModelClass.LLM
75+ 
76+ 
77+def resolve_adapter_class_path(model_path: Path) -> str:
78+ """Return adapter class path for ``--model_type transformers``.
79+ 
80+ VLMTransformersModel is not registered yet; VLM/DiT raise until a dedicated
81+ generic adapter exists.
82+ """
83+ 
84+ kind = detect_transformers_kind(model_path)
85+ 
86+ if kind == ModelClass.VLM:
87+ raise UnsupportedError(
88+ "VLM quantization is not supported for model_type transformers",
89+ action="Please use a dedicated model adapter for multimodal models (VLM/DiT)",
90+ )
91+ if kind == ModelClass.DIT:
92+ raise UnsupportedError(
93+ "DiT quantization is not supported for model_type transformers",
94+ action="Please use a dedicated model adapter for multimodal models (VLM/DiT)",
95+ )
96+ return LLM_ADAPTER_CLASS_PATH
@@ -0,0 +1,20 @@
1+# -*- coding: UTF-8 -*-
2+ 
3+from pathlib import Path
4+ 
5+from msmodelslim.model.plugin_factory.base_loader import BaseModelAdapterLoader
6+ 
7+from .detect import LLM_ADAPTER_CLASS_PATH, resolve_adapter_class_path
8+ 
9+ 
10+class TransformersAdapterLoader(BaseModelAdapterLoader):
11+ ADAPTER_CLASS_PATH = LLM_ADAPTER_CLASS_PATH
12+ 
13+ def load(
14+ self,
15+ model_type: str,
16+ model_path: Path,
17+ trust_remote_code: bool = False,
18+ ):
19+ self.ADAPTER_CLASS_PATH = resolve_adapter_class_path(model_path)
20+ return super().load(model_type, model_path, trust_remote_code)
@@ -0,0 +1,103 @@
1+#!/usr/bin/env python
2+# -*- coding: UTF-8 -*-
3+ 
4+"""
5+-------------------------------------------------------------------------
6+This file is part of the MindStudio project.
7+Copyright (c) 2026 Huawei Technologies Co.,Ltd.
8+ 
9+MindStudio is licensed under Mulan PSL v2.
10+You can use this software according to the terms and conditions of the Mulan PSL v2.
11+You may obtain a copy of Mulan PSL v2 at:
12+ 
13+ http://license.coscl.org.cn/MulanPSL2
14+ 
15+THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
16+EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
17+MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
18+See the Mulan PSL v2 for more details.
19+-------------------------------------------------------------------------
20+"""
21+ 
22+from typing import Any, Generator, List
23+ 
24+from torch import nn
25+ 
26+from msmodelslim.core.base.protocol import ProcessRequest
27+from msmodelslim.core.const import DeviceType
28+from msmodelslim.utils.logging import logger_setter
29+from msmodelslim.utils.exception import InvalidModelError
30+from msmodelslim.utils.exception_decorator import exception_handler
31+from ..common.layer_wise_forward import (
32+ generated_decoder_layer_visit_func,
33+ transformers_generated_forward_func,
34+)
35+from ..common.transformers import TransformersModel
36+from ..interface_hub import (
37+ ModelInfoInterface,
38+ ModelSlimPipelineInterfaceV1,
39+)
40+ 
41+ 
42+@logger_setter()
43+class LLMTransformersModel( # pylint: disable=too-many-ancestors
44+ TransformersModel,
45+ ModelInfoInterface,
46+ ModelSlimPipelineInterfaceV1,
47+):
48+ """Generic HuggingFace LLM adapter for ``--model_type transformers``.
49+ 
50+ Suitable for standard decoder-layer LLMs. MoE / VLM / custom structures
51+ still need dedicated adapters.
52+ """
53+ 
54+ def get_model_type(self) -> str:
55+ return self.model_type
56+ 
57+ def get_model_pedigree(self) -> str:
58+ return "llm_transformers"
59+ 
60+ def handle_dataset(self, dataset: Any, device: DeviceType = DeviceType.NPU) -> List[Any]:
61+ with exception_handler(
62+ 'You are handling dataset with llm transformers model adapter but failed',
63+ ms_err_cls=InvalidModelError,
64+ action='Please ensure llm transformers model adapter match your model',
65+ ):
66+ return self._get_tokenized_data(dataset, device)
67+ 
68+ def init_model(self, device: DeviceType = DeviceType.NPU) -> nn.Module:
69+ with exception_handler(
70+ 'You are initializing model with llm transformers model adapter but failed',
71+ ms_err_cls=InvalidModelError,
72+ action='Please ensure llm transformers model adapter match your model. '
73+ 'For multimodal model (VLM/DiT) quantization, please implement a dedicated model adapter',
74+ ):
75+ return self._load_model(device)
76+ 
77+ def generate_model_visit(self, model: nn.Module) -> Generator[ProcessRequest, Any, None]:
78+ with exception_handler(
79+ 'You are generating model visit with llm transformers model adapter but failed',
80+ ms_err_cls=InvalidModelError,
81+ action='Please ensure llm transformers model adapter match your model',
82+ ):
83+ yield from generated_decoder_layer_visit_func(model)
84+ 
85+ def generate_model_forward(
86+ self,
87+ model: nn.Module,
88+ inputs: Any,
89+ ) -> Generator[ProcessRequest, Any, None]:
90+ with exception_handler(
91+ 'You are generating model forward with llm transformers model adapter but failed',
92+ ms_err_cls=InvalidModelError,
93+ action='Please ensure llm transformers model adapter match your model',
94+ ):
95+ yield from transformers_generated_forward_func(model, inputs)
96+ 
97+ def enable_kv_cache(self, model: nn.Module, need_kv_cache: bool) -> None:
98+ with exception_handler(
99+ 'You are enabling kv cache with llm transformers model adapter but failed',
100+ ms_err_cls=InvalidModelError,
101+ action='Please ensure llm transformers model adapter match your model',
102+ ):
103+ return self._enable_kv_cache(model, need_kv_cache)
@@ -24,13 +24,13 @@ import shutil
24import tempfile24import tempfile
25import unittest25import unittest
26from pathlib import Path26from pathlib import Path
27-from unittest.mock import MagicMock27+from unittest.mock import MagicMock, patch
28 28 
29from testing_utils.mock import mock_init_config29from testing_utils.mock import mock_init_config
30 30 
31from msmodelslim.core.const import QuantType31from msmodelslim.core.const import QuantType
32from msmodelslim.core.practice.interface import Metadata32from msmodelslim.core.practice.interface import Metadata
33-from msmodelslim.utils.exception import SchemaValidateError, ToDoError33+from msmodelslim.utils.exception import SchemaValidateError, ToDoError, UnsupportedError
34 34 
35mock_init_config()35mock_init_config()
36 36 
@@ -225,6 +225,38 @@ class TestCheckConfig(TestNaiveQuantizationAppBase):
225class TestGetBestPractice(TestNaiveQuantizationAppBase):225class TestGetBestPractice(TestNaiveQuantizationAppBase):
226 """测试 get_best_practice"""226 """测试 get_best_practice"""
227 227 
228+ def _make_app(self):
229+ from msmodelslim.app.naive_quantization.application import NaiveQuantizationApplication
230+ 
231+ return NaiveQuantizationApplication(
232+ practice_manager=MagicMock(),
233+ quant_service=MagicMock(),
234+ model_factory=MagicMock(),
235+ )
236+ 
237+ def _get_best_practice_with_mocked_yaml(self, model_type, apiversion, config_id="test_w8a8"):
238+ mock_model_adapter = MagicMock()
239+ mock_model_adapter.model_type = model_type
240+ mock_model_adapter.get_model_type.return_value = model_type
241+ app = self._make_app()
242+ mock_config = MagicMock()
243+ mock_config.apiversion = apiversion
244+ mock_config.metadata.config_id = config_id
245+ with (
246+ patch(
247+ "msmodelslim.app.naive_quantization.application.yaml_safe_load",
248+ return_value={},
249+ ),
250+ patch(
251+ "msmodelslim.app.naive_quantization.application.PracticeConfig.model_validate",
252+ return_value=mock_config,
253+ ),
254+ ):
255+ return app.get_best_practice(
256+ model_adapter=mock_model_adapter,
257+ config_path=Path("config.yaml"),
258+ )
259+ 
228 def test_get_best_practice_with_config_path(self):260 def test_get_best_practice_with_config_path(self):
229 """测试指定 config_path 时直接返回配置"""261 """测试指定 config_path 时直接返回配置"""
230 from msmodelslim.app.naive_quantization.application import NaiveQuantizationApplication262 from msmodelslim.app.naive_quantization.application import NaiveQuantizationApplication
@@ -290,6 +322,36 @@ spec: {}
290 quant_type=QuantType.W8A8,322 quant_type=QuantType.W8A8,
291 )323 )
292 324 
325+ def test_get_best_practice_raises_unsupported_error_when_transformers_and_vlm_apiversion(self):
326+ with self.assertRaises(UnsupportedError) as ctx:
327+ self._get_best_practice_with_mocked_yaml("transformers", "multimodal_vlm_modelslim_v1")
328+ 
329+ self.assertIn("VLM", str(ctx.exception))
330+ self.assertIn("transformers", str(ctx.exception))
331+ 
332+ def test_get_best_practice_raises_unsupported_error_when_transformers_and_sd_apiversion(self):
333+ with self.assertRaises(UnsupportedError) as ctx:
334+ self._get_best_practice_with_mocked_yaml("transformers", "multimodal_sd_modelslim_v1")
335+ 
336+ self.assertIn("DiT", str(ctx.exception))
337+ self.assertIn("transformers", str(ctx.exception))
338+ 
339+ def test_get_best_practice_returns_config_when_transformers_and_modelslim_v1(self):
340+ config = self._get_best_practice_with_mocked_yaml("transformers", "modelslim_v1")
341+ 
342+ self.assertEqual(config.metadata.config_id, "test_w8a8")
343+ self.assertEqual(config.apiversion, "modelslim_v1")
344+ 
345+ def test_get_best_practice_returns_config_when_other_model_type_and_vlm_apiversion(self):
346+ config = self._get_best_practice_with_mocked_yaml(
347+ "Qwen2.5-VL-7B-Instruct",
348+ "multimodal_vlm_modelslim_v1",
349+ config_id="vlm_ok",
350+ )
351+ 
352+ self.assertEqual(config.metadata.config_id, "vlm_ok")
353+ self.assertEqual(config.apiversion, "multimodal_vlm_modelslim_v1")
354+ 
293 355 
294class TestQuantParameterValidation(TestNaiveQuantizationAppBase):356class TestQuantParameterValidation(TestNaiveQuantizationAppBase):
295 """测试 quant 方法参数校验"""357 """测试 quant 方法参数校验"""
@@ -0,0 +1,89 @@
1+#!/usr/bin/env python
2+# -*- coding: UTF-8 -*-
3+ 
4+"""
5+-------------------------------------------------------------------------
6+This file is part of the MindStudio project.
7+Copyright (c) 2026 Huawei Technologies Co.,Ltd.
8+ 
9+MindStudio is licensed under Mulan PSL v2.
10+You can use this software according to the terms and conditions of the Mulan PSL v2.
11+You may obtain a copy of Mulan PSL v2 at:
12+ 
13+ http://license.coscl.org.cn/MulanPSL2
14+ 
15+THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
16+EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
17+MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
18+See the Mulan PSL v2 for more details.
19+-------------------------------------------------------------------------
20+"""
21+ 
22+import unittest
23+from pathlib import Path
24+from unittest.mock import patch
25+ 
26+from msmodelslim.model.transformers.detect import (
27+ LLM_ADAPTER_CLASS_PATH,
28+ ModelClass,
29+ detect_transformers_kind,
30+ resolve_adapter_class_path,
31+)
32+from msmodelslim.utils.exception import SecurityError, UnsupportedError
33+ 
34+ 
35+class TestDetectTransformersKind(unittest.TestCase):
36+ def setUp(self):
37+ self.model_path = Path("/tmp/transformers-model")
38+ 
39+ def test_detect_transformers_kind_returns_llm_when_config_has_no_hints(self):
40+ with patch("msmodelslim.model.transformers.detect.json_safe_load", return_value={"model_type": "llama"}):
41+ self.assertEqual(detect_transformers_kind(self.model_path), ModelClass.LLM)
42+ 
43+ def test_detect_transformers_kind_returns_vlm_when_vision_config_present(self):
44+ with patch("msmodelslim.model.transformers.detect.json_safe_load", return_value={"vision_config": {}}):
45+ self.assertEqual(detect_transformers_kind(self.model_path), ModelClass.VLM)
46+ 
47+ def test_detect_transformers_kind_returns_dit_when_diffusers_version_present(self):
48+ with patch(
49+ "msmodelslim.model.transformers.detect.json_safe_load",
50+ return_value={"_diffusers_version": "0.30.0"},
51+ ):
52+ self.assertEqual(detect_transformers_kind(self.model_path), ModelClass.DIT)
53+ 
54+ def test_detect_transformers_kind_raises_unsupported_error_when_config_missing(self):
55+ with patch(
56+ "msmodelslim.model.transformers.detect.json_safe_load",
57+ side_effect=SecurityError("invalid path"),
58+ ):
59+ with self.assertRaises(UnsupportedError) as ctx:
60+ detect_transformers_kind(self.model_path)
61+ 
62+ self.assertIn("config.json", str(ctx.exception))
63+ self.assertIn("transformers", str(ctx.exception))
64+ 
65+ 
66+class TestResolveAdapterClassPath(unittest.TestCase):
67+ def setUp(self):
68+ self.model_path = Path("/tmp/transformers-model")
69+ 
70+ def test_resolve_adapter_class_path_returns_llm_path_when_llm_config(self):
71+ with patch("msmodelslim.model.transformers.detect.json_safe_load", return_value={}):
72+ self.assertEqual(resolve_adapter_class_path(self.model_path), LLM_ADAPTER_CLASS_PATH)
73+ 
74+ def test_resolve_adapter_class_path_raises_unsupported_error_when_vlm_config(self):
75+ with patch("msmodelslim.model.transformers.detect.json_safe_load", return_value={"vision_config": {}}):
76+ with self.assertRaises(UnsupportedError) as ctx:
77+ resolve_adapter_class_path(self.model_path)
78+ 
79+ self.assertIn("VLM", str(ctx.exception))
80+ 
81+ def test_resolve_adapter_class_path_raises_unsupported_error_when_dit_config(self):
82+ with patch(
83+ "msmodelslim.model.transformers.detect.json_safe_load",
84+ return_value={"_diffusers_version": "0.30.0"},
85+ ):
86+ with self.assertRaises(UnsupportedError) as ctx:
87+ resolve_adapter_class_path(self.model_path)
88+ 
89+ self.assertIn("DiT", str(ctx.exception))
@@ -0,0 +1,130 @@
1+#!/usr/bin/env python
2+# -*- coding: UTF-8 -*-
3+ 
4+"""
5+-------------------------------------------------------------------------
6+This file is part of the MindStudio project.
7+Copyright (c) 2026 Huawei Technologies Co.,Ltd.
8+ 
9+MindStudio is licensed under Mulan PSL v2.
10+You can use this software according to the terms and conditions of the Mulan PSL v2.
11+You may obtain a copy of Mulan PSL v2 at:
12+ 
13+ http://license.coscl.org.cn/MulanPSL2
14+ 
15+THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
16+EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
17+MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
18+See the Mulan PSL v2 for more details.
19+-------------------------------------------------------------------------
20+"""
21+ 
22+import unittest
23+from pathlib import Path
24+from types import SimpleNamespace
25+from unittest.mock import patch
26+ 
27+from msmodelslim.model.transformers.detect import LLM_ADAPTER_CLASS_PATH
28+from msmodelslim.model.transformers.loader import TransformersAdapterLoader
29+from msmodelslim.model.transformers.model_adapter import LLMTransformersModel
30+from msmodelslim.utils.exception import UnsupportedError, VersionError
31+ 
32+ 
33+def _mock_adapter_init(self, model_type, model_path, trust_remote_code=False):
34+ self.model_type = model_type
35+ self.model_path = model_path
36+ self.trust_remote_code = trust_remote_code
37+ 
38+ 
39+class TestTransformersAdapterLoader(unittest.TestCase):
40+ def setUp(self):
41+ self.model_type = "transformers"
42+ self.model_path = Path("/tmp/transformers-model")
43+ self.loader = TransformersAdapterLoader()
44+ 
45+ def test_adapter_class_path_points_to_llm_transformers_model_when_defined(self):
46+ self.assertEqual(
47+ TransformersAdapterLoader.ADAPTER_CLASS_PATH,
48+ LLM_ADAPTER_CLASS_PATH,
49+ )
50+ self.assertEqual(
51+ TransformersAdapterLoader.ADAPTER_CLASS_PATH,
52+ "msmodelslim.model.transformers.model_adapter:LLMTransformersModel",
53+ )
54+ 
55+ def _load_with_patches(self, config=None, **load_kwargs):
56+ if config is None:
57+ config = {}
58+ with patch("msmodelslim.model.transformers.detect.json_safe_load", return_value=config):
59+ with patch("msmodelslim.model.plugin_factory.base_loader.DependencyChecker.set_plugin"):
60+ with patch("msmodelslim.model.plugin_factory.base_loader.DependencyChecker._check_single"):
61+ with patch("msmodelslim.model.plugin_factory.base_loader.get_require_packages", return_value={}):
62+ with patch("msmodelslim.model.plugin_factory.base_loader.import_module") as mock_import:
63+ mock_import.return_value = SimpleNamespace(LLMTransformersModel=LLMTransformersModel)
64+ with patch(
65+ "msmodelslim.model.transformers.model_adapter.TransformersModel.__init__",
66+ _mock_adapter_init,
67+ ):
68+ return self.loader.load(
69+ model_type=self.model_type,
70+ model_path=self.model_path,
71+ **load_kwargs,
72+ )
73+ 
74+ def test_load_returns_llm_transformers_model_when_trust_remote_code_true(self):
75+ adapter = self._load_with_patches(trust_remote_code=True)
76+ 
77+ self.assertIsInstance(adapter, LLMTransformersModel)
78+ self.assertEqual(adapter.model_type, self.model_type)
79+ self.assertEqual(adapter.model_path, self.model_path)
80+ self.assertTrue(adapter.trust_remote_code)
81+ 
82+ def test_load_passes_false_when_trust_remote_code_omitted(self):
83+ adapter = self._load_with_patches()
84+ self.assertFalse(adapter.trust_remote_code)
85+ 
86+ def test_load_raises_unsupported_error_when_vlm_config(self):
87+ with self.assertRaises(UnsupportedError) as ctx:
88+ self._load_with_patches(config={"vision_config": {}})
89+ 
90+ self.assertIn("VLM", str(ctx.exception))
91+ self.assertIn("transformers", str(ctx.exception))
92+ 
93+ def test_load_raises_unsupported_error_when_dit_config(self):
94+ with self.assertRaises(UnsupportedError) as ctx:
95+ self._load_with_patches(config={"_diffusers_version": "0.30.0"})
96+ 
97+ self.assertIn("DiT", str(ctx.exception))
98+ self.assertIn("transformers", str(ctx.exception))
99+ 
100+ def test_precheck_sets_plugin_name_when_model_type_valid(self):
101+ with patch(
102+ "msmodelslim.model.plugin_factory.base_loader.msmodelslim_config",
103+ SimpleNamespace(model_adapter_dependencies={}),
104+ ):
105+ with patch("msmodelslim.model.plugin_factory.base_loader.DependencyChecker.set_plugin") as mock_set:
106+ with patch("msmodelslim.model.plugin_factory.base_loader.DependencyChecker._check_single"):
107+ self.loader.precheck(
108+ model_type=self.model_type,
109+ model_path=self.model_path,
110+ )
111+ 
112+ plugin_name = mock_set.call_args[0][0]
113+ self.assertEqual(plugin_name, f"msmodelslim.model_adapter.plugins:{self.model_type}")
114+ 
115+ def test_precheck_sets_is_match_false_when_dependency_check_fails(self):
116+ self.loader._require_packages = {"numpy": ">=1.26"}
117+ with patch(
118+ "msmodelslim.model.plugin_factory.base_loader.msmodelslim_config",
119+ SimpleNamespace(model_adapter_dependencies={}),
120+ ):
121+ with patch(
122+ "msmodelslim.model.plugin_factory.base_loader.DependencyChecker._check_single",
123+ side_effect=VersionError("dependency mismatch"),
124+ ):
125+ self.loader.precheck(
126+ model_type=self.model_type,
127+ model_path=self.model_path,
128+ )
129+ 
130+ self.assertFalse(self.loader._is_match)
@@ -0,0 +1,155 @@
1+#!/usr/bin/env python
2+# -*- coding: UTF-8 -*-
3+ 
4+"""
5+-------------------------------------------------------------------------
6+This file is part of the MindStudio project.
7+Copyright (c) 2026 Huawei Technologies Co.,Ltd.
8+ 
9+MindStudio is licensed under Mulan PSL v2.
10+You can use this software according to the terms and conditions of the Mulan PSL v2.
11+You may obtain a copy of Mulan PSL v2 at:
12+ 
13+ http://license.coscl.org.cn/MulanPSL2
14+ 
15+THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
16+EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
17+MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
18+See the Mulan PSL v2 for more details.
19+-------------------------------------------------------------------------
20+"""
21+ 
22+import unittest
23+from pathlib import Path
24+from unittest.mock import MagicMock, patch
25+ 
26+from msmodelslim.core.const import DeviceType
27+from msmodelslim.model.transformers.model_adapter import LLMTransformersModel
28+from msmodelslim.utils.exception import InvalidModelError
29+ 
30+ 
31+class TestLLMTransformersModel(unittest.TestCase):
32+ def setUp(self):
33+ self.model_type = "transformers"
34+ self.model_path = Path(".")
35+ 
36+ def _make_adapter(self):
37+ with patch(
38+ "msmodelslim.model.transformers.model_adapter.TransformersModel.__init__",
39+ return_value=None,
40+ ):
41+ adapter = LLMTransformersModel(model_type=self.model_type, model_path=self.model_path)
42+ adapter.model_type = self.model_type
43+ adapter.model_path = self.model_path
44+ return adapter
45+ 
46+ def test_get_model_type_returns_transformers_when_called(self):
47+ adapter = self._make_adapter()
48+ self.assertEqual(adapter.get_model_type(), "transformers")
49+ 
50+ def test_get_model_pedigree_returns_llm_transformers_when_called(self):
51+ adapter = self._make_adapter()
52+ self.assertEqual(adapter.get_model_pedigree(), "llm_transformers")
53+ 
54+ def test_init_model_returns_model_when_load_succeeds(self):
55+ adapter = self._make_adapter()
56+ mock_model = MagicMock()
57+ adapter._load_model = MagicMock(return_value=mock_model)
58+ 
59+ result = adapter.init_model(device=DeviceType.NPU)
60+ 
61+ self.assertIs(result, mock_model)
62+ adapter._load_model.assert_called_once_with(DeviceType.NPU)
63+ 
64+ def test_init_model_raises_invalid_model_error_when_load_fails(self):
65+ adapter = self._make_adapter()
66+ adapter._load_model = MagicMock(side_effect=Exception("Loading failed"))
67+ 
68+ with self.assertRaises(InvalidModelError) as ctx:
69+ adapter.init_model(device=DeviceType.NPU)
70+ 
71+ self.assertIn("dedicated model adapter", ctx.exception.action)
72+ self.assertIn("VLM/DiT", ctx.exception.action)
73+ 
74+ def test_handle_dataset_returns_tokenized_data_when_helper_succeeds(self):
75+ adapter = self._make_adapter()
76+ mock_dataset = ["data1", "data2"]
77+ adapter._get_tokenized_data = MagicMock(return_value=mock_dataset)
78+ 
79+ result = adapter.handle_dataset(dataset="test_data", device=DeviceType.CPU)
80+ 
81+ self.assertEqual(result, mock_dataset)
82+ adapter._get_tokenized_data.assert_called_once_with("test_data", DeviceType.CPU)
83+ 
84+ def test_handle_dataset_raises_invalid_model_error_when_helper_fails(self):
85+ adapter = self._make_adapter()
86+ adapter._get_tokenized_data = MagicMock(side_effect=Exception("Processing failed"))
87+ 
88+ with self.assertRaises(InvalidModelError):
89+ adapter.handle_dataset(dataset="test_data", device=DeviceType.CPU)
90+ 
91+ @patch("msmodelslim.model.transformers.model_adapter.generated_decoder_layer_visit_func")
92+ def test_generate_model_visit_yields_requests_when_helper_succeeds(self, mock_visit):
93+ mock_request = MagicMock()
94+ mock_visit.return_value = iter([mock_request])
95+ adapter = self._make_adapter()
96+ mock_model = MagicMock()
97+ 
98+ result = list(adapter.generate_model_visit(mock_model))
99+ 
100+ self.assertEqual(result, [mock_request])
101+ mock_visit.assert_called_once_with(mock_model)
102+ 
103+ @patch("msmodelslim.model.transformers.model_adapter.generated_decoder_layer_visit_func")
104+ def test_generate_model_visit_raises_invalid_model_error_when_helper_fails(self, mock_visit):
105+ mock_visit.side_effect = Exception("visit failed")
106+ adapter = self._make_adapter()
107+ 
108+ with self.assertRaises(InvalidModelError):
109+ list(adapter.generate_model_visit(MagicMock()))
110+ 
111+ @patch("msmodelslim.model.transformers.model_adapter.transformers_generated_forward_func")
112+ def test_generate_model_forward_yields_requests_when_helper_succeeds(self, mock_forward):
113+ mock_request = MagicMock()
114+ mock_forward.return_value = iter([mock_request])
115+ adapter = self._make_adapter()
116+ mock_model = MagicMock()
117+ mock_inputs = MagicMock()
118+ 
119+ result = list(adapter.generate_model_forward(mock_model, mock_inputs))
120+ 
121+ self.assertEqual(result, [mock_request])
122+ mock_forward.assert_called_once_with(mock_model, mock_inputs)
123+ 
124+ @patch("msmodelslim.model.transformers.model_adapter.transformers_generated_forward_func")
125+ def test_generate_model_forward_raises_invalid_model_error_when_helper_fails(self, mock_forward):
126+ mock_forward.side_effect = Exception("forward failed")
127+ adapter = self._make_adapter()
128+ 
129+ with self.assertRaises(InvalidModelError):
130+ list(adapter.generate_model_forward(MagicMock(), MagicMock()))
131+ 
132+ def test_enable_kv_cache_delegates_to_helper_when_need_kv_cache_true(self):
133+ adapter = self._make_adapter()
134+ mock_model = MagicMock()
135+ adapter._enable_kv_cache = MagicMock(return_value=None)
136+ 
137+ adapter.enable_kv_cache(model=mock_model, need_kv_cache=True)
138+ 
139+ adapter._enable_kv_cache.assert_called_once_with(mock_model, True)
140+ 
141+ def test_enable_kv_cache_delegates_to_helper_when_need_kv_cache_false(self):
142+ adapter = self._make_adapter()
143+ mock_model = MagicMock()
144+ adapter._enable_kv_cache = MagicMock(return_value=None)
145+ 
146+ adapter.enable_kv_cache(model=mock_model, need_kv_cache=False)
147+ 
148+ adapter._enable_kv_cache.assert_called_once_with(mock_model, False)
149+ 
150+ def test_enable_kv_cache_raises_invalid_model_error_when_helper_fails(self):
151+ adapter = self._make_adapter()
152+ adapter._enable_kv_cache = MagicMock(side_effect=Exception("Enable KV cache failed"))
153+ 
154+ with self.assertRaises(InvalidModelError):
155+ adapter.enable_kv_cache(model=MagicMock(), need_kv_cache=True)