|
5 | 5 |
|
6 | 6 | from ...models.transformers.transformer_cosmos3 import Cosmos3OmniTransformer |
7 | 7 | from ...pipelines.cosmos.pipeline_cosmos3_omni import _EMBODIMENT_TO_DOMAIN_ID, CosmosActionCondition |
8 | | -from ...schedulers import UniPCMultistepScheduler |
| 8 | +from ...schedulers import FlowMatchEulerDiscreteScheduler, UniPCMultistepScheduler |
9 | 9 | from ...utils.torch_utils import randn_tensor |
10 | 10 | from ..modular_pipeline import ModularPipelineBlocks, PipelineState |
11 | 11 | from ..modular_pipeline_utils import ComponentSpec, ConfigSpec, InputParam, OutputParam |
@@ -116,6 +116,11 @@ def intermediate_outputs(self) -> list[OutputParam]: |
116 | 116 | type_hint=list[int], |
117 | 117 | description="Indexes of conditioned vision latent frames.", |
118 | 118 | ), |
| 119 | + OutputParam( |
| 120 | + "vision_conditioning_latents", |
| 121 | + type_hint=torch.Tensor, |
| 122 | + description="Clean encoded vision latents used to re-anchor image conditioning each step.", |
| 123 | + ), |
119 | 124 | ] |
120 | 125 |
|
121 | 126 | @torch.no_grad() |
@@ -166,6 +171,7 @@ def __call__(self, components: Cosmos3OmniModularPipeline, state: PipelineState) |
166 | 171 | block_state.vision_condition_mask[:, 0, 0] > 0, as_tuple=False |
167 | 172 | ).flatten() |
168 | 173 | block_state.vision_condition_indexes_for_pack = [int(idx.item()) for idx in vision_condition_indexes] |
| 174 | + block_state.vision_conditioning_latents = x0_tokens_vision |
169 | 175 |
|
170 | 176 | self.set_block_state(state, block_state) |
171 | 177 | return components, state |
@@ -1237,3 +1243,89 @@ def __call__(self, components: Cosmos3OmniModularPipeline, state: PipelineState) |
1237 | 1243 | ) |
1238 | 1244 | self.set_block_state(state, block_state) |
1239 | 1245 | return components, state |
| 1246 | + |
| 1247 | + |
| 1248 | +class Cosmos3DistilledSetTimestepsStep(ModularPipelineBlocks): |
| 1249 | + model_name = "cosmos3-omni" |
| 1250 | + |
| 1251 | + @property |
| 1252 | + def description(self) -> str: |
| 1253 | + return "Initializes the fixed distilled sampling schedule from the pipeline's `distilled_sigmas` config." |
| 1254 | + |
| 1255 | + @property |
| 1256 | + def expected_components(self) -> list[ComponentSpec]: |
| 1257 | + return [ComponentSpec("scheduler", FlowMatchEulerDiscreteScheduler)] |
| 1258 | + |
| 1259 | + @property |
| 1260 | + def expected_configs(self) -> list[ConfigSpec]: |
| 1261 | + return [ |
| 1262 | + ConfigSpec(name="is_distilled", default=True), |
| 1263 | + ConfigSpec(name="distilled_sigmas", default=None), |
| 1264 | + ] |
| 1265 | + |
| 1266 | + @property |
| 1267 | + def inputs(self) -> list[InputParam]: |
| 1268 | + return [ |
| 1269 | + InputParam.template("num_inference_steps", required=False, default=None), |
| 1270 | + InputParam( |
| 1271 | + name="guidance_scale", |
| 1272 | + type_hint=float, |
| 1273 | + default=None, |
| 1274 | + description=( |
| 1275 | + "Unused for distilled checkpoints; classifier-free guidance is baked into the weights and the " |
| 1276 | + "scale is forced to 1.0. Passing a value other than 1.0 raises an error." |
| 1277 | + ), |
| 1278 | + ), |
| 1279 | + ] |
| 1280 | + |
| 1281 | + @property |
| 1282 | + def intermediate_outputs(self) -> list[OutputParam]: |
| 1283 | + return [ |
| 1284 | + OutputParam("timesteps", type_hint=torch.Tensor, description="Scheduler timesteps for denoising."), |
| 1285 | + OutputParam("num_warmup_steps", type_hint=int, description="Number of scheduler warmup steps."), |
| 1286 | + OutputParam( |
| 1287 | + "num_inference_steps", |
| 1288 | + type_hint=int, |
| 1289 | + description="Resolved number of denoising steps (fixed by the distilled schedule).", |
| 1290 | + ), |
| 1291 | + OutputParam( |
| 1292 | + name="guidance_scale", |
| 1293 | + type_hint=float, |
| 1294 | + description="Resolved classifier-free guidance scale (always 1.0 for distilled checkpoints).", |
| 1295 | + ), |
| 1296 | + ] |
| 1297 | + |
| 1298 | + @torch.no_grad() |
| 1299 | + def __call__(self, components: Cosmos3OmniModularPipeline, state: PipelineState) -> PipelineState: |
| 1300 | + block_state = self.get_block_state(state) |
| 1301 | + device = components._execution_device |
| 1302 | + |
| 1303 | + sigmas = components.config.distilled_sigmas |
| 1304 | + if not sigmas: |
| 1305 | + raise ValueError( |
| 1306 | + "Cosmos3DistilledSetTimestepsStep requires the pipeline config `distilled_sigmas` to be set " |
| 1307 | + "(populated from the distilled checkpoint's `modular_model_index.json`). Load a distilled Cosmos3 " |
| 1308 | + "checkpoint or use `Cosmos3OmniModularPipeline` for base checkpoints." |
| 1309 | + ) |
| 1310 | + sigmas = [float(s) for s in sigmas] |
| 1311 | + distilled_steps = len(sigmas) |
| 1312 | + |
| 1313 | + if block_state.num_inference_steps is not None and block_state.num_inference_steps != distilled_steps: |
| 1314 | + raise ValueError( |
| 1315 | + "This is a distilled checkpoint; the step count is fixed by the pipeline's " |
| 1316 | + f"`distilled_sigmas` config ({distilled_steps} steps). " |
| 1317 | + f"`num_inference_steps` must be {distilled_steps} or left unset (got {block_state.num_inference_steps})." |
| 1318 | + ) |
| 1319 | + if block_state.guidance_scale is not None and block_state.guidance_scale != 1.0: |
| 1320 | + raise ValueError( |
| 1321 | + "This is a distilled checkpoint; classifier-free guidance is baked into the weights. " |
| 1322 | + f"`guidance_scale` must be 1.0 or left unset (got {block_state.guidance_scale})." |
| 1323 | + ) |
| 1324 | + |
| 1325 | + components.scheduler.set_timesteps(sigmas=sigmas, device=device) |
| 1326 | + block_state.num_inference_steps = distilled_steps |
| 1327 | + block_state.guidance_scale = 1.0 |
| 1328 | + block_state.timesteps = components.scheduler.timesteps |
| 1329 | + block_state.num_warmup_steps = len(block_state.timesteps) - distilled_steps * components.scheduler.order |
| 1330 | + self.set_block_state(state, block_state) |
| 1331 | + return components, state |
0 commit comments