torch.distributions.lkj_cholesky 的源代码
# mypy: 允许未类型化定义
"""
这与 NumPyro(https://github.com/pyro-ppl/numpyro)中的实现紧密相关。
原始版权声明:
# 版权:Pyro 项目贡献者。
# SPDX-License-Identifier: Apache-2.0
"文档"
导入
数学
导入
火炬
来自 torch.distributions
导入
测试版,
约束
来自 torch.distributions.distribution
导入
分布
来自 torch.distributions.utils
导入
广播全部
__全部__ = [
LKJCholesky]
文档
类 LKJCholesky(
分布):
r"""
LKJ 分布用于相关矩阵的下三角 Cholesky 因子。
分布由“浓度”参数 :math:`η` 控制
以使从相关矩阵 :math:`M` 生成的概率
一个与 :math:`\det(M)^{\eta - 1}` 成比例的 Cholesky 因子。由于这个原因,
当 `concentration == 1` 时,我们在 Cholesky 上具有均匀分布
相关矩阵的因子
L ~ LKJCholesky(维度, 浓度)
X = L @ L' ~ LKJCorr(维度, 浓度)
注意,这个分布是对...
而不是协方差矩阵本身,因此与[1]中的推导略有不同
因此与[1]中的推导略有不同
`LKJCorr`分布。对于采样,它使用洋葱方法。
[1] 第 3 节。
示例::
>>> # xdoctest: +IGNORE_WANT("非确定性")
>>> l = LKJCholesky(3, 0.5)
>>> l.sample() # l @ l.T 是一个 3x3 相关矩阵的样本
tensor([[ 1.0000, 0.0000, 0.0000],
[ 0.3516, 0.9361, 0.0000],
[-0.1899, 0.4748, 0.8593]])
参数:
矩阵的维度(dim):矩阵的维度
浓度(浮点数或张量):浓度/形状参数
分布(通常称为 eta)
**参考文献**
基于 vines 和扩展洋葱方法生成随机相关矩阵(2009 年)
丹尼尔·莱万多夫斯基,多罗塔·库罗维卡,哈利·乔
多变量分析杂志. 100. 10.1016/j.jmva.2009.04.008
"""
边界约束 = {
浓度:
约束.
正的}
支持 =
约束.
胡尔维茨分解
定义 __init__(self, dim, concentration=1.0,
验证参数=None):
如果
维度 < 2:
提升 ValueError(
f"期望维度为大于或等于 2 的整数。找到的维度为 dim="{dim}
点
)
自我.
暗 =
暗
(我.
注意力,) =
广播所有(
注意力)
批处理形状 =
自身.
浓度.
大小()
事件形状 =
PyTorch.
大小((
维度, dim))
这用于在[1]的第 3.2 节中绘制贝塔分布的矢量化样本。
边缘浓度 =
自.
专注 + 0.5 * (
自我.
暗淡 - 2)
偏移 =
火炬.arange(
self.dim - 1,
数据类型=
自身.
浓度.
数据类型,
设备=
自我.
注意力.
设备,
)
偏移 =
火炬.
猫
([
偏移.
新零((1,)),
偏移])
beta_conc1 = 偏移量 + 0.5
beta_conc0 = 边际浓度.
展平(-1) - 0.5 *
偏移
自身._beta =
测试版(beta_conc1, beta_conc0)
超级().__init__(batch_shape, event_shape, validate_args)
[文档] def expand(self, batch_shape, _instance=None):
new = self._get_checked_instance(LKJCholesky, _instance)
batch_shape = torch.Size(batch_shape)
new.dim = self.dim
new.concentration = self.concentration.expand(batch_shape)
new._beta = self._beta.expand(batch_shape + (self.dim,))
super(LKJCholesky, new).__init__(
batch_shape, self.event_shape, validate_args=False
)
new._validate_args = self._validate_args
return new
[文档] def sample(self, sample_shape=torch.Size()):
# 这使用的是洋葱法,但与[1]第 3.2 节有一些不同:
# - 将循环向量化,也适用于异构的 eta。
# - 同样的算法可以推广到 n=1。
# - 由于我们正在采样 Cholesky 因子,因此程序简化了。
# 替代相关系数矩阵本身,因此
# 我们只需要生成 `w`。
y = self._beta.sample(sample_shape).unsqueeze(-1)
u_normal = torch.randn(
self._extended_shape(sample_shape), dtype=y.dtype, device=y.device
).tril(-1)
u_hypersphere = u_normal / u_normal.norm(dim=-1, keepdim=True)
# Replace NaNs in first row
u_hypersphere[..., 0, :].fill_(0.0)
w = torch.sqrt(y) * u_hypersphere
# 填充对角线元素;夹具以提高数值稳定性
eps = torch.finfo(w.dtype).tiny
diag_elems = torch.clamp(1 - torch.sum(w**2, dim=-1), min=eps).sqrt()
w += torch.diag_embed(diag_elems)
return w
[文档] def log_prob(self, value):
查看:https://mc-stan.org/docs/2_25/functions-reference/cholesky-lkj-correlation-distribution.html
相关矩阵的概率与成比例
行列式 ** (浓度 - 1) = prod(L_ii ^ 2(浓度 - 1))
此外,从 Cholesky 因子到变换的雅可比矩阵
相关系数矩阵是:
# prod(L_ii ^ (D - i))
因此,Cholesky 因子的概率与成比例
# prod(L_ii ^ (2 * 浓度 - 2 + D - i)) = prod(L_ii ^ 指数_i)
# with order_i = 2 * 浓度 - 2 + D - i
if self._validate_args:
self._validate_sample(value)
diag_elems = value.diagonal(dim1=-1, dim2=-2)[..., 1:]
order = torch.arange(2, self.dim + 1, device=self.concentration.device)
order = 2 * (self.concentration - 1).unsqueeze(-1) + self.dim - order
unnormalized_log_pdf = torch.sum(order * diag_elems.log(), dim=-1)
# Compute normalization constant (page 1999 of [1])
dm1 = self.dim - 1
alpha = self.concentration + 0.5 * dm1
denominator = torch.lgamma(alpha) * dm1
numerator = torch.mvlgamma(alpha - 0.5, dm1)
π常数在[1]中是 D * (D - 1) / 4 * log(π)
π常数在多伽玛对数中是 (D - 1) * (D - 2) / 4 * log(π)
因此,我们需要添加一个 π常数 = (D - 1) * log(π) / 2
π常数 = 0.5 * dm1 * math.log(math.pi)
normalize_term = pi_constant + 分子 - 分母
return 未归一化的对数概率密度 - normalize_term