Prototype NVFP4 with FP8 UE5M3 block scales - #3325
Conversation
Co-authored-by: Teddy Do <tdophung@nvidia.com> Co-authored-by: Varun Thumbe <vthumbe@nvidia.com> Signed-off-by: Tim Moon <tmoon@nvidia.com>
for more information, see https://pre-commit.ci
This comment was marked as outdated.
This comment was marked as outdated.
* [PyTorch] Enable e5m3 fused GEMM kernels from cuDNN Signed-off-by: Kaining Zhong <kainingz@nvidia.com> * have to pad to 256 to use cuDNN Signed-off-by: Kaining Zhong <kainingz@nvidia.com> * fix: need to pass scale_dtype Signed-off-by: Kaining Zhong <kainingz@nvidia.com> * route wgrad to cuDNN's wgrad API Signed-off-by: Kaining Zhong <kainingz@nvidia.com> * Support grouped linear with NVFP4-UE5M3 NVFP4-UE5M3 grouped GEMM falls back to dense GEMMs. Generalize usage of wgrad kernel and use when tensors sizes are not 256-aligned. Fix inconsistent m,n,k GEMM notation. Remove ue5m3 hacks in op fuser tests. Add ue5m3 to grouped MLP tests. Signed-off-by: Tim Moon <tmoon@nvidia.com> * Fix typos Co-authored-by: Codex <noreply@openai.com> Signed-off-by: Tim Moon <tmoon@nvidia.com> --------- Signed-off-by: Kaining Zhong <kainingz@nvidia.com> Signed-off-by: Tim Moon <tmoon@nvidia.com> Co-authored-by: Tim Moon <tmoon@nvidia.com> Co-authored-by: Codex <noreply@openai.com>
Signed-off-by: Tim Moon <tmoon@nvidia.com>
Signed-off-by: Tim Moon <tmoon@nvidia.com>
0987860 to
ab3a9b3
Compare
for more information, see https://pre-commit.ci
Signed-off-by: Tim Moon <tmoon@nvidia.com>
for more information, see https://pre-commit.ci
Signed-off-by: Tim Moon <tmoon@nvidia.com>
for more information, see https://pre-commit.ci
Signed-off-by: Tim Moon <tmoon@nvidia.com>
Greptile SummaryThis proof-of-concept adds UE5M3 block-scale support throughout NVFP4 recipes, quantization kernels, tensor metadata, GEMM integration, and grouped-MLP execution.
Confidence Score: 5/5The PR appears safe to merge. No blocking failure remains. Important Files Changed
Flowchart%%{init: {'theme': 'neutral'}}%%
flowchart LR
R[NVFP4 recipe] --> Q[Quantizer with scale dtype]
Q --> K[Native NVFP4 quantization]
K --> T[FP4 data and block scales]
T --> G[Scale-aware GEMM]
G --> O[PyTorch and grouped-MLP output]
Reviews (24): Last reviewed commit: "Respect no-tensor-scaling in grouped ten..." | Re-trigger Greptile |
Signed-off-by: Tim Moon <tmoon@nvidia.com>
for more information, see https://pre-commit.ci
Signed-off-by: Tim Moon <tmoon@nvidia.com>
for more information, see https://pre-commit.ci
This comment was marked as outdated.
This comment was marked as outdated.
Signed-off-by: Tim Moon <tmoon@nvidia.com>
Review suggestion from @ptrendx Co-authored-by: Tim Moon <4406448+timmoon10@users.noreply.github.com> Signed-off-by: Tim Moon <4406448+timmoon10@users.noreply.github.com>
|
/te-ci L1 |
| const NVTETensor global_scale, size_t h, size_t w, | ||
| size_t scale_stride_h, size_t scale_stride_w, size_t start_offset, | ||
| size_t block_len, cudaStream_t stream); | ||
| size_t block_len, NVTEDType scale_dtype, cudaStream_t stream); |
There was a problem hiding this comment.
Why do we need that API break? We have scale tensor there which has dtype inside it, then why do we need to pass it also as an additional argument?
There was a problem hiding this comment.
In general, we should think very carefully here why we would do this (and if really needed then I would much rather have v2 variants rather than break the APi).
There was a problem hiding this comment.
We did think about maintaining backward compatibility, but I'm not sure if it's worth the effort since these are advanced functions used specifically for Mcore DDP integration: #3325 (comment)
| @pytest.mark.skipif(not recipe_available, reason=reason_for_no_recipe) | ||
| @pytest.mark.parametrize("return_transpose", [False, True], ids=["rowwise", "with_columnwise"]) | ||
| @pytest.mark.parametrize("use_4over6", [False, True], ids=["standard", "4over6"]) | ||
| def test_disable_second_level_scale_uses_only_block_scale( |
There was a problem hiding this comment.
Not really for this test, but for this file - we need to actually test E5M# here - this includes creating the reference implementation just like we have for regular nvFP4 recipe.
| def expected_e4m3_max(tensor_type): | ||
| if not expected_use_4over6(tensor_type): | ||
| return 448 | ||
| return 0 |
There was a problem hiding this comment.
Why? Is the result of this function something else than e4m3 max? If so, the name of the function should change too.
There was a problem hiding this comment.
There are two changes to how we're interpreting e4m3_max:
- It is now an override for the maximum scale dtype value. A value of zero means we use the actual dtype max. This is only used for 4over6, which is not supported with e5m3.
- We can no longer assume that the NVFP4 scales are e4m3. I agree it would be better to rename the variable to something like
scale_maxso it's no longer married to e4m3. However, I'm trying to limit the amount this PR touches 4over6 logic.
| .value("kBFloat16", transformer_engine::DType::kBFloat16) \ | ||
| .value("kFloat8E4M3", transformer_engine::DType::kFloat8E4M3) \ | ||
| .value("kFloat8E5M2", transformer_engine::DType::kFloat8E5M2) \ | ||
| .value("kFloat8E8M0", transformer_engine::DType::kFloat8E8M0) \ |
There was a problem hiding this comment.
Why do we put it in the middle of this enum type? That would be breaking for places that have this enum as int.
There was a problem hiding this comment.
This matches the dtype order in the C API: https://github.com/timmoon10/TransformerEngine/blob/58dfc41046b7068dffc29eba80ecf65005988009/transformer_engine/common/include/transformer_engine/transformer_engine.h#L25
Also, we explicitly specify the value for each Python enum, so the order doesn't matter.
| ) | ||
| columnwise_amax = torch.empty(num_tensors, dtype=torch.float32, device=device) | ||
| elif compatible_recipe.float8_block_scaling(): | ||
| scale_inv_dtype = DType.kFloat32 |
There was a problem hiding this comment.
Earlier in this file amax buffers are always created, whereas it should check whether the second level scale should be disabled.
|
|
||
|
|
||
| @dataclass(repr=False) | ||
| class NVFP4BlockScaling(Recipe): |
There was a problem hiding this comment.
If it's just one UE5M3 toggle, we can actually extend it to NVFP4BlockScaling here so that it automatically inherits the Mcore --fp4-param-gather feature. I agree that real hybrid recipes like mixing with mxfp8 should continue to live in CustomRecipe structure.
There was a problem hiding this comment.
You're right that basic UE5M3 support just requires setting recipe.fp8_format = Format.UE5M3. This can already be done in Mcore by setting config.fp8_recipe_attrs = {"fp8_format": Format.UE5M3} (once NVIDIA/Megatron-LM#6341 merges).
However, the real practical benefit is that UE5M3 allows us to change the tensor scaling. We skip tensor scaling in the forward pass, while keeping current scaling in the backward pass. This is all a research question and I figure the recipe recommendations might change at any moment, so I'm resistant to hard-coding it in TE. CustomRecipe is a much better approach for unstable research recipes.
Signed-off-by: Tim Moon <tmoon@nvidia.com>
Signed-off-by: Tim Moon <tmoon@nvidia.com>
98792fe to
93cbd0a
Compare
for more information, see https://pre-commit.ci
Signed-off-by: Tim Moon <tmoon@nvidia.com>
|
/te-ci L1 |
Description
This is a proof-of-concept for NVFP4 with FP8 UE5M3 block scales. Quantization is supported natively and GEMMs go through the cuDNN Frontend kernels (see NVIDIA/cudnn-frontend#545).
Type of change
Changes
Checklist: