greenwam / cosmos_framework /callbacks /dit_image_sample.py
multimodalart's picture
multimodalart HF Staff
Initial GreenWAM demo
a1833c8 verified
Raw History Blame Contribute Delete
3.34 kB
# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: OpenMDW-1.1
"""Fixed class-conditioned image sampling callback for DiT training."""
from __future__ import annotations
from contextlib import nullcontext
from typing import Any
import torch
import torchvision
import wandb
from cosmos_framework.callbacks.every_n import EveryN
from cosmos_framework.model._base import ImaginaireModel
from cosmos_framework.trainer import ImaginaireTrainer
from cosmos_framework.utils import distributed
class DiTImageSampleCallback(EveryN):
"""Generate fixed ImageNet class samples through ``model.generate_image``."""
def __init__(
self,
every_n: int = 5000,
class_ids: list[int] | None = None,
cfg_scales: list[float] | None = None,
num_steps: int = 50,
seed: int = 0,
is_ema: bool = True,
run_at_start: bool = False,
) -> None:
super().__init__(every_n=every_n, run_at_start=run_at_start)
self.class_ids = class_ids or [0, 1, 2, 3]
self.cfg_scales = cfg_scales or [1.0, 1.25, 1.5, 2.0]
self.num_steps = num_steps
self.seed = seed
self.is_ema = is_ema
self.rank = distributed.get_rank()
@torch.no_grad()
def every_n_impl(
self,
trainer: ImaginaireTrainer,
model: ImaginaireModel,
data_batch: dict[str, torch.Tensor],
output_batch: dict[str, torch.Tensor],
loss: torch.Tensor,
iteration: int,
) -> None:
del trainer, data_batch, output_batch, loss
if not hasattr(model, "generate_image"):
raise AttributeError("DiTImageSampleCallback requires model.generate_image().")
if self.is_ema and not model.config.ema.enabled:
return
was_training = model.training
context: Any = model.ema_scope("dit_image_sample") if self.is_ema else nullcontext()
generated_rows: list[torch.Tensor] = []
seed_list = [self.seed + sample_idx for sample_idx in range(len(self.class_ids))]
try:
with context:
for cfg_scale in self.cfg_scales:
images = model.generate_image(
class_ids=self.class_ids,
num_steps=self.num_steps,
cfg_scale=cfg_scale,
seed=seed_list,
) # [B,3,H,W]
if self.rank == 0:
generated_rows.append(images.detach().float().cpu()) # [B,3,H,W]
finally:
if was_training:
model.train()
if self.rank != 0 or wandb.run is None or not generated_rows:
return
grid_images = torch.cat(generated_rows, dim=0) # [R*B,3,H,W]
grid = torchvision.utils.make_grid(grid_images, nrow=len(self.class_ids), padding=2, normalize=False) # [3,H,W]
grid_np = grid.clamp(0.0, 1.0).permute(1, 2, 0).numpy() # [H,W,3]
tag = "ema" if self.is_ema else "reg"
caption = f"classes={self.class_ids}, cfg={self.cfg_scales}, steps={self.num_steps}, seed={self.seed}, {tag}"
wandb.log(
{f"dit_image_sample/{tag}": wandb.Image(grid_np, caption=caption)},
step=iteration,
)