Spaces:
Running on Zero
Running on Zero
| # SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. | |
| # SPDX-License-Identifier: Apache-2.0 | |
| from __future__ import annotations | |
| from typing import TYPE_CHECKING | |
| from lipforcing.callbacks.callback import Callback | |
| from lipforcing.utils.distributed import world_size | |
| import lipforcing.utils.logging_utils as logger | |
| import torch | |
| import wandb | |
| try: | |
| from torch.distributed.tensor import DTensor | |
| except ImportError: | |
| DTensor = None | |
| if TYPE_CHECKING: | |
| from lipforcing.methods import FastGenModel | |
| def _get_local_numel(param: torch.Tensor) -> int: | |
| """Get the local (sharded) number of elements for a parameter. | |
| For DTensor (FSDP2), returns the local shard size. | |
| For regular tensors, returns the full size. | |
| """ | |
| if DTensor is not None and isinstance(param, DTensor): | |
| return param._local_tensor.numel() | |
| return param.numel() | |
| class ParamCountCallback(Callback): | |
| def on_train_begin(self, model: FastGenModel, **kwargs) -> None: | |
| # get modules | |
| modules = {"model": model, **model.model_dict} | |
| # iterate over modules | |
| output = {} | |
| for name, module in modules.items(): | |
| # Logical (full model) param counts | |
| trainable_params = sum(p.numel() for p in module.parameters() if p.requires_grad) | |
| total_params = sum(p.numel() for p in module.parameters()) | |
| # Local (sharded) param counts - what's actually in memory on this rank | |
| local_trainable_params = sum(_get_local_numel(p) for p in module.parameters() if p.requires_grad) | |
| local_total_params = sum(_get_local_numel(p) for p in module.parameters()) | |
| # check if parameter counts are different across ranks | |
| if world_size() > 1: | |
| trainable_params = self.gather_param_counts(trainable_params) | |
| total_params = self.gather_param_counts(total_params) | |
| local_trainable_params = self.gather_param_counts(local_trainable_params) | |
| local_total_params = self.gather_param_counts(local_total_params) | |
| if len(set(total_params)) == 1 and len(set(trainable_params)) == 1: | |
| trainable_params = trainable_params[0] | |
| total_params = total_params[0] | |
| if len(set(local_total_params)) == 1 and len(set(local_trainable_params)) == 1: | |
| local_trainable_params = local_trainable_params[0] | |
| local_total_params = local_total_params[0] | |
| # logging | |
| module_name = module.__class__.__name__ | |
| output.update( | |
| { | |
| f"{name}/trainable_params": trainable_params, | |
| f"{name}/total_params": total_params, | |
| f"{name}/local_trainable_params": local_trainable_params, | |
| f"{name}/local_total_params": local_total_params, | |
| } | |
| ) | |
| if isinstance(trainable_params, list): | |
| logger.warning(f"Parameter counts differ across ranks for {module_name}.") | |
| for rank, (p_train, p) in enumerate(zip(trainable_params, total_params)): | |
| logger.info( | |
| f"{name} ({module_name}) has {p_train * 1.e-6:.2f} M trainable and {p * 1.e-6:.2f} M total params on rank {rank}." | |
| ) | |
| else: | |
| logger.info( | |
| f"{name} ({module_name}) has {trainable_params * 1.e-6:.2f} M trainable and {total_params * 1.e-6:.2f} M total params (logical)." | |
| ) | |
| # Report local/sharded counts | |
| if isinstance(local_trainable_params, list): | |
| for rank, (p_train, p) in enumerate(zip(local_trainable_params, local_total_params)): | |
| logger.info( | |
| f"{name} ({module_name}) has {p_train * 1.e-6:.2f} M trainable and {p * 1.e-6:.2f} M total params LOCAL on rank {rank}." | |
| ) | |
| else: | |
| is_sharded = local_total_params < total_params if not isinstance(total_params, list) else True | |
| if is_sharded: | |
| logger.info( | |
| f"{name} ({module_name}) has {local_trainable_params * 1.e-6:.2f} M trainable and {local_total_params * 1.e-6:.2f} M total params LOCAL per rank (sharding ratio: {world_size()}x)." | |
| ) | |
| else: | |
| logger.info(f"{name} ({module_name}) is NOT sharded (local == logical params).") | |
| if wandb.run: | |
| wandb.run.summary.update(output) | |
| def gather_param_counts(self, param_count): | |
| """ | |
| Gather parameter counts across all ranks. | |
| Args: | |
| param_count: Parameter count to gather. | |
| Returns: | |
| List of parameter counts across all ranks. | |
| """ | |
| param_count = torch.tensor( | |
| [param_count], dtype=torch.long, device="cuda" if torch.cuda.is_available() else "cpu" | |
| ) | |
| param_count_list = [torch.zeros_like(param_count) for _ in range(world_size())] | |
| torch.distributed.all_gather(param_count_list, param_count) | |
| return [p.item() for p in param_count_list] | |