Fix bugs related to uninitialised GPU memory and istep - #2722
Fix bugs related to uninitialised GPU memory and istep#2722sophie-xhonneux wants to merge 16 commits into
Conversation
…ophiex/dev-ssl/ssl-and-diff-july
…/val_workers' into sophiex/dev-ssl/ssl-and-diff-july
…poisoning (#2717) NaN * 0 = NaN, so a non-finite per-token loss at a mask-excluded position poisoned the whole per-sample sum in _masked_l1_loss_per_sample. Use torch.where(mask.bool(), ...) so excluded positions contribute exactly 0; NaNs inside the mask still propagate as before.
…2718) The skip decision was evaluated only on rank 0 and broadcast, so a NaN/spike loss on any other rank was never skipped and its gradients poisoned the all-reduce. Now every rank checks its local loss and the per-rank flags are combined with an all-reduce MAX (logical OR), so a spike on any rank skips the batch on all ranks. Logging remains rank-0-only.
clessig
left a comment
There was a problem hiding this comment.
Minor comments. This PR seems to have some unrelated changes and would break FSDP again, I think.
| # Intermediate encoder levels are tapped before the encoder's final LayerNorm, so they | ||
| # arrive on very different (and much larger) scales than the final level. Normalize each | ||
| # level before concatenating so the fusion and the downstream SSL head see a common scale. | ||
| self.level_norms = nn.ModuleList([nn.LayerNorm(dim_embed) for _ in range(num_levels)]) |
There was a problem hiding this comment.
This change seems unrelated to the problem that is addressed in the PR?
|
|
||
| def forward(self, levels: list[torch.Tensor]) -> torch.Tensor: | ||
| return self.proj(torch.cat(levels, dim=-1)) | ||
| normed = [norm(level) for norm, level in zip(self.level_norms, levels, strict=True)] |
| num_params_q_aux = ( | ||
| np.prod(self.encoder.q_aux.shape) if self.encoder.q_aux.requires_grad else 0 | ||
| ) | ||
| # if self.encoder.q_aux: |
There was a problem hiding this comment.
This change was introduced to make FSDP work
There was a problem hiding this comment.
see my comment below, the statement breaks in single GPU mode if q_aux exists because it is a nn.Parameter tensor so not bool or can be reduced to bool
| print(f" Learnable spatial queries: {num_params_q_cells:,}") | ||
| if self.encoder.q_aux: | ||
| print(f" Learnable auxiliary queries: {num_params_q_aux:,}") | ||
| # if self.encoder.q_aux: |
There was a problem hiding this comment.
we are printing parameters, this should not break FSDP, it doesn't create the parameters
| @@ -0,0 +1,86 @@ | |||
| # (C) Copyright 2024 WeatherGenerator contributors. | |||
| num_params_q_aux = ( | ||
| np.prod(self.encoder.q_aux.shape) if self.encoder.q_aux.requires_grad else 0 | ||
| ) | ||
| # if self.encoder.q_aux: |
There was a problem hiding this comment.
this broke in single GPU mode for me :/ can you explain the error to me?
There was a problem hiding this comment.
we are in the function that prints parameters, does not seem like it will break FSDP
There was a problem hiding this comment.
Yes I think you're right it is not fsdp but whether it breaks I think depends if you have register tokens or not right?
You do have register tokens (I guess), so now it works. But if we want to run with 0 register tokens with this code, we are now doing shape of None and we get an error?
There was a problem hiding this comment.
So I think we should actually have:
if self.encoder.q_aux is not None:
There was a problem hiding this comment.
yes! you are right, I couldn't think of the right check at midnight yesterday so I commented it out, but what you right is correct I think
| token_counts = mask_f.sum(dim=-1) | ||
| valid_samples = token_counts > 0 | ||
| per_sample_loss = (per_token_loss * mask_f).sum(dim=-1) / token_counts.clamp(min=1.0) | ||
| # torch.where (not multiply): NaN at mask-excluded positions must not poison the sum. |
There was a problem hiding this comment.
Can merge this PR in last so it is after the PR that explicitly does this
| if OmegaConf.is_dict(conf): | ||
| for key in list(conf.keys()): | ||
| key = str(key) | ||
| # key = str(key) # this lines breaks target_source_correspondence please stop re introducing it |
There was a problem hiding this comment.
Do we need this for anything else in the config?
I get around it with e.g. target_source_correspondence: {"0": {"0": independent}} in target source I think, in case it is necessary for something else in the config
There was a problem hiding this comment.
I would ask the question the other way around, is there any reason this line is needed, it breaks the type assumptions of YAML
clessig
left a comment
There was a problem hiding this comment.
Can we clean up the PR so that we don't change 20 files unrelated to the problems. In engines.py are also changes that are unrelated?
Description
Change torch.empty to torch.zeros in StreamEmbedder to ensure there is no uninitialised GPU memory, also skip streams
with stream embed Identity when considering memory to allocate (#2721)
Fix istep not being passed along in loss_calculator.py (#2720)
Config changes for space JEPA (no need to review)
Remark: Linting is broken, but this was true before.
Issue Number
Closes #2720 #2721
Is this PR a draft? Mark it as draft.
Checklist before asking for review
./scripts/actions.sh lint./scripts/actions.sh unit-test./scripts/actions.sh integration-testlaunch-slurm.py --time 60