-
Notifications
You must be signed in to change notification settings - Fork 322
V4 mxfp4 #1372
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
base: main
Are you sure you want to change the base?
V4 mxfp4 #1372
Changes from 11 commits
41d24f9
29e3276
0a97f49
f27ef47
6e5f3a2
c16d3f4
0172e30
17798e5
091066b
4d6332e
60faa86
19c34ef
779fe25
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -164,3 +164,86 @@ def act_quant( | |||||
| x.copy_(y) | ||||||
| return x | ||||||
| return y, s | ||||||
|
|
||||||
|
|
||||||
| @tilelang.jit(pass_configs=pass_configs) | ||||||
| def fp4_quant_kernel(N, block_size=32, in_dtype=BF16, scale_dtype=FE8M0, inplace=False): | ||||||
| """Block-wise FP4 (E2M1) quantization with power-of-2 (UE8M0) scales. | ||||||
|
|
||||||
| ``inplace=True`` does a fused quant+dequant back to ``in_dtype`` (the MXFP4 | ||||||
| fake-quant used for QAT). ``inplace=False`` emits packed E2M1 plus the UE8M0 | ||||||
| scale. The pow2 scale is computed via the same IEEE-754 bit tricks as the FP8 | ||||||
| path (``fast_round_scale``); the inner cast goes through ``FP4`` explicitly so | ||||||
| the value is rounded onto the E2M1 grid regardless of the output storage dtype. | ||||||
| """ | ||||||
| M = T.symbolic("M") | ||||||
| fp4_max = 6.0 | ||||||
| fp4_max_inv = 1.0 / fp4_max | ||||||
| blk_m = 32 | ||||||
| group_size = block_size | ||||||
| compute_dtype = FP32 | ||||||
| out_dtype = in_dtype if inplace else FP4 | ||||||
|
|
||||||
| @T.prim_func | ||||||
| def fp4_quant_kernel_( | ||||||
| X: T.Tensor[(M, N), in_dtype], | ||||||
| Y: T.Tensor[(M, N), out_dtype], | ||||||
| S: T.Tensor[(M, T.ceildiv(N, group_size)), scale_dtype], | ||||||
| ): | ||||||
| with T.Kernel(T.ceildiv(M, blk_m), T.ceildiv(N, group_size), threads=128) as ( | ||||||
| pid_m, | ||||||
| pid_n, | ||||||
| ): | ||||||
| x_shared = T.alloc_shared((blk_m, group_size), in_dtype) | ||||||
| x_local = T.alloc_fragment((blk_m, group_size), in_dtype) | ||||||
| amax_local = T.alloc_fragment((blk_m,), compute_dtype) | ||||||
| s_local = T.alloc_fragment((blk_m,), compute_dtype) | ||||||
| y_local = T.alloc_fragment((blk_m, group_size), out_dtype) | ||||||
| y_shared = T.alloc_shared((blk_m, group_size), out_dtype) | ||||||
|
|
||||||
| for _ in T.Pipelined(1, num_stages=2): | ||||||
| T.copy(X[pid_m * blk_m, pid_n * group_size], x_shared) | ||||||
| T.copy(x_shared, x_local) | ||||||
| T.reduce_absmax(x_local, amax_local, dim=1) | ||||||
| for i in T.Parallel(blk_m): | ||||||
| amax_local[i] = T.max(amax_local[i], 6 * (2**-126)) | ||||||
| s_local[i] = fast_round_scale(amax_local[i], fp4_max_inv) | ||||||
| if inplace: | ||||||
| for i, j in T.Parallel(blk_m, group_size): | ||||||
| y_local[i, j] = T.Cast( | ||||||
| out_dtype, | ||||||
| T.Cast(compute_dtype, T.Cast(FP4, T.clamp(x_local[i, j] / s_local[i], -fp4_max, fp4_max))) | ||||||
| * s_local[i], | ||||||
| ) | ||||||
| else: | ||||||
| for i, j in T.Parallel(blk_m, group_size): | ||||||
| y_local[i, j] = T.clamp(x_local[i, j] / s_local[i], -fp4_max, fp4_max) | ||||||
| for i in T.Parallel(blk_m): | ||||||
| S[pid_m * blk_m + i, pid_n] = T.Cast(scale_dtype, s_local[i]) | ||||||
| T.copy(y_local, y_shared) | ||||||
| T.copy(y_shared, Y[pid_m * blk_m, pid_n * group_size]) | ||||||
|
|
||||||
| return fp4_quant_kernel_ | ||||||
|
|
||||||
|
|
||||||
| def fp4_act_quant( | ||||||
| x: torch.Tensor, | ||||||
| block_size: int = 32, | ||||||
| inplace: bool = False, | ||||||
| ) -> torch.Tensor: | ||||||
| """Block-wise FP4 (E2M1, ``1 x block_size`` UE8M0) quantization. | ||||||
|
|
||||||
| ``inplace=True`` does the fused quant+dequant back to BF16 (the MXFP4 fake-quant | ||||||
| used by QAT) and returns ``x``; ``inplace=False`` returns ``(packed_e2m1, scale)``. | ||||||
| """ | ||||||
| N = x.size(-1) | ||||||
| assert N % block_size == 0 | ||||||
| z = x.contiguous() | ||||||
| y = torch.empty_like(z) if inplace else z.new_empty(*z.shape[:-1], N // 2, dtype=torch.float4_e2m1fn_x2) | ||||||
|
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. In To fix this,
Suggested change
|
||||||
| s = z.new_empty(*z.size()[:-1], N // block_size, dtype=torch.float8_e8m0fnu) | ||||||
| kernel = fp4_quant_kernel(N, block_size, inplace=inplace) | ||||||
| kernel(z.view(-1, N), y.view(-1, y.size(-1)), s.view(-1, N // block_size)) | ||||||
| if inplace: | ||||||
| x.copy_(y) | ||||||
| return x | ||||||
| return y, s | ||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| # DEPRECATED: contents moved to act_quant.py. | ||
| # | ||
| # The FP4 (E2M1) quant kernel + `fp4_act_quant` wrapper now live in | ||
| # `miles_plugins.models.deepseek_v4.ops.kernel.act_quant`. The other kernels that | ||
| # used to live here (fp8_gemm / fp4_gemm / sparse_attn / hc_split_sinkhorn) were | ||
| # unused dead code and have been dropped. | ||
| # | ||
| # This file is intentionally empty and can be deleted (`rm` was blocked in the | ||
| # environment that performed the migration). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In
forward_raw, whenself.rotateisFalse, theelsebranch (lines 165-168) only checksself.use_fp8_qatand does not handleself.use_mxfp4_qat. Ifself.use_mxfp4_qatis enabled andself.rotateisFalse, the MXFP4 QAT simulation will be silently skipped for the non-rotatednopehead dimension.Consider adding the
self.use_mxfp4_qatcheck to theelsebranch as well to ensure consistency and robustness.