Skip to content
This repository was archived by the owner on May 20, 2026. It is now read-only.

Commit 38b0309

Browse files
sajadnabhinavg4
andauthored
Adding DiT training and Inference (#27)
* first dit commit. Signed-off-by: Sajad Norouzi <snorouzi@nvidia.com> * inference working with old checkpoint. Signed-off-by: Sajad Norouzi <snorouzi@nvidia.com> * add on_validation_start step to generate video. Signed-off-by: Sajad Norouzi <snorouzi@nvidia.com> * EDM loss bug fix. Signed-off-by: Sajad Norouzi <snorouzi@nvidia.com> * Refactor DIT components: add comments for potential removals and clarify usage of common elements in the codebase. Signed-off-by: Sajad Norouzi <snorouzi@nvidia.com> * Add utility functions for tensor operations and dynamic imports - Introduced `batch_ops.py` with functions for tensor broadcasting and element-wise operations (addition, multiplication, subtraction, division). - Added `dynamic_import.py` for dynamic loading of classes or functions from specified paths. - Created `torch_split_tensor_for_cp.py` for tensor splitting and concatenation in checkpoint parallelism. - Implemented a new data module for handling multimodal datasets in `base.py` and `diffusion_energon_datamodule.py`. - Added `diffusion_taskencoder.py` for encoding image/video samples for diffusion tasks. - Updated `edm_pipeline.py` to utilize new utility functions for tensor operations. - Included a script for preparing the Energon dataset in `prepare_energon_dataset_butterfly.py`. Signed-off-by: Sajad Norouzi <snorouzi@nvidia.com> * remove unused layers from spec. Signed-off-by: Sajad Norouzi <snorouzi@nvidia.com> * Remove unused AdaLN and STDiTLayerWithAdaLN classes from dit_layer_spec.py to streamline the codebase. Signed-off-by: Sajad Norouzi <snorouzi@nvidia.com> * Refactor DIT model files: removed unused functions from `dit_provider.py`, updated import paths in `dit.py`, and adjusted training parameters for `task_encoder_seq_length` and `save_interval` to optimize configuration. Signed-off-by: Sajad Norouzi <snorouzi@nvidia.com> * Add README for DiT model setup: include instructions for cloning repositories, dataset location, Docker setup, installation, and running the model on the butterfly dataset. Signed-off-by: Sajad Norouzi <snorouzi@nvidia.com> * Remove unused files and refactor DIT model components: deleted obsolete files including `decode_cosmos_latent.py`, `attention_wan.py`, `diffusers_vae.py`, `dit_embeddings.py`, `dit_inference.py`, and others. Updated import paths and streamlined the codebase for improved maintainability. Signed-off-by: Sajad Norouzi <snorouzi@nvidia.com> * Add new components for DiT model: - Introduced `attention_wan.py` with implementations for WAN self and cross attention layers, including adaptive layer normalization. - Added `time_pos_embeddings.py` for various temporal positional embedding strategies. - Created `dit_data_process.py` for data handling in DiT model training, including batch processing and sequence length encoding. - Implemented `dit_model_provider.py` for model configuration and instantiation of DiT models. - Added utility functions in `edm_utils.py` for EDM sampling and scaling processes. These additions enhance the DiT model's capabilities and streamline data processing and model configuration. Signed-off-by: Sajad Norouzi <snorouzi@nvidia.com> * revert THD to SBHD and use remove prints. Signed-off-by: Sajad Norouzi <snorouzi@nvidia.com> * add support sequence for sequence packing. Signed-off-by: Sajad Norouzi <snorouzi@nvidia.com> * move files in proper location, uncomment cp related code, cleaning. Signed-off-by: Sajad Norouzi <snorouzi@nvidia.com> * fix lint errors. Signed-off-by: Sajad Norouzi <snorouzi@nvidia.com> * fix README lint error. Signed-off-by: Sajad Norouzi <snorouzi@nvidia.com> * add the missing copyright. Signed-off-by: Sajad Norouzi <snorouzi@nvidia.com> * removed dependency on nemo CausalVideoTokenizer by implementing a minimal Tokenizer version. Signed-off-by: Sajad Norouzi <snorouzi@nvidia.com> * remove nemo vae modules. Signed-off-by: Sajad Norouzi <snorouzi@nvidia.com> * update readme.md Signed-off-by: Sajad Norouzi <snorouzi@nvidia.com> * remove unused temporal_self_attention attribute from DiTWithAdaLNSubmodules class Signed-off-by: Sajad Norouzi <snorouzi@nvidia.com> * create an abstract DiffusionTaskEncoder to encompass the sequence packing logic that is shared across models. Signed-off-by: Sajad Norouzi <snorouzi@nvidia.com> * fix use seq_length instead of packing_buffer_size to set max number of tokens. Signed-off-by: Sajad Norouzi <snorouzi@nvidia.com> * Add the missing copy write. Minor update to the config. Signed-off-by: Sajad Norouzi <snorouzi@nvidia.com> * lint still complains about the nemo_vfm file. Signed-off-by: Sajad Norouzi <snorouzi@nvidia.com> * move data modules to common, move cosmos to common/tokenizer instad of common/models. Make inference compatible with the new data loading. Signed-off-by: Sajad Norouzi <snorouzi@nvidia.com> * uncomment omega config overwrite. Signed-off-by: Sajad Norouzi <snorouzi@nvidia.com> * change import (remote lint complains). Signed-off-by: Sajad Norouzi <snorouzi@nvidia.com> --------- Signed-off-by: Sajad Norouzi <snorouzi@nvidia.com> Co-authored-by: Abhinav Garg <abhinavg@stanford.edu>
1 parent 0a6addd commit 38b0309

41 files changed

Lines changed: 4836 additions & 0 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

dfm/src/common/tokenizers/__init__ .py

Whitespace-only changes.

dfm/src/common/tokenizers/cosmos/__init__.py

Whitespace-only changes.
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
# SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
2+
# SPDX-License-Identifier: Apache-2.0
3+
#
4+
# Licensed under the Apache License, Version 2.0 (the "License");
5+
# you may not use this file except in compliance with the License.
6+
# You may obtain a copy of the License at
7+
#
8+
# http://www.apache.org/licenses/LICENSE-2.0
9+
#
10+
# Unless required by applicable law or agreed to in writing, software
11+
# distributed under the License is distributed on an "AS IS" BASIS,
12+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
# See the License for the specific language governing permissions and
14+
# limitations under the License.
Lines changed: 193 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,193 @@
1+
# SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
2+
# SPDX-License-Identifier: Apache-2.0
3+
#
4+
# Licensed under the Apache License, Version 2.0 (the "License");
5+
# you may not use this file except in compliance with the License.
6+
# You may obtain a copy of the License at
7+
#
8+
# http://www.apache.org/licenses/LICENSE-2.0
9+
#
10+
# Unless required by applicable law or agreed to in writing, software
11+
# distributed under the License is distributed on an "AS IS" BASIS,
12+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
# See the License for the specific language governing permissions and
14+
# limitations under the License.
15+
16+
# pylint: disable=C0115,C0116,C0301
17+
18+
"""A library for Causal Video Tokenizer inference."""
19+
20+
from pathlib import Path
21+
22+
import numpy as np
23+
import torch
24+
from huggingface_hub import get_token as get_hf_token
25+
from huggingface_hub import hf_hub_download
26+
from tqdm import tqdm
27+
28+
from dfm.src.common.tokenizers.cosmos.cosmos1.video_tokenizer_utils import (
29+
load_jit_model,
30+
numpy2tensor,
31+
pad_video_batch,
32+
tensor2numpy,
33+
unpad_video_batch,
34+
)
35+
36+
37+
class CausalVideoTokenizer(torch.nn.Module):
38+
def __init__(
39+
self,
40+
checkpoint_dir: str = None,
41+
load_full_model: bool = True,
42+
load_dec_model: bool = True,
43+
load_enc_model: bool = True,
44+
device: str = "cuda",
45+
dtype: str = "bfloat16",
46+
) -> None:
47+
super().__init__()
48+
49+
checkpoint = Path(checkpoint_dir)
50+
self._full_model_path = str(checkpoint / "autoencoder.jit")
51+
self._enc_model_path = str(checkpoint / "encoder.jit")
52+
self._dec_model_path = str(checkpoint / "decoder.jit")
53+
self._dtype = dtype
54+
self._device = device
55+
56+
self._full_model = load_jit_model(self._full_model_path, self._device) if load_full_model else None
57+
self._enc_model = load_jit_model(self._enc_model_path, self._device) if load_enc_model else None
58+
self._dec_model = load_jit_model(self._dec_model_path, self._device) if load_dec_model else None
59+
60+
@classmethod
61+
def from_pretrained(
62+
cls,
63+
tokenizer_type="Cosmos-Tokenizer-DV4x8x8",
64+
load_encoder=True,
65+
load_decoder=True,
66+
load_full_model=False,
67+
use_pytorch=False,
68+
dtype="bfloat16",
69+
):
70+
cls._hf_model_name = f"nvidia/{tokenizer_type}"
71+
72+
# Requires setting HF_TOKEN env variable
73+
hf_token = get_hf_token()
74+
75+
full_model_path = hf_hub_download(
76+
repo_id=cls._hf_model_name,
77+
filename="autoencoder.jit",
78+
token=hf_token,
79+
)
80+
81+
_ = hf_hub_download(
82+
repo_id=cls._hf_model_name,
83+
filename="encoder.jit",
84+
token=hf_token,
85+
)
86+
87+
_ = hf_hub_download(
88+
repo_id=cls._hf_model_name,
89+
filename="decoder.jit",
90+
token=hf_token,
91+
)
92+
93+
# No need to load in encoder and decoder with full model loaded
94+
if load_full_model:
95+
load_encoder = False
96+
load_decoder = False
97+
98+
# Assumes HF downloads all files to same local dir
99+
ckpt_dir = str(Path(full_model_path).parent)
100+
args = {
101+
"checkpoint_dir": ckpt_dir,
102+
"dtype": dtype,
103+
"load_enc_model": load_encoder,
104+
"load_dec_model": load_decoder,
105+
"load_full_model": load_full_model,
106+
}
107+
return cls(**args)
108+
109+
@torch.no_grad()
110+
def autoencode(self, input_tensor: torch.Tensor) -> torch.Tensor:
111+
"""Reconstrcuts a batch of video tensors after embedding into a latent.
112+
113+
Args:
114+
video: The input video Bx3xTxHxW layout, range [-1..1].
115+
Returns:
116+
The reconstructed video, layout Bx3xTxHxW, range [-1..1].
117+
"""
118+
if self._full_model is not None:
119+
output_tensor = self._full_model(input_tensor)
120+
output_tensor = output_tensor[0] if isinstance(output_tensor, tuple) else output_tensor
121+
else:
122+
output_latent = self.encode(input_tensor)[0]
123+
output_tensor = self.decode(output_latent)
124+
return output_tensor
125+
126+
@torch.no_grad()
127+
def encode(self, input_tensor: torch.Tensor) -> tuple[torch.Tensor]:
128+
"""Encodes a numpy video into a CausalVideo latent or code.
129+
130+
Args:
131+
input_tensor: The input tensor Bx3xTxHxW layout, range [-1..1].
132+
Returns:
133+
For causal continuous video (CV) tokenizer, the tuple contains:
134+
- The latent embedding, Bx16x(t)x(h)x(w), where the compression
135+
rate is (T/t x H/h x W/w), and channel dimension of 16.
136+
For causal discrete video (DV) tokenizer, the tuple contains:
137+
1) The indices, Bx(t)x(h)x(w), from a codebook of size 64K, which
138+
is formed by FSQ levels of (8,8,8,5,5,5).
139+
2) The discrete code, Bx6x(t)x(h)x(w), where the compression rate
140+
is again (T/t x H/h x W/w), and channel dimension of 6.
141+
"""
142+
assert input_tensor.ndim == 5, "input video should be of 5D."
143+
144+
output_latent = self._enc_model(input_tensor)
145+
if isinstance(output_latent, torch.Tensor):
146+
return output_latent
147+
return output_latent[:-1]
148+
149+
@torch.no_grad()
150+
def decode(self, input_latent: torch.Tensor) -> torch.Tensor:
151+
"""Encodes a numpy video into a CausalVideo latent.
152+
153+
Args:
154+
input_latent: The continuous latent Bx16xtxhxw for CV,
155+
or the discrete indices Bxtxhxw for DV.
156+
Returns:
157+
The reconstructed tensor, layout [B,3,1+(T-1)*8,H*16,W*16] in range [-1..1].
158+
"""
159+
assert input_latent.ndim >= 4, "input latent should be of 5D for continuous and 4D for discrete."
160+
return self._dec_model(input_latent)
161+
162+
def forward(
163+
self,
164+
video: np.ndarray,
165+
temporal_window: int = 17,
166+
) -> np.ndarray:
167+
"""Reconstructs video using a pre-trained CausalTokenizer autoencoder.
168+
Given a video of arbitrary length, the forward invokes the CausalVideoTokenizer
169+
in a sliding manner with a `temporal_window` size.
170+
171+
Args:
172+
video: The input video BxTxHxWx3 layout, range [0..255].
173+
temporal_window: The length of the temporal window to process, default=25.
174+
Returns:
175+
The reconstructed video in range [0..255], layout BxTxHxWx3.
176+
"""
177+
assert video.ndim == 5, "input video should be of 5D."
178+
num_frames = video.shape[1] # can be of any length.
179+
output_video_list = []
180+
for idx in tqdm(range(0, (num_frames - 1) // temporal_window + 1)):
181+
# Input video for the current window.
182+
start, end = idx * temporal_window, (idx + 1) * temporal_window
183+
input_video = video[:, start:end, ...]
184+
185+
# Spatio-temporally pad input_video so it's evenly divisible.
186+
padded_input_video, crop_region = pad_video_batch(input_video)
187+
input_tensor = numpy2tensor(padded_input_video, dtype=self._dtype, device=self._device)
188+
output_tensor = self.autoencode(input_tensor)
189+
padded_output_video = tensor2numpy(output_tensor)
190+
output_video = unpad_video_batch(padded_output_video, crop_region)
191+
192+
output_video_list.append(output_video)
193+
return np.concatenate(output_video_list, axis=1)
Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
# SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
2+
# SPDX-License-Identifier: Apache-2.0
3+
#
4+
# Licensed under the Apache License, Version 2.0 (the "License");
5+
# you may not use this file except in compliance with the License.
6+
# You may obtain a copy of the License at
7+
#
8+
# http://www.apache.org/licenses/LICENSE-2.0
9+
#
10+
# Unless required by applicable law or agreed to in writing, software
11+
# distributed under the License is distributed on an "AS IS" BASIS,
12+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
# See the License for the specific language governing permissions and
14+
# limitations under the License.
15+
16+
"""Utility functions for the inference libraries."""
17+
18+
import numpy as np
19+
import torch
20+
21+
22+
_DTYPE, _DEVICE = torch.bfloat16, "cuda"
23+
_UINT8_MAX_F = float(torch.iinfo(torch.uint8).max)
24+
_SPATIAL_ALIGN = 16
25+
_TEMPORAL_ALIGN = 8
26+
27+
28+
def load_jit_model(jit_filepath: str = None, device: str = "cuda") -> torch.jit.ScriptModule:
29+
"""Loads a torch.jit.ScriptModule from a filepath.
30+
31+
Args:
32+
jit_filepath: The filepath to the JIT-compiled model.
33+
device: The device to load the model onto, default=cuda.
34+
Returns:
35+
The JIT compiled model loaded to device and on eval mode.
36+
"""
37+
model = torch.jit.load(jit_filepath, map_location=device)
38+
return model.eval().to(device)
39+
40+
41+
def numpy2tensor(
42+
input_image: np.ndarray,
43+
dtype: torch.dtype = _DTYPE,
44+
device: str = _DEVICE,
45+
range_min: int = -1,
46+
) -> torch.Tensor:
47+
"""Converts image(dtype=np.uint8) to `dtype` in range [0..255].
48+
49+
Args:
50+
input_image: A batch of images in range [0..255], BxHxWx3 layout.
51+
Returns:
52+
A torch.Tensor of layout Bx3xHxW in range [-1..1], dtype.
53+
"""
54+
ndim = input_image.ndim
55+
indices = list(range(1, ndim))[-1:] + list(range(1, ndim))[:-1]
56+
image = input_image.transpose((0,) + tuple(indices)) / _UINT8_MAX_F
57+
if range_min == -1:
58+
image = 2.0 * image - 1.0
59+
return torch.from_numpy(image).to(dtype).to(device)
60+
61+
62+
def tensor2numpy(input_tensor: torch.Tensor, range_min: int = -1) -> np.ndarray:
63+
"""Converts tensor in [-1,1] to image(dtype=np.uint8) in range [0..255].
64+
65+
Args:
66+
input_tensor: Input image tensor of Bx3xHxW layout, range [-1..1].
67+
Returns:
68+
A numpy image of layout BxHxWx3, range [0..255], uint8 dtype.
69+
"""
70+
if range_min == -1:
71+
input_tensor = (input_tensor.float() + 1.0) / 2.0
72+
ndim = input_tensor.ndim
73+
output_image = input_tensor.clamp(0, 1).cpu().numpy()
74+
output_image = output_image.transpose((0,) + tuple(range(2, ndim)) + (1,))
75+
return (output_image * _UINT8_MAX_F + 0.5).astype(np.uint8)
76+
77+
78+
def pad_video_batch(
79+
batch: np.ndarray,
80+
temporal_align: int = _TEMPORAL_ALIGN,
81+
spatial_align: int = _SPATIAL_ALIGN,
82+
) -> tuple[np.ndarray, list[int]]:
83+
"""Pads a batch of videos to be divisible by `temporal_align` or `spatial_align`.
84+
85+
Zero pad spatially. Reflection pad temporally to handle causality better.
86+
Args:
87+
batch: The batch of videos to pad., layout BxFxHxWx3, in any range.
88+
align: The alignment to pad to.
89+
Returns:
90+
The padded batch and the crop region.
91+
"""
92+
num_frames, height, width = batch.shape[-4:-1]
93+
align = spatial_align
94+
height_to_pad = (align - height % align) if height % align != 0 else 0
95+
width_to_pad = (align - width % align) if width % align != 0 else 0
96+
97+
align = temporal_align
98+
frames_to_pad = (align - (num_frames - 1) % align) if (num_frames - 1) % align != 0 else 0
99+
100+
crop_region = [
101+
frames_to_pad >> 1,
102+
height_to_pad >> 1,
103+
width_to_pad >> 1,
104+
num_frames + (frames_to_pad >> 1),
105+
height + (height_to_pad >> 1),
106+
width + (width_to_pad >> 1),
107+
]
108+
batch = np.pad(
109+
batch,
110+
(
111+
(0, 0),
112+
(0, 0),
113+
(height_to_pad >> 1, height_to_pad - (height_to_pad >> 1)),
114+
(width_to_pad >> 1, width_to_pad - (width_to_pad >> 1)),
115+
(0, 0),
116+
),
117+
mode="constant",
118+
)
119+
batch = np.pad(
120+
batch,
121+
(
122+
(0, 0),
123+
(frames_to_pad >> 1, frames_to_pad - (frames_to_pad >> 1)),
124+
(0, 0),
125+
(0, 0),
126+
(0, 0),
127+
),
128+
mode="edge",
129+
)
130+
return batch, crop_region
131+
132+
133+
def unpad_video_batch(batch: np.ndarray, crop_region: list[int]) -> np.ndarray:
134+
"""Unpads video with `crop_region`.
135+
136+
Args:
137+
batch: A batch of numpy videos, layout BxFxHxWxC.
138+
crop_region: [f1,y1,x1,f2,y2,x2] first, top, left, last, bot, right crop indices.
139+
140+
Returns:
141+
np.ndarray: Cropped numpy video, layout BxFxHxWxC.
142+
"""
143+
assert len(crop_region) == 6, "crop_region should be len of 6."
144+
f1, y1, x1, f2, y2, x2 = crop_region
145+
return batch[..., f1:f2, y1:y2, x1:x2, :]

dfm/src/common/utils/__init__.py

Whitespace-only changes.

0 commit comments

Comments
 (0)