-
Notifications
You must be signed in to change notification settings - Fork 68
fix: Use Karras sigma schedule for Cosmos Predict2.5 SFT inference #33
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -51,6 +51,55 @@ | |
| from fastgen.utils.basic_utils import str2bool | ||
|
|
||
|
|
||
| _COSMOS_KARRAS_SIGMA_MAX = 200.0 | ||
| _COSMOS_KARRAS_SIGMA_MIN = 0.01 | ||
| _COSMOS_KARRAS_RHO = 7.0 | ||
|
|
||
|
|
||
| def _build_cosmos_predict2_karras_schedule( | ||
| num_steps: int, | ||
| num_train_timesteps: int, | ||
| device: Union[torch.device, str], | ||
| ) -> Tuple[torch.Tensor, torch.Tensor]: | ||
| """Build the Karras sigma/timestep schedule used by official Cosmos Predict2.5 inference.""" | ||
| if num_steps <= 0: | ||
| raise ValueError(f"num_steps must be positive, got {num_steps}") | ||
|
|
||
| ramp = torch.arange(num_steps + 1, dtype=torch.float64) / num_steps | ||
| min_inv_rho = _COSMOS_KARRAS_SIGMA_MIN ** (1 / _COSMOS_KARRAS_RHO) | ||
| max_inv_rho = _COSMOS_KARRAS_SIGMA_MAX ** (1 / _COSMOS_KARRAS_RHO) | ||
| sigmas = (max_inv_rho + ramp * (min_inv_rho - max_inv_rho)) ** _COSMOS_KARRAS_RHO | ||
| sigmas = sigmas / (1 + sigmas) | ||
|
|
||
| timesteps = (sigmas * num_train_timesteps).to(device=device, dtype=torch.int64) | ||
| sigmas = torch.cat([sigmas, sigmas.new_zeros(1)]).to(dtype=torch.float32) | ||
| return sigmas, timesteps | ||
|
|
||
|
|
||
| def _reset_unipc_scheduler_state(scheduler: UniPCMultistepScheduler) -> None: | ||
| scheduler.model_outputs = [None] * scheduler.config.solver_order | ||
| scheduler.lower_order_nums = 0 | ||
| scheduler.last_sample = None | ||
| scheduler._step_index = None | ||
| scheduler._begin_index = None | ||
|
|
||
|
|
||
| def _apply_cosmos_predict2_karras_schedule( | ||
| scheduler: UniPCMultistepScheduler, | ||
| num_steps: int, | ||
| device: Union[torch.device, str], | ||
| ) -> None: | ||
| sigmas, timesteps = _build_cosmos_predict2_karras_schedule( | ||
| num_steps=num_steps, | ||
| num_train_timesteps=scheduler.config.num_train_timesteps, | ||
| device=device, | ||
| ) | ||
| scheduler.sigmas = sigmas | ||
| scheduler.timesteps = timesteps | ||
| scheduler.num_inference_steps = len(timesteps) | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
| _reset_unipc_scheduler_state(scheduler) | ||
|
|
||
|
|
||
| # ---------------------- DiT Network ----------------------- | ||
|
|
||
|
|
||
|
|
@@ -767,6 +816,7 @@ def __init__( | |
| use_wan_fp32_strategy: bool = True, | ||
| # FPS for temporal position embeddings | ||
| fps: float = 24.0, | ||
| use_karras_sigma_schedule: bool = True, | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
The PR description says to "gate this behind a sensible default or config flag so it only affects the SFT inference path, not distillation." Setting There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟠 Major 🧩 Analysis chain🏁 Script executed: #!/bin/bash
# Inspect CosmosPredict2 call sites and config overrides for the new schedule flag.
rg -n -C2 'use_karras_sigma_schedule|CosmosPredict2|config_sft|distill|sample\(' .Repository: NVlabs/FastGen Length of output: 50371 🏁 Script executed: #!/bin/bash
set -euo pipefail
# Inspect the CosmosPredict2 constructor, sample method, and config usages.
for f in \
fastgen/networks/cosmos_predict2/network.py \
fastgen/configs/net.py \
fastgen/configs/experiments/CosmosPredict2/config_sft.py \
fastgen/configs/experiments/CosmosPredict2/config_sft_14b.py \
fastgen/configs/experiments/CosmosPredict2/config_sft_v2w.py \
fastgen/configs/experiments/CosmosPredict2/config_dmd2.py \
fastgen/configs/experiments/CosmosPredict2/config_dmd2_14b.py \
fastgen/configs/experiments/CosmosPredict2/config_dmd2_v2w.py \
scripts/inference/video_model_inference.py
do
if [ -f "$f" ]; then
echo "===== $f ====="
wc -l "$f"
fi
done
echo "===== network.py outline ====="
ast-grep outline fastgen/networks/cosmos_predict2/network.py --view expanded || true
echo "===== network.py relevant slices ====="
sed -n '740,930p' fastgen/networks/cosmos_predict2/network.py
echo "===== net.py cosmos slice ====="
sed -n '270,310p' fastgen/configs/net.py
echo "===== Cosmos SFT configs ====="
sed -n '1,120p' fastgen/configs/experiments/CosmosPredict2/config_sft.py
printf '\n'
sed -n '1,120p' fastgen/configs/experiments/CosmosPredict2/config_sft_14b.py
printf '\n'
sed -n '1,120p' fastgen/configs/experiments/CosmosPredict2/config_sft_v2w.py
printf '\n'
sed -n '1,160p' fastgen/configs/experiments/CosmosPredict2/config_dmd2.py
printf '\n'
sed -n '1,160p' fastgen/configs/experiments/CosmosPredict2/config_dmd2_14b.py
printf '\n'
sed -n '1,160p' fastgen/configs/experiments/CosmosPredict2/config_dmd2_v2w.py
echo "===== inference call sites around model.sample ====="
sed -n '660,720p' scripts/inference/video_model_inference.pyRepository: NVlabs/FastGen Length of output: 24679 🏁 Script executed: #!/bin/bash
set -euo pipefail
# Focused searches for explicit schedule overrides and direct CosmosPredict2 usage.
rg -n -C3 'use_karras_sigma_schedule\s*=|use_karras_sigma_schedule|CosmosPredict2\(' fastgen scripts testsRepository: NVlabs/FastGen Length of output: 4873 Move the Karras default to the SFT config path. 🤖 Prompt for AI Agents |
||
| # Video2world (image-to-video) mode settings | ||
| is_video2world: bool = False, | ||
| num_conditioning_frames: int = 1, | ||
|
|
@@ -824,6 +874,9 @@ def __init__( | |
| # Default FPS for temporal position embeddings | ||
| self.fps = fps | ||
|
|
||
| # Match official Cosmos Predict2.5 SFT inference schedule by default. | ||
| self.use_karras_sigma_schedule = use_karras_sigma_schedule | ||
|
|
||
| # Initialize the sample scheduler for inference later (otherwise it causes issues with Meta init) | ||
| self.sample_scheduler = None | ||
|
|
||
|
|
@@ -1105,6 +1158,7 @@ def sample( | |
| num_conditioning_frames: int = 1, | ||
| conditional_frame_timestep: float = 0.0, | ||
| denoise_replace_gt_frames: bool = True, | ||
| use_karras_sigma_schedule: Optional[bool] = None, | ||
| **kwargs, | ||
| ) -> torch.Tensor: | ||
| """Multistep sampling using the FlowUniPC method. | ||
|
|
@@ -1138,11 +1192,15 @@ def sample( | |
| Use 0.0 to indicate clean frames with no noise. | ||
| denoise_replace_gt_frames: Whether to replace velocity for conditioning frames | ||
| with analytical velocity (noise - gt_frames). Default True. | ||
| use_karras_sigma_schedule: Whether to use the official Cosmos Predict2.5 | ||
| Karras sigma schedule for SFT inference. Defaults to self.use_karras_sigma_schedule. | ||
|
|
||
| Returns: | ||
| The denoised sample tensor. | ||
| """ | ||
| assert self.schedule_type == "rf", f"{self.schedule_type} is not supported" | ||
| if use_karras_sigma_schedule is None: | ||
| use_karras_sigma_schedule = self.use_karras_sigma_schedule | ||
|
|
||
| # Set timesteps with shift parameter (matching official Cosmos inference) | ||
| if self.sample_scheduler is None: | ||
|
|
@@ -1155,6 +1213,8 @@ def sample( | |
| else: | ||
| self.sample_scheduler.config.flow_shift = shift | ||
| self.sample_scheduler.set_timesteps(num_inference_steps=num_steps, device=noise.device) | ||
| if use_karras_sigma_schedule: | ||
| _apply_cosmos_predict2_karras_schedule(self.sample_scheduler, num_steps=num_steps, device=noise.device) | ||
| timesteps = self.sample_scheduler.timesteps | ||
|
|
||
| # Initialize latents with proper scaling based on the initial timestep | ||
|
|
@@ -1210,7 +1270,7 @@ def sample( | |
| # Store initial noise for velocity replacement | ||
| initial_noise = latents.clone() | ||
|
|
||
| for idx, timestep in tqdm(enumerate(timesteps), total=num_steps, desc="Sampling"): | ||
| for idx, timestep in tqdm(enumerate(timesteps), total=len(timesteps), desc="Sampling"): | ||
| # Normalize timestep to [0, 1] range | ||
| t = (timestep / self.sample_scheduler.config.num_train_timesteps).expand(latents.shape[0]) | ||
| t = self.noise_scheduler.safe_clamp(t, min=self.noise_scheduler.min_t, max=self.noise_scheduler.max_t).to( | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
sigmasnever moved todevice, breaking GPU inferencerampis always created on CPU viatorch.arange(...), so all derivedsigmascomputations stay on CPU.timestepsis correctly moved with.to(device=device, ...), but the finalsigmastensor that is returned (and ultimately stored inscheduler.sigmas) stays on CPU. Whenscheduler.step()is called during GPU inference it will index into the CPUsigmastensor and use the resulting CPU 0-dim tensors in arithmetic with CUDA latent tensors, raising aRuntimeError: Expected all tensors to be on the same device. The priorset_timesteps(device=noise.device)call correctly places sigmas on device — this Karras override silently undoes that.