Pass the random seed generator to pipe

I wonder how the generator from this line:

torch.manual_seed(1024)

is passed to the pipe() method in this line:

pipe(prompt).images[0]

These two lines is from one of the cells in the stable_diffusion.ipynb notebook in the first lesson in Part 2 2022/2023.

In the doc on __call__ for pipelines, there is an optional parameter: generator. It is defined as:

generator: typing.Union[torch._C.Generator, typing.List[torch._C.Generator], NoneType] = None

I interpret this as the optional generator can be a Union of a single torch._C.Generator, a list of torch._C.Generator, and NoneType. And the default value if None.

Thus, if we don’t want it to be None, wouldn’t we need to pass in a value?

So in the following line:

torch.manual_seed(1024)
pipe(prompt).images[0]

How come we don’t need to pass in the generator, for example, something like the following?

a_generator = torch.manual_seed(1024)
pipe(prompt, generator=<a union containing a_generator>).images[0]

So torch._C.Generator was the old name of torch. Generator per pytorch issue.

Could Python experts and StableDiffusion library experts enlighten me?

Hello,

Calling torch.manual_seed(1024) sets the seed for all subsequent PyTorch random number generation operations that are to be executed, regardless of whether they belong to other modules, are part a third-party library, etc. For example, in the snippet below,

torch.manual_seed(1024)
a = random_func1()
b = random_func2()

Variables a and b will evaluate to the same values every time thanks to the first line, and it is not necessary to invoke torch.manual_seed inside random_func1 or random_func2.

Does that make sense?

1 Like

Thanks Borna Ahmadzadeh!

Are there configurations some where that enable once execute “torch.manual_seed(1024)” sets the seed for all subsequent PyTorch random number generation operations?

1 Like

I’m not sure what you are asking, could you please rephrase your question?

1 Like

I didn’t understand how and when the returned torch.Generator from this line torch.manual_seed(1024) got passed into the pipe object (through the call() method). If no generator is passed in for the optional generator parameter, then the default value is none according to the torch documentation. So that is why I was thinking may be there is some sort of configuration which automatically assigns the returned generator from the torch.manual_seed(1024) call to the optional parameter in the the call() method for the pipe object.

In this HuggingFace page, I see sample code in passing generator to pipe():

generator = torch.Generator("cuda").manual_seed(1024)
image = pipe(prompt, guidance_scale=7.5, generator=generator).images[0]

In the two line code above, a generator returned from the first line is passed in to the pipe() for the generator parameter (i.e. generator=generator).

How come when having this line:

torch.manual_seed(1024)

the generator from this line is automatically assigned to the generator parameter in pipe()?

I am thinking may be torch.manual_seed(1024) returns a global torch.Generator that sets a fixed seed for all torch random number generators, and that some where in the call() or in pipe, something like torch.rand(<some number>) that uses the seed generated by the torch.manual_seed() and that the generator parameter in the call() is not used, thus no need to pass in any generator value.

This is my guess. What is your thought?

1 Like

torch.manual_seed(1024) performs two actions: First, it sets the global PyTorch RNG seed to 1024, so all succeeding operations will be deterministic. Second, as you say, it returns a Generator object that can optionally be used as an argument in RNG methods to explicitly manage determinism. In other words, creating a generator using torch.Generator("cuda").manual_seed(1024) that is thereafter passed to pseudo-random PyTorch functions would yield identical results with simply calling torch.manual_seed(1024) at the beginning of the program and assigning None to the generator keyword argument in relevant functions, as demonstrated below.

>>> import torch
>>> _ = torch.manual_seed(1024)
>>> torch.randn((2, 2), generator=None)
tensor([[-1.4837,  0.2671],
        [-1.8337, -0.1047]])
>>> torch.randn((2, 2), generator=None)
tensor([[ 0.6002, -0.5496],
        [ 1.0391,  0.2261]])
>>> generator = torch.manual_seed(1024)
>>> torch.randn((2, 2), generator=generator)
tensor([[-1.4837,  0.2671],
        [-1.8337, -0.1047]])
>>> torch.randn((2, 2), generator=generator)
tensor([[ 0.6002, -0.5496],
        [ 1.0391,  0.2261]])
2 Likes

Thanks for confirming my guess, provided further details. The demonstration helped.

I finally get it. Thank you.

1 Like