Repository navigation
[OMNIML-6284] Make awq_lite independent of how the calibration tokens are batched #2996
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?
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 | ||||||
|---|---|---|---|---|---|---|---|---|
|
|
@@ -1526,8 +1526,8 @@ def get_weight_scale(weight, block_size=None): | |||||||
| scale = scale.mean(0).to(torch.float32) | ||||||||
| return scale | ||||||||
|
|
||||||||
| def get_act_scale(x): | ||||||||
| return x.abs().contiguous().view(-1, x.shape[-1]).mean(0).to(torch.float32) | ||||||||
| def get_act_scale_sum(x): | ||||||||
| return x.abs().reshape(-1, x.shape[-1]).sum(0, dtype=torch.float32) | ||||||||
|
|
||||||||
| def get_scale(x_max, w_max, alpha, tensor_parallel_group=None): | ||||||||
| scales = ( | ||||||||
|
|
@@ -1549,7 +1549,7 @@ def update_loss(self, out, out_actual, alpha): | |||||||
| out = out[0] if isinstance(out, tuple) else out | ||||||||
| out = out.to_local() if hasattr(out, "to_local") else out | ||||||||
| out_actual = out_actual.to_local() if hasattr(out_actual, "to_local") else out_actual | ||||||||
| loss = (out - out_actual).float().pow(2).mean() | ||||||||
| loss = (out - out_actual).float().pow(2).sum() | ||||||||
| self.awq_lite.loss[alpha] += loss.to(self.awq_lite.loss[alpha].device) | ||||||||
|
|
||||||||
| def update_best_params(self): | ||||||||
|
|
@@ -1586,9 +1586,9 @@ def forward(self, input, *args, **kwargs): | |||||||
| # Get local tensor from Dtensor | ||||||||
| input = input.to_local() if hasattr(input, "to_local") else input | ||||||||
|
|
||||||||
| self.awq_lite.act_scale += get_act_scale(self.input_quantizer(input)) | ||||||||
| self.awq_lite.act_scale += get_act_scale_sum(self.input_quantizer(input)) | ||||||||
| self.awq_lite.num_cache_steps += 1 | ||||||||
| self.awq_lite.num_tokens += input.numel() / input.shape[-1] | ||||||||
| self.awq_lite.num_tokens += input.numel() // input.shape[-1] | ||||||||
| if self.awq_lite.is_input_quantized: | ||||||||
| with set_quantizer_by_cfg_context( | ||||||||
| self.input_quantizer, [{"quantizer_name": "*", "enable": True}] | ||||||||
|
|
@@ -1658,7 +1658,7 @@ def sync_act_scale_across_dp(module, data_parallel_group): | |||||||
| ): | ||||||||
| # Hack: MoEs forward all tokens through all experts if _if_calib is True | ||||||||
| module._if_calib = True | ||||||||
| module.awq_lite.act_scale = module.awq_lite.act_scale / module.awq_lite.num_cache_steps | ||||||||
| module.awq_lite.act_scale = module.awq_lite.act_scale / module.awq_lite.num_tokens | ||||||||
|
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. [IMPORTANT Correctness] The divisor changed to
if input.numel() == 0 or not self.awq_lite.is_enabled: # line 1581 — DTensor *global* numel
return out_actual
if AWQLiteHelper.cache_mode:
input = input.to_local() if hasattr(input, "to_local") else input # line 1587
...
self.awq_lite.num_cache_steps += 1
self.awq_lite.num_tokens += input.numel() // input.shape[-1] # *local* numelThe early return reads Why it matters: Suggested fix — clamp the divisor rather than moving the loop guard. Note that changing line 1657 to
Suggested change
Collaborator
Author
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. Thanks for tracing this. I measured the premise and it does not hold, so I am leaving the code as it is. Before this PR a rank with an empty local shard did not compute |
||||||||
|
|
||||||||
| has_nan_local = torch.any(torch.isnan(module.awq_lite.act_scale)) or torch.any( | ||||||||
| torch.isnan(module.awq_lite.weight_scale) | ||||||||
|
|
||||||||
Uh oh!
There was an error while loading. Please reload this page.
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.
Normalize activation sums after data-parallel synchronization.
When data-parallel ranks see different token counts, this division creates a local mean on each rank.
sync_act_scale_across_dpthen averages those means equally. For ranks with one token at activation 10 and three tokens at activation 0, the result is 5 instead of the token-weighted mean of 2.5. Sum activation statistics and token counts across the data-parallel group before dividing, so uneven expert routing does not change the calibrated scale.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.
Agreed that this is the right next step, and I would take it as a follow-up. This PR makes the statistic token-weighted within a rank; the data-parallel average across ranks is unchanged (equal weight per rank, as before), which equals the token-weighted mean whenever the ranks see equal token counts, as dense layers do. Weighting by token count across ranks for MoE experts with uneven routing means summing the activation sums and token counts across the group before dividing: a change to the collectives that needs its own multi-rank test. The description now states this scope.
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.
@shengliangxu Understood. This PR makes activation statistics token-weighted within each rank. It leaves the existing equal-weight data-parallel average unchanged.
A separate follow-up for token weighting across ranks is reasonable, with a multi-rank test for uneven expert token counts. The reported description update makes that scope explicit.
Would you like me to open a GitHub issue for that follow-up?
You are interacting with an AI system.