【PyTorch】【distributions】normal.py

目录

【PyTorch】【distributions】normal.py 

1. 类构造和属性:

构造方法

属性:

2. 采样方法:

sample() 方法

rsample() 方法

3. 对数概率:

4. 累积分布函数 (CDF):

5. 逆累积分布函数 (ICDF):

6. 熵(Entropy):

7. 自然参数(Natural Parameters):

8. 对数归一化常数(Log Normalizer):

总结


【PyTorch】【distributions】normal.py 

这段代码定义了一个 正态分布(Normal distribution) 类,用于生成、采样、计算对数概率、计算累积分布函数(CDF)等。

该类继承自 ExponentialFamily,并使用 PyTorch 张量来表示分布的参数。

下面我会逐个解释每个方法和公式。

1. 类构造和属性:

构造方法
def __init__(self, loc, scale, validate_args=None):
    self.loc, self.scale = broadcast_all(loc, scale)
    if isinstance(loc, Number) and isinstance(scale, Number):
        batch_shape = torch.Size()
    else:
        batch_shape = self.loc.size()
    super(Normal, self).__init__(batch_shape, validate_args=validate_args)

  • loc: 正态分布的 均值(mean),通常表示为 μ。
  • scale: 正态分布的 标准差(standard deviation),通常表示为\sigma^2
  • batch_shape: 如果 locscale 是单一数值,那么就是标量形状,否则会根据 loc 的形状来广播(broadcast_all)形成批次形状。
属性:
  • mean: 返回 均值 μ。
  • stddev: 返回 标准差 σ。
  • variance: 返回 方差,即\sigma^2

2. 采样方法:

sample() 方法
def sample(self, sample_shape=torch.Size()):
    shape = self._extended_shape(sample_shape)
    with torch.no_grad():
        return torch.normal(self.loc.expand(shape), self.scale.expand(shape))

  • 从正态分布中采样的 sample 方法返回一个形状为 sample_shape 的样本。
  • torch.normal(mean, std) 用于生成正态分布的随机样本,其中 mean 是均值,std 是标准差。
rsample() 方法
def rsample(self, sample_shape=torch.Size()):
    shape = self._extended_shape(sample_shape)
    eps = _standard_normal(shape, dtype=self.loc.dtype, device=self.loc.device)
    return self.loc + eps * self.scale

  • 重参数化采样:这是深度学习中常用的技术,尤其是在变分推断和生成模型中。通过对标准正态分布的样本 eps 进行线性变换(\mu + \epsilon \cdot \sigma)来生成样本。
  • eps 是从标准正态分布 \mathcal{N}(0, 1)中采样的噪声。

3. 对数概率:

def log_prob(self, value):
    if self._validate_args:
        self._validate_sample(value)
    var = (self.scale ** 2)
    log_scale = math.log(self.scale) if isinstance(self.scale, Real) else self.scale.log()
    return -((value - self.loc) ** 2) / (2 * var) - log_scale - math.log(math.sqrt(2 * math.pi))

该方法计算正态分布的 对数概率,公式为:

\log p(x) = -\frac{(x - \mu)^2}{2\sigma^2} - \log(\sigma) - \frac{1}{2} \log(2\pi)

  • μ 是均值,σ是标准差,x是观察值。
  • 该公式即是正态分布的概率密度函数(PDF)的对数。

4. 累积分布函数 (CDF):

def cdf(self, value):
    if self._validate_args:
        self._validate_sample(value)
    return 0.5 * (1 + torch.erf((value - self.loc) * self.scale.reciprocal() / math.sqrt(2)))

  • 计算正态分布的 累积分布函数 P(X \leq x),公式为:

F(x) = \frac{1}{2} \left[ 1 + \text{erf}\left(\frac{x - \mu}{\sigma \sqrt{2}}\right) \right]

其中,erf 是误差函数,Python 中的 torch.erf 是误差函数的实现。

5. 逆累积分布函数 (ICDF):

