Repository navigation
Autoquant and GPTQ in support in Megatron-Core [OMNIML-3151] #1562
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 53 commits
676eac4
9143e02
57e5f26
2dea94a
918ed6a
05a436f
ff20eca
985da85
d88e54a
5f291a6
5c9cd43
c5c7a2e
d63bf70
2ba29fd
e14fa62
e985e93
9088966
68e6837
967d5ef
5b141b5
55ed710
2acbc03
f40849e
a59eecd
f68bab8
8f94cf8
fbaa4a3
d4d91c8
bbf7f35
fe80756
6c2d446
29475fe
850799b
470b48f
b9a1a6b
0608daf
289dbdc
35bd94f
f0d1c55
351c6c6
14cd2df
9749a49
20a888d
d6b01ac
6d75cbc
51ba351
f518c1a
848c317
c976a23
485750b
8c307de
eccabc8
961abac
2e5100c
054e353
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
|
realAsma marked this conversation as resolved.
|
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -378,13 +378,14 @@ def get_score(self, recipe: QuantRecipe) -> float: | |||||||||||||||||||||||
| total_score += importance.cpu().item() | ||||||||||||||||||||||||
| continue | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| if parallel_state.expert_model_parallel_group.is_initialized(): | ||||||||||||||||||||||||
| # TODO: Support expert model parallelism for score estimation | ||||||||||||||||||||||||
| warnings.warn("AutoQuantize does not support expert model parallelism yet.") | ||||||||||||||||||||||||
| importance = importance.cpu() | ||||||||||||||||||||||||
| importance = DistributedProcessGroup.get_dist_syncd_obj( | ||||||||||||||||||||||||
| importance, | ||||||||||||||||||||||||
| [parallel_state.tensor_parallel_group, parallel_state.data_parallel_group], | ||||||||||||||||||||||||
| [ | ||||||||||||||||||||||||
| parallel_state.tensor_parallel_group, | ||||||||||||||||||||||||
| parallel_state.data_parallel_group, | ||||||||||||||||||||||||
| parallel_state.expert_model_parallel_group, | ||||||||||||||||||||||||
| ], | ||||||||||||||||||||||||
| sum, | ||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||
|
jenchen13 marked this conversation as resolved.
|
||||||||||||||||||||||||
| total_score += importance.item() | ||||||||||||||||||||||||
|
|
@@ -408,13 +409,12 @@ def get_cost(self, recipe: QuantRecipe, cost_weight: float | None = None) -> flo | |||||||||||||||||||||||
| cost += weight_size * recipe.compression | ||||||||||||||||||||||||
| continue | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| if parallel_state.expert_model_parallel_group.is_initialized(): | ||||||||||||||||||||||||
| # TODO: Support expert model parallelism | ||||||||||||||||||||||||
| warnings.warn("AutoQuantize does not support expert model parallelism yet.") | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| weight_size = DistributedProcessGroup.get_dist_syncd_obj( | ||||||||||||||||||||||||
| weight_size, | ||||||||||||||||||||||||
| [parallel_state.tensor_parallel_group], | ||||||||||||||||||||||||
| [ | ||||||||||||||||||||||||
| parallel_state.tensor_parallel_group, | ||||||||||||||||||||||||
| parallel_state.expert_model_parallel_group, | ||||||||||||||||||||||||
| ], | ||||||||||||||||||||||||
| sum, | ||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||
|
jenchen13 marked this conversation as resolved.
|
||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
|
|
@@ -466,6 +466,8 @@ class _AutoQuantizeBaseSearcher(BaseSearcher, ABC): | |||||||||||||||||||||||
| # gate_proj, up_proj, down_proj for Qwen3 like MoE models | ||||||||||||||||||||||||
| r"^(.*?\.mlp\.experts)\.\d+\.(gate_proj|up_proj|down_proj)$", | ||||||||||||||||||||||||
| r"^(.*?\.mixer\.experts)\.\d+\.(up_proj|down_proj)$", # NemotronH MoE experts | ||||||||||||||||||||||||
| # NemotronH MoE experts in MCore naming (linear_fc1=gate+up fused, linear_fc2=down) | ||||||||||||||||||||||||
| r"^(.*?\.mlp\.experts\.local_experts)\.\d+\.(linear_fc1|linear_fc2)$", | ||||||||||||||||||||||||
| r"^(.*?)\.(gate_proj|up_proj)$", # gate_proj, up_proj for llama like models | ||||||||||||||||||||||||
| r"^(.*?)\.(\d+\.(w1|w2|w3))$", # mixtral experts | ||||||||||||||||||||||||
| r"^(.*?)\.((w1_linear|w2_linear|w3_linear)\.\d+)$", # dbrx experts | ||||||||||||||||||||||||
|
|
@@ -875,6 +877,15 @@ def _get_total_weight_size(modules): | |||||||||||||||||||||||
| for module in modules | ||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| @staticmethod | ||||||||||||||||||||||||
| def _get_total_weight_size_from_candidate_stats(candidate_stats): | ||||||||||||||||||||||||
| no_quant_recipe = QuantRecipe(quant_cfg=None) | ||||||||||||||||||||||||
| total_weight_size = 0 | ||||||||||||||||||||||||
| for candidate_stat in candidate_stats.values(): | ||||||||||||||||||||||||
| no_quant_idx = candidate_stat["formats"].index(no_quant_recipe) | ||||||||||||||||||||||||
| total_weight_size += candidate_stat["costs"][no_quant_idx] | ||||||||||||||||||||||||
| return total_weight_size | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| def _get_constraints_for_search(self, max_weight_size, lower_bound=None): | ||||||||||||||||||||||||
| constraints = { | ||||||||||||||||||||||||
| "weight_size_after_compression": ( | ||||||||||||||||||||||||
|
|
@@ -905,9 +916,14 @@ def run_search(self): | |||||||||||||||||||||||
| ) | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| compression = self._get_formatted_weight_compression_constraint() | ||||||||||||||||||||||||
| total_weight_size = self._cost_model.total_weight_size( | ||||||||||||||||||||||||
| self.model.named_modules(), self._is_auto_quantize_module, self.config["cost"] | ||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||
| if self.candidate_stats: | ||||||||||||||||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @jenchen13 why do we need the else branch here?
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The else branch is never used.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. hmm I think i kept it for backward compatibility |
||||||||||||||||||||||||
| total_weight_size = self._get_total_weight_size_from_candidate_stats( | ||||||||||||||||||||||||
| self.candidate_stats | ||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||
| else: | ||||||||||||||||||||||||
| total_weight_size = self._cost_model.total_weight_size( | ||||||||||||||||||||||||
| self.model.named_modules(), self._is_auto_quantize_module, self.config["cost"] | ||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||
|
realAsma marked this conversation as resolved.
Outdated
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Does this work? |
||||||||||||||||||||||||
| self.cost_denominator = total_weight_size | ||||||||||||||||||||||||
| max_weight_size = total_weight_size * compression | ||||||||||||||||||||||||
| if verbose: | ||||||||||||||||||||||||
|
|
@@ -928,12 +944,16 @@ def run_search(self): | |||||||||||||||||||||||
| best_recipe = {} | ||||||||||||||||||||||||
| best_constraints, best_scores = 0, 0 | ||||||||||||||||||||||||
| for name, best_hparam_recipe_info in best_recipe_info.items(): | ||||||||||||||||||||||||
| # Solvers could give different solutions for the same layer across DP/TP groups even though | ||||||||||||||||||||||||
| # the scores and costs are the same. Lets make sure the same recipe is selected across DP/TP | ||||||||||||||||||||||||
| # Solvers could give different solutions for the same layer across DP/TP/EP groups even though | ||||||||||||||||||||||||
| # the scores and costs are the same. Lets make sure the same recipe is selected across DP/TP/EP | ||||||||||||||||||||||||
| _ps = self.model.get_submodule(name.split(".quant_recipe")[0]).parallel_state | ||||||||||||||||||||||||
| best_format = DistributedProcessGroup.get_dist_syncd_obj( | ||||||||||||||||||||||||
| best_hparam_recipe_info["format"], | ||||||||||||||||||||||||
| [_ps.data_parallel_group, _ps.tensor_parallel_group], | ||||||||||||||||||||||||
| [ | ||||||||||||||||||||||||
| _ps.data_parallel_group, | ||||||||||||||||||||||||
| _ps.tensor_parallel_group, | ||||||||||||||||||||||||
| _ps.expert_model_parallel_group, | ||||||||||||||||||||||||
| ], | ||||||||||||||||||||||||
| lambda a: a[0], | ||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -16,6 +16,7 @@ | |
| """Support quantization for megatron linear layers.""" | ||
|
|
||
| import types | ||
| from contextlib import contextmanager | ||
| from typing import Any | ||
|
|
||
| import megatron.core.parallel_state as mcore_parallel | ||
|
|
@@ -39,11 +40,13 @@ | |
| from modelopt.torch.utils import warn_rank_0 | ||
| from modelopt.torch.utils.distributed import ParallelState | ||
|
|
||
| from ..algorithms import AutoQuantizeGradientSearcher | ||
| from ..conversion import maybe_promote_nvfp4_static_quantizer | ||
| from ..nn import QuantModule, QuantModuleRegistry, SequentialQuantizer, TensorQuantizer | ||
| from ..nn.modules.quant_linear import RealQuantLinear | ||
| from ..qtensor import QTensorWrapper | ||
| from ..utils import sync_moe_expert_amax | ||
| from ..utils.layerwise_calib import LayerActivationCollector | ||
| from .custom import CUSTOM_MODEL_PLUGINS, _ParallelLinear | ||
|
|
||
| try: | ||
|
|
@@ -612,10 +615,11 @@ def _setup(self): | |
| expert_model_parallel_group=mcore_parallel.get_expert_model_parallel_group(), | ||
| ) | ||
|
|
||
| # Initialize parallel state for submodules local_experts.*.linear_fc1 and local_experts.*.linear_fc2 | ||
| # These child linears are still native MCore modules here. Seed `_parallel_state` | ||
| # directly so the later QuantModule conversion sees the intended parallel state. | ||
| for expert in self.local_experts: | ||
| expert.linear_fc1.parallel_state = self.parallel_state | ||
| expert.linear_fc2.parallel_state = self.parallel_state | ||
| expert.linear_fc1._parallel_state = self.parallel_state | ||
| expert.linear_fc2._parallel_state = self.parallel_state | ||
|
|
||
| def layer_sync_moe_local_experts_amax(self, sync_weight_amax=False): | ||
| """Sync quantizer amax across local experts in a SequentialMLP. | ||
|
|
@@ -721,9 +725,10 @@ def _setup(self): | |
| tensor_parallel_group=mcore_parallel.get_expert_tensor_parallel_group(), | ||
| expert_model_parallel_group=mcore_parallel.get_expert_model_parallel_group(), | ||
| ) | ||
| # initialize parallel state for submodules linear_fc1 and linear_fc2 | ||
| self.linear_fc1.parallel_state = self.parallel_state | ||
| self.linear_fc2.parallel_state = self.parallel_state | ||
| # These child linears are still native MCore modules here. Seed `_parallel_state` | ||
| # directly so the later QuantModule conversion sees the intended parallel state. | ||
| self.linear_fc1._parallel_state = self.parallel_state | ||
| self.linear_fc2._parallel_state = self.parallel_state | ||
|
|
||
| @QuantModuleRegistry.register({TEDotProductAttention: "TEDotProductAttention"}) | ||
| class _QuantTEDotProductAttention(QuantModule): | ||
|
|
@@ -779,3 +784,43 @@ def sharded_state_dict(self, prefix="", sharded_offsets=(), metadata=None): | |
| # Affine KVCache Quant bias vector. | ||
| state_dict = self.state_dict(prefix="", keep_vars=True) | ||
| return make_sharded_tensors_for_checkpoint(state_dict, prefix, {}, sharded_offsets) | ||
|
|
||
|
|
||
| def _is_supported_megatron_model(model: torch.nn.Module) -> bool: | ||
| return isinstance(model, MegatronModule) | ||
|
|
||
|
|
||
| @contextmanager | ||
| def _megatron_grad_ckpt_context(model: torch.nn.Module): | ||
| # Megatron configures activation recompute at model build time via TransformerConfig, | ||
| # so there is no runtime flag to flip here. | ||
| yield | ||
|
|
||
|
|
||
| def _is_param_grad_enabled_for_megatron(pname: str, model: torch.nn.Module) -> bool: | ||
| return "weight" in pname | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [IMPORTANT Performance] The whole point of the Why it matters: with Suggestion: enable grad more selectively. If the concern is pipeline parallelism (where embedding lives only on the first PP stage, so |
||
|
|
||
|
|
||
| AutoQuantizeGradientSearcher.register_custom_support( | ||
| _is_supported_megatron_model, | ||
| _megatron_grad_ckpt_context, | ||
| _is_param_grad_enabled_for_megatron, | ||
| ) | ||
|
|
||
|
|
||
| def get_mcore_layerwise_calibration_layers( | ||
| model: torch.nn.Module, | ||
| ) -> list[torch.nn.Module] | torch.nn.ModuleList | None: | ||
| if not hasattr(model, "decoder") or not hasattr(model.decoder, "layers"): | ||
| return None | ||
| decoder_layers = model.decoder.layers | ||
| if getattr(model, "output_layer", None) is None: | ||
| return decoder_layers | ||
| layers = list(decoder_layers) | ||
| layers.append(model.output_layer) | ||
| return layers | ||
|
Comment on lines
+819
to
+821
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [IMPORTANT Algorithm] When
Correctness is preserved (the patched forward handles Suggestion: if |
||
|
|
||
|
|
||
| LayerActivationCollector.register_decoder_layer_support( | ||
| _is_supported_megatron_model, get_mcore_layerwise_calibration_layers | ||
| ) | ||
Uh oh!
There was an error while loading. Please reload this page.