4、概率分布(下):多元分布与分布变换
好,咱们接着聊概率分布。上一章我们把单变量的各种分布捋了一遍,这一章要上点难度了——多元分布。说白了,就是从处理一个随机变量,变成同时处理多个随机变量。你想想看,现实世界里哪有那么多单一变量?图像是像素矩阵,文本是词向量序列,连天气预报都得同时报温度、湿度、风速。所以多元分布才是实战中的主角。
4.1 多元正态分布:MultivariateNormalDiag
多元正态分布,我愿称之为「概率分布界的瑞士军刀」。它扩展了单变量正态分布,能描述多个变量之间的联合分布。
在 TensorFlow Probability 里,最简单的实现是 MultivariateNormalDiag。名字里的 "Diag" 指的是协方差矩阵是对角矩阵——说白了就是假设各个维度之间相互独立。虽然这个假设有点强,但胜在计算快、参数少,很多场景下够用了。
核心参数:
loc:均值向量,形状为 [batch_shape, event_size]scale_diag:标准差向量,形状与 loc 相同
import tensorflow as tf
import tensorflow_probability as tfp
tfd = tfp.distributions
# 定义一个二维正态分布
mvn = tfd.MultivariateNormalDiag(
loc=[1.0, 2.0],
scale_diag=[0.5, 1.0]
)
# 采样
samples = mvn.sample(5)
print(samples.shape) # (5, 2)
# 计算对数概率
log_prob = mvn.log_prob([[0.5, 1.5], [1.0, 2.0]])
print(log_prob) # 两个点的对数概率
我个人习惯在模型初期先用 MultivariateNormalDiag 做快速原型。等模型跑通了,再考虑换成更复杂的协方差结构。我在项目中遇到过一个问题:用全协方差矩阵做贝叶斯线性回归,结果训练时间从几分钟飙到几小时。后来换成对角矩阵,效果只差了 2%,但速度快了 20 倍。嗯,这里要注意——不是所有场景都需要复杂的协方差结构。
4.2 狄利克雷分布:Dirichlet
狄利克雷分布,说白了就是「分布之上的分布」。它是 Beta 分布在多维空间的推广,专门用来描述概率向量的分布。什么叫概率向量?就是元素非负且和为 1 的向量,比如 [0.2, 0.5, 0.3]。
狄利克雷分布有一个很直观的参数 concentration(浓度参数)。这个参数控制着概率向量的「集中程度」:
- 浓度参数都大于 1:概率向量倾向于均匀分布
- 浓度参数都小于 1:概率向量倾向于集中在某一个类别上
- 浓度参数相等:分布是对称的
# 定义一个三维狄利克雷分布
dirichlet = tfd.Dirichlet(
concentration=[2.0, 2.0, 2.0]
)
# 采样
samples = dirichlet.sample(3)
print(samples)
# 每行元素和为 1,例如 [0.3, 0.4, 0.3]
# 浓度参数不同时
dirichlet_skewed = tfd.Dirichlet(
concentration=[0.1, 0.1, 5.0]
)
samples_skewed = dirichlet_skewed.sample(3)
# 第三个维度会明显偏大
实战技巧:狄利克雷分布是 LDA(隐含狄利克雷分配)主题模型的核心。我曾经用它做文本聚类,浓度参数设得太小,结果每个文档的主题分布都极端集中——说白了就是每个文档只属于一个主题,失去了混合的意义。后来把浓度参数调到 0.5 左右,效果才正常。
4.3 威沙特分布:Wishart
威沙特分布,是多元正态分布协方差矩阵的共轭先验。如果你在做贝叶斯多元分析,威沙特分布几乎是绕不开的。
它的参数有两个:自由度 df 和尺度矩阵 scale。自由度必须大于等于维度数,否则矩阵不是正定的。
# 定义一个 2x2 的威沙特分布
wishart = tfd.Wishart(
df=5,
scale=tf.eye(2) # 单位矩阵
)
# 采样得到协方差矩阵
samples = wishart.sample(2)
print(samples.shape) # (2, 2, 2)
# 每个样本是一个 2x2 的正定矩阵
注意:威沙特分布对自由度很敏感。自由度太小,采样出的矩阵可能接近奇异矩阵,导致数值不稳定。我建议自由度至少设为维度数 + 2。我曾经在这个坑里摔过——自由度设成 3,维度是 3,结果模型训练到一半就 NaN 了。排查了半天才发现是协方差矩阵不可逆。
4.4 分布变换与 Bijectors 基础
好,接下来是这一章的重头戏——分布变换。你想想看,很多时候我们需要的分布不是标准形式。比如我想定义一个在 [0, 1] 区间上的正态分布,或者一个对数正态分布。这时候就需要对基础分布做变换。
TensorFlow Probability 用 Bijectors(双射器)来实现分布变换。Bijector 说白了就是一个可逆的、可微的变换函数。它能把一个简单分布(比如标准正态)映射成复杂分布。
为什么叫「双射」?因为变换必须是一一对应的,这样才能保证概率密度可以正确计算。变换后的概率密度公式是:
p_Y(y) = p_X(f^{-1}(y)) * |det(J_{f^{-1}}(y))|
其中 J 是雅可比矩阵。别被公式吓到,TFP 已经帮你算好了。
# 用 Exp bijector 把正态分布变成对数正态分布
exp_bijector = tfb.Exp()
log_normal = tfd.TransformedDistribution(
distribution=tfd.Normal(loc=0.0, scale=1.0),
bijector=exp_bijector
)
# 采样
samples = log_normal.sample(5)
print(samples) # 所有值都是正数
# 计算对数概率
log_prob = log_normal.log_prob([0.5, 1.0, 2.0])
print(log_prob)
常用的 Bijectors 有:
| Bijector | 变换 | 用途 |
|---|---|---|
Exp |
y = exp(x) | 将实数映射到正数 |
Softplus |
y = log(1 + exp(x)) | 平滑的正数映射 |
Sigmoid |
y = 1 / (1 + exp(-x)) | 将实数映射到 (0, 1) |
Affine |
y = scale * x + shift | 线性缩放和平移 |
SoftmaxCentered |
Softmax 变换 | 将实数向量映射到概率单纯形 |
核心思想:Bijectors 可以链式组合。比如你想定义一个在 [a, b] 区间上的分布,可以先从标准正态开始,经过 Sigmoid 映射到 (0, 1),再经过 Affine 缩放到 [a, b]。这种组合方式让复杂分布的构建变得极其灵活。
4.5 自定义分布
有时候内置分布不够用,就得自己动手。TFP 提供了两种方式来自定义分布:
方式一:继承 tfd.Distribution
你需要实现 _log_prob 和 _sample_n 这两个核心方法。其他方法(如 mean、variance)是可选的,但实现了能提高效率。
class MyCustomDistribution(tfd.Distribution):
def __init__(self, alpha, beta):
self._alpha = alpha
self._beta = beta
super().__init__(
dtype=tf.float32,
reparameterization_type=tfd.FULLY_REPARAMETERIZED,
validate_args=False,
allow_nan_stats=True
)
def _log_prob(self, x):
# 自定义对数概率密度
return self._alpha * tf.math.log(x) + self._beta * x
def _sample_n(self, n, seed=None):
# 自定义采样方法
# 这里用 rejection sampling 或者直接调用底层采样
pass
方式二:用 TransformedDistribution 组合
这种方式更推荐。你不需要从头实现采样和概率计算,只需要定义变换关系。
# 定义一个在 [0, 1] 区间上的 Beta 分布变换
base_dist = tfd.Beta(
concentration1=2.0,
concentration0=2.0
)
# 用 Affine 变换缩放到 [0, 5]
scaled_beta = tfd.TransformedDistribution(
distribution=base_dist,
bijector=tfb.Affine(scale=5.0)
)
# 采样
samples = scaled_beta.sample(5)
print(samples) # 值在 [0, 5] 之间
我的建议:能用组合就别自己写。自定义分布容易在梯度计算和采样效率上出问题。我曾经手写了一个复杂的分布,结果反向传播时梯度全是 NaN,排查了两天才发现是雅可比矩阵算错了。用 Bijectors 组合,这些细节 TFP 都帮你处理好了。
小结
这一章我们聊了多元分布的核心内容:
- MultivariateNormalDiag:快速原型首选,对角协方差省时省力
- Dirichlet:概率向量的分布,LDA 等主题模型的基础
- Wishart:协方差矩阵的先验,注意自由度别设太小
- Bijectors:分布变换的瑞士军刀,链式组合威力无穷
- 自定义分布:能组合就别手写,省心省力
下一章我们会把这些分布用到实际的贝叶斯模型中。到时候你会发现,理解了这些分布,贝叶斯推断就成功了一半。嗯,先消化一下,咱们下章见。