def icdf(self, value):
    return self.loc + self.scale * torch.erfinv(2 * value - 1) * math.sqrt(2)

  • 逆累积分布函数 (Inverse CDF) 也叫 分位数函数,用于从概率值(0 到 1 之间的值)推算出对应的随机变量值 xxx。
  • 公式为:

x = \mu + \sigma \cdot \sqrt{2} \cdot \text{erf}^{-1}(2p - 1)

其中 p 是概率值, \text{erf}^{-1}是误差函数的逆函数。

6. 熵(Entropy):

def entropy(self):
    return 0.5 + 0.5 * math.log(2 * math.pi) + torch.log(self.scale)

  • 是信息论中的概念,用来度量随机变量的不确定性。正态分布的熵公式为:

H(X) = \frac{1}{2} \log(2\pi e \sigma^2)

这里的 self.scale 即为标准差 σ。

7. 自然参数(Natural Parameters):

@property
def _natural_params(self):
    return (self.loc / self.scale.pow(2), -0.5 * self.scale.pow(2).reciprocal())

  • 正态分布的 自然参数 是参数化 Exponential 家族分布的一种方式。对于正态分布:

\eta_1 = \frac{\mu}{\sigma^2}, \quad \eta_2 = -\frac{1}{2\sigma^2}

这些自然参数在变分推断和优化中非常重要,因为它们使得计算变得更加高效。

8. 对数归一化常数(Log Normalizer):

def _log_normalizer(self, x, y):
    return -0.25 * x.pow(2) / y + 0.5 * torch.log(-math.pi / y)
  • 对数归一化常数是正态分布的对数概率的归一化常数,确保概率分布的积分为 1。
  • 这个公式涉及到高斯分布的标准化系数:

\log Z = -\frac{1}{2} \log(2\pi \sigma^2)

这个常数确保整个概率分布的归一化性质,即积分为 1。


总结

这段代码定义了一个 正态分布 类,支持通过 PyTorch 来进行样本采样、计算对数概率、累积概率、熵等操作。主要公式包括:

  1. 对数概率\log p(x) = -\frac{(x - \mu)^2}{2\sigma^2} - \log(\sigma) - \frac{1}{2} \log(2\pi)
  2. 累积分布函数F(x) = \frac{1}{2} \left[ 1 + \text{erf}\left(\frac{x - \mu}{\sigma \sqrt{2}}\right) \right]
  3. 逆累积分布函数x = \mu + \sigma \cdot \sqrt{2} \cdot \text{erf}^{-1}(2p - 1)
  4. H(X) = \frac{1}{2} \log(2\pi e \sigma^2)

这些公式和方法实现了正态分布的所有基本功能,可以用于概率计算、生成样本和进行变分推断等任务。

import math
from numbers import Real
from numbers import Number

import torch
from torch.distributions import constraints
from torch.distributions.exp_family import ExponentialFamily
from torch.distributions.utils import _standard_normal, broadcast_all


