2、TensorFlow Probability 基础:安装与配置、TFP 核心组件概览

好,咱们正式开始动手了。这一章我带你先把环境搭好,再快速过一遍 TFP 的四大核心组件。说白了,这就是你接下来所有贝叶斯编程的「工具箱」。我个人习惯先把工具认全,再谈怎么用,这样后面学起来会顺手很多。

2.1 安装与配置:别让环境卡住你

安装 TFP 其实很简单,但有几个坑我得提前跟你说。我曾经在一台老旧的服务器上折腾了整整一下午,最后发现是 Python 版本不兼容——嗯,这种经历一次就够了。

2.1.1 基础安装命令

我建议你直接用 pip 安装,干净利落:

# 安装 TensorFlow 和 TFP
pip install tensorflow==2.12.0
pip install tensorflow-probability==0.20.0
注意版本匹配:TFP 和 TensorFlow 的版本必须严格对应。比如 TFP 0.20.x 对应 TF 2.12.x。你想想看,如果版本对不上,导入时就会报一堆莫名其妙的错。我曾经就因为这个浪费过时间。

2.1.2 验证安装

装完之后,我习惯先跑个简单的导入测试:

import tensorflow as tf
import tensorflow_probability as tfp

print("TF version:", tf.__version__)
print("TFP version:", tfp.__version__)

# 创建一个最简单的正态分布,验证安装
normal_dist = tfp.distributions.Normal(loc=0., scale=1.)
print("采样结果:", normal_dist.sample(5))

如果能看到版本号和采样结果,恭喜你,环境搞定了。

2.1.3 避坑指南:GPU 配置

如果你要用 GPU 加速,记得检查 CUDA 和 cuDNN 的版本。我建议你直接用 tf.config.list_physical_devices('GPU') 看一眼。为什么?因为 TFP 的 MCMC 采样在 GPU 上能快好几倍,尤其是数据量大的时候。

我的小技巧:在 Jupyter Notebook 里,我习惯先跑 %load_ext autoreload%autoreload 2,这样修改代码后不用重启内核。做贝叶斯建模时,你会频繁调整模型结构,这个设置能省不少时间。

2.2 TFP 核心组件概览:四大金刚

TFP 的核心组件其实就四个:distributionsbijectorsmcmcvi。我管它们叫「四大金刚」。每个组件解决一类问题,咱们一个一个看。

2.2.1 tfp.distributions:概率分布工厂

这是你用得最多的模块。说白了,它就是各种概率分布的「工厂」。你想用正态分布?伯努利分布?还是更复杂的混合分布?这里都有。

# 创建分布对象
normal = tfp.distributions.Normal(loc=0., scale=1.)
bernoulli = tfp.distributions.Bernoulli(probs=0.7)

# 基本操作
print("PDF at 0:", normal.prob(0.))      # 概率密度函数
print("LogPDF at 0:", normal.log_prob(0.)) # 对数概率密度
print("采样5个:", normal.sample(5))        # 随机采样
核心概念:在 TFP 里,分布对象是「可微的」。这意味着你可以对 log_prob 求梯度。这在变分推断和梯度下降中至关重要。你想想看,如果分布不可微,很多优化算法就没法用了。

2.2.2 tfp.bijectors:分布变形金刚

Bijectors 是 TFP 里最巧妙的设计之一。它能把一个简单分布「变形」成复杂分布。我刚开始学的时候觉得这玩意儿有点抽象,后来在项目中做约束参数时才发现它有多好用。

# 用 Softplus 把实数映射到正数
softplus = tfp.bijectors.Softplus()
normal = tfp.distributions.Normal(loc=0., scale=1.)

# 构造一个正数分布
positive_normal = tfp.distributions.TransformedDistribution(
    distribution=normal,
    bijector=softplus
)

print("采样(都是正数):", positive_normal.sample(5))

为什么会用到这个?举个例子:你要建模一个标准差参数,它必须是正数。直接用正态分布采样可能得到负数,但用 Softplus 变换后,所有采样值都是正的。嗯,这就是 bijector 的妙处。

2.2.3 tfp.mcmc:贝叶斯推断的引擎

