dq.shen
dq.shen

Reputation: 351

How do I create a normal distribution in pytorch?

I want to create a random normal distribution with a given mean and std.

Upvotes: 25

Views: 72011

Answers (8)

Samik
Samik

Reputation: 1

As per the current Pytorch 2.5 documentation, you can do it using torch.normal:

torch.normal(mean=torch.arange(1., 11.), std=torch.arange(1, 0, -0.1))

Upvotes: 0

Abda
Abda

Reputation: 1

import torch
import matplotlib.pyplot as plt



def multivariate_normal_2d(samples):
    mean = torch.zeros(2)
    cov = torch.eye(2)
    dist = torch.distributions.MultivariateNormal(mean, cov)
    return dist.log_prob(samples).exp()

def double_multivariate_normal_2d(samples):
    mean1 = torch.tensor([1.0, 1.0])
    mean2 = torch.tensor([-1.0, -1.0])
    cov = torch.eye(2)
    dist1 = torch.distributions.MultivariateNormal(mean1, cov)
    dist2 = torch.distributions.MultivariateNormal(mean2, cov)
    return 0.5 * (dist1.log_prob(samples).exp() + dist2.log_prob(samples).exp())

class DomainSampler2D:
    def __init__(self, lower_bounds, upper_bounds, prefer_additional_samples=True):
        self.lower_bounds = torch.tensor(lower_bounds)
        self.upper_bounds = torch.tensor(upper_bounds)
        self.dimensions = len(lower_bounds)
        self.prefer_additional_samples = prefer_additional_samples
        self.dimension_ranges = self._calculate_ranges()

    def _calculate_ranges(self):
        if torch.allclose(self.upper_bounds - self.lower_bounds, torch.zeros(self.dimensions)):
            aspect_ratios = torch.zeros(self.dimensions)
            aspect_ratios[0] = 1.0
            return aspect_ratios
        else:
            absolute_ranges = torch.abs(self.upper_bounds - self.lower_bounds)
            return absolute_ranges / torch.sum(absolute_ranges)

    def _calculate_samples_per_dimension(self, total_samples):
        active_dimensions = ~torch.isclose(self.dimension_ranges, torch.zeros(self.dimensions))
        scaling_factor = torch.prod(self.dimension_ranges[active_dimensions])
        relevant_dimensions_count = torch.sum(active_dimensions)
        samples_per_dimension = torch.pow(total_samples / scaling_factor, 1 / relevant_dimensions_count)
        samples_per_dimension = self.dimension_ranges * samples_per_dimension
        return torch.max(samples_per_dimension, torch.ones(samples_per_dimension.shape))

    def _adjust_samples_to_match(self, total_samples, samples_per_dimension):
        integer_samples_per_dimension = torch.round(samples_per_dimension).to(dtype=torch.int)
        current_total = torch.prod(integer_samples_per_dimension)

        if current_total == total_samples:
            return integer_samples_per_dimension

        sample_differences = samples_per_dimension - integer_samples_per_dimension

        if current_total > total_samples and not self.prefer_additional_samples:
            most_over_sampled_dimension = torch.argmin(sample_differences)
            integer_samples_per_dimension[most_over_sampled_dimension] -= 1

        elif current_total < total_samples and self.prefer_additional_samples:
            most_under_sampled_dimension = torch.argmax(sample_differences)
            integer_samples_per_dimension[most_under_sampled_dimension] += 1

        return integer_samples_per_dimension

    def generate_samples(self, total_samples):
        samples_per_dimension = self._calculate_samples_per_dimension(total_samples)
        samples_per_dimension = self._adjust_samples_to_match(total_samples, samples_per_dimension)
        grid_points = [torch.linspace(start, end, count) for start, end, count in zip(self.lower_bounds, self.upper_bounds, samples_per_dimension)]
        mesh = torch.meshgrid(*grid_points, indexing="ij")
        return torch.stack(mesh, dim=-1).reshape(-1, self.dimensions)

