Skip to content

Commit d76ef5b

Browse files
committed
Merge branch 'main' into jingyux/megatron-lora
2 parents 2b09c92 + b895dc5 commit d76ef5b

File tree

30 files changed

+863
-312
lines changed

30 files changed

+863
-312
lines changed

CHANGELOG.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,13 @@ Model Optimizer Changelog (Linux)
99
- Deprecated ``quantize_mode`` argument in ``examples/onnx_ptq/evaluate.py`` to support strongly typing. Use ``engine_precision`` instead.
1010
- Deprecated TRT-LLM's TRT backend in ``examples/llm_ptq`` and ``examples/vlm_ptq``. Tasks ``build`` and ``benchmark`` support are removed and replaced with ``quant``. For performance evaluation, please use ``trtllm-bench`` directly.
1111
- ``--export_fmt`` flag in ``examples/llm_ptq`` is removed. By default we export to the unified Hugging Face checkpoint format.
12-
- ``int8_sq`` quantization format is deprecated from the ``examples/vlm_ptq`` with respect to the TensorRT-LLM's torch backend switch. Please refer to the previous releases if this quantization format is needed.
1312
- Deprecated ``examples/vlm_eval`` as it depends on the deprecated TRT-LLM's TRT backend.
1413

1514
**New Features**
1615

1716
- ``high_precision_dtype`` default to fp16 in ONNX quantization, i.e. quantized output model weights are now FP16 by default.
1817
- Upgrade TensorRT-LLM dependency to 1.1.0rc2.
18+
- Support Phi-4-multimodal and Qwen2.5-VL quantized HF checkpoint export in ``examples/vlm_ptq``.
1919

