Repository navigation
[trainer, perf] feat: reuse reduced loss denominators #941
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 |
|---|---|---|
| @@ -0,0 +1,71 @@ | ||
| from types import SimpleNamespace | ||
|
|
||
| import pytest | ||
| import torch | ||
|
|
||
| import veomni.utils.loss_utils as loss_utils | ||
|
|
||
|
|
||
| def test_reduce_global_loss_token_reduces_each_denominator_once(monkeypatch): | ||
| calls = [] | ||
|
|
||
| def fake_all_reduce(value, op="mean", group=None): | ||
| calls.append((value, op, group)) | ||
| return value + 10 | ||
|
|
||
| monkeypatch.setattr(loss_utils, "all_reduce", fake_all_reduce) | ||
|
|
||
| reduced = loss_utils.reduce_global_loss_token( | ||
| { | ||
| "foundation_tokens": torch.tensor(3), | ||
| "image_decoder_tokens": torch.tensor(0), | ||
| } | ||
| ) | ||
|
|
||
| assert reduced == {"foundation_tokens": 13, "image_decoder_tokens": 10} | ||
| assert calls == [(3, "sum", None), (0, "sum", None)] | ||
|
|
||
|
|
||
| def test_mean_global_loss_reuses_pre_reduced_denominator(monkeypatch): | ||
| def fail_all_reduce(*args, **kwargs): | ||
| raise AssertionError("denominator should already be reduced") | ||
|
|
||
| monkeypatch.setattr(loss_utils, "all_reduce", fail_all_reduce) | ||
| monkeypatch.setattr( | ||
| loss_utils, | ||
| "get_parallel_state", | ||
| lambda: SimpleNamespace(sp_enabled=False, fsdp_size=2), | ||
| ) | ||
|
|
||
| loss_dict = loss_utils.mean_global_loss( | ||
| {"foundation_loss": torch.tensor(4.0)}, | ||
| {"foundation_tokens": torch.tensor(3)}, | ||
| {"foundation_tokens": torch.tensor(5)}, | ||
| {"foundation_tokens": 6}, | ||
| ) | ||
|
|
||
| assert loss_dict["foundation_loss"].item() == pytest.approx(4.0) | ||
|
|
||
|
|
||
| def test_mean_global_loss_falls_back_to_reducing_denominator(monkeypatch): | ||
| calls = [] | ||
|
|
||
| def fake_all_reduce(value, op="mean", group=None): | ||
| calls.append((value, op, group)) | ||
| return value * 2 | ||
|
|
||
| monkeypatch.setattr(loss_utils, "all_reduce", fake_all_reduce) | ||
| monkeypatch.setattr( | ||
| loss_utils, | ||
| "get_parallel_state", | ||
| lambda: SimpleNamespace(sp_enabled=False, fsdp_size=1), | ||
| ) | ||
|
|
||
| loss_dict = loss_utils.mean_global_loss( | ||
| {"foundation_loss": torch.tensor(4.0)}, | ||
| {"foundation_tokens": torch.tensor(2)}, | ||
| {"foundation_tokens": torch.tensor(5)}, | ||
| ) | ||
|
|
||
| assert loss_dict["foundation_loss"].item() == pytest.approx(0.8) | ||
| assert calls == [(5, "sum", None)] | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -73,7 +73,7 @@ | |
| is_nccl_backend, | ||
| synchronize, | ||
| ) | ||
| from ..utils.loss_utils import count_loss_token, mean_global_loss | ||
| from ..utils.loss_utils import count_loss_token, mean_global_loss, reduce_global_loss_token | ||
| from ..utils.model_utils import pretty_print_trainable_parameters | ||
| from .callbacks import ( | ||
| ChannelLossCallback, | ||
|
|
@@ -675,7 +675,10 @@ def postforward( | |
| ) -> tuple[torch.Tensor, dict[str, torch.Tensor]]: | ||
| """Postprocess model outputs after forward pass.""" | ||
| loss_dict: Dict[str, torch.Tensor] = mean_global_loss( | ||
| outputs.loss, self.micro_batch_token_len, self.micro_batches_token_len | ||
| outputs.loss, | ||
| self.micro_batch_token_len, | ||
| self.micro_batches_token_len, | ||
| getattr(self, "global_micro_batches_token_len", None), | ||
|
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.
It never raises, so a trainer that sets Suggest declaring |
||
| ) | ||
| loss = torch.stack(list(loss_dict.values())).sum() | ||
| return loss, loss_dict | ||
|
|
@@ -766,6 +769,7 @@ def train_step( | |
|
|
||
| # token num for fixed_ce_loss in postforward | ||
| self.micro_batches_token_len = count_loss_token(micro_batches) | ||
| self.global_micro_batches_token_len = reduce_global_loss_token(self.micro_batches_token_len) | ||
| num_micro_steps = len(micro_batches) | ||
| # forward and backward pass with gradient_accumulationsteps | ||
| for micro_step, micro_batch in enumerate(micro_batches): | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -55,6 +55,7 @@ def mean_global_loss( | |
| losses: Union[dict[str, torch.Tensor], torch.Tensor], | ||
| micro_batch_token_len: dict[str, torch.Tensor], | ||
| micro_batches_token_len: dict[str, torch.Tensor], | ||
| global_micro_batches_token_len: dict[str, float] | None = None, | ||
| ): | ||
| """Calcuate the global mean loss. Avg on all_reduced_token_num instead of on dp_size. | ||
| - cur_losses[key] = cur_loss * cur_token_num / global_batches_token_num * get_parallel_state().fsdp_size | ||
|
|
@@ -72,7 +73,10 @@ def mean_global_loss( | |
| if get_parallel_state().sp_enabled: | ||
| cur_token_len = all_reduce(cur_token_len.item(), op="sum", group=get_parallel_state().sp_group) | ||
|
|
||
| all_reduced_len = all_reduce((micro_batches_token_len[f"{loss_name}_tokens"].item()), op="sum") | ||
| if global_micro_batches_token_len is None: | ||
| all_reduced_len = all_reduce((micro_batches_token_len[f"{loss_name}_tokens"].item()), op="sum") | ||
| else: | ||
| all_reduced_len = global_micro_batches_token_len[f"{loss_name}_tokens"] | ||
|
|
||
| if all_reduced_len != 0: | ||
| cur_loss = cur_loss * cur_token_len / all_reduced_len * get_parallel_state().fsdp_size | ||
|
|
@@ -88,3 +92,8 @@ def mean_global_loss( | |
| loss_dict[key] = cur_loss | ||
|
|
||
| return loss_dict | ||
|
|
||
|
|
||
| def reduce_global_loss_token(micro_batches_token_len: dict[str, torch.Tensor]) -> dict[str, float]: | ||
| """All-reduce per-step loss-token denominators once for reuse across micro-batches.""" | ||
| return {key: all_reduce(value.item(), op="sum") for key, value in micro_batches_token_len.items()} | ||
|
Comment on lines
+97
to
+99
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. Performance & Robustness Improvement: Pack All-Reduce Calls and Add Distributed Guard\n\n1. Performance: Currently,
|
||
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.
Update the unit test to match the optimized single-call packed
all_reduceimplementation and mock the distributed initialization check.