Spaces:
Running on L40S
Running on L40S
| # SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. | |
| # SPDX-License-Identifier: OpenMDW-1.1 | |
| from __future__ import annotations | |
| from dataclasses import dataclass | |
| from typing import Tuple | |
| import torch | |
| import torch.distributed as dist | |
| import torch.utils.data | |
| import wandb | |
| from cosmos_framework.model._base import ImaginaireModel | |
| from cosmos_framework.utils import distributed, log | |
| from cosmos_framework.utils.callback import Callback | |
| from cosmos_framework.utils.easy_io import easy_io | |
| class _LossRecord: | |
| loss: float = 0 | |
| iter_count: int = 0 | |
| name: str = None | |
| def reset(self) -> None: | |
| self.loss = 0 | |
| self.iter_count = 0 | |
| def get_stat(self, return_valid_mask_sum: bool = False) -> Tuple[float, float]: | |
| if self.iter_count == 0: | |
| self.loss = torch.tensor([float("nan")], device="cuda") # [1] | |
| self.iter_count = 1 | |
| msg_str = f"{self.name}: sum_loss={self.loss.item()}/iter_count={self.iter_count}=" | |
| avg_loss_tensor = self.loss / self.iter_count | |
| # Create a mask (1 if valid, 0 if NaN or Inf) | |
| valid_mask = torch.tensor([torch.isfinite(avg_loss_tensor).float()], device="cuda") # [1] | |
| msg_str += f"avg_loss={avg_loss_tensor.item()}, valid_mask={valid_mask.item()}, " | |
| # Replace NaN/Inf with 0 to avoid affecting sum | |
| avg_loss_tensor = torch.where( | |
| torch.isfinite(avg_loss_tensor), | |
| avg_loss_tensor, | |
| torch.tensor([0.0], device="cuda"), # [1] | |
| ) | |
| # Reduce across all ranks | |
| dist.all_reduce(avg_loss_tensor, op=dist.ReduceOp.SUM) # Sum of valid losses | |
| dist.all_reduce(valid_mask, op=dist.ReduceOp.SUM) # Count of valid losses | |
| msg_str += f" | all_reduce: avg_loss={avg_loss_tensor.item()}, valid_mask={valid_mask.item()}" | |
| # Compute final average, avoiding division by zero | |
| if valid_mask.item() > 0: | |
| final_avg_loss = (avg_loss_tensor / valid_mask).item() | |
| valid_mask_sum = valid_mask.item() | |
| else: | |
| final_avg_loss = 0.0 # Default to zero if all values were invalid | |
| valid_mask_sum = 0 | |
| avg_loss = final_avg_loss | |
| msg_str += f" | final: avg_loss={final_avg_loss}" | |
| if self.name is not None: | |
| log.debug(msg_str, rank0_only=False) | |
| self.reset() | |
| if return_valid_mask_sum: | |
| return avg_loss, valid_mask_sum | |
| else: | |
| return avg_loss | |
| class WandbCallback(Callback): | |
| def __init__( | |
| self, | |
| logging_iter_multipler: int = 1, | |
| save_logging_iter_multipler: int = 1, | |
| save_s3: bool = False, | |
| ) -> None: | |
| super().__init__() | |
| self.final_loss_log = _LossRecord() | |
| self.final_all_loss_log = {} | |
| self.logging_iter_multipler = logging_iter_multipler | |
| self.save_logging_iter_multipler = save_logging_iter_multipler | |
| assert self.logging_iter_multipler > 0, "logging_iter_multipler should be greater than 0" | |
| self.save_s3 = save_s3 | |
| self.wandb_extra_tag = f"@{logging_iter_multipler}" if logging_iter_multipler > 1 else "" | |
| self.name = "wandb_loss_log" + self.wandb_extra_tag | |
| self.unstable_count = torch.zeros(1, device="cuda") # [1] | |
| def on_training_step_end( | |
| self, | |
| model: ImaginaireModel, | |
| data_batch: dict[str, torch.Tensor], | |
| output_batch: dict[str, torch.Tensor], | |
| loss: torch.Tensor, | |
| iteration: int = 0, | |
| ) -> None: | |
| if torch.isnan(loss) or torch.isinf(loss): | |
| log.critical( | |
| f"Unstable loss {loss} at iteration {iteration}", | |
| rank0_only=False, | |
| ) | |
| self.unstable_count += 1 | |
| self.final_loss_log.loss += loss.detach().float() | |
| self.final_loss_log.iter_count += 1 | |
| for key in output_batch.keys(): | |
| if "loss" in key: | |
| if key not in self.final_all_loss_log: | |
| self.final_all_loss_log[key] = _LossRecord() | |
| self.final_all_loss_log[key].loss += output_batch[key].detach().float() | |
| self.final_all_loss_log[key].iter_count += 1 | |
| if iteration % (self.config.trainer.logging_iter * self.logging_iter_multipler) == 0: | |
| avg_final_loss = self.final_loss_log.get_stat() | |
| avg_final_all_loss = {} | |
| for key in self.final_all_loss_log.keys(): | |
| avg_final_all_loss[key] = self.final_all_loss_log[key].get_stat() | |
| dist.all_reduce(self.unstable_count, op=dist.ReduceOp.SUM) | |
| if distributed.is_rank0() and wandb.run is not None: | |
| info = {} | |
| info.update( | |
| { | |
| f"train{self.wandb_extra_tag}/loss": avg_final_loss, | |
| f"train{self.wandb_extra_tag}/unstable_count": self.unstable_count.item(), | |
| "iteration": iteration, | |
| } | |
| ) | |
| for key, loss in avg_final_all_loss.items(): | |
| info.update( | |
| { | |
| f"train{self.wandb_extra_tag}_detail/{key}": loss, | |
| } | |
| ) | |
| if self.save_s3: | |
| if ( | |
| iteration | |
| % ( | |
| self.config.trainer.logging_iter | |
| * self.logging_iter_multipler | |
| * self.save_logging_iter_multipler | |
| ) | |
| == 0 | |
| ): | |
| easy_io.dump( | |
| info, | |
| f"s3://rundir/{self.name}/Train_Iter{iteration:09d}.json", | |
| ) | |
| if wandb: | |
| wandb.log(info, step=iteration) | |
| # reset unstable count | |
| self.unstable_count.zero_() | |