-
Notifications
You must be signed in to change notification settings - Fork 2.6k
[#10519][feat] AutoDeploy: support HF fine grained FP8 #10650
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 all commits
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 |
|---|---|---|
|
|
@@ -319,3 +319,61 @@ def _fake( | |
| N_half = weight_quantized.shape[-2] | ||
| N = N_half * 2 | ||
| return torch.empty((*input.shape[:-1], N), dtype=input.dtype, device=input.device) | ||
|
|
||
|
|
||
| @torch.library.custom_op("auto_deploy::torch_fake_quant_hf_fp8_linear", mutates_args=()) | ||
| def torch_fake_quant_hf_fp8_linear( | ||
|
Comment on lines
+324
to
+325
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. ditto re name |
||
| input: torch.Tensor, # [..., K] | ||
| weight_quantized: torch.Tensor, # [N, K] float8_e4m3fn | ||
| bias: Optional[torch.Tensor], # [N] or None | ||
| input_scale: List[torch.Tensor], # unused for HF FP8 (input quantized on the fly) | ||
| weight_scale: List[torch.Tensor], # [weight_scale_inv] | ||
| input_zp: List[torch.Tensor], # unused | ||
| weight_zp: List[torch.Tensor], # unused | ||
| ) -> torch.Tensor: | ||
| """HuggingFace FineGrainedFP8 linear operation. | ||
| - weight_scale[0] = weight_scale_inv (per-block weight scale) | ||
| - input_scale, input_zp, weight_zp are unused | ||
| - block_size is inferred from weight and weight_scale_inv shapes | ||
| """ | ||
|
Comment on lines
+334
to
+338
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. docstring seems incomplete? |
||
| from transformers.integrations.finegrained_fp8 import act_quant, w8a8_block_fp8_matmul_triton | ||
|
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. is there no other kernel/implementation available for this?
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. why not do a global import? |
||
|
|
||
| weight_scale_inv = weight_scale[0] | ||
|
|
||
| # Infer block_size from weight and weight_scale_inv shapes | ||
| # weight shape: [N, K], weight_scale_inv shape: [N/block_n, K/block_k] | ||
| N, K = weight_quantized.shape | ||
| scale_n, scale_k = weight_scale_inv.shape | ||
| block_n = N // scale_n | ||
| block_k = K // scale_k | ||
| block_size = [block_n, block_k] | ||
|
|
||
| qinput, scale = act_quant(input, block_size[1]) | ||
| output = w8a8_block_fp8_matmul_triton( | ||
| qinput, | ||
| weight_quantized, | ||
| scale, | ||
| weight_scale_inv, | ||
| block_size, | ||
| output_dtype=input.dtype, | ||
| ) | ||
|
|
||
| if bias is not None: | ||
| output = output + bias | ||
|
|
||
| return output.to(dtype=input.dtype) | ||
|
|
||
|
|
||
| @torch_fake_quant_hf_fp8_linear.register_fake | ||
| def _torch_fake_quant_hf_fp8_linear_fake( | ||
| input: torch.Tensor, | ||
| weight_quantized: torch.Tensor, | ||
| bias: Optional[torch.Tensor], | ||
| input_scale: List[torch.Tensor], | ||
| weight_scale: List[torch.Tensor], | ||
| input_zp: List[torch.Tensor], | ||
| weight_zp: List[torch.Tensor], | ||
| ) -> torch.Tensor: | ||
| """Fake implementation for torch.export tracing.""" | ||
| out_features = weight_quantized.shape[0] | ||
| return torch.empty((*input.shape[:-1], out_features), dtype=input.dtype, device=input.device) | ||
|
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. Please check if this PR needs a rebase now that #10635 has merged |
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -633,3 +633,93 @@ def _apply( | |||||
| return gm, TransformInfo( | ||||||
| skipped=False, num_matches=cnt, is_clean=cnt == 0, has_valid_shapes=True | ||||||
| ) | ||||||
|
|
||||||
|
|
||||||
| @TransformRegistry.register("quantize_hf_fp8_linear_from_config") | ||||||
| class HFFineGrainedFP8LinearQuantization(Quantization): | ||||||
| """Quantization transform for HuggingFace FineGrainedFP8 (block-wise FP8) models. | ||||||
|
|
||||||
| This transform replaces linear ops with the HF FineGrainedFP8 quantized op. | ||||||
| The HF FP8 format uses per-block weight scales (weight_scale_inv) and | ||||||
| dynamic input quantization. | ||||||
|
|
||||||
| Config format (from HF config.json): | ||||||
| "quantization_config": { | ||||||
| "quant_method": "fp8", | ||||||
| "weight_block_size": [128, 128], | ||||||
| "modules_to_not_convert": ["lm_head"] | ||||||
| } | ||||||
| """ | ||||||
|
|
||||||
| algo_name = "fp8" | ||||||
|
|
||||||
| def target_op(self): | ||||||
| return torch.ops.auto_deploy.torch_fake_quant_hf_fp8_linear.default | ||||||
|
|
||||||
| def quantize_weight(self, w: torch.Tensor) -> torch.Tensor: | ||||||
| return torch.empty_like(w, dtype=torch.float8_e4m3fn, device=w.device) | ||||||
|
|
||||||
| def scale_names(self) -> List[str]: | ||||||
| return ["weight_scale_inv"] | ||||||
|
|
||||||
| def default_scales(self, original_weight_shape: Tuple) -> Dict[str, torch.Tensor]: | ||||||
| # Default block size is 128x128 for HF FP8 | ||||||
| N, K = original_weight_shape | ||||||
| block_n, block_k = 128, 128 | ||||||
| scale_shape = (N // block_n, K // block_k) | ||||||
| return {"weight_scale_inv": torch.ones(scale_shape, dtype=torch.bfloat16)} | ||||||
|
|
||||||
| def build_custom_args_for_linear(self, scales: Dict[str, Node]) -> Tuple: | ||||||
| return ([], [scales["weight_scale_inv"]], [], []) | ||||||
|
|
||||||
| def load_hook(self, state_dict, prefix, *args, weight_name: str): | ||||||
| """Load hook to handle HF FineGrainedFP8 checkpoint format. | ||||||
|
|
||||||
| HF FP8 checkpoints store: | ||||||
| - weight: float8_e4m3fn tensor | ||||||
| - weight_scale_inv: per-block scale tensor | ||||||
| """ | ||||||
| if weight_name not in state_dict: | ||||||
| return | ||||||
|
|
||||||
| weight = state_dict[weight_name] | ||||||
| if weight.dtype == torch.float8_e4m3fn: | ||||||
| scale_inv_name = weight_name + "_scale_inv" | ||||||
| if scale_inv_name in state_dict: | ||||||
| # Rename to match our buffer name | ||||||
| mod_prefix = weight_name.rsplit(".", 1)[0] | ||||||
| state_dict[mod_prefix + ".weight_scale_inv"] = state_dict[scale_inv_name] | ||||||
|
|
||||||
| def _apply( | ||||||
| self, | ||||||
| gm: GraphModule, | ||||||
| cm: CachedSequenceInterface, | ||||||
| factory: ModelFactory, | ||||||
| shared_config: SharedConfig, | ||||||
| ) -> Tuple[GraphModule, TransformInfo]: | ||||||
| qcfg = factory.get_quant_config() | ||||||
| if not qcfg: | ||||||
| return gm, TransformInfo( | ||||||
| skipped=True, num_matches=0, is_clean=True, has_valid_shapes=True | ||||||
| ) | ||||||
|
|
||||||
| quant_method = str(qcfg.get("quant_method", "")).lower() | ||||||
| if quant_method != self.algo_name: | ||||||
| return gm, TransformInfo( | ||||||
| skipped=True, num_matches=0, is_clean=True, has_valid_shapes=True | ||||||
| ) | ||||||
|
|
||||||
| excluded = qcfg.get("modules_to_not_convert", []) | ||||||
|
|
||||||
| cnt = 0 | ||||||
| for n in gm.graph.nodes: | ||||||
| if not is_linear_op(n): | ||||||
| continue | ||||||
| if should_skip_quantization(n, excluded): | ||||||
| continue | ||||||
| self._insert_quantized_linear(gm, n, is_quantized_graph=False) | ||||||
| cnt += 1 | ||||||
|
|
||||||
| return gm, TransformInfo( | ||||||
| skipped=False, num_matches=cnt, is_clean=False, has_valid_shapes=(cnt == 0) | ||||||
|
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
|
||||||
| ) | ||||||
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.
is
hf_fp8an accurate naming here? Shouldn't we name it according to the algorithm (dynamic fp8) rather than the source