Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit cbecc33

Browse files
[docs] Reproducibility (#12237)
* init * dupe * feedback
1 parent 5237a82 commit cbecc33

File tree

2 files changed

+39
-84
lines changed

2 files changed

+39
-84
lines changed

‎docs/source/en/_toctree.yml‎

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
- local: using-diffusers/callback
2222
title: Pipeline callbacks
2323
- local: using-diffusers/reusing_seeds
24-
title: Reproducible pipelines
24+
title: Reproducibility
2525
- local: using-diffusers/schedulers
2626
title: Load schedulers and models
2727
- local: using-diffusers/scheduler_features
@@ -62,8 +62,6 @@
6262
title: Scheduler features
6363
- local: using-diffusers/callback
6464
title: Pipeline callbacks
65-
- local: using-diffusers/reusing_seeds
66-
title: Reproducible pipelines
6765
- local: using-diffusers/image_quality
6866
title: Controlling image quality
6967

‎docs/source/en/using-diffusers/reusing_seeds.md‎

Lines changed: 38 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -10,129 +10,86 @@ an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express o
1010
specific language governing permissions and limitations under the License.
1111
-->
1212

13-
# Reproducible pipelines
13+
# Reproducibility
1414

15-
Diffusion models are inherently random which is what allows it to generate different outputs every time it is run. But there are certain times when you want to generate the same output every time, like when you're testing, replicating results, and even [improving image quality](#deterministic-batch-generation). While you can't expect to get identical results across platforms, you can expect reproducible results across releases and platforms within a certain tolerance range (though even this may vary).
15+
Diffusion is a random process that generates a different output every time. For certain situations like testing and replicating results, you want to generate the same result each time, across releases and platforms within a certain tolerance range.
1616

17-
This guide will show you how to control randomness for deterministic generation on a CPU and GPU.
17+
This guide will show you how to control sources of randomness and enable deterministic algorithms.
1818

19-
> [!TIP]
20-
> We strongly recommend reading PyTorch's [statement about reproducibility](https://pytorch.org/docs/stable/notes/randomness.html):
21-
>
22-
> "Completely reproducible results are not guaranteed across PyTorch releases, individual commits, or different platforms. Furthermore, results may not be reproducible between CPU and GPU executions, even when using identical seeds."
23-
24-
## Control randomness
25-
26-
During inference, pipelines rely heavily on random sampling operations which include creating the
27-
Gaussian noise tensors to denoise and adding noise to the scheduling step.
28-
29-
Take a look at the tensor values in the [`DDIMPipeline`] after two inference steps.
30-
31-
```python
32-
from diffusers import DDIMPipeline
33-
import numpy as np
34-
35-
ddim = DDIMPipeline.from_pretrained( "google/ddpm-cifar10-32", use_safetensors=True)
36-
image = ddim(num_inference_steps=2, output_type="np").images
37-
print(np.abs(image).sum())
38-
```
39-
40-
Running the code above prints one value, but if you run it again you get a different value.
41-
42-
Each time the pipeline is run, [torch.randn](https://pytorch.org/docs/stable/generated/torch.randn.html) uses a different random seed to create the Gaussian noise tensors. This leads to a different result each time it is run and enables the diffusion pipeline to generate a different random image each time.
19+
## Generator
4320

44-
But if you need to reliably generate the same image, that depends on whether you're running the pipeline on a CPU or GPU.
21+
Pipelines rely on [torch.randn](https://pytorch.org/docs/stable/generated/torch.randn.html), which uses a different random seed each time, to create the initial noisy tensors. To generate the same output on a CPU or GPU, use a [Generator](https://docs.pytorch.org/docs/stable/generated/torch.Generator.html) to manage how random values are generated.
4522

4623
> [!TIP]
47-
> It might seem unintuitive to pass `Generator` objects to a pipeline instead of the integer value representing the seed. However, this is the recommended design when working with probabilistic models in PyTorch because a `Generator` is a *random state* that can be passed to multiple pipelines in a sequence. As soon as the `Generator`is consumed, the *state* is changed in place which means even if you passed the same `Generator` to a different pipeline, it won't produce the same result because the state is already changed.
24+
> If reproducibility is important to your use case, we recommend always using a CPU `Generator`. The performance loss is often negligible and you'll generate more similar values.
4825
49-
<hfoptions id="hardware">
50-
<hfoption id="CPU">
26+
<hfoptions id="generator">
27+
<hfoption id="GPU">
28+
29+
The GPU uses a different random number generator than the CPU. Diffusers solves this issue with the [`~utils.torch_utils.randn_tensor`] function to create the random tensor on a CPU and then moving it to the GPU. This function is used everywhere inside the pipeline and you don't need to explicitly call it.
5130

52-
To generate reproducible results on a CPU, you'll need to use a PyTorch [Generator](https://pytorch.org/docs/stable/generated/torch.Generator.html) and set a seed. Now when you run the code, it always prints a value of `1491.1711` because the `Generator` object with the seed is passed to all the random functions in the pipeline. You should get a similar, if not the same, result on whatever hardware and PyTorch version you're using.
31+
Use [manual_seed](https://docs.pytorch.org/docs/stable/generated/torch.manual_seed.html) as shown below to set a seed.
5332

54-
```python
33+
```py
5534
import torch
5635
import numpy as np
5736
from diffusers import DDIMPipeline
5837

59-
ddim = DDIMPipeline.from_pretrained("google/ddpm-cifar10-32", use_safetensors=True)
60-
generator = torch.Generator(device="cpu").manual_seed(0)
38+
ddim = DDIMPipeline.from_pretrained("google/ddpm-cifar10-32", device_map="cuda")
39+
generator = torch.manual_seed(0)
6140
image = ddim(num_inference_steps=2, output_type="np", generator=generator).images
6241
print(np.abs(image).sum())
6342
```
6443

6544
</hfoption>
66-
<hfoption id="GPU">
45+
<hfoption id="CPU">
6746

68-
Writing a reproducible pipeline on a GPU is a bit trickier, and full reproducibility across different hardware is not guaranteed because matrix multiplication - which diffusion pipelines require a lot of - is less deterministic on a GPU than a CPU. For example, if you run the same code example from the CPU example, you'll get a different result even though the seed is identical. This is because the GPU uses a different random number generator than the CPU.
47+
Set `device="cpu"` in the `Generator`and use [manual_seed](https://docs.pytorch.org/docs/stable/generated/torch.manual_seed.html) to set a seed for generating random numbers.
6948

70-
```python
49+
```py
7150
import torch
7251
import numpy as np
7352
from diffusers import DDIMPipeline
7453

75-
ddim = DDIMPipeline.from_pretrained("google/ddpm-cifar10-32", use_safetensors=True)
76-
ddim.to("cuda")
77-
generator = torch.Generator(device="cuda").manual_seed(0)
54+
ddim = DDIMPipeline.from_pretrained("google/ddpm-cifar10-32")
55+
generator = torch.Generator(device="cpu").manual_seed(0)
7856
image = ddim(num_inference_steps=2, output_type="np", generator=generator).images
7957
print(np.abs(image).sum())
8058
```
8159

82-
To avoid this issue, Diffusers has a [`~utils.torch_utils.randn_tensor`] function for creating random noise on the CPU, and then moving the tensor to a GPU if necessary. The [`~utils.torch_utils.randn_tensor`] function is used everywhere inside the pipeline. Now you can call [torch.manual_seed](https://pytorch.org/docs/stable/generated/torch.manual_seed.html) which automatically creates a CPU `Generator` that can be passed to the pipeline even if it is being run on a GPU.
60+
</hfoption>
61+
</hfoptions>
8362

84-
```python
85-
import torch
86-
import numpy as np
87-
from diffusers import DDIMPipeline
63+
The `Generator` object should be passed to the pipeline instead of an integer seed. `Generator` maintains a *random state* that is consumed and modified when used. Once consumed, the same `Generator` object produces different results in subsequent calls, even across different pipelines, because it's *state* has changed.
8864

89-
ddim = DDIMPipeline.from_pretrained("google/ddpm-cifar10-32", use_safetensors=True)
90-
ddim.to("cuda")
65+
```py
9166
generator = torch.manual_seed(0)
92-
image = ddim(num_inference_steps=2, output_type="np", generator=generator).images
93-
print(np.abs(image).sum())
94-
```
95-
96-
> [!TIP]
97-
> If reproducibility is important to your use case, we recommend always passing a CPU `Generator`. The performance loss is often negligible and you'll generate more similar values than if the pipeline had been run on a GPU.
98-
99-
Finally, more complex pipelines such as [`UnCLIPPipeline`], are often extremely
100-
susceptible to precision error propagation. You'll need to use
101-
exactly the same hardware and PyTorch version for full reproducibility.
10267

103-
</hfoption>
104-
</hfoptions>
68+
for _ in range(5):
69+
- image = pipeline(prompt, generator=generator)
70+
+ image = pipeline(prompt, generator=torch.manual_seed(0))
71+
```
10572

10673
## Deterministic algorithms
10774

108-
You can also configure PyTorch to use deterministic algorithms to create a reproducible pipeline. The downside is that deterministic algorithms may be slower than non-deterministic ones and you may observe a decrease in performance.
75+
PyTorch supports [deterministic algorithms](https://docs.pytorch.org/docs/stable/notes/randomness.html#avoiding-nondeterministic-algorithms) - where available - for certain operations so they produce the same results. Deterministic algorithms may be slower and decrease performance.
10976

110-
Non-deterministic behavior occurs when operations are launched in more than one CUDA stream. To avoid this, set the environment variable [CUBLAS_WORKSPACE_CONFIG](https://docs.nvidia.com/cuda/cublas/index.html#results-reproducibility) to `:16:8` to only use one buffer size during runtime.
111-
112-
PyTorch typically benchmarks multiple algorithms to select the fastest one, but if you want reproducibility, you should disable this feature because the benchmark may select different algorithms each time. Set Diffusers [enable_full_determinism](https://github.com/huggingface/diffusers/blob/142f353e1c638ff1d20bd798402b68f72c1ebbdd/src/diffusers/utils/testing_utils.py#L861) to enable deterministic algorithms.
77+
Use Diffusers' [enable_full_determinism](https://github.com/huggingface/diffusers/blob/142f353e1c638ff1d20bd798402b68f72c1ebbdd/src/diffusers/utils/testing_utils.py#L861) function to enable deterministic algorithms.
11378

11479
```py
80+
import torch
81+
from diffusers_utils import enable_full_determinism
82+
11583
enable_full_determinism()
11684
```
11785

118-
Now when you run the same pipeline twice, you'll get identical results.
86+
Under the hood, `enable_full_determinism` works by:
11987

120-
```py
121-
import torch
122-
from diffusers import DDIMScheduler, StableDiffusionPipeline
123-
124-
pipe = StableDiffusionPipeline.from_pretrained("stable-diffusion-v1-5/stable-diffusion-v1-5", use_safetensors=True).to("cuda")
125-
pipe.scheduler = DDIMScheduler.from_config(pipe.scheduler.config)
126-
g = torch.Generator(device="cuda")
88+
- Setting the environment variable [CUBLAS_WORKSPACE_CONFIG](https://docs.nvidia.com/cuda/cublas/index.html#results-reproducibility) to `:16:8` to only use one buffer size during rntime. Non-deterministic behavior occurs when operations are used in more than one CUDA stream.
89+
- Disabling benchmarking to find the fastest convolution operation by setting `torch.backends.cudnn.benchmark=False`. Non-deterministic behavior occurs because the benchmark may select different algorithms each time depending on hardware or benchmarking noise.
90+
- Disabling TensorFloat32 (TF32) operations in favor of more precise and consistent full-precision operations.
12791

128-
prompt = "A bear is playing a guitar on Times Square"
12992

130-
g.manual_seed(0)
131-
result1 = pipe(prompt=prompt, num_inference_steps=50, generator=g, output_type="latent").images
93+
## Resources
13294

133-
g.manual_seed(0)
134-
result2 = pipe(prompt=prompt, num_inference_steps=50, generator=g, output_type="latent").images
135-
136-
print("L_inf dist =", abs(result1 - result2).max())
137-
"L_inf dist = tensor(0., device='cuda:0')"
138-
```
95+
We strongly recommend reading PyTorch's developer notes about [Reproducibility](https://docs.pytorch.org/docs/stable/notes/randomness.html). You can try to limit randomness, but it is not *guaranteed* even with an identical seed.

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /