Thanks to visit codestin.com
Credit goes to github.com

Skip to content

fix(ddpm): use _execution_device, validate inputs, free hooks (#13649)#13671

Open
Anai-Guo wants to merge 1 commit into
huggingface:mainfrom
Anai-Guo:fix/ddpm-pipeline-execution-device-and-validation
Open

fix(ddpm): use _execution_device, validate inputs, free hooks (#13649)#13671
Anai-Guo wants to merge 1 commit into
huggingface:mainfrom
Anai-Guo:fix/ddpm-pipeline-execution-device-and-validation

Conversation

@Anai-Guo

@Anai-Guo Anai-Guo commented May 1, 2026

Copy link
Copy Markdown

What does this PR do?

Fixes the three issues called out in the ddpm model/pipeline review (cc @hlky). All three changes live in pipelines/ddpm/pipeline_ddpm.py and follow the suggested fixes from the issue, with precedents from DDIMPipeline and ConsistencyModelPipeline.

Issue 1 — DDPMPipeline does not run latents on the offload execution device

DDPMPipeline declares model_cpu_offload_seq = "unet" but initializes the latent on self.device (which stays CPU under enable_model_cpu_offload()) and never calls self.maybe_free_model_hooks() before returning. Switched to self._execution_device, threaded that into the randn_tensor call (and the mps branch), and added the maybe_free_model_hooks() call before the return so the offload contract is honored and the UNet doesn't stay resident on the accelerator.

Issue 2 — Generator lists are not validated against batch_size

A short generator list either gets silently treated like a single generator or raises a raw IndexError. Added the same ValueError guard DDIMPipeline and ConsistencyModelPipeline use, so users get a clear message when len(generator) != batch_size.

Issue 3 — Invalid output_type values silently return NumPy

Any output_type other than "pil" previously fell through to NumPy, including typos and "pt". Added an explicit output_type in {"pt", "np", "pil"} check at the top of __call__, made "pt" actually return the tensor (skipping the .cpu().numpy() round-trip), and updated the docstring to mention torch.Tensor. Behavior for the documented values ("pil", default; "np") is unchanged.

Reproductions

The repros from #13649 are short and self-contained — see the issue body for runnable scripts that demonstrate each failure mode.

Before submitting

  • Did you read the contributor guideline?
  • Did you read our philosophy doc (important for complex PRs)?
  • Was this discussed/approved via a GitHub issue or the forum? Please add a link to it if that's the case. Yes — ddpm model/pipeline review #13649.
  • Did you make sure to update the documentation with your changes? Doc string updated in this file; no separate docs page touched.
  • Did you write any new necessary tests?

Who can review?

@hlky — this addresses all three issues from the ddpm review.

🤖 Generated with Claude Code

…gface#13649)

Issue 1: replace self.device with self._execution_device so model_cpu_offload's
execution device is honored, and call self.maybe_free_model_hooks() before return
to satisfy the offload contract.

Issue 2: validate that len(generator) == batch_size for list generators, raising
ValueError instead of silently mishandling per-sample seeding (matches DDIM/
ConsistencyModel pipelines).

Issue 3: validate output_type and add 'pt' tensor output. Previously any value
other than 'pil' silently fell through to NumPy.

Closes huggingface#13649.
@github-actions github-actions Bot added pipelines size/S PR with diff < 50 LOC labels May 1, 2026

@hlky hlky left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @Anai-Guo.

I left a few comments. Some are just confirming that the PR resolves the reported issues, and a couple are broader questions for maintainers about whether these patterns should become review/agent rules:

  • validating output_type
  • preferring VaeImageProcessor.postprocess(...) over manual clamp/permute/NumPy/PIL handling

Overall, this looks like it addresses the ddpm findings from #13649.

Also, feel free to tag me on PRs that come from the review issues / meta issue since I can usually confirm whether they address the reported finding. For approval/merge, please also tag an appropriate maintainer since I can review the issue context but cannot approve. In general, I’m happy to help clarify the reports or point out related patterns from #13656 if useful.

Comment on lines +101 to +102
if output_type not in ["pt", "np", "pil"]:
raise ValueError(f"output_type must be one of ['pt', 'np', 'pil'], got '{output_type}'.")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cc @yiyixuxu Should output_type validation be a review rule?

Comment on lines +104 to +108
if isinstance(generator, list) and len(generator) != batch_size:
raise ValueError(
f"You have passed a list of generators of length {len(generator)}, but requested an effective batch"
f" size of {batch_size}. Make sure the batch size matches the length of the generators."
)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great, this resolves issue 2

Comment on lines 110 to +128
@@ -108,12 +120,12 @@ def __call__(
else:
image_shape = (batch_size, self.unet.config.in_channels, *self.unet.config.sample_size)

if self.device.type == "mps":
if device.type == "mps":
# randn does not work reproducibly on mps
image = randn_tensor(image_shape, generator=generator, dtype=self.unet.dtype)
image = image.to(self.device)
image = image.to(device)
else:
image = randn_tensor(image_shape, generator=generator, device=self.device, dtype=self.unet.dtype)
image = randn_tensor(image_shape, generator=generator, device=device, dtype=self.unet.dtype)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great, this + a later line resolves issue 1

Comment on lines 143 to +147
image = (image / 2 + 0.5).clamp(0, 1)
image = image.cpu().permute(0, 2, 3, 1).numpy()
if output_type == "pil":
image = self.numpy_to_pil(image)
if output_type != "pt":
image = image.cpu().permute(0, 2, 3, 1).numpy()
if output_type == "pil":
image = self.numpy_to_pil(image)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As per additional review in #13663 (comment) this could use VaeImageProcessor, this would fix all output_type's without the if statements. cc @yiyixuxu For awareness, this is another case that supports introducing VaeImageProcessor usage as a review rule.

@Anai-Guo

Anai-Guo commented May 1, 2026

Copy link
Copy Markdown
Author

Thanks @hlky for the review! Glad the fix resolves both reported issues.

For the line 147 suggestion — happy to refactor to VaeImageProcessor.postprocess(...) in this PR if @yiyixuxu agrees that's the preferred direction; otherwise I can keep this PR scoped to the original two bugs and leave the broader VaeImageProcessor migration for a follow-up. Just let me know which you prefer.

@Anai-Guo

Copy link
Copy Markdown
Author

Friendly ping @yiyixuxu — this has been waiting since early May. @hlky confirmed it resolves both reported issues from #13649; the only open item is the line-147 question of whether to refactor to VaeImageProcessor.postprocess(...) here vs. leaving the broader migration for a follow-up.

Happy to do the VaeImageProcessor refactor in this PR if that's the preferred direction — otherwise it's ready to merge scoped to the two original bugs. Just let me know which you'd prefer.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

pipelines size/S PR with diff < 50 LOC

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants