question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

[Feature request] Let user provide his own randn data for samplers in sampling.py

See original GitHub issue

Please add an option for samplers to accept an argument with random data and use that if it is provided.

The reason for this is as follows.

We use samplers in stable diffusion to generate pictures, and we use seeds to make it possible for other users to reproduce results.

In a batch of one image, everything works perfectly: set seed beforehand, generate noise, run sampler, and get the image everyone else will be able to get.

If the user produces a batch of multiple images (which is desirable because it works faster than multiple independent batches), the expectation is that each image will have its own seed and will be reproducible individually outside of the batch. I achieve that for DDIM and PLMS samplers from stable diffusion by preparing the correct random noise according to seeds beforehand, and since those samplers do not have randomness in them, it works well.

Samplers here use torch.randn in a loop, so samples in a batch will get different random data than samples produced individually, which results in different output.

An example of what I want to have:

from

def sample_euler_ancestral(model, x, sigmas, extra_args=None, callback=None, disable=None):
    """Ancestral sampling with Euler method steps."""
    extra_args = {} if extra_args is None else extra_args
    s_in = x.new_ones([x.shape[0]])
    for i in trange(len(sigmas) - 1, disable=disable):
        denoised = model(x, sigmas[i] * s_in, **extra_args)
        sigma_down, sigma_up = get_ancestral_step(sigmas[i], sigmas[i + 1])
        if callback is not None:
            callback({'x': x, 'i': i, 'sigma': sigmas[i], 'sigma_hat': sigmas[i], 'denoised': denoised})
        d = to_d(x, sigmas[i], denoised)
        # Euler method
        dt = sigma_down - sigmas[i]
        x = x + d * dt
        x = x + torch.randn_like(x) * sigma_up
    return x

to

def sample_euler_ancestral(model, x, sigmas, extra_args=None, callback=None, disable=None, user_random_data=None):
    """Ancestral sampling with Euler method steps."""
    extra_args = {} if extra_args is None else extra_args
    s_in = x.new_ones([x.shape[0]])
    for i in trange(len(sigmas) - 1, disable=disable):
        denoised = model(x, sigmas[i] * s_in, **extra_args)
        sigma_down, sigma_up = get_ancestral_step(sigmas[i], sigmas[i + 1])
        if callback is not None:
            callback({'x': x, 'i': i, 'sigma': sigmas[i], 'sigma_hat': sigmas[i], 'denoised': denoised})
        d = to_d(x, sigmas[i], denoised)
        # Euler method
        dt = sigma_down - sigmas[i]
        x = x + d * dt
        x = x + (torch.randn_like(x) if user_random_data is None else user_random_data[i]) * sigma_up
    return x

(difference only in next-to-last line)

Issue Analytics

  • State:closed
  • Created a year ago
  • Comments:18 (8 by maintainers)

github_iconTop GitHub Comments

2reactions
crowsonkbcommented, Nov 7, 2022

“New York City, oil on canvas”, guidance scale 5.5, all samples drawn with eta=1:

12 steps, DPM++ 2S: grid-0271-2

24 steps, DPM++ 2S: grid-0272-2

50 steps, Euler ancestral: grid-0275-2

2reactions
crowsonkbcommented, Nov 7, 2022

I added the callable version in a branch: https://github.com/crowsonkb/k-diffusion/tree/noise-samplers, the ancestral samplers take a noise_sampler= argument now that is a callable with two arguments, sigma and sigma_next, the interval to return the noise for. I also added a (non-default) torchsde.BrownianTree based noise sampler which produces more stable samples across different numbers of steps and different ancestral samplers (they should in fact converge to the same limiting image using this given enough steps). You use it like this:

ns = K.sampling.BrownianTreeNoiseSampler(x, sigma_min, sigma_max)
samples = K.sampling.sample_something_ancestral(..., noise_sampler=ns)

I’d like people to try it out, it’s pretty neat!

Read more comments on GitHub >

github_iconTop Results From Across the Web

Generating Random Data in Python (Guide)
Watch it together with the written tutorial to deepen your understanding: Generating Random Data in Python. How random is random?
Read more >
Mastering Random Sampling in Python - Towards Data Science
In this post, we will discuss how to randomly sample items from lists as well as how to generate pseudorandom numbers in python....
Read more >
Sample Dataset - an overview | ScienceDirect Topics
A dataset (example set) is a collection of data with a defined structure. Table 2.1 shows a dataset. It has a well-defined structure...
Read more >
Function rand() or sample() for MCMC sampling and similar?
I was wondering whether to use rand() or StatsBase.sample() to name an MCMC ... not adapted to your use case, I'd appreciate an...
Read more >
Chapter 4. NumPy Basics: Arrays and Vectorized Computation
Because NumPy provides an easy-to-use C API, it is very easy to pass data to ... This feature has made Python a language...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found