Reputation: 351
I want to create a random normal distribution with a given mean and std.
Upvotes: 25
Views: 72011
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
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
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
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
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
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
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 byloc
andscale
.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
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