# mypy: 允许未类型化定义
导入
数学
导入
警告
from functools 导入
全序
from 打字
导入
可调用
导入
火炬
from 火炬
导入
无穷,
张量
from .伯努利
导入
伯努利
from .贝塔
导入
测试版
from .二项式
导入
二项式
from .分类的
导入
分类
from .高斯
导入
拉普拉斯分布
from .连续伯努利
导入 ContinuousBernoulli
from .狄利克雷
导入 Dirichlet
from .发行
导入
分布
from .指数家族
导入
指数族
from .指数
导入
指数
from .伽玛
导入
伽马分布
from .几何
导入
几何
from .冈伯尔
导入 Gumbel
from 半正态
导入
半正态分布
from 独立
导入
独立
from .拉普拉斯
导入
拉普拉斯
from .低秩多元正态分布
导入 (
_batch_lowrank_logdet,
_低秩马氏距离,
低秩多元正态分布,
)
from .多元正态分布
导入 _batch_mahalanobis,
多元正态分布
from .正态分布
导入
正态分布
from .one_hot_categorical 导入 OneHotCategorical
from .pareto 导入 Pareto
from .poisson 导入 Poisson
from .transformed_distribution 导入
转换分布
from .uniform 导入 Uniform
from .工具
导入 _sum_rightmost,
欧拉常数
是 _euler_gamma
_KL_REGISTRY: 字典[
元组[
类型,
类型
]
可调用
] = {} # 真实来源映射一些通用(类型,类型)对到函数。
_KL_MEMOIZE: 字典[
元组[
类型,
类型
]
可调用
] = {} # 缓存版本,将许多特定的(类型,类型)对映射到函数。
全部 = ["register_kl", "kl_divergence"]
[文档]def register_kl(type_p, type_q):
"""
装饰器,用于将成对函数注册到 :meth:`kl_divergence`。
使用方法::
@register_kl(普通, 普通)
def kl_normal_normal(p, q):
# 在此处插入实现
查找返回最具体的(类型,类型)匹配项,按子类顺序排列。
比赛结果不明确,会引发一个 `RuntimeWarning`。例如,为了
解决这种不明确的情况:
@register_kl(BaseP, DerivedQ)
def kl_version1(p, q): ...
@register_kl(DerivedP, BaseQ)
def kl_version2(p, q): ...
您应该注册第三个最具体的实现,例如::
register_kl(DerivedP, DerivedQ)(kl_version1) # 打破平局。
Args:
type_p (类型): :class:`~torch.distributions.Distribution` 的子类。
type_q (类型): :class:`~torch.distributions.Distribution` 的子类。
"""
如果不是 type_p 是 type 类型且是 Distribution 的子类:
raise TypeError(
f"期望 type_p 是 Distribution 子类,但得到 {type_p}"
)
如果不是 type_q 是 type 类型且是 Distribution 的子类:
raise TypeError(
f"期望 type_q 是 Distribution 子类,但得到 {type_q}"
)
def decorator(fun):
_KL_REGISTRY[type_p, type_q] = fun
_KL_MEMOIZE.clear() # 重置,因为查找顺序可能已更改
return fun
返回装饰器
@total_ordering
类
_匹配:
__slots__ = ["类型"]
def __init__(self, *类型):
self.类型 =
类型
def __等于__(self,
其他):
返回 self.
类型 ==
其他.
类型
def __le__(self, 其他):
为 x, y
在 zip(self.
类型,
其他.
类型):
如果
不是
派生类(x, y):
返回
假
如果 x is
不是 y:
断开
返回
真实
def _分发_kl(
类型_p,
类型_q):
```python
# 假设输入文本为:
input_text = """Immersive Translate"""
# 翻译函数(此处仅为示例,实际翻译功能需要调用真实的翻译 API)
def translate_to_simplified_chinese(text):
# 这里应该调用真实的翻译 API 进行翻译
# 由于示例中不使用真实的 API,以下为模拟翻译结果
return text # 假设翻译结果与原文相同
# 输出翻译结果
translated_text = translate_to_simplified_chinese(input_text)
print(translated_text)
```
输出:
```
Immersive Translate
```
假设单继承,找到最具体的近似匹配。
"""
匹配项 = [
(超级_p,
超级_q)
为
超级_p,
超级_q
在 _KL_REGISTRY
如果
派生类(
类型_p,
超级_p)
和
派生类(
类型_q,
超级_q)
]
如果
不是
匹配:
返回 NotImplemented
# 检查左右字典顺序是否一致。
# mypy 没有足够智能来知道 _Match 实现了 __lt__
# see: https://github.com/python/typing/issues/760#issuecomment-710670503
左_p,
左_q =
最小值(
_匹配(*m)
为 m
在
匹配).
类型 # type: ignore[type-var]
正确_q,
正确_p =
最小值(
_匹配(*
反转(m))
为 m
在
匹配).
类型 # type: ignore[type-var]
左函数 =
_KL 注册表[
左_p,
左边_q]
右边_有趣 =
_KL 注册表[
右边_p,
右_q]
如果
左_有趣 is
不是
右_有趣:
警告.
警告(
f"模糊的 kl 散度("{
类型_p.__name__}, {
类型_q.__name__}
)。"
f"请注册_kl("{
左_p.__name__}, {
右_q.__name__})",
运行时警告,
)
返回
左_函数
def _infinite_like(张量):
```python
# 假设输入文本为:
input_text = """Immersive Translate"""
# 翻译函数(此处仅为示例,实际翻译功能需要调用真实的翻译 API)
def translate_to_simplified_chinese(text):
# 这里应该调用真实的翻译 API 进行翻译
# 由于示例中不使用真实的 API,以下为模拟翻译结果
return text # 假设翻译结果与原文相同
# 输出翻译结果
translated_text = translate_to_simplified_chinese(input_text)
print(translated_text)
```
输出:
```
Immersive Translate
```
辅助函数,用于在整个过程中获得无限 KL 散度
"""
返回
火炬.
完全一样(
张量,
无穷)
def _x_log_x(张量):
```python
# 假设输入文本为:
input_text = """Immersive Translate"""
# 翻译函数(此处仅为示例,实际翻译功能需要调用真实的翻译 API)
def translate_to_simplified_chinese(text):
# 这里应该调用真实的翻译 API 进行翻译
# 由于示例中不使用真实的 API,以下为模拟翻译结果
return text # 假设翻译结果与原文相同
# 输出翻译结果
translated_text = translate_to_simplified_chinese(input_text)
print(translated_text)
```
输出:
```
Immersive Translate
```
计算 x log x 的实用函数
"""
返回
火炬.
特别.xlogy(
张量,
张量)
当 x=0 时产生正确结果
定义
_批跟踪_XXT(bmat):
""
计算具有任意尾部批次维度的 XX^{T}的迹的实用函数
""
n = bmat.尺寸(-1)
m = bmat.大小(-2)
平坦轨迹 = bmat.
重塑(-1, m * n).pow(2).
总和(-1)
返回 flat_trace.
重塑(bmat.
形状
[-2])
[文档]定义 kl_divergence(p: Distribution, q: Distribution) -> Tensor:
r"""
计算两个分布之间的 Kullback-Leibler 散度:\(KL(p \| q)\)
.. math::
\(KL(p \| q) = \int p(x) \log\frac {p(x)} {q(x)} \,dx\)
Args:
p(分布):一个 :class:`~torch.distributions.Distribution` 对象。
q(分布):一个 :class:`~torch.distributions.Distribution` 对象。
返回:
Tensor:形状为 `batch_shape` 的一批 KL 散度。
Raises:
NotImplementedError:如果分布类型尚未通过
meth:`register_kl`。
"""
try:
fun = _KL_MEMOIZE[type(p), type(q)]
except KeyError:
fun = _dispatch_kl(type(p), type(q))
`_KL_MEMOIZE[type(p), type(q)] = fun`
`if fun is NotImplemented:`
` raise NotImplementedError(`
` f"No KL(p || q) is implemented for p type {p.__class__.__name__} and q type {q.__class__.__name__}"`
)
return fun(p, q)
################################################################################
# KL 散度实现
################################################################################
# 相同的分布
@register_kl(伯努利分布,
伯努利分布)
定义
_kl_伯努利_伯努利(p, q):
t1 = p.概率 * (
火炬.
神经网络.
功能性.
软性加(-q.logits)
- 火炬.
神经网络.
功能性.
软性加(-p.logits)
)
t1[q.概率 == 0] =
无
t1[p.概率 == 0] = 0
t2 = (1 - p.概率) * (
火炬.
神经网络.
功能性.
软性加(q.logits) -
火炬.
神经网络.
功能性.
软性加(p.logits)
)
t2[q.概率 == 1] =
无
t2[p.概率 == 1] = 0
return t1 + t2
@register_kl(测试版,
测试版)
定义
_kl_贝塔_贝塔(p, q):
sum_params_p = p.浓度 1 + p.
浓度 0
求和参数_q = q.
浓度 1 + q.
浓度 0
t1 = q.集中 1.lgamma() + q.
集中力 0.lgamma() + (
求和参数_p).lgamma()
t2 = p.集中 1.lgamma() + p.
集中力 0.lgamma() + (
求和参数_q).lgamma()
t3 = (p.浓度 1 - q.
集中 1) *
火炬.
挖耳勺(p.
集中 1)
t4 = (p.浓度 0 - q.
集中力 0) *
火炬.
挖耳勺(p.
集中力 0)
t5 = (求和参数_q -
求和参数_p) *
火炬.
挖耳勺(
求和参数_p)
return t1 - t2 + t3 + t4 + t5
@register_kl(二项分布,
二项分布)
定义 _kl_binomial_binomial(p, q):
# from https://math.stackexchange.com/questions/2214993/
# kullback-leibler-divergence-for-binomial-distributions-p-and-q
如果 (p.
总数 < q.
总数).
任何():
提升
不支持的操作异常(
"KL 之间二项分布的 q.total_count > p.total_count 未实现"
)
kl = p.总数 * (
p.概率 * (p.logits - q.logits) + (-p.
概率).log1p() - (-q.
概率).log1p()
)
inf_idxs = p.总数 > q.
总数
kl[inf_idxs] = 无限之恋(kl[
无穷索引])
返回 kl
@register_kl(离散型随机变量分布,
离散型随机变量分布)
def _kl_分类_分类(p, q):
t = p.概率 * (p.logits - q.logits)
t[q.
概率 == 0).
展开为(t)] =
无
t[p.
概率 == 0).
展开为(t)] = 0
返回 t.
总和(-1)
@register_kl(连续伯努利,
连续伯努利)
def _kl_连续_伯努利_连续_伯努利(p, q):
t1 = p.平均值 * (p.logits - q.logits)
t2 = p._cont_bern_log_norm() + 火炬.log1p(-p.
概率)
t3 = -q._cont_bern_log_norm() - 火炬.log1p(-q.
概率)
返回 t1 + t2 + t3
@register_kl(Dirichlet 分布,
Dirichlet 分布)
def _kl_狄利克雷_狄利克雷(p, q):
来自 http://bariskurt.com/kullback-leibler-divergence-between-two-dirichlet-and-beta-distributions/
总 P 浓度 = p.
浓度.
总和(-1)
总 Q 浓度 = q.
浓度.
总和(-1)
t1 = 总 P 浓度.lgamma() -
总 Q 浓度.lgamma()
t2 = (p.浓度.lgamma() - q.
浓度.lgamma()).
总和(-1)
t3 = p.concentration - q.concentration
t4 = p.浓度.
挖耳勺() -
总浓度.
挖耳勺().
展平(-1)
返回 t1 - t2 + (t3 * t4).
总和(-1)
@register_kl(指数,
指数)
def 指数指数(p, q):
比率 = q.
比率 / p.rate
t1 = -比率.
日志()
返回 t1 +
比率 - 1
@register_kl(指数族,
指数族)
定义 _kl_expfamily_expfamily(p, q):
如果
不是
类型(p) ==
类型(q):
抛出
不支持的操作异常(
不同指数分布族之间的交叉 KL 散度无法\
使用 Bregman 散度来计算
)
参数数量 = [
numpy.detach().
需要梯度_() for np
在 p.
自然参数]
参数数量 q = q.
自然参数
lg 正态分布 = p.
对数归一化器(*
参数数量)
梯度 =
火炬.
自动微分.
研究生(
lg_正常.
总和(),
参数数量,
创建图=
是)
结果 = q.
对数归一化器(*q_nparams) - lg_normal
为 pnp, qnp, g
在 zip(p_nparams, q_nparams,
渐变):
term = (qnp - pnp) * g
结果 -= _sum_rightmost(term,
长度(q.event_shape))
return 结果
@register_kl(伽马,
伽马)
定义 _kl_gamma_gamma(p, q):
t1 = q.concentration * (p.速率 / q.rate).
日志()
t2 = 火炬.lgamma(q.
浓度) -
火炬.lgamma(p.
浓度)
t3 = (p.concentration - q.浓度) *
火炬.
挖耳勺(p.
浓度)
t4 = (q.速率 - p.rate) * (p.concentration / p.rate)
return t1 + t2 + t3 + t4
@register_kl(Gumbel, Gumbel)
定义
_kl_高斯-高斯(p, q):
ct1 = p.缩放 / q.
缩放
ct2 = q.定位 / q.
缩放
ct3 = p.定位 / q.
缩放
t1 = -ct1.日志() - ct2 + ct3
t2 = ct1 * 欧拉常数
t3 = 火炬.exp(ct2 + (1 + ct1).lgamma() - ct3)
返回 t1 + t2 + t3 - (1 +
欧拉常数)
@register_kl(几何的,
几何的)
定义
几何几何 KL 散度(p, q):
返回 -p.
熵() -
火炬.log1p(-q.
概率) / p.
概率 - q.logits
@register_kl(半正态,
半正态)
def 半正态分布半正态分布 KL 散度(p, q):
返回
_kl_普通_普通(p.base_dist, q.base_dist)
@register_kl(拉普拉斯分布,
拉普拉斯分布)
def _kl_拉普拉斯_拉普拉斯(p, q):
# 来自 http://www.mast.queensu.ca/~communications/Papers/gil-msc11.pdf
缩放比例 = p.
缩放 / q.
缩放
位置绝对差 = (p.
定位 - q.
位置).
绝对值()
t1 = -比例尺.
日志()
t2 = 位置绝对差 / q.
缩放
t3 = 比例尺 *
火炬.exp(-
局部绝对差异 / p.
比例)
返回 t1 + t2 + t3 - 1
@register_kl(低秩多元正态分布,
低秩多元正态分布)
def _kl_lowrankmultivariatenormal_低秩多元正态分布(p, q):
如果 p.
事件形状 != q.event_shape:
提升 ValueError(
两个低秩多元正态分布之间的 KL 散度\
不同事件形状之间的 KL 散度无法计算
)
term1 = _批量低秩对数行列式(
q._未广播的协方差因子, q._unbroadcasted_cov_diag, q.
电容三联
) - _批量低秩对数行列式(
p._未广播的协方差因子, p._unbroadcasted_cov_diag, p.
电容三联
)
项 3 = _batch_lowrank_mahalanobis(
q._未广播的协方差因子,
q._unbroadcasted_cov_diag,
q.定位 - p.
位置,
q.三相电容,
)
根据术语 2 扩展
# inv(qcov) @ pcov = [inv(qD) - inv(qD) @ qW @ inv(qC) @ qW.T @ inv(qD)] @ (pW @ pW.T + pD)
# = [inv(qD) - A.T @ A] @ (pD + pW @ pW.T)
qWt_qDinv = q._未广播的协方差因子.mT / q._unbroadcasted_cov_diag.
展平(-2)
A = 火炬.
线性代数.
解三角形方程(q.
三相电容, qWt_qDinv,
上=
错误)
term21 = (p.未广播的协方差诊断 / q._unbroadcasted_cov_diag).
总和(-1)
term22 = _batch_trace_XXT(
p._未广播覆盖率因子 * q.
_未广播覆盖率对角线.rsqrt().
展平(-1)
)
term23 = _batch_trace_XXT(A * p._unbroadcasted_cov_diag.平方根().
展平(-2))
term24 = _批量追踪_XXT(A.
矩阵乘法(p.
_未广播覆盖率因子))
term2 = term21 + term22 - term23 - term24
返回 0.5 * (term1 + term2 + term3 - p.event_shape[0])
@register_kl(多元正态分布,
低秩多元正态分布)
定义
_kl_多变量正态低秩多变量正态(p, q):
如果 p.
事件形状 != q.event_shape:
抛出
值错误(
两个(低秩)多元正态分布之间的 KL-散度\
无法计算不同的事件形状
)
term1 = _batch_lowrank_logdet(
q._unbroadcasted_cov_factor, q.未广播的协方差诊断, q.
电容三阶
) - 2 * p.未广播的尺度三阶.
对角线(dim1=-2, dim2=-1).
日志().
总和(-1)
项 3 = _batch_lowrank_mahalanobis(
q._unbroadcasted_cov_factor,
q._unbroadcasted_cov_diag,
q.定位 - p.
位置,
q._capacitance_tril,
)
根据术语 2 进行展开
inv(qcov) @ pcov = [inv(qD) - inv(qD) @ qW @ inv(qC) @ qW.T @ inv(qD)] @ p_tril @ p_tril.T
= [inv(qD) - A.T @ A] @ p_tril @ p_tril.T
qWt_qDinv = q.未广播覆盖率因子.mT / q.
未广播对角覆盖率.
展平(-2)
A = 火炬.
线性代数.
解三角形方程(q.
电容三联, qWt_qDinv,
上=
错误)
term21 = _批量追踪_XXT(
p._未广播的缩放三线_ * q.
_未广播的协方差对角线_.
平方根的倒数().
展平(-1)
)
term22 = _批量跟踪_XXT(A.
矩阵乘法(p.
_未广播的尺度三重))
term2 = term21 - term22
return 0.5 * (term1 + term2 + 项 3 - p.event_shape[0])
@register_kl(低秩多元正态分布,
多元正态分布)
定义
_kl_低秩多元正态_多元正态分布(p, q):
如果 p.
事件形状 != q.event_shape:
提升 ValueError(
"两个(低秩)多元正态分布之间的 KL 散度,当事件形状不同时无法计算"\
不同事件形状之间的 KL 散度无法计算
)
term1 = 2 * q._未广播的尺度三重.
对角线(dim1=-2, dim2=-1).
日志().
总和(
-1
) - _批量低秩对数行列式(
p._未广播的协方差因子, p.
未广播的协方差诊断, p.
电容三联
)
项 3 =
批量马氏距离(q.
未广播的尺度三联, (q.
定位 - p.
位置))
根据术语 2 扩展
@ pcov = inv(qcov) @ q_tril @ q_tril.T @ (pW @ pW.T + pD)
合并批次形状 =
火炬._C._infer_size(
q._unbroadcasted_scale_tril.形状
[-2
] p._unbroadcasted_cov_factor.
形状
[-2]
)
n = p.event_shape[0]
q_scale_tril = q.未广播的尺度三重.
展开(
合并批次形状 + (n, n))
p_协方差因子 = p.
_未广播的协方差因子.
展开(
合并批次形状 + (n, p.
协方差因子.
大小(-1))
)
p_对角协方差 =
火炬.
嵌入式诊断(p.
_未广播的对角协方差.
平方根()).
展开(
合并批次形状 + (n, n)
)
项 21 =
_批量追踪_XXT(
火炬.
线性代数.
解三角形方程(
q_缩放_三倍,
p_协方差因子,
上=
错误)
)
项 22 =
_批量追踪_XXT(
火炬.
线性代数.
解三角形方程(
q_缩放_三倍,
p_协方差对角线,
上=
错误)
)
term2 = term21 + term22
返回 0.5 * (term1 + term2 + term3 - p.event_shape[0])
@register_kl(多元正态分布,
多元正态分布)
定义 _kl_multivariatenormal_multivariatenormal(p, q):
# 来自 https://en.wikipedia.org/wiki/Multivariate_normal_distribution#Kullback%E2%80%93Leibler_divergence
如果 p.
事件形状 != q.event_shape:
抛出
值错误(
"KL-散度是两个多元正态分布之间的散度\
无法计算不同的事件形状
)
half_term1 = q._未广播的缩放三重积分.
对角线(dim1=-2, dim2=-1).
日志().
总和(
-1
) - p._未广播的缩放三重积分.
对角线(dim1=-2, dim2=-1).
日志().
总和(-1)
合并批次形状 =
火炬._C.
_推断大小(
q._未广播的尺度三角.
形状
[-2
] p.
_未广播的尺度三角.
形状
[-2]
)
n = p.event_shape[0]
q_scale_tril = q._unbroadcasted_scale_tril.展开(combined_batch_shape + (n, n))
p_scale_tril = p._未广播的尺度三重.
展开(
合并批次形状 + (n, n))
术语 2 =
_批次跟踪_XXT(
火炬.
线性代数.
解三角形方程(q_scale_tril, p_scale_tril,
上=
错误)
)
项 3 =
批量马氏距离(q.
_未广播的尺度三重, (q.
定位 - p.
位置))
return half_term1 + 0.5 * (term2 + 项 3 - n)
@register_kl(正态分布,
正态分布)
定义 _kl_normal_normal(p, q):
变量比率 = (p.
缩放 / q.
比例).pow(2)
t1 = ((p.定位 - q.
位置) / q.
比例).pow(2)
return 0.5 * (变量比率 + t1 - 1 -
变量比率.
日志())
@register_kl(单热分类变量,
单热分类变量)
定义 _kl_onehotcategorical_onehotcategorical(p, q):
return _kl_分类_分类(p.
_分类, q.
_分类)
@register_kl(帕累托,
帕累托)
定义
_kl_帕累托_帕累托(p, q):
来自 http://www.mast.queensu.ca/~communications/Papers/gil-msc11.pdf
缩放比例 = p.
缩放 / q.
缩放
alpha 比例 = q.alpha / p.alpha
t1 = q.alpha * 缩放比例.
日志()
t2 = -alpha 比例.
日志()
结果 = t1 + t2 +
alpha 比例 - 1
结果[p.
支持.
下限 < q.
支持.
下限] =
无
返回
结果
@register_kl(泊松,
泊松)
def _kl 泊松泊松(p, q):
返回 p.
速率 * (p.rate.
日志() - q.rate.
日志()) - (p.
速率 - q.rate)
@register_kl(转换分布,
转换分布)
def _kl 变换变换(p, q):
如果 p.
转换 != q.
转换:
提升
未实现异常
如果 p.
事件形状 != q.event_shape:
提升
未实现异常
返回
针对性差异(p.base_dist, q.base_dist)
@register_kl(均匀分布,
均匀分布)
def _kl_均匀_均匀(p, q):
结果 = ((q.
高 - q.
低) / (p.
高 - p.
低)).
日志()
结果
[q.
低 > p.
低) | (q.
高 < p.
高)] =
无
返回
结果
# 不同分布
@register_kl(伯努利分布,
泊松)
定义
_kl_伯努利泊松(p, q):
返回 -p.
熵() - (p.
概率 * q.rate.
日志() - q.rate)
@register_kl(测试版,
连续伯努利)
定义
_kl_beta 连续伯努利(p, q):
返回 (
-p.熵()
- p.平均值 * q.logits
- 火炬.log1p(-q.
概率)
- q._cont_bern_log_norm()
)
@register_kl(测试版,
帕累托)
定义
_kl_beta 无穷(p, q):
返回
_无限似(p.
集中 1)
@register_kl(测试版,
指数)
定义
_kl_β_指数(p, q):
返回 (
-p.熵()
- q.rate.日志()
+ q.rate * (p.浓度 1 / (p.
浓度 1 + p.
集中 0))
)
@register_kl(测试版,
伽马)
定义
_kl_β_γ(p, q):
t1 = -p.熵()
t2 = q.浓度.lgamma() - q.concentration * q.rate.
日志()
t3 = (q.concentration - 1) * (
p.集中 1.
挖耳勺() - (p.
浓度 1 + p.
集中 0).
挖耳勺()
)
t4 = q.速率 * p.
浓度 1 / (p.
浓度 1 + p.
集中力 0)
return t1 + t2 - t3 + t4
添加 Beta-Laplace KL 散度
@register_kl(测试版,
正态分布)
定义 _kl_beta_normal(p, q):
E_beta = p.浓度 1 / (p.
浓度 1 + p.
集中力 0)
var_normal = q.比例.pow(2)
t1 = -p.熵()
t2 = 0.5 * (正态变量 * 2 *
数学.
π).
日志()
t3 = (
β能 * (1 -
β能) / (p.
浓度 1 + p.
浓度 0 + 1)
+ E_beta.pow(2)
) * 0.5
t4 = q.定位 * E_beta
t5 = q.位置.pow(2) * 0.5
return t1 + t2 + (t3 - t4 + t5) / 正常变量
@register_kl(测试版,
均匀分布)
定义
_kl_beta 均匀(p, q):
结果 = -p.
熵() + (q.
高 - q.
低).
日志()
结果
[q.
低 > p.
支持.
下限) | (q.
高 < p.
支持.
上限)] =
无
返回
结果
注意,连续伯努利分布与贝塔分布之间的 KL 散度没有闭式解
@register_kl(连续伯努利,
帕累托)
def _kl_连续_伯努利_无穷大(p, q):
返回
无限之爱(p.
概率)
@register_kl(连续伯努利,
指数)
def 连续伯努利指数分布(p, q):
返回 -p.
熵() -
火炬.
日志(q.rate) + q.
速率 * p.
平均值
注意,连续伯努利分布与伽马分布之间的 KL 散度没有闭式解
持续伯努利-拉普拉斯 KL 散度
@register_kl(连续伯努利,
正态分布)
def _kl_连续_伯努利_正态(p, q):
t1 = -p.熵()
t2 = 0.5 * (数学.
日志(2.0 *
数学.
π) +
火炬.
平方(q.
定位 / q.
比例)) +
火炬.
日志(
q.缩放
)
t3 = (p.方差 +
火炬.
平方(p.
均值) - 2.0 * q.
定位 * p.
均值) / (
2.0 * 火炬.
平方(q.
比例)
)
返回 t1 + t2 + t3
@register_kl(连续伯努利,
均匀分布)
def _kl_连续_伯努利_均匀(p, q):
结果 = -p.
熵() + (q.
高 - q.
低).
日志()
返回
火炬.
哪里(
火炬.
最大值(
火炬.
大于等于(q.
低, p.
支持.
下限),
火炬.
小于等于(q.
高, p.
支持.
上限),
),
火炬.
喜欢的(
结果) *
无穷,
结果,
)
@register_kl(指数,
测试版)
@register_kl(指数,
连续伯努利)
@register_kl(指数,
帕累托)
@register_kl(指数,
均匀分布)
def _kl 指数无穷(p, q):
返回 _infinite_like(p.rate)
@register_kl(指数,
伽马)
def _kl 指数伽马(p, q):
比率 = q.
速率 / p.
速率
t1 = -q.concentration * 火炬.
日志(
比率)
返回 (
t1
+ 比率
+ q.浓度.lgamma()
+ q.concentration * 欧拉常数
- (1 + 欧拉常数)
)
@register_kl(指数, Gumbel)
def _kl_exponential_gumbel(p, q):
scale_rate_prod = p.速率 * q.
缩放
loc_scale_ratio = q.定位 / q.
缩放
t1 = scale_rate_prod.日志() - 1
t2 = 火炬.exp(
局部缩放比例) *
缩放速率乘积 / (
缩放速率乘积 + 1)
t3 = 缩放速率乘积.
相互()
返回 t1 -
位置尺度比 + t2 + t3
指数拉普拉斯 KL 散度
@register_kl(指数,
正态分布)
def _指数拉普拉斯正态分布(p, q):
var_normal = q.比例.pow(2)
速率平方 = p.rate.pow(2)
t1 = 0.5 * 火炬.
日志(
速率平方 * var_normal * 2 *
数学.
π)
t2 = 速率平方.
相互()
t3 = q.定位 / p.rate
t4 = q.位置.pow(2) * 0.5
返回 t1 - 1 + (t2 - t3 + t4) /
正常变量
@register_kl(伽马,
测试版)
@register_kl(伽马,
连续伯努利)
@register_kl(伽马,
帕累托)
@register_kl(伽马,
均匀分布)
定义
无穷大伽马(p, q):
返回
无穷大似(p.
浓度)
@register_kl(伽马,
指数)
定义
_kl_伽马指数(p, q):
返回 -p.
熵() - q.rate.
日志() + q.rate * p.concentration / p.rate
@register_kl(伽马, Gumbel)
定义
_kl_伽马高斯(p, q):
贝塔尺度乘积 = p.rate * q.
缩放
定位缩放比例 = q.
定位 / q.
缩放
t1 = (
(p.concentration - 1) * p.浓度.
挖耳勺()
- p.浓度.lgamma()
- p.concentration
)
t2 = 贝塔缩放乘积.
日志() + p.concentration /
贝塔缩放乘积
t3 = (
火炬.exp(
定位缩放比例)
* (1 + beta_scale_prod.相互()).pow(-p.
浓度)
- loc_scale_ratio
)
return t1 + t2 + t3
# TODO: 添加伽马拉普拉斯 KL 散度
@register_kl(伽马,
正态分布)
定义 _kl_gamma_normal(p, q):
var_普通 = q.
比例.pow(2)
beta_平方 = p.rate.pow(2)
t1 = (
0.5 * 火炬.
日志(
beta_平方 *
var_普通 * 2 *
数学.
π)
- p.concentration
- p.浓度.lgamma()
)
t2 = 0.5 * (p.浓度.pow(2) + p.
浓度) /
beta 平方
t3 = q.定位 * p.concentration / p.
速率
t4 = 0.5 * q.位置.pow(2)
return (
t1
+ (p.concentration - 1) * p.浓度.
挖耳勺()
+ (t2 - t3 + t4) / var_normal
)
@register_kl(Gumbel, 测试版)
@register_kl(Gumbel, 连续伯努利)
@register_kl(Gumbel, 指数)
@register_kl(Gumbel, 伽马)
@register_kl(Gumbel, 帕累托)
@register_kl(Gumbel, 均匀分布)
定义 _kl_gumbel_infinity(p, q):
返回
无限之恋(p.
位置)
# TODO: 添加 Gumbel-Laplace KL 散度
@register_kl(Gumbel, 正态分布)
定义 _kl_gumbel_normal(p, q):
参数比 = p.
缩放 / q.
缩放
t1 = (参数比率 /
数学.
平方根(2 *
数学.
π)).
日志()
t2 = (数学.
圆周率 *
参数比率 * 0.5).pow(2) / 3
t3 = ((p.定位 + p.
缩放 *
欧拉常数 - q.
位置) / q.
比例).pow(2) * 0.5
返回 -t1 + t2 + t3 - (
欧拉常数 + 1)
@register_kl(拉普拉斯分布,
测试版)
@register_kl(拉普拉斯分布,
连续伯努利)
@register_kl(拉普拉斯分布,
指数)
@register_kl(拉普拉斯分布,
伽马)
@register_kl(拉普拉斯分布,
帕累托)
@register_kl(拉普拉斯分布,
均匀分布)
def _kl_laplace_无穷(p, q):
返回
_无限_like(p.
位置)
@register_kl(拉普拉斯分布,
正态分布)
def _kl_laplace_正态(p, q):
var_normal = q.比例.pow(2)
scale_sqr_var_ratio_比例 = p.
比例.pow(2) / var_normal
t1 = 0.5 * 火炬.
日志(2 *
标准化平方方差比率 /
数学.
π)
t2 = 0.5 * p.位置.pow(2)
t3 = p.定位 * q.
定位
t4 = 0.5 * q.位置.pow(2)
返回 -t1 +
标准化平方方差比率 + (t2 - t3 + t4) / var_normal - 1
@register_kl(正态分布,
测试版)
@register_kl(正态分布,
连续伯努利)
@register_kl(正态分布,
指数)
@register_kl(正态分布,
伽马)
@register_kl(正态分布,
帕累托)
@register_kl(正态分布,
均匀分布)
定义 _kl_normal_infinity(p, q):
返回 _infinite_like(p.
位置)
@register_kl(正态分布, Gumbel)
定义 _kl_normal_gumbel(p, q):
均值尺度比 = p.
定位 / q.
缩放
变量尺度平方比 = (p.
缩放 / q.
比例).pow(2)
位置尺度比 = q.
定位 / q.
缩放
t1 = 变量尺度平方比.
日志() * 0.5
t2 = 平均尺度比 -
定位尺度比
t3 = 火炬.exp(-
平均尺度比 + 0.5 *
变量尺度平方比 +
位置尺度比)
返回 -t1 + t2 + t3 - (0.5 * (1 +
数学.
日志(2 *
数学.
π)))
@register_kl(正态分布,
拉普拉斯分布)
定义 _kl_normal_laplace(p, q):
loc_diff = p.定位 - q.
定位
scale_ratio = p.缩放 / q.
缩放
loc_diff_scale_ratio = 位置差异 / p.
缩放
t1 = 火炬.
日志(
比例尺)
t2 = (
数学.
平方根(2 /
数学.
π) * p.
缩放 *
火炬.exp(-0.5 *
位置差异比例尺.pow(2))
)
t3 = 位置差异 *
火炬.erf(
数学.
平方根(0.5) *
位置差异缩放比)
return -t1 + (t2 + t3) / q.缩放 - (0.5 * (1 +
数学.
日志(0.5 *
数学.
π)))
@register_kl(帕累托,
测试版)
@register_kl(帕累托,
连续伯努利)
@register_kl(帕累托,
均匀分布)
定义
_kl_帕累托无穷(p, q):
return _无限似(p.
比例)
@register_kl(帕累托,
指数)
定义
_kl_帕累托指数(p, q):
比率乘数 = p.
缩放 * q.
速率
t1 = (p.alpha / 比率乘数).
日志()
t2 = p.阿尔法.
相互()
t3 = p.alpha * 比率乘数 / (p.alpha - 1)
结果 = t1 - t2 + t3 - 1
结果[p.alpha <= 1] =
无
返回
结果
@register_kl(帕累托,
伽马)
定义
_kl_pareto 伽玛(p, q):
通用术语 = p.
比例.
日志() + p.
阿尔法.
相互()
t1 = p.阿尔法.
日志() -
通用术语
t2 = q.浓度.lgamma() - q.concentration * q.rate.
日志()
t3 = (1 - q.浓度) *
通用术语
t4 = q.rate * p.alpha * p.缩放 / (p.alpha - 1)
结果 = t1 + t2 + t3 + t4 - 1
结果[p.alpha <= 1] =
无
return 结果
# TODO: 添加帕累托-拉普拉斯 KL 散度
@register_kl(帕累托,
正态分布)
定义 _kl_pareto_normal(p, q):
var_normal = 2 * q.比例.pow(2)
通用术语 = p.
缩放 / (p.alpha - 1)
t1 = (数学.
平方根(2 *
数学.
π) * q.
缩放 * p.alpha / p.
比例).
日志()
t2 = p.阿尔法.
相互()
t3 = p.alpha * 常用术语.pow(2) / (p.alpha - 2)
t4 = (p.alpha * 通用术语 - q.
位置).pow(2)
结果 = t1 - t2 + (t3 + t4) / var_normal - 1
结果[p.alpha <= 2] =
无
返回
结果
@register_kl(泊松,
伯努利分布)
@register_kl(泊松,
二项分布)
def _kl_poisson_infinity(p, q):
返回 _infinite_like(p.rate)
@register_kl(均匀分布,
测试版)
def _kl_uniform_beta(p, q):
通用术语 = p.
高 - p.
低
t1 = 火炬.
日志(
常用术语)
t2 = (
(q.浓度 1 - 1)
* (_x_log_x(p.高) - _x_log_x(p.
低) -
通用术语)
/ 通用术语
)
t3 = (
(q.浓度 0 - 1)
* (_x_log_x(1 - p.高) - _x_log_x(1 - p.
低) +
常用术语)
/ 通用术语
)
t4 = (
q.集中 1.lgamma()
+ q.集中力 0.lgamma()
- (q.浓度 1 + q.
集中力 0).lgamma()
)
结果 = t3 + t4 - t1 - t2
结果
[p.
高 > q.
支持.
上限) | (p.
低 < q.
支持.
下限)] =
无
返回
结果
@register_kl(均匀分布,
连续伯努利)
def _kl_uniform_continuous_bernoulli(p, q):
结果 = (
-p.熵()
- p.平均值 * q.logits
- 火炬.log1p(-q.
概率)
- q._cont_bern_log_norm()
)
返回
火炬.
哪里(
火炬.
最大值(
火炬.
大于等于(p.
高, q.
支持.
上限),
火炬.
小于等于(p.
低, q.
支持.
下限),
),
火炬.
喜欢的(
结果) *
无穷,
结果,
)
@register_kl(均匀分布,
指数)
def _kl_均匀指数(p, q):
结果 = q.
速率 * (p.
高 + p.
低) / 2 - ((p.
高 - p.
低) * q.rate).
日志()
结果[p.
低 < q.
支持.
下限] =
无
返回
结果
@register_kl(均匀分布,
伽马)
def _kl_uniform_gamma(p, q):
通用术语 = p.
高 - p.
低
t1 = 常用术语.
日志()
t2 = q.浓度.lgamma() - q.concentration * q.rate.
日志()
t3 = (
(1 - q.浓度)
* (_x_log_x(p.高) - _x_log_x(p.
低) -
常用术语)
/ 通用术语
)
t4 = q.速率 * (p.
高 + p.
低) / 2
结果 = -t1 + t2 + t3 + t4
结果[p.
低 < q.
支持.
下限] =
无
返回
结果
@register_kl(均匀分布, Gumbel)
def _kl_uniform_gumbel(p, q):
通用术语 = q.
缩放 / (p.
高 - p.
低)
高局部差异 = (p.
高 - q.
位置) / q.
缩放
低局部差异 = (p.
低 - q.
位置) / q.
缩放
t1 = 常见术语.
日志() + 0.5 * (
高本地化差异 +
低本地化差异)
t2 = 通用术语 * (
火炬.exp(-
高本地化差异) -
火炬.exp(-
低局部差异))
返回 t1 - t2
# TODO: 均匀拉普拉斯 KL 散度
@register_kl(均匀分布,
正态分布)
def _kl 均匀正态(p, q):
通用术语 = p.
高 - p.
低
t1 = (数学.
平方根(
数学.
圆周率 * 2) * q.
缩放 /
常用术语).
日志()
t2 = (常用术语).pow(2) / 12
t3 = ((p.高 + p.
低 - 2 * q.
位置) / 2).pow(2)
返回 t1 + 0.5 * (t2 + t3) / q.
比例.pow(2)
@register_kl(均匀分布,
帕累托)
def _kl_均匀帕累托(p, q):
支持均匀 = p.
高 - p.
低
t1 = (q.alpha * q.比例.pow(q.
阿尔法) * (
支持均匀)).
日志()
t2 = (_x_log_x(p.高) - _x_log_x(p.
低) -
支持均匀) /
支持均匀
结果 = t2 * (q.alpha + 1) - t1
结果[p.
低 < q.
支持.
下限] =
无
返回
结果
@register_kl(独立,
独立)
def _独立独立(p, q):
如果 p.
重新解释的批量维度数 != q.
重新解释的批量维度:
提升
未实现异常
结果 =
KL 散度(p.base_dist, q.base_dist)
返回 _sum_rightmost(
结果, p.
重新解释的批量维度)
@register_kl(柯西,
柯西)
def _kl_cauchy_cauchy(p, q):
# 来自 https://arxiv.org/abs/1905.10965
t1 = ((p.缩放 + q.
比例).pow(2) + (p.
定位 - q.
位置).pow(2)).
日志()
t2 = (4 * p.缩放 * q.
比例).
日志()
返回 t1 - t2
def _add_kl_info():
将已实现的 KL 函数列表添加到 kl_divergence 的文档中。
行 = [
KL 散度目前实现了以下分布对的计算:
]
为 p, q
在
排序(
_KL_REGISTRY, 键=lambda p_q: (p_q[0].__name__, p_q[1].__name__)
):
行数.append(
f"* :class:`~torch.distributions."{p.__name__}
`和 :class:`~torch.distributions."{q.__name__}
)
kl_info = "\n\t".连接(
行)
如果
KL 散度.__doc__:
KL 散度.__doc__ +=
KL 信息