class Normal(ExponentialFamily):
    r"""
    Creates a normal (also called Gaussian) distribution parameterized by
    :attr:`loc` and :attr:`scale`.

    Example::

        >>> m = Normal(torch.tensor([0.0]), torch.tensor([1.0]))
        >>> m.sample()  # normally distributed with loc=0 and scale=1
        tensor([ 0.1046])

    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)
    """
    arg_constraints = {'loc': constraints.real, 'scale': constraints.positive}
    support = constraints.real
    has_rsample = True
    _mean_carrier_measure = 0

    @property
    def mean(self):
        return self.loc

    @property
    def stddev(self):
        return self.scale

    @property
    def variance(self):
        return self.stddev.pow(2)

    def __init__(self, loc, scale, validate_args=None):
        self.loc, self.scale = broadcast_all(loc, scale)
        if isinstance(loc, Number) and isinstance(scale, Number):
            batch_shape = torch.Size()
        else:
            batch_shape = self.loc.size()
        super(Normal, self).__init__(batch_shape, validate_args=validate_args)

    def expand(self, batch_shape, _instance=None):
        new = self._get_checked_instance(Normal, _instance)
        batch_shape = torch.Size(batch_shape)
        new.loc = self.loc.expand(batch_shape)
        new.scale = self.scale.expand(batch_shape)
        super(Normal, new).__init__(batch_shape, validate_args=False)
        new._validate_args = self._validate_args
        return new

    def sample(self, sample_shape=torch.Size()):
        shape = self._extended_shape(sample_shape)
        with torch.no_grad():
            return torch.normal(self.loc.expand(shape), self.scale.expand(shape))

    def rsample(self, sample_shape=torch.Size()):
        shape = self._extended_shape(sample_shape)
        eps = _standard_normal(shape, dtype=self.loc.dtype, device=self.loc.device)
        return self.loc + eps * self.scale

    def log_prob(self, value):
        if self._validate_args:
            self._validate_sample(value)
        # compute the variance
        var = (self.scale ** 2)
        log_scale = math.log(self.scale) if isinstance(self.scale, Real) else self.scale.log()
        return -((value - self.loc) ** 2) / (2 * var) - log_scale - math.log(math.sqrt(2 * math.pi))

    def cdf(self, value):
        if self._validate_args:
            self._validate_sample(value)
        return 0.5 * (1 + torch.erf((value - self.loc) * self.scale.reciprocal() / math.sqrt(2)))

    def icdf(self, value):
        return self.loc + self.scale * torch.erfinv(2 * value - 1) * math.sqrt(2)

    def entropy(self):
        return 0.5 + 0.5 * math.log(2 * math.pi) + torch.log(self.scale)

    @property
    def _natural_params(self):
        return (self.loc / self.scale.pow(2), -0.5 * self.scale.pow(2).reciprocal())

    def _log_normalizer(self, x, y):
        return -0.25 * x.pow(2) / y + 0.5 * torch.log(-math.pi / y)

代码报错:UserWarning: std(): degrees of freedom is <= 0. Correction should be strictly less than the reduction factor (input numel divided by output numel). (Triggered internally at C:\cb\pytorch_1000000000000\work\aten\src\ATen\native\ReduceOps.cpp:1808.) rewards = (rewards - rewards.mean()) / (rewards.std() + 1e-7) D:\anaconda\envs\9-25-pytorch\Lib\site-packages\torch\nn\modules\loss.py:538: UserWarning: Using a target size (torch.Size([1])) that is different to the input size (torch.Size([])). This will likely lead to incorrect results due to broadcasting. Please ensure they have the same size. return F.mse_loss(input, target, reduction=self.reduction) Traceback (most recent call last): File "D:\python草稿程序\ppo-uav-5\main.py", line 585, in <module> train_uav_env() File "D:\python草稿程序\ppo-uav-5\main.py", line 483, in train_uav_env ppo.update() File "D:\python草稿程序\ppo-uav-5\PPO.py", line 190, in update logprobs_cont, logprobs_disc, state_values, dist_entropy_cont, dist_entropy_disc = self.policy.evaluate( ^^^^^^^^^^^^^^^^^^^^^ File "D:\python草稿程序\ppo-uav-5\PPO.py", line 110, in evaluate dist_continuous = MultivariateNormal(action_mean, cov_mat) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "D:\anaconda\envs\9-25-pytorch\Lib\site-packages\torch\distributions\multivariate_normal.py", line 178, in __init__ super().__init__(batch_shape, event_shape, validate_args=validate_args) File "D:\anaconda\envs\9-25-pytorch\Lib\site-packages\torch\distributions\distribution.py", line 70, in __init__ raise ValueError( ValueError: Expected parameter loc (Tensor of shape (2,)) of distribution MultivariateNormal(loc: torch.Size([2]), covariance_matrix: torch.Size([2, 2])) to satisfy the constraint IndependentConstraint(Real(), 1), but found invalid values: tensor([nan, nan], device='cuda:0', grad_fn=<ExpandBackward0>)
最新发布
03-18
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

资源存储库

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值