mirror of
https://github.com/invoke-ai/InvokeAI
synced 2024-08-30 20:32:17 +00:00
14372e3818
- Pass the seed from `latents_a` to the output latents. Fixed an issue where using `BlendLatentsInvocation` could result in different outputs during denoising even when the alpha or slerp weight was 0. ## Explanation `LatentsField` has an optional `seed` field. During denoising, if this `seed` field is not present, we **fall back to 0 for the seed**. The seed is used during denoising in a few ways: 1. Initializing the scheduler. The seed is used in two places in `invokeai/app/invocations/latent.py`. The `get_scheduler()` utility function has special handling for `DPMSolverSDEScheduler`, which appears to need a seed for deterministic outputs. `DenoiseLatentsInvocation.init_scheduler()` has special handling for schedulers that accept a generator - the generator needs to be seeded in a particular way. At the time of this commit, these are the Invoke-supported schedulers that need this seed: - DDIMScheduler - DDPMScheduler - DPMSolverMultistepScheduler - EulerAncestralDiscreteScheduler - EulerDiscreteScheduler - KDPM2AncestralDiscreteScheduler - LCMScheduler - TCDScheduler 2. Adding noise during inpainting. If a mask is used for denoising, and we are not using an inpainting model, we add noise to the unmasked area. If, for some reason, we have a mask but no noise, the seed is used to add noise. I wonder if we should instead assert that if a mask is provided, we also have noise. This is done in `invokeai/backend/stable_diffusion/diffusers_pipeline.py` in `StableDiffusionGeneratorPipeline.latents_from_embeddings()`. When we create noise to be used in denoising, we are expected to set `LatentsField.seed` to the seed used to create the noise. This introduces some awkwardness when we manipulate any "latents" that will be used for denoising. We have to pass the seed along for every operation. If the wrong seed or no seed is passed along, we can get unexpected outputs during denoising. One notable case relates to blending latents (slerping tensors). If we slerp two noise tensors (`LatentsField`s) _without_ passing along the seed from the source latents, when we denoise with a seed-dependent scheduler*, the schedulers use the fallback seed of 0 and we get the wrong output. This is most obvious when slerping with a weight of 0, in which case we expect the exact same output after denoising. *It looks like only the DPMSolver* schedulers are affected, but I haven't tested all of them. Passing the seed along in the output fixes this issue. |
||
---|---|---|
.. | ||
api | ||
assets/images | ||
invocations | ||
services | ||
shared | ||
util | ||
__init__.py | ||
api_app.py | ||
run_app.py |