2020
0.35 (2025-09-04)
2121
^^^^^^^^^^^^^^^^^

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ Model Optimizer is also integrated with [NVIDIA NeMo](https://github.com/NVIDIA-
2626

2727
## Latest News
2828

29+
- [2025/09/17] [An Introduction to Speculative Decoding for Reducing Latency in AI Inference](https://developer.nvidia.com/blog/an-introduction-to-speculative-decoding-for-reducing-latency-in-ai-inference/)
30+
- [2025/09/11] [How Quantization Aware Training Enables Low-Precision Accuracy Recovery](https://developer.nvidia.com/blog/how-quantization-aware-training-enables-low-precision-accuracy-recovery/)
2931
- [2025/08/29] [Fine-Tuning gpt-oss for Accuracy and Performance with Quantization Aware Training](https://developer.nvidia.com/blog/fine-tuning-gpt-oss-for-accuracy-and-performance-with-quantization-aware-training/)
3032
- [2025/08/01] [Optimizing LLMs for Performance and Accuracy with Post-Training Quantization](https://developer.nvidia.com/blog/optimizing-llms-for-performance-and-accuracy-with-post-training-quantization/)
3133
- [2025/06/24] [Introducing NVFP4 for Efficient and Accurate Low-Precision Inference](https://developer.nvidia.com/blog/introducing-nvfp4-for-efficient-and-accurate-low-precision-inference/)

docs/source/guides/4_distillation.rst

Lines changed: 9 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,9 @@ a more powerful teacher model using :mod:`modelopt.torch.distill <modelopt.torch
1616
interaction between the two.
1717
#. **Distillation training**: Seamlessly use the meta-model in place of the original model and run
1818
the original script with only one additional line of code for loss calculation.
19-
#. **Checkpoint and re-load**: Save the model via :meth:`mto.save <modelopt.torch.opt.conversion.save>` and
20-
restore via :meth:`mto.restore <modelopt.torch.opt.conversion.restore>`. See :ref:`saving and restoring <save-restore>`
21-
to learn more.
19+
#. **Checkpoint and re-load**: Save the model via :meth:`mto.save <modelopt.torch.opt.conversion.save>`
20+
Note that restoring the model (via :meth:`mto.restore <modelopt.torch.opt.conversion.restore>`)
21+
will not reinstantiate the distillation meta-model, in order to avoid unpickling issues.
2222

2323
*To find out more about Distillation and related concepts, please refer to the below section*
2424
:ref:`Distillation Concepts <distillation-concepts>`.
@@ -44,7 +44,7 @@ Example usage:
4444
4545
# Configure and convert for distillation
4646
distillation_config = {
47-
# `teacher_model` is a model class or callable, or a tuple.
47+
# `teacher_model` is a model, model class, callable, or a tuple.
4848
# If a tuple, it must be of the form (model_cls_or_callable,) or
4949
# (model_cls_or_callable, args) or (model_cls_or_callable, args, kwargs).
5050
"teacher_model": teacher_model,
@@ -53,15 +53,9 @@ Example usage:
5353
}
5454
distillation_model = mtd.convert(model, mode=[("kd_loss", distillation_config)])
5555
56-
# Export model in original class form
56+
# Export model in original class, with only previously-present attributes
5757
model_exported = mtd.export(distillation_model)
5858
59-
.. note::
60-
The config requires a (non-lambda) Callable to return a teacher model in place of the model
61-
itself. This is to avoid re-saving the teacher state dict upon saving the Distillation
62-
meta model. Thus, the same callable must be available in the namespace when restoring via
63-
the :meth:`mto.restore <modelopt.torch.opt.conversion.restore>` utility.
64-
6559
.. tip::
6660
When training the student on a small corpus of ground truth data, consider using :class:`MFTLoss <modelopt.torch.distill.MFTLoss>` for to perform Minifinetuning in lieu of the standard
6761
:class:`LogitsDistillationLoss <modelopt.torch.distill.losses.LogitsDistillationLoss>`. This will allow the student to learn from the teacher's distribution while adapting to the new data, improving the specialization of the new data without overwriting teacher's general knowledge.
@@ -170,10 +164,12 @@ outputs in the same order as well:
170164
The intermediate outputs for the losses are captured by the
171165
:class:`DistillationModel <modelopt.torch.distill.distillation_model.DistillationModel>` and then the loss(es) are
172166
invoked using :meth:`DistillationModel.compute_kd_loss() <modelopt.torch.distill.distillation_model.DistillationModel.compute_kd_loss>`.
173-
If present, the original student's non-distillation loss is passed in as an argument.
167+
If present, the original student's non-distillation loss can be passed in as an argument.
174168

175169
Writing a custom loss function is often necessary, especially to handle outputs that need to be processed
176-
to obtain the logits and activations.
170+
to obtain the logits and activations. Additional arguments to the loss function can be passed in to
171+
:meth:`DistillationModel.compute_kd_loss() <modelopt.torch.distill.distillation_model.DistillationModel.compute_kd_loss>`
172+
as ``kwargs``.
177173

178174
Loss Balancer
179175
^^^^^^^^^^^^^

examples/llm_distill/README.md

Lines changed: 16 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -39,13 +39,9 @@ First obtain both a pretrained model to act as the teacher and a (usually smalle
3939
```python
4040
from transformers import AutoModelForCausalLM
4141

42-
# Define student
42+
# Define student & teacher
4343
student_model = AutoModelForCausalLM.from_pretrained("meta-llama/Llama-3.1-8B-Instruct")
44-
45-
# Define callable which returns teacher
46-
def teacher_factory():
47-
teacher_model = AutoModelForCausalLM.from_pretrained("meta-llama/Llama-3.1-70B-Instruct")
48-
return teacher_model
44+
teacher_model = AutoModelForCausalLM.from_pretrained("meta-llama/Llama-3.1-70B-Instruct")
4945
```
5046

5147
### Set up the meta model
@@ -58,15 +54,15 @@ Please see an example Distillation setup below. This example assumes the outputs
5854
import modelopt.torch.distill as mtd
5955

6056
distillation_config = {
61-
"teacher_model": teacher_factory, # model initializer
57+
"teacher_model": teacher_model,
6258
"criterion": mtd.LogitsDistillationLoss(), # callable receiving student and teacher outputs, in order
6359
"loss_balancer": mtd.StaticLossBalancer(), # combines multiple losses; omit if only one distillation loss used
6460
}
6561

6662
distillation_model = mtd.convert(student_model, mode=[("kd_loss", distillation_config)])
6763
```
6864

69-
The `teacher_model` can be either a callable which returns an `nn.Module` or a tuple of `(model_cls, args, kwargs)`. The `criterion` is the distillation loss used between student and teacher tensors. The `loss_balancer` determines how the original and distillation losses are combined (if needed).
65+
The `teacher_model` can be either a `nn.Module`, a callable which returns an `nn.Module`, or a tuple of `(model_cls, args, kwargs)`. The `criterion` is the distillation loss used between student and teacher tensors. The `loss_balancer` determines how the original and distillation losses are combined (if needed).
7066

7167
See [Distillation](https://nvidia.github.io/TensorRT-Model-Optimizer/guides/4_distillation.html) for more info.
7268

@@ -158,35 +154,33 @@ Keep in mind the training loss of the distillation run is not directly comparabl
158154
### Train teacher
159155

160156
```bash
161-
accelerate launch --multi_gpu --mixed_precision bf16 main.py \
157+
accelerate launch --config-file ./accelerate_config/fsdp2.yaml \
158+
main.py \
162159
--single_model \
163160
--teacher_name_or_path 'meta-llama/Llama-2-7b-hf' \
164161
--output_dir ./llama2-7b-sft \
165-
--logging_steps 5 \
166-
--max_steps 400 \
167-
--max_seq_length 2048 \
162+
--max_length 2048 \
168163
--per_device_train_batch_size 1 \
169164
--per_device_eval_batch_size 4 \
170-
--gradient_checkpointing True \
171-
--fsdp 'full_shard auto_wrap' \
172-
--fsdp_transformer_layer_cls_to_wrap LlamaDecoderLayer
165+
--max_steps 400 \
166+
--logging_steps 5
173167
```
174168

175169
### Distill teacher into student
176170

177171
```bash
178-
accelerate launch --multi_gpu --mixed_precision bf16 main.py \
172+
accelerate launch --config-file ./accelerate_config/fsdp2.yaml \
173+
--fsdp_cpu_ram_efficient_loading False \
174+
--fsdp_activation_checkpointing False \
175+
main.py \
179176
--teacher_name_or_path ./llama2-7b-sft \
180177
--student_name_or_path 'TinyLlama/TinyLlama-1.1B-intermediate-step-1431k-3T' \
181178
--output_dir ./llama2-distill \
182-
--logging_steps 5 \
183-
--max_steps 200 \
184-
--max_seq_length 2048 \
179+
--max_length 2048 \
185180
--per_device_train_batch_size 1 \
186181
--per_device_eval_batch_size 4 \
187-
--gradient_checkpointing False \
188-
--fsdp 'full_shard auto_wrap' \
189-
--fsdp_transformer_layer_cls_to_wrap LlamaDecoderLayer
182+
--max_steps 200 \
183+
--logging_steps 5
190184
```
191185

192186
> [!NOTE]
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
compute_environment: LOCAL_MACHINE
2+
debug: false
3+
distributed_type: FSDP
4+
downcast_bf16: 'no'
5+
enable_cpu_affinity: false
6+
fsdp_config:
7+
fsdp_activation_checkpointing: true
8+
fsdp_auto_wrap_policy: TRANSFORMER_BASED_WRAP
9+
fsdp_cpu_ram_efficient_loading: true
10+
fsdp_offload_params: false
11+
fsdp_reshard_after_forward: true
12+
fsdp_state_dict_type: SHARDED_STATE_DICT
13+
fsdp_transformer_layer_cls_to_wrap: LlamaDecoderLayer
14+
fsdp_version: 2
15+
machine_rank: 0
16+
main_training_function: main
17+
mixed_precision: bf16
18+
num_machines: 1
19+
num_processes: gpu
20+
rdzv_backend: static
21+
same_network: true
22+
tpu_env: []
23+
tpu_use_cluster: false
24+
tpu_use_sudo: false
25+
use_cpu: false

examples/llm_distill/main.py

Lines changed: 14 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@
2121
import torch
2222
import torch.distributed
2323
import transformers
24-
from accelerate import PartialState
2524
from accelerate.logging import get_logger
2625
from transformers import AutoTokenizer
2726
from trl import SFTTrainer
@@ -48,38 +47,28 @@ class TrainingArguments(transformers.TrainingArguments):
4847
do_train: bool = True
4948
do_eval: bool = True
5049
save_strategy: str = "no"
51-
max_seq_length: int = 1024
50+
max_length: int = 1024
5251
optim: str = "adamw_torch"
5352
learning_rate: float = 1e-5
5453
lr_scheduler_type: str = "cosine"
5554
dataloader_drop_last: bool = True
5655
dataset_num_proc: int = 8
57-
dataset_batch_size: int = 500
5856
bf16: bool = True
5957
tf32: bool = True
6058

6159

6260
def llama_text_format_func(sample):
63-
texts = []
64-
for p, q, r in zip(sample["system_prompt"], sample["question"], sample["response"]):
65-
if not p:
66-
texts.append(f"<s>[INST] {q}[/INST]\n{r}</s>")
67-
else:
68-
texts.append(f"<s>[INST] <<SYS>>{p}<</SYS>>\n{q}[/INST]\n{r}</s>")
69-
return texts
61+
p, q, r = sample["system_prompt"], sample["question"], sample["response"]
62+
if not p:
63+
return f"<s>[INST] {q}[/INST]\n{r}</s>"
64+
else:
65+
return f"<s>[INST] <<SYS>>{p}<</SYS>>\n{q}[/INST]\n{r}</s>"
7066

7167

7268
class KDSFTTrainer(SFTTrainer, KDTrainer):
7369
pass
7470

7571

76-
def _teacher_factory(model_name_or_path):
77-
return transformers.AutoModelForCausalLM.from_pretrained(
78-
model_name_or_path,
79-
device_map=PartialState().process_index,
80-
)
81-
82-
8372
def train():
8473
parser = transformers.HfArgumentParser((ModelArguments, TrainingArguments))
8574
model_args, training_args = parser.parse_args_into_dataclasses()
@@ -117,34 +106,31 @@ def train():
117106

118107
if model_args.single_model:
119108
logger.info("Loading single model only...")
120-
model = _teacher_factory(model_path)
109+
model = transformers.AutoModelForCausalLM.from_pretrained(
110+
model_path, dtype=torch.bfloat16 if training_args.bf16 else None
111+
)
121112
logger.info("Model loaded.")
122113
else:
123114
logger.info("Loading student model...")
124115
model = transformers.AutoModelForCausalLM.from_pretrained(
125-
model_args.student_name_or_path,
126-
device_map=PartialState().process_index,
116+
model_args.student_name_or_path, dtype=torch.bfloat16 if training_args.bf16 else None
127117
)
128118
logger.info("Student loaded.")
129119
# Load checkpoint
130120
logger.info("Loading teacher model and converting to Distillation model...")
121+
teacher_model = transformers.AutoModelForCausalLM.from_pretrained(
122+
model_args.teacher_name_or_path, dtype=torch.bfloat16 if training_args.bf16 else None
123+
)
131124
kd_config = {
132-
"teacher_model": (
133-
_teacher_factory,
134-
(model_args.teacher_name_or_path,),
135-
{},
136-
),
125+
"teacher_model": teacher_model,
137126
"criterion": LMLogitsLoss(),
138-
"expose_minimal_state_dict": False, # FSDP forces us to disable this
139127
}
140128
model = mtd.convert(model, mode=[("kd_loss", kd_config)])
141129
logger.info("Models converted.")
142130

143131
# Fix problematic settings that logger.info excessive warnings
144132
model.generation_config.temperature = None
145133
model.generation_config.top_p = None
146-
if training_args.gradient_checkpointing:
147-
training_args.gradient_checkpointing_kwargs = {"use_reentrant": False}
148134

149135
# Trainer
150136
trainer_cls = SFTTrainer if model_args.single_model else KDSFTTrainer
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
pyarrow
2-
trl==0.13.0
2+
trl>=0.23.0

examples/llm_qat/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ Quantization Aware Training (QAT) helps to improve the model accuracy beyond pos
1111
| Support Matrix | View the support matrix to see quantization compatibility and feature availability across different models | \[[Link](#support-matrix)\] | |
1212
| End to End QAT | Example scripts demonstrating quantization techniques for optimizing Hugging Face models | \[[Link](#end-to-end-qat-example)\] | \[[docs](https://nvidia.github.io/TensorRT-Model-Optimizer/guides/1_quantization.html)\] |
1313
| End to End QAD | Example scripts demonstrating quantization aware distillation techniques for optimizing Hugging Face models | \[[Link](#end-to-end-qad-example)\] | \[[docs](https://nvidia.github.io/TensorRT-Model-Optimizer/guides/1_quantization.html)\] |
14+
| NeMo QAT/QAD Simplified Flow | Example script demonstrating end-to-end QAT/QAD in NeMo | \[[Link](../nemo_run/qat/README.md)\] | |
1415
| Evaluate Accuracy | Evaluating model accuracy after QAT/QAD (with fake quantization) | \[[Link](#testing-qat-model-with-llm-benchmarks-for-accuracy-evaluation)\] | |
1516
| Deployment | Deploying the model after QAT/QAD | \[[Link](#deployment)\] | |
1617
| QLoRA | Model training with reduced GPU memory | \[[Link](#end-to-end-qlora-with-real-quantization)\] | |
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
# SPDX-FileCopyrightText: Copyright (c) 2023-2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
2+
# SPDX-License-Identifier: Apache-2.0
3+
#
4+
# Licensed under the Apache License, Version 2.0 (the "License");
5+
# you may not use this file except in compliance with the License.
6+
# You may obtain a copy of the License at
7+
#
8+
# http://www.apache.org/licenses/LICENSE-2.0
9+
#
10+
# Unless required by applicable law or agreed to in writing, software
11+
# distributed under the License is distributed on an "AS IS" BASIS,
12+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
# See the License for the specific language governing permissions and
14+
# limitations under the License.
15+
16+
import argparse
17+
18+
from nemo.collections.llm.modelopt import setup_trainer_and_restore_model_with_modelopt_spec
19+
20+
from modelopt.torch.export.plugins.nemo_run import _get_most_recent_ckpt
21+
from modelopt.torch.utils.plugins.megatron_mmlu import megatron_mmlu
22+
23+
24+
def parse_args():
25+
parser = argparse.ArgumentParser(
26+
description=(
27+
"Run MMLU evaluation with ModelOpt Megatron model. Provide either --nemo_ckpt"
28+
"or --finetuned_ckpt_dir"
29+
)
30+
)
31+
group = parser.add_mutually_exclusive_group(required=True)
32+
group.add_argument("--nemo_ckpt", type=str, required=False, help="Path to NeMo checkpoint.")
33+
group.add_argument(
34+
"--finetuned_ckpt_dir",
35+
required=False,
36+
type=str,
37+
help="Checkpoint directory of 1 or more finetuned models",
38+
)
39+
parser.add_argument(
40+
"--tensor_parallelism", type=int, default=1, help="Tensor parallelism size."
41+
)
42+
parser.add_argument(
43+
"--pipeline_parallelism", type=int, default=1, help="Pipeline parallelism size."
44+
)
45+
return parser.parse_args()
46+
47+
48+
if __name__ == "__main__":
49+
args = parse_args()
50+
ckpt_path = args.nemo_ckpt
51+
if args.finetuned_ckpt_dir:
52+
ckpt_path = _get_most_recent_ckpt(args.finetuned_ckpt_dir)
53+
model, trainer = setup_trainer_and_restore_model_with_modelopt_spec(
54+
ckpt_path,
55+
tensor_model_parallel_size=args.tensor_parallelism,
56+
pipeline_model_parallel_size=args.pipeline_parallelism,
57+
devices=args.tensor_parallelism * args.pipeline_parallelism,
58+
)
59+
tokenizer = model.tokenizer.tokenizer
60+
megatron_mmlu(model.module, tokenizer)
File renamed without changes.

0 commit comments

Comments
 (0)