mirror of
https://github.com/NVIDIA/Model-Optimizer.git
synced 2026-10-02 03:14:52 +08:00
Update MOE block detection logic and enable in huggingface_script.sh (#962)
### What does this PR do? Type of change: Bug fix Add moe expert calib ratio in huggingface_script.sh Also fix minimax2.5 MOE detection which does not follow other HF MOE layer convention ### Usage scripts/huggingface_example.sh --model <MiniMax-M2.5> --quant nvfp4 --moe_calib_experts_ratio 1.0 --trust_remote_code ### Before your PR is "*Ready for review*" Make sure you read and follow [Contributor guidelines](https://github.com/NVIDIA/Model-Optimizer/blob/main/CONTRIBUTING.md) and your commits are signed (`git commit -s -S`). Make sure you read and follow the [Security Best Practices](https://github.com/NVIDIA/Model-Optimizer/blob/main/SECURITY.md#security-coding-practices-for-contributors) (e.g. avoiding hardcoded `trust_remote_code=True`, using `torch.load(..., weights_only=True)`, avoiding `pickle`, etc.). - Is this change backward compatible?: ✅ / ❌ / N/A <!--- If ❌, explain why. --> - If you copied code from any other source, did you follow IP policy in [CONTRIBUTING.md](https://github.com/NVIDIA/Model-Optimizer/blob/main/CONTRIBUTING.md#-copying-code-from-other-sources)?: ✅ / ❌ / N/A <!--- Mandatory --> - Did you write any new necessary tests?: ✅ / ❌ / N/A <!--- Mandatory for new features or examples. --> - Did you update [Changelog](https://github.com/NVIDIA/Model-Optimizer/blob/main/CHANGELOG.rst)?: ✅ / ❌ / N/A <!--- Only for new features, API changes, critical bug fixes or backward incompatible changes. --> ### Additional Information <!-- E.g. related issue. --> <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Configure MOE calibration experts ratio for quantization via an environment/option, enabling finer control over calibration. * **Bug Fixes** * Improved detection of sparse MOE blocks to handle varying expert/topology layouts, inferring expert counts when needed for more reliable processing. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Signed-off-by: Chenjie Luo <chenjiel@nvidia.com>
This commit is contained in:
@@ -139,6 +139,10 @@ if [ -n "$CALIB_SEQ" ]; then
|
||||
PTQ_ARGS+=" --calib_seq=$CALIB_SEQ "
|
||||
fi
|
||||
|
||||
if [ -n "$MOE_CALIB_EXPERTS_RATIO" ]; then
|
||||
PTQ_ARGS+=" --moe_calib_experts_ratio=$MOE_CALIB_EXPERTS_RATIO "
|
||||
fi
|
||||
|
||||
if ! $VERBOSE; then
|
||||
PTQ_ARGS+=" --no-verbose "
|
||||
fi
|
||||
|
||||
@@ -36,7 +36,7 @@ parse_options() {
|
||||
USE_SEQ_DEVICE_MAP=false
|
||||
|
||||
# Parse command-line options
|
||||
ARGS=$(getopt -o "" -l "model:,quant:,kv_cache_quant:,tp:,pp:,sparsity:,awq_block_size:,calib:,calib_batch_size:,auto_quantize_bits:,output:,batch:,tasks:,lm_eval_tasks:,lm_eval_limit:,simple_eval_tasks:,trust_remote_code,use_seq_device_map,gpu_max_mem_percentage:,kv_cache_free_gpu_memory_fraction:,low_memory_mode,no-verbose,calib_dataset:,calib_seq:,auto_quantize_method:,auto_quantize_score_size:,auto_quantize_checkpoint:" -n "$0" -- "$@")
|
||||
ARGS=$(getopt -o "" -l "model:,quant:,kv_cache_quant:,tp:,pp:,sparsity:,awq_block_size:,calib:,calib_batch_size:,auto_quantize_bits:,output:,batch:,tasks:,lm_eval_tasks:,lm_eval_limit:,simple_eval_tasks:,trust_remote_code,use_seq_device_map,gpu_max_mem_percentage:,kv_cache_free_gpu_memory_fraction:,low_memory_mode,no-verbose,calib_dataset:,calib_seq:,auto_quantize_method:,auto_quantize_score_size:,auto_quantize_checkpoint:,moe_calib_experts_ratio:" -n "$0" -- "$@")
|
||||
|
||||
eval set -- "$ARGS"
|
||||
while true; do
|
||||
@@ -68,6 +68,7 @@ parse_options() {
|
||||
--auto_quantize_method ) AUTO_QUANTIZE_METHOD="$2"; shift 2;;
|
||||
--auto_quantize_score_size ) AUTO_QUANTIZE_SCORE_SIZE="$2"; shift 2;;
|
||||
--auto_quantize_checkpoint ) AUTO_QUANTIZE_CHECKPOINT="$2"; shift 2;;
|
||||
--moe_calib_experts_ratio ) MOE_CALIB_EXPERTS_RATIO="$2"; shift 2;;
|
||||
-- ) shift; break ;;
|
||||
* ) break ;;
|
||||
esac
|
||||
@@ -156,5 +157,6 @@ parse_options() {
|
||||
echo "auto_quantize_method: $AUTO_QUANTIZE_METHOD"
|
||||
echo "auto_quantize_score_size: $AUTO_QUANTIZE_SCORE_SIZE"
|
||||
echo "auto_quantize_checkpoint: $AUTO_QUANTIZE_CHECKPOINT"
|
||||
echo "moe_calib_experts_ratio: $MOE_CALIB_EXPERTS_RATIO"
|
||||
echo "================="
|
||||
}
|
||||
|
||||
@@ -1115,7 +1115,7 @@ def _is_sparse_moe_block(module):
|
||||
|
||||
All HuggingFace MoE blocks (Mixtral, Qwen3Moe, Qwen2Moe, Qwen3Next, Llama4, MiniMax, etc.)
|
||||
share a common structural pattern: a ``gate`` (TopKRouter) sub-module with routing attributes
|
||||
(``top_k`` and ``num_experts``), and an ``experts`` sub-module.
|
||||
(``top_k``, some may have ``num_experts``), and an ``experts`` sub-module.
|
||||
|
||||
This function detects that pattern instead of relying on class names, making it forward-compatible
|
||||
with new MoE architectures. Some MoE models (e.g. Glm4MoeMoE) have ``gate`` and ``experts`` but
|
||||
@@ -1134,7 +1134,12 @@ def _is_sparse_moe_block(module):
|
||||
return True
|
||||
|
||||
# Fallback: top_k + num_experts on the block itself (older transformers, e.g. v4.x Qwen3Next)
|
||||
return hasattr(module, "top_k") and hasattr(module, "num_experts")
|
||||
if hasattr(module, "top_k"):
|
||||
if not hasattr(module, "num_experts") and hasattr(module.experts, "__len__"):
|
||||
module.num_experts = len(module.experts)
|
||||
return hasattr(module, "num_experts")
|
||||
|
||||
return False
|
||||
|
||||
|
||||
def register_sparse_moe_on_the_fly(model):
|
||||
|
||||
@@ -133,11 +133,19 @@ class TestIsSparseBlock:
|
||||
module.gate = gate
|
||||
assert _is_sparse_moe_block(module) is False
|
||||
|
||||
def test_block_level_only_top_k_returns_false(self):
|
||||
"""Only top_k on block (no num_experts) -> fallback fails."""
|
||||
def test_block_level_top_k_infers_num_experts(self):
|
||||
"""top_k on block + experts with __len__ -> num_experts is inferred, returns True."""
|
||||
module = nn.Module()
|
||||
module.experts = nn.ModuleList([nn.Linear(8, 8)])
|
||||
module.top_k = 2
|
||||
assert _is_sparse_moe_block(module) is True
|
||||
assert module.num_experts == 1
|
||||
|
||||
def test_block_level_top_k_no_len_returns_false(self):
|
||||
"""top_k on block but experts has no __len__ -> cannot infer num_experts, returns False."""
|
||||
module = nn.Module()
|
||||
module.experts = nn.Module()
|
||||
module.top_k = 2
|
||||
assert _is_sparse_moe_block(module) is False
|
||||
|
||||
def test_block_level_only_num_experts_returns_false(self):
|
||||
|
||||
Reference in New Issue
Block a user