MCMC 是贝叶斯推断的核心引擎。TFP 提供了多种采样器,我用的最多的是 HamiltonianMonteCarlo(HMC)和 NoUTurnSampler(NUTS)。

# 一个简单的 MCMC 示例
target_log_prob_fn = lambda x: tfp.distributions.Normal(loc=0., scale=1.).log_prob(x)

hmc = tfp.mcmc.HamiltonianMonteCarlo(
    target_log_prob_fn=target_log_prob_fn,
    step_size=0.1,
    num_leapfrog_steps=3
)

# 采样
samples, _ = tfp.mcmc.sample_chain(
    num_results=1000,
    current_state=0.,
    kernel=hmc,
    num_burnin_steps=500
)
注意:MCMC 采样需要调参。step_size 太大容易发散,太小又跑得慢。我建议你先用 tfp.mcmc.SimpleStepSizeAdaptation 自动调整步长,等模型稳定了再手动微调。

2.2.4 tfp.vi:变分推断的捷径

当数据量很大时,MCMC 可能跑不动。这时候变分推断(VI)就派上用场了。它用优化代替采样,速度快很多,但精度会差一些。

# 变分推断示例
# 假设我们要近似一个后验分布
surrogate_posterior = tfp.distributions.Normal(
    loc=tf.Variable(0.),
    scale=tf.Variable(1.)
)

# 优化 ELBO
losses = tfp.vi.fit_surrogate_posterior(
    target_log_prob_fn=lambda x: tfp.distributions.Normal(loc=0., scale=1.).log_prob(x),
    surrogate_posterior=surrogate_posterior,
    optimizer=tf.optimizers.Adam(learning_rate=0.1),
    num_steps=1000
)
我的经验:在项目中,我通常先用 VI 快速探索模型空间,找到合理的参数范围后,再用 MCMC 做精细采样。这样既快又准,两全其美。

2.3 张量与分布的基本操作

TFP 的分布是建立在 TensorFlow 张量之上的。这意味着你可以利用 TensorFlow 的自动微分、GPU 加速和批处理能力。

2.3.1 批处理:一次处理多个分布

# 创建一批正态分布
batch_normal = tfp.distributions.Normal(
    loc=[0., 1., 2.],  # 三个不同的均值
    scale=[1., 0.5, 2.]  # 三个不同的标准差
)

# 采样:返回形状为 (3,) 的张量
samples = batch_normal.sample()
print("批采样:", samples)

# 计算每个分布的概率
probs = batch_normal.prob([0.5, 1.5, 2.5])
print("概率值:", probs)

2.3.2 事件形状与批形状

这是初学者最容易搞混的概念。我简单解释一下:

  • 批形状(Batch Shape):独立分布的个数。比如上面例子中的 3 个分布。
  • 事件形状(Event Shape):每个分布采样的维度。比如多元正态分布的维度。
# 多元正态分布
mvn = tfp.distributions.MultivariateNormalDiag(
    loc=[0., 0.],  # 2维均值
    scale_diag=[1., 1.]  # 2维标准差
)

print("批形状:", mvn.batch_shape)  # 空,因为没有批处理
print("事件形状:", mvn.event_shape)  # [2],因为是2维分布
记住这个原则:批形状决定「有多少个分布」,事件形状决定「每个分布采样出来是什么形状」。搞清楚了这两个概念,你就能灵活地处理各种复杂的模型结构。

2.3.3 可微性:贝叶斯编程的基石

TFP 的分布操作都是可微的。这意味着你可以对 log_prob 求梯度,然后用梯度下降优化参数。这是变分推断和 HMC 的基础。

# 演示可微性
x = tf.Variable(0.5)
with tf.GradientTape() as tape:
    dist = tfp.distributions.Normal(loc=0., scale=1.)
    log_prob = dist.log_prob(x)

grad = tape.gradient(log_prob, x)
print("梯度:", grad)  # 输出 -0.5,因为正态分布的 log_prob 对 x 的导数是 -x

嗯,到这里,TFP 的基础你已经掌握了。下一章我们会用这些工具搭建第一个完整的贝叶斯模型。记住,工具是死的,但组合方式是活的。多动手试试,你会发现 TFP 的强大之处。