dwehr's picture
Migrate action viewer to local Cosmos generation
9f818c5
Raw
History Blame Contribute Delete
12.7 kB
# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: OpenMDW-1.1
from typing import Any, Optional
import torch
from cosmos_framework.model._base import ImaginaireModel
from cosmos_framework.utils.callback import Callback
from cosmos_framework.utils.log import logger
from cosmos_framework.utils.misc import get_data_batch_size
from cosmos_framework.utils.training_telemetry.utils import (
get_checkpoint_strategy,
get_telemetry_recorder,
import_training_telemetry,
)
class TelemetryCallback(Callback):
"""Callback for Telemetry"""
def __init__(
self,
) -> None:
super().__init__()
self.training_telemetry = import_training_telemetry()
self.recorder = get_telemetry_recorder()
self.spans: dict[self.training_telemetry.events.EventName, self.training_telemetry.Span] = {}
self.iteration_elapsed = 0.0
self.forward_elapsed = 0.0
self.backward_elapsed = 0.0
self.dataloader_elapsed = 0.0
self.validation_elapsed = 0.0
self.validation_loss = 0.0
self.validation_iter = 0
self.checkpoint_strategy = "async"
self.checkpoint_interval = 0
self.validation_interval = 0
def _start_span(self, span_name: Any, metrics: Any = None, verbosity: Any = None, color: Any = None) -> None:
"""Start a span with the given event name and optional metrics, color, and verbosity"""
if span_name in self.spans:
logger.warning(f"Span {span_name} already started, stopping it but this is unexpected")
self.recorder.stop(self.spans[span_name])
del self.spans[span_name]
if verbosity is None:
verbosity = self.training_telemetry.Verbosity.INFO
self.spans[span_name] = self.recorder.start(
name=span_name,
color=color,
verbosity=verbosity,
metrics=metrics,
)
def _stop_span(self, span_name: Any, metrics: Any = None) -> float:
"""Stop a span and return the elapsed time"""
span = self.spans.get(span_name)
if span is None:
logger.warning(f"Span {span_name} was not started, this is unexpected")
return 0.0
if metrics is not None:
span.add_metrics(metrics)
self.recorder.stop(span)
del self.spans[span_name]
return span.duration.elapsed
def on_train_start(self, model: ImaginaireModel, iteration: int = 0) -> None:
span_name = self.training_telemetry.SpanName.TRAINING_LOOP
self._start_span(span_name)
self.iteration_elapsed = 0
self.forward_elapsed = 0
self.backward_elapsed = 0
self.dataloader_elapsed = 0
try:
self.checkpoint_strategy = get_checkpoint_strategy(self.config.checkpoint)
except Exception as e:
logger.warning(f"Failed to get checkpoint strategy using default {self.checkpoint_strategy}: {e}")
try:
self.checkpoint_interval = self.config.checkpoint.save_iter
self.validation_interval = self.config.trainer.validation_iter
except Exception as e:
logger.warning(
f"Failed to get intervals using default {self.checkpoint_interval} and {self.validation_interval}: {e}"
)
def on_training_step_batch_start(
self, model: ImaginaireModel, data: dict[str, torch.Tensor], iteration: int = 0
) -> None:
span_name = self.training_telemetry.SpanName.ITERATION
self._start_span(
span_name,
color=self.training_telemetry.SpanColor.BLUE,
verbosity=self.training_telemetry.Verbosity.PROFILING,
)
def on_before_forward(self, iteration: int = 0) -> None:
span_name = self.training_telemetry.SpanName.MODEL_FORWARD
self._start_span(
span_name,
color=self.training_telemetry.SpanColor.GREEN,
verbosity=self.training_telemetry.Verbosity.PROFILING,
)
def on_after_forward(self, iteration: int = 0) -> None:
span_name = self.training_telemetry.SpanName.MODEL_FORWARD
self.forward_elapsed += self._stop_span(span_name)
def on_before_backward(self, model: ImaginaireModel, loss: torch.Tensor, iteration: int = 0) -> None:
span_name = self.training_telemetry.SpanName.MODEL_BACKWARD
self._start_span(
span_name,
color=self.training_telemetry.SpanColor.YELLOW,
verbosity=self.training_telemetry.Verbosity.PROFILING,
)
def on_after_backward(self, model: ImaginaireModel, iteration: int = 0) -> None:
span_name = self.training_telemetry.SpanName.MODEL_BACKWARD
self.backward_elapsed += self._stop_span(span_name)
def on_before_dataloading(self, iteration: int = 0) -> None:
span_name = self.training_telemetry.SpanName.DATA_LOADING
self._start_span(
span_name,
color=self.training_telemetry.SpanColor.GREEN,
verbosity=self.training_telemetry.Verbosity.PROFILING,
)
def on_after_dataloading(self, iteration: int = 0) -> None:
span_name = self.training_telemetry.SpanName.DATA_LOADING
self.dataloader_elapsed += self._stop_span(span_name)
def on_optimizer_init_start(self) -> None:
span_name = self.training_telemetry.SpanName.OPTIMIZER_INIT
self._start_span(span_name)
def on_optimizer_init_end(self) -> None:
span_name = self.training_telemetry.SpanName.OPTIMIZER_INIT
self._stop_span(span_name)
def on_before_optimizer_step(
self,
model: ImaginaireModel,
optimizer: torch.optim.Optimizer,
scheduler: torch.optim.lr_scheduler.LRScheduler,
grad_scaler: torch.amp.GradScaler,
iteration: int = 0,
) -> None:
pass
def on_before_zero_grad(
self,
model: ImaginaireModel,
optimizer: torch.optim.Optimizer,
scheduler: torch.optim.lr_scheduler.LRScheduler,
iteration: int = 0,
) -> None:
pass
def on_training_step_batch_end(
self,
model: ImaginaireModel,
data_batch: dict[str, torch.Tensor],
output_batch: dict[str, torch.Tensor],
loss: torch.Tensor,
iteration: int = 0,
) -> None:
span_name = self.training_telemetry.SpanName.ITERATION
self.iteration_elapsed += self._stop_span(span_name)
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:
"""Unlike on_training_step_batch_end, this function is called when the optimizer is updated, and the iteration incremented."""
if iteration % self.config.trainer.logging_iter == 0:
event_name = self.training_telemetry.events.EventName.TRAINING_ITERATIONS
avg_iteration_time = self.iteration_elapsed / self.config.trainer.logging_iter
avg_forward_time = self.forward_elapsed / self.config.trainer.logging_iter
avg_backward_time = self.backward_elapsed / self.config.trainer.logging_iter
avg_dataloader_time = self.dataloader_elapsed / self.config.trainer.logging_iter
batch_size = get_data_batch_size(data_batch)
# throughput = num_floating_point_operations(batch_size) / (
# avg_iteration_time * 10**12 * distributed.get_world_size)
metrics = self.training_telemetry.IterationMetrics.create(
current_iteration=iteration,
num_iterations=self.config.trainer.logging_iter,
interval=self.config.trainer.logging_iter,
average_iteration_time=avg_iteration_time,
average_forward_time=avg_forward_time,
average_backward_time=avg_backward_time,
average_dataloader_time=avg_dataloader_time,
tflops=0.0,
tokens_per_second=0.0,
loss=loss.item(),
batch_size=batch_size,
)
self.recorder.event(self.training_telemetry.events.Event.create(event_name, metrics))
self.iteration_elapsed = 0
self.forward_elapsed = 0
self.backward_elapsed = 0
self.dataloader_elapsed = 0
def on_validation_start(
self, model: ImaginaireModel, dataloader_val: torch.utils.data.DataLoader, iteration: int = 0
) -> None:
span_name = self.training_telemetry.SpanName.VALIDATION_LOOP
self._start_span(span_name)
self.validation_loss = 0.0
self.validation_iter = 0
self.validation_elapsed = 0.0
def on_validation_step_start(
self, model: ImaginaireModel, data: dict[str, torch.Tensor], iteration: int = 0
) -> None:
span_name = self.training_telemetry.SpanName.ITERATION
self._start_span(
span_name,
color=self.training_telemetry.SpanColor.RED,
verbosity=self.training_telemetry.Verbosity.PROFILING,
)
def on_validation_step_end(
self,
model: ImaginaireModel,
data_batch: dict[str, torch.Tensor],
output_batch: dict[str, torch.Tensor],
loss: torch.Tensor,
iteration: int = 0,
) -> None:
self.validation_loss += loss.item()
self.validation_iter += 1
span_name = self.training_telemetry.SpanName.ITERATION
self.validation_elapsed += self._stop_span(span_name)
def on_validation_end(self, model: ImaginaireModel, iteration: int = 0) -> None:
if self.validation_iter == 0:
return
span_name = self.training_telemetry.SpanName.VALIDATION_LOOP
avg_validation_time = self.validation_elapsed / self.validation_iter
metrics = self.training_telemetry.IterationMetrics.create(
current_iteration=iteration,
num_iterations=self.validation_iter,
interval=self.validation_interval,
average_iteration_time=avg_validation_time,
loss=self.validation_loss / self.validation_iter,
)
self._stop_span(span_name, metrics=metrics)
self.validation_elapsed = 0.0
self.validation_loss = 0.0
self.validation_iter = 0
def on_load_checkpoint_start(self, model: ImaginaireModel) -> None:
span_name = self.training_telemetry.SpanName.CHECKPOINT_LOAD
self._start_span(span_name)
def on_load_checkpoint_end(
self, model: ImaginaireModel, iteration: int = 0, checkpoint_path: Optional[str] = None
) -> None:
span_name = self.training_telemetry.SpanName.CHECKPOINT_LOAD
metrics = self.training_telemetry.CheckpointMetrics.create(
checkpoint_type=self.training_telemetry.CheckPointType.GLOBAL,
current_iteration=iteration,
checkpoint_directory=checkpoint_path,
)
self._stop_span(span_name, metrics=metrics)
def on_load_checkpoint(self, model: ImaginaireModel, state_dict: dict[Any]) -> None:
pass
def on_save_checkpoint_start(self, model: ImaginaireModel, iteration: int = 0) -> None:
span_name = (
self.training_telemetry.SpanName.CHECKPOINT_SAVE_SYNC
if self.checkpoint_strategy == "sync"
else self.training_telemetry.SpanName.CHECKPOINT_SAVE_ASYNC
)
checkpoint_metrics = self.training_telemetry.CheckpointMetrics.create(
checkpoint_type=self.training_telemetry.CheckPointType.GLOBAL,
current_iteration=iteration,
interval=self.checkpoint_interval,
)
self._start_span(span_name, metrics=checkpoint_metrics)
def on_save_checkpoint_end(self, model: ImaginaireModel, iteration: int = 0) -> None:
span_name = (
self.training_telemetry.SpanName.CHECKPOINT_SAVE_SYNC
if self.checkpoint_strategy == "sync"
else self.training_telemetry.SpanName.CHECKPOINT_SAVE_ASYNC
)
self._stop_span(span_name)
def on_save_checkpoint_success(self, iteration: int = 0, elapsed_time: float = 0) -> None:
pass
def on_save_checkpoint(self, model: ImaginaireModel, state_dict: dict[Any]) -> None:
pass
def on_train_end(self, model: ImaginaireModel, iteration: int = 0) -> None:
span_name = self.training_telemetry.SpanName.TRAINING_LOOP
self._stop_span(span_name)
def on_app_end(self) -> None:
pass