## What does this PR do?
**Type of change:** New feature
- So far users we didnt have the NAS step from the Minitron paper so
users had to manually find a pruned architecture which fits that params
constraints and for that they have to tweak different combinations of
width and depth pruning
- This PR adds a simplified version of the NAS from paper. We first find
all candidate subnets that fit the user's params constraint and sort
them by parameter count.
- [Paper] Then we pick top K candidates, do distillation for ~2B tokens
and then select the one with best score (LM Loss / MMLU / other metric
we care about). Note that the one among these top K with highest params
is often not the best pruned model
- [ModelOpt] Then we pick top K candidates, and select the one with best
score (LM Loss / MMLU / other metric we care about). While doing KD
gives better indication on which one to pick, skipping it makes the
pruning much faster, much less compute intense, and finish everything in
single prune API instead of first exporting top K models, doing KD and
eval for all K models separately. We do print a Note in pruning step to
let users know this so they can do KD if they want slightly better
pruned model.
- Further full KD is still needed as usual
- We also restrict the search space choices (e.g. `hidden_size` multiple
of 256, `ffn_hidden_size` multiple of 512) to make the process
efficient. Users can configure this if they want to.
## Usage
<!-- You can potentially add a usage example below. -->
Pruning API is same as before:
```python
import modelopt.torch.prune as mtp
mtp.prune(
model,
mode="mcore_minitron",
constraints=constraints,
dummy_input=None, # Not used
config=config,
)
```
1. Manual Pruning (Existing):
```python
constraints = {"export_config": {"hidden_size: 3072", "ffn_hidden_size": 9216}}
config = {"forward_loop": forward_loop, "checkpoint": "/path/to/cache/pruning/scores.pth"}
mtp.prune(...)
```
2. NAS-based Auto Pruning (New):
```python
constraints = {"params": 6e9}. # prune to 6B params
config = {"forward_loop": forward_loop, "checkpoint": "/path/to/cache/pruning/scores.pth"}
# define the score_func to maximize (e.g MMLU, negative val loss, etc.)
from modelopt.torch.utils.plugins.megatron_mmlu import megatron_mmlu
def score_func(m):
return megatron_mmlu(m, tokenizer, percentage=0.05) # 5% sampled data for faster eval
config["score_func"] = score_func
# overwrite search space choices (showing defaults):
config["max_width_pruning"] = 0.4
config["max_depth_pruning"] = 0.2
config["hparams_to_skip"] = [] # can be used to disable pruning some hparams e.g. ["num_attention_heads"]
config["top_k"] = 10 # might be better to use 20 at the cost of longer time to prune
mtp.prune(...)
```
To configure search space (shows defaults):
```python
ss_config = mtp.mcore_minitron.get_mcore_minitron_config(
hidden_size_divisor=256,
ffn_hidden_size_divisor=512,
mamba_head_dim_divisor=8,
num_moe_experts_divisor=8,
num_layers_divisor=2,
)
mtp.prune(model, mode=[("mcore_minitron", ss_config)], ....)
```
## Testing
**Qwen3-8B -> 6B (~2 hours on 8xA5000)**
```python
0.4350 score -> {'num_layers': 34, 'hidden_size': 3328, 'ffn_hidden_size': 11264}
BEST 0.5705 score -> {'num_layers': 30, 'hidden_size': 3584, 'ffn_hidden_size': 11776}
0.4051 score -> {'num_layers': 36, 'hidden_size': 3840, 'ffn_hidden_size': 8192}
0.4593 score -> {'num_layers': 36, 'hidden_size': 3584, 'ffn_hidden_size': 9216}
0.2737 score -> {'num_layers': 36, 'hidden_size': 3072, 'ffn_hidden_size': 11776}
0.5556 score -> {'num_layers': 32, 'hidden_size': 3584, 'ffn_hidden_size': 10752}
0.3198 score -> {'num_layers': 28, 'hidden_size': 4096, 'ffn_hidden_size': 10240}
0.4119 score -> {'num_layers': 36, 'hidden_size': 4096, 'ffn_hidden_size': 7168}
0.3808 score -> {'num_layers': 36, 'hidden_size': 3328, 'ffn_hidden_size': 10240}
0.4783 score -> {'num_layers': 34, 'hidden_size': 3840, 'ffn_hidden_size': 8704}
```
**Nemotron-Nano-9B-v2 -> 7B (~2.5 hours on 8xA5000)**
```python
0.2629 score -> {'num_layers': 54, 'hidden_size': 4352, 'mamba_num_heads': 88, 'mamba_head_dim': 72, 'ffn_hidden_size': 15360}
0.2778 score -> {'num_layers': 50, 'hidden_size': 4480, 'mamba_num_heads': 128, 'mamba_head_dim': 56, 'ffn_hidden_size': 15680}
0.5041 score -> {'num_layers': 56, 'hidden_size': 4096, 'mamba_num_heads': 96, 'mamba_head_dim': 80, 'ffn_hidden_size': 14336}
BEST 0.6043 score -> {'num_layers': 48, 'hidden_size': 4352, 'mamba_num_heads': 120, 'mamba_head_dim': 80, 'ffn_hidden_size': 13824}
0.0772 score -> {'num_layers': 56, 'hidden_size': 4352, 'mamba_num_heads': 112, 'mamba_head_dim': 80, 'ffn_hidden_size': 10240}
0.3550 score -> {'num_layers': 50, 'hidden_size': 4480, 'mamba_num_heads': 112, 'mamba_head_dim': 64, 'ffn_hidden_size': 15680}
0.1016 score -> {'num_layers': 56, 'hidden_size': 4352, 'mamba_num_heads': 120, 'mamba_head_dim': 72, 'ffn_hidden_size': 10752}
0.5461 score -> {'num_layers': 46, 'hidden_size': 4480, 'mamba_num_heads': 128, 'mamba_head_dim': 72, 'ffn_hidden_size': 14848}
0.1992 score -> {'num_layers': 54, 'hidden_size': 4480, 'mamba_num_heads': 80, 'mamba_head_dim': 80, 'ffn_hidden_size': 14336}
0.5881 score -> {'num_layers': 48, 'hidden_size': 4480, 'mamba_num_heads': 112, 'mamba_head_dim': 80, 'ffn_hidden_size': 13824}
```
## 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.
- **Is this change backward compatible?**: Yes <!--- If No, explain why.
-->
- **Did you write any new necessary tests?**: Yes
- **Did you add or update any necessary documentation?**: Yes
- **Did you update
[Changelog](https://github.com/NVIDIA/Model-Optimizer/blob/main/CHANGELOG.rst)?**:
Yes <!--- Only for new features, API changes, critical bug fixes or bw
breaking changes. -->
## Additional Information
OMNIML-3043
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
## Release Notes
* **New Features**
* Added NAS-based Auto Pruning for Minitron models as an alternative to
manual pruning using parameter constraints
* Introduced parameter counting capabilities for architecture search
* **Documentation**
* Expanded pruning guides with detailed examples and workflows for both
manual and automatic pruning approaches
* Updated configuration documentation with granular divisor parameters
* **Improvements**
* Enhanced parameter counting support for models with dynamic or
mixture-of-experts modules
<sub>✏️ Tip: You can customize this high-level summary in your review
settings.</sub>
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
---------
Signed-off-by: Keval Morabia <28916987+kevalmorabia97@users.noreply.github.com>
Pruning
Model pruning is a technique that removes redundant or less important parameters/connections from a neural network to reduce complexity and improve efficiency while maintaining performance.
Pruning can involve removal (prune) of Linear and Conv layers; and Transformer attention, MLP, MoE, Mamba, and depth of the model.
This section focuses on applying Model Optimizer's state-of-the-art complementary pruning modes to enable you to search for the best subnet architecture from your provided base model:
- Minitron: A pruning method developed by NVIDIA Research for pruning GPT (and later extended to Mamba, MoE, and Hybrid Transformer Mamba) models in NVIDIA Megatron-LM or NeMo framework. It uses the activation magnitudes to prune the embedding hidden size; mlp ffn hidden size; transformer attention heads; mamba heads and head dimension; MoE number of experts, ffn hidden size, and shared expert intermediate size; and number of layers of the model.
- FastNAS: A pruning method recommended for Computer Vision models. Given a pretrained model, FastNAS finds the subnet which maximizes the score function while meeting the given constraints.
- GradNAS: A light-weight pruning method recommended for language models like Hugging Face BERT, GPT-J. It uses the gradient information to prune the model's linear layers and attention heads to meet the given constraints.
| Section | Description | Link | Docs |
|---|---|---|---|
| Pre-Requisites | Required & optional packages to use this technique | [Link] | |
| Getting Started | Learn how to use the pruning API | [Link] | [docs] |
| Support Matrix | View the support matrix to see available pruning algorithms and their compatibility with different models and frameworks | [Link] | |
| Examples | Examples of different pruning methods | [Link] | |
| Pruning Guidelines | Guidelines for choosing how and how much to prune for best results | [Link] | |
| Resources | Extra links to relevant resources | [Link] |
Pre-Requisites
For Minitron pruning for Megatron-LM / NeMo models, use the NeMo container (e.g., nvcr.io/nvidia/nemo:25.11) which has all the dependencies installed. Make sure to upgrade Model Optimizer to the latest version using pip.
For FastNAS pruning for PyTorch Computer Vision models, no additional dependencies are required.
For GradNAS pruning for Hugging Face BERT / GPT-J, no additional dependencies are required.
Getting Started
As part of the pruning process, you will need to set up the training and/or validation data loaders, and optionally define a validation score function (Minitron, FastNAS) or loss function (GradNAS) and specify the desired pruning constraints (See Support Matrix for available pruning constraints).
To prune your model, you can simply call the mtp.prune API and save the pruned model. If the model is pruned using Minitron, you can use your standard saving and loading functions since it is a homogeneous pruning; while for FastNAS or GradNAS, you need to use mto.save and mto.restore to save and restore the heterogeneous pruned model.
Minitron
Minitron pruning supports two modes:
- Manual Pruning: Manually specify the target dimensions for each pruning axis (e.g.,
constraints = {"export_config": {"hidden_size": 3072, "ffn_hidden_size": 9216}}) - NAS-based Auto Pruning (New): Specify a target parameter count (e.g.,
constraints = {"params": 6e9}) and let the algorithm automatically search for the best architecture that maximizes a user-defined score function (e.g. MMLU, negative validation loss, etc.)
Please see example snippets of both modes for Minitron pruning on Megatron-Core GPT model below. For end-to-end examples script (M-LM / NeMo framework), please refer to the examples below.
Common Setup
import modelopt.torch.prune as mtp
from megatron.core.models.gpt import GPTModel
from megatron.core.post_training.modelopt.gpt.model_specs import get_gpt_modelopt_spec
from megatron.core.transformer.transformer_config import TransformerConfig
# Load the Megatron-Core GPTModel MambaModel with ModelOpt transformer layer spec
model_config = TransformerConfig(...)
model = GPTModel(
config=model_config,
transformer_layer_spec=get_gpt_modelopt_spec(model_config, remap_te_layernorm=True),
...
)
# Set up the forward loop to run on 512-1024 train samples
# For Megatron-LM framework, you can use the following utility function
from megatron.training.training import evaluate_and_print_results
def forward_loop(_):
evaluate_and_print_results(prefix, forward_step, train_iterator, model, ...)
# Run the pruning process (if model is a list then pass model[0] to the prune API)
# Save minitron scores at checkpoint so we can re-run pruning with different constraints without running the forward loop again
# NOTE: Skip checkpoint on re-running if you want to change the dataset and re-calibrate
model, pruning_scores = mtp.prune(
model,
mode="mcore_minitron",
constraints=constraints,
dummy_input=None, # Not used
config=config,
)
Note
Fine-tuning / distillation is required after pruning to recover the accuracy. Please refer to end-to-end pruning and distillation tutorial for more details.
1. Manual Pruning
This mode can be useful when you know the exact dimensions you want to prune to (e.g. fitting a specific latency / memory budget).
# Specify the pruning constraints (Check Support Matrix for available pruning dimensions)
constraints = {"export_config": {"hidden_size": 3072, "ffn_hidden_size": 9216}}
config = {"forward_loop": forward_loop, "checkpoint": "/path/to/cache/pruning/scores.pth"}
mtp.prune(...)
Under the Hood:
- Importance Scoring: Runs forward passes on calibration data (512-1024 samples) to compute activation magnitudes for each neuron/head/layer (takes ~5 minutes for an 8B model)
- Ranking: Ranks all parameters within each pruning dimension (e.g., all hidden dimensions, all attention heads) by their importance scores
- Pruning: Removes the least important parameters to meet the specified target dimensions in
export_config - Weight Slicing: Slices the model weights according to the pruned architecture (homogeneous pruning - all layers pruned uniformly)
Tip
Checkout the Pruning Guidelines section for more details on how to choose the best pruning strategy and distillation hyperparameters.
2. NAS-based Auto Pruning
This mode can be useful when you don't know the exact dimensions you want to prune to and want the algorithm to search for the best architecture that maximizes a user-defined score function at the cost of longer runtime.
# Define the score function to maximize (e.g., MMLU, negative validation loss, etc.)
# The algorithm will search for the best architecture that maximizes this score
from modelopt.torch.utils.plugins.megatron_mmlu import megatron_mmlu
def score_func(m):
return megatron_mmlu(m, tokenizer, percentage=0.05) # 5% sampled data for faster eval
# Specify target parameter count and configure the auto pruning algorithm
constraints = {"params": 6e9} # Prune to 6B parameters
config = {
"forward_loop": forward_loop,
"checkpoint": "/path/to/cache/pruning/scores.pth",
"score_func": score_func,
# Optional: Configure search space constraints (showing defaults)
"max_width_pruning": 0.4, # Maximum 40% per width pruning hparam
"max_depth_pruning": 0.2, # Maximum 20% per depth pruning hparam (num_layers)
"hparams_to_skip": [], # Disable pruning specific hparams, e.g., ["num_attention_heads"]
"top_k": 10, # Number of top architectures to evaluate (use 20 for better results at the cost of 2x time)
}
mtp.prune(...)
Under the Hood:
- Importance Scoring: Same as manual pruning - computes activation magnitudes for all parameters (takes ~5 minutes for an 8B model)
- Search Space Construction: Generates a search space of possible architectures based search space config and other configs (
max_width_pruning,max_depth_pruning,hparams_to_skip) - Architecture Search: Find candidate architectures that meet the parameter constraint and evaluate
top_k(based on number of parameters) of them usingscore_funce.g. MMLU, negative validation loss, etc. (takes ~10 mins per candidate for an 8B model pruning) - Best Architecture Selection: Returns the architecture (best
export_config) with the highest actual score from the top-K evaluated architectures - Weight Slicing: Slices the model weights according to the best pruned architecture found
Note
As per the original paper, ideally we need to perform a short Knowledge Distillation on ~2B tokens for all top-K candidate architectures before evaluating the score function, which will take a lot longer to prune, require splitting the pruning process into multiple stages and a lot more compute for pruning but can lead to better pruned model. If you are interested to do this, you can take the top-K candidate's
export_configfrom the pruning logs and then export all models separately and perform Knowledge Distillation on each of them before evaluating the score function.
Advanced Configuration
For finer control over the search space (e.g., granularity of pruning choices), you can configure the divisors:
# Configure search space granularity (showing defaults)
ss_config = mtp.mcore_minitron.get_mcore_minitron_config(
hidden_size_divisor=256,
ffn_hidden_size_divisor=512,
mamba_head_dim_divisor=8,
num_moe_experts_divisor=8,
num_layers_divisor=2,
)
# Use the custom search space config
mtp.prune(model, mode=[("mcore_minitron", ss_config)], ...)
If your model parameters are already sorted and you just want to prune the weights, you can skip the sorting step by setting "skip_sorting": True in config instead of passing forward_loop.
Support Matrix
| Algorithm | Model | Pruning Constraints |
|---|---|---|
| Minitron | Megatron-core / NeMo based GPT / Mamba / MoE / Hybrid LLM Models1 | Manual: export_config with width (hidden_size, ffn_hidden_size, num_attention_heads, mamba_num_heads, mamba_head_dim, num_moe_experts, moe_ffn_hidden_size, moe_shared_expert_intermediate_size) and/or depth (num_layers) pruned valuesAuto: params (requires score_func in config) |
| FastNAS | Computer Vision models | flops, params |
| GradNAS | HuggingFace BERT, GPT-J | flops, params |
1.Only Pipeline Parallel models are supported. Hugging Face models can be converted to Megatron-LM/NeMo format and used subsequently.
Examples
Minitron Pruning for Megatron-LM / NeMo Framework LLMs (e.g. Qwen 3, Nemotron Nano)
Checkout the Minitron pruning example for the Megatron-LM Framework or NeMo Framework which showcases the usage of the powerful Minitron pruning algorithm developed by NVIDIA Research for pruning LLMs like Llama-3.1-8B, Qwen3-8B, Nemotron-Nano-9B-v2, Nemotron-3-Nano-30B-A3B, etc. Both frameworks support importing from a Hugging Face pretrained checkpoint.
You can also look at the NeMo tutorial notebooks here which showcase the usage of Minitron pruning followed by distillation for Qwen3-8B step-by-step in NeMo framework. Hugging Face models can also be converted to NeMo format and used subsequently as shown in the tutorial.
Some of the models pruned using Minitron method followed by distillation and post-training are:
FastNAS Pruning for PyTorch Computer Vision Models
Check out the FastNAS pruning example usage in the documentation.
You can also take a look at FastNAS pruning interactive notebook cifar_resnet in this directory which showcases the usage of FastNAS for pruning a ResNet 20 model for the CIFAR-10 dataset. The notebook also shows how to profile the model to understand the search space of possible pruning options and demonstrates how to save and restore pruned models.
GradNAS Pruning for HuggingFace Language Models (e.g. BERT)
Checkout the BERT pruning example in chained_optimizations directory which showcases the usage of GradNAS for pruning BERT model for Question Answering followed by fine-tuning with distillation and quantization. The example also demonstrates how to save and restore pruned models.
Pruning Guidelines
Minitron
This section provides recommendations for choosing pruning strategies and distillation hyperparameters for Minitron pruning to help achieve the best latency-accuracy trade-offs.
Depth Pruning
Depth pruning reduces the number of layers (num_layers) in the model.
Advantages:
- Simpler to configure - only 1 parameter to tune
- Faster inference than width-pruned models at a fixed number of parameters
Recommendations:
- Up to 1/3rd parameter reduction can generally result in a model above the Pareto frontier with good latency-accuracy trade-off (when using a good quality dataset for distillation with ~80-100B tokens)
- For pruning >50%, use iterative pruning: compress by 30%, perform distillation, then compress again
Examples:
- Qwen3-8B (
num_layers=36) → 6B (num_layers=24) - Llama-3.1-8B (
num_layers=32) → 4.5B (num_layers=16)
Width Pruning
Width pruning reduces model dimensions per layer such as hidden_size, ffn_hidden_size, num_attention_heads, mamba_num_heads, mamba_head_dim, num_moe_experts, moe_ffn_hidden_size, and moe_shared_expert_intermediate_size.
Advantages:
- Better accuracy than depth-pruned models at a fixed number of parameters
Recommendations:
- Start with pruning
hidden_sizeandffn_hidden_sizeas the simplest configuration - Up to 1/3rd parameter reduction can generally result in a model above the Pareto frontier with good latency-accuracy trade-off (when using a good quality dataset for distillation with ~80-100B tokens)
- Axis sensitivity: MLP dimensions (
ffn_hidden_size) can typically be pruned more aggressively than embedding dimensions (hidden_size) and attention/Mamba dimensions (num_attention_heads,mamba_num_heads,mamba_head_dim) - For pruning >50%, use iterative pruning: compress by 30%, perform distillation, then compress again
Examples:
- Qwen3-8B (
ffn_hidden_size=12288,hidden_size=4096) → 6B (ffn_hidden_size=9216,hidden_size=3584) - Llama-3.1-8B (
ffn_hidden_size=14336,hidden_size=4096) → 4.5B (ffn_hidden_size=9216,hidden_size=3072) - Nemotron-H-8B-Base-8K (
ffn_hidden_size=21504,hidden_size=4096,mamba_num_heads=128) → Nemotron-H-4B-Base-8K (ffn_hidden_size=12288,hidden_size=3072,mamba_num_heads=112) - See paper
Depth and Width Pruning
For optimal results, combine depth and width pruning. This will require more tuning to find the best architecture.
Examples:
- NVIDIA-Nemotron-Nano-12B-v2 (
ffn_hidden_size=20480,hidden_size=5120,num_layers=62) → NVIDIA-Nemotron-Nano-9B-v2 (ffn_hidden_size=15680,hidden_size=4480,num_layers=56) - See paper
General Pruning Guidelines
- Pruning ratio: Anything >50% pruning is hard to recover. For such aggressive pruning, iterative pruning (compress → distill → compress again) is recommended.
- Latency-accuracy trade-off: The more pruning you do, the faster your model will be at the cost of lower accuracy. Choose based on your requirements.
- Dataset quality: Use a high-quality dataset for distillation. If you don't have a specific dataset, Nemotron-Pretraining-SFT-v1 is recommended.
- Post-training: Further post-training (e.g., instruction tuning, preference alignment) is needed after pruning and distillation on pre-training datasets to improve reasoning capabilities. A good dataset for post-training is Nemotron-Post-Training-Dataset-v2.
Distillation Hyperparameters
After pruning, distillation is required to recover model accuracy. Below are recommended starting hyperparameters for distillation:
| Hyperparameter | Recommendation |
|---|---|
| Sequence Length | 8192 (or 4096 if dataset has smaller sequences) |
| Global Batch Size (GBS) | 768 |
| Micro Batch Size (MBS) | As large as your GPU memory can accommodate |
| Learning Rate (LR) | 1e-4 → 1e-5 (linear decay) for 30-50% pruning • More compression → higher LR • Less compression → lower LR • As model gets larger → reduce LR to avoid divergence |
| Warmup Steps | 100 |
| Training Max Steps | Num training tokens / (Seq len × GBS) • Recommended: 80-100B tokens |
| Data Composition | • Standard models: 100% pre-training data • Reasoning models: 70% reasoning data + 30% pre-training data |
Tip
If you know the maximum learning rate used during the original training, a good rule of thumb for knowledge distillation is to use 1/5th of that maximum LR when compressing by ~50%.