def plot_distributions(samples, u_values, v_values):
    samples_numpy = samples.numpy()
    u_numpy = u_values.numpy()
    v_numpy = v_values.numpy()

    fig, axes = plt.subplots(1, 2, figsize=(14, 6))

    scatter1 = axes[0].scatter(samples_numpy[:, 0], samples_numpy[:, 1], c=u_numpy, cmap='viridis')
    axes[0].set_title('Multivariate Normal Distribution')
    axes[0].set_xlabel('x')
    axes[0].set_ylabel('y')
    fig.colorbar(scatter1, ax=axes[0], label='Density')

    scatter2 = axes[1].scatter(samples_numpy[:, 0], samples_numpy[:, 1], c=v_numpy, cmap='plasma')
    axes[1].set_title('Double Multivariate Normal Distribution')
    axes[1].set_xlabel('x')
    axes[1].set_ylabel('y')
    fig.colorbar(scatter2, ax=axes[1], label='Density')

    plt.tight_layout()
    plt.show()


N = 100
sampler = DomainSampler2D([-3.0, -3.0], [3.0, 3.0]).generate_samples(N**2)

plot_distributions(samples, multivariate_normal_2d(samples), double_multivariate_normal_2d(samples))

Upvotes: 0

M. Deckers
M. Deckers

Reputation: 1293

You can create your distribution like described here in the docs. In your case this should be the correct call, including sampling from the created distribution:

from torch.distributions import normal

m = normal.Normal(4.0, 0.5)
s = m.sample()

If you want to get a sample of a certain size/shape, you can pass it to sample(), for example

s = m.sample([5, 5])

for a 5x5-Tensor.

Upvotes: 2

Charlie Parker
Charlie Parker

Reputation: 5266

For all distribution see: https://pytorch.org/docs/stable/distributions.html#

click on right menu to jump to normal (or search in the docs).

An example code:

import torch

num_samples = 3
Din = 1
mu, std = 0, 1
x = torch.distributions.normal.Normal(loc=mu, scale=std).sample((num_samples, Din))

print(x)

For details on torch distributions (with emphasis on uniform) see my SO answer here: https://stackoverflow.com/a/62919760/1601580

Upvotes: 0

Pankaj Mishra
Pankaj Mishra

Reputation: 471

It depends on what you want to generate.

For generating standard normal distribution use -

torch.randn()

for all all distribution (say normal, poisson or uniform etc) use torch.distributions.Normal() or torch.distribution.Uniform(). A detail of all these methods can be seen here - https://pytorch.org/docs/stable/distributions.html#normal

Once you define these methods you can use .sample method to generate the number of instances. It also allows you to generates a sample_shape shaped sample or sample_shape shaped batch of samples if the distribution parameters are batched.

Upvotes: 0

Furkan
Furkan

Reputation: 545

You can easily use torch.Tensor.normal_() method.

Let's create a matrix Z (a 1d tensor) of dimension 1 × 5, filled with random elements samples from the normal distribution parameterized by mean = 4 and std = 0.5.

torch.empty(5).normal_(mean=4,std=0.5)

Result:

tensor([4.1450, 4.0104, 4.0228, 4.4689, 3.7810])

Upvotes: 27

kmario23
kmario23

Reputation: 61505

For a standard normal distribution (i.e. mean=0 and variance=1), you can use torch.randn()

For your case of custom mean and std, you can use torch.distributions.Normal()


Init signature:
tdist.Normal(loc, scale, validate_args=None)

Docstring:
Creates a normal (also called Gaussian) distribution parameterized by loc and scale.

Args:
loc (float or Tensor): mean of the distribution (often referred to as mu)
scale (float or Tensor): standard deviation of the distribution (often referred to as sigma)


Here's an example:

In [32]: import torch.distributions as tdist

In [33]: n = tdist.Normal(torch.tensor([4.0]), torch.tensor([0.5]))

In [34]: n.sample((2,))
Out[34]: 
tensor([[ 3.6577],
        [ 4.7001]])

Upvotes: 14

gui11aume
gui11aume

Reputation: 2948

A simple option is to use the randn function from the base module. It creates a random sample from the standard Gaussian distribution. To change the mean and the standard deviation you just use addition and multiplication. Below I create sample of size 5 from your requested distribution.

import torch
torch.randn(5) * 0.5 + 4 # tensor([4.1029, 4.5351, 2.8797, 3.1883, 4.3868])

Upvotes: 12

Related Questions