贝叶斯神经网络:入门
什么是贝叶斯推断?理解先验、似然和后验
贝叶斯推断是一种统计方法,它涉及随着更多证据或信息的获取而更新假设的概率。这个过程使用贝叶斯定理,这是概率理论中的一个基本原则,它将随机事件的条件概率和边际概率联系起来。
贝叶斯推断使我们能够纳入先验信念,在观察到新数据时更新这些信念,并得出一个反映先验和新数据的后验信念。让我们分解贝叶斯推断的关键组件:先验、似然和后验。
先验、似然和后验:贝叶斯推断的基础构件
1. 先验 p(theta)\ 先验代表我们在看到任何数据之前对参数(theta)的初始信念。它是一个概率分布,概括了我们现有的知识或假设。例如,如果我们估计硬币落地为正面的概率,我们可能假设一个均匀的先验(p(theta) = 0.5),表示公正的硬币。
2. 似然 p(D \theta)\ 似然量化了在给定特定参数值(theta)的情况下观察到数据(D)的概率。它衡量了特定参数解释观察到的数据的程度。在我们的硬币示例中,如果我们在10次掷硬币中观察到7次正面,给定硬币是公平的(theta = 0.5),可以使用二项分布计算数据的似然。
3. 后验 p(theta \D)\ 后验表示在观察到数据 (D) 后关于参数 (theta) 的更新信念。它使用贝叶斯定理结合了先验和似然:\ p(theta\D) = p(D \theta) X p(theta)/p(D)
这里,( p(D)) 是证据,一个归一化项,确保后验分布的总和为 1。后验反映了初始信念和新证据,提供了参数的精确估计。
贝叶斯推断示例:抛硬币
想象一下我们想要估计硬币正面朝上的概率(theta)。我们从对硬币是公正的先验信念开始,因此我们分配 p(theta) = {Uniform}(0, 1)。经过 10 次掷硬币,我们观察到 7 次正面和 3 次反面。
- 先验: p(theta) = {均匀}(0, 1) — 我们假设0到1之间的所有概率是同等可能的。\ - 似然: p(D \theta) = {二项分布}(7次正面, 10次翻转, theta)\ - 后验: 在观察到数据后,后验将我们的先验信念与似然结合,以更新我们对(theta)的信念。
后验分布反映了我们对硬币正面朝上的概率的更新信念,这个信念是基于观察到的数据。
贝叶斯神经网络(BNNs)是什么?
贝叶斯神经网络(BNNs)将贝叶斯推断的概念扩展到神经网络中。在传统的神经网络中,权重被视为在训练过程中学习到的固定参数。然而,在BNNs中,权重被视为具有概率分布的随机变量。这种方法使BNNs能够通过考虑权重的不确定性来对预测中的不确定性建模。
贝叶斯神经网络中的后验
在BNN中,目标是计算给定训练数据的权重后验分布。我们定义:
- 权重 ( W ): 神经网络的参数。
- 数据 ( D ) : 训练数据集,由输入-输出对组成。
给定数据的权重的后验分布,p(W \D),使用贝叶斯定理计算:
p(W \D) = p(D \W) X (p(W)/p(D))
- 先验 ( p(W) ): 在观察任何数据之前对权重的初始信念。
- 似然 ( p(D \W): 给定权重的数据的概率。
- 后验 (p(W \ D) ): 在观察到数据后对权重的更新信念。
使用MNIST展示贝叶斯神经网络
为了理解BNN在实际中的工作原理,我们将使用MNIST数据集,这是一个流行的数据集,包含28x28像素的手写数字(0-9)的灰度图像。我们的目标是使用贝叶斯卷积神经网络(CNN)对这些数字进行分类。
为什么在MNIST中使用贝叶斯神经网络?
1. 不确定性估计:BNNs 提供了对其预测的不确定性度量,这对于安全关键应用至关重要。\ 2. 抗过拟合能力:贝叶斯框架有助于对模型进行正则化,使其在数据稀缺时不易过拟合。\ 3. 更好的泛化能力:通过考虑权重的不确定性,BNNs 可以更好地泛化到未见过的数据。
代码解释:在MNIST上使用贝叶斯神经网络
我们将使用PyTorch构建一个贝叶斯神经网络,着重于实现BNN于MNIST的关键步骤。
1. 导入库和设置
我们首先导入必要的库并设置超参数。
import torch
import torch.nn as nn
import torch.nn.functional as F
from torch.utils.data import DataLoader
from torchvision import datasets, transforms
import numpy as np
import matplotlib.pyplot as plt
# 超参数
batch_size = 64
learning_rate = 0.001
epochs = 20
n_samples = 10 # 用于不确定性估计的后验样本数量
# 检查GPU
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
print(f'Using device: {device}')
2. 加载 MNIST 数据集
我们加载MNIST数据集并应用标准的预处理转换,例如规范化图像。
# 数据加载与转换
transform = transforms.Compose([transforms.ToTensor(), transforms.Normalize((0.5,), (0.5,))])
train_dataset = datasets.MNIST(root='./data', train=True, transform=transform, download=True)
test_dataset = datasets.MNIST(root='./data', train=False, transform=transform, download=True)
train_loader = DataLoader(dataset=train_dataset, batch_size=batch_size, shuffle=True)
test_loader = DataLoader(dataset=test_dataset, batch_size=batch_size, shuffle=False)
3. 定义贝叶斯卷积层
我们定义了一个自定义的贝叶斯卷积层(BayesianConv2d),它在每次前向传递中从高斯分布中采样权重。
class BayesianConv2d(nn.Module):
def __init__(self, in_channels, out_channels, kernel_size, stride=1, padding=0):
super(BayesianConv2d, self).__init__()
self.in_channels = in_channels
self.out_channels = out_channels
self.kernel_size = kernel_size
self.stride = stride
self.padding = padding
# 权重参数
self.weight_mu = nn.Parameter(torch.Tensor(out_channels, in_channels, kernel_size, kernel_size).uniform_(-0.1, 0.1))
self.weight_logvar = nn.Parameter(torch.Tensor(out_channels, in_channels, kernel_size, kernel_size).uniform_(-3, -1))
# 偏置参数
self.bias_mu = nn.Parameter(torch.Tensor(out_channels).uniform_(-0.1, 0.1))
self.bias_logvar = nn.Parameter(torch.Tensor(out_channels).uniform_(-3, -1))
def forward(self, x):
# 从正态分布中采样权重和偏置
weight = self.sample_weight()
bias = self.sample_bias()
# 卷积操作
return F.conv2d(x, weight, bias, stride=self.stride, padding=self.padding)
def sample_weight(self):
# 从高斯后验中采样权重
return self.weight_mu + torch.exp(0.5 * self.weight_logvar) * torch.randn_like(self.weight_logvar).to(device)
def sample_bias(self):
# 从高斯后验中采样偏置
return self.bias_mu + torch.exp(0.5 * self.bias_logvar) * torch.randn_like(self.bias_logvar).to(device)
def kl_divergence(self):
# 计算变分权重的KL散度
kl = 0.5 * torch.sum(
self.weight_mu**2 + torch.exp(self.weight_logvar) - 1 - self.weight_logvar
) + 0.5 * torch.sum(
self.bias_mu**2 + torch.exp(self.bias_logvar) - 1 - self.bias_logvar
)
return kl
4. 定义具有CNN架构的贝叶斯神经网络(BNN)
我们使用利用贝叶斯推断的卷积层定义了一个BNN。
class BayesianCNN(nn.Module):
def __init__(self):
super(BayesianCNN, self).__init__()
self.conv1 = BayesianConv2d(1, 32, kernel_size=3, stride=1, padding=1)
self.conv2 = BayesianConv2d(32, 64, kernel_size=3, stride=1, padding=1)
self.fc1 = nn.Linear(64 * 7 * 7, 128)
self.fc2 = nn.Linear(128, 10)
def forward(self, x):
x = F.relu(self.conv1(x))
x = F.max_pool2d(x, 2)
x = F.relu(self.conv2(x))
x = F.max_pool2d(x, 2)
x = x.view(-1, 64 * 7 * 7) # 展平为全连接层
x = F.relu(self.fc1(x))
x = self.fc2(x)
return x
def kl_divergence(self):
# 计算所有贝叶斯层的KL散度总和
kl = self.conv1.kl_divergence() + self.conv2.kl_divergence()
return kl
5. 训练贝叶斯神经网络
训练函数使用证据下界(ELBO)损失训练BNN,该损失结合了负对数似然和KL散度。
def elbo_loss(output, target, kl_divergence, beta=1.0):
# 交叉熵损失(负对数似然)
nll = F.cross_entropy(output, target, reduction='mean')
# ELBO损失
elbo = nll + beta * kl_divergence / len(train_loader.dataset)
return elbo
model = BayesianCNN().to(device)
optimizer = torch.optim.Adam(model.parameters(), lr=learning_rate)
def train(model, train_loader, optimizer, epochs):
model.train()
for epoch in range(epochs):
total_loss = 0
for batch_idx, (data, target) in enumerate(train_loader):
data, target = data.to(device), target.to(device)
optimizer.zero_grad()
output = model(data)
kl_divergence = model.kl_divergence()
loss = elbo_loss(output, target, kl_divergence)
loss.backward()
optimizer.step()
total_loss += loss.item()
print(f"Epoch {epoch+1}/{epochs}, Loss: {total_loss / len(train_loader)}")
# 训练模型
train(model, train_loader, optimizer, epochs)
6. 不确定性估计的推理
最后,我们通过多次采样权重来进行推理,并估计预测的均值和不确定性。
def predict_with_sampling(model, data, n_samples):
model.eval()
output_samples = []
with torch.no_grad():
for _ in range(n_samples):
output_samples.append(model(data)) # 在每次前向传递过程中采样权重
output_samples = torch.stack(output_samples)
mean_output = torch.mean(output_samples, dim=0)
std_output = torch.std(output_samples, dim=0)
return mean_output, std_output
# 示例:获取一批的预测和置信区间
examples = enumerate(test_loader)
batch_idx, (example_data, example_targets) = next(examples)
example_data = example_data.to(device)
# 使用采样进行推理
mean_output, std_output = predict_with_sampling(model, example_data, n_samples)
# 显示一些图像及其预测置信度
plt.figure(figsize=(10, 5))
for i in range(6):
plt.subplot(2, 3, i+1)
plt.imshow(example_data[i].cpu().numpy().squeeze(), cmap='gray')
pred_prob = torch.softmax(mean_output[i], dim=0) # 对均值输出应用softmax
pred_class = torch.argmax(pred_prob).item() # 选择概率最高的类别
plt.title(f'Pred: {pred_class}, Conf: {pred_prob.max():.2f}')
plt.axis('off')
plt.show()
Conclusion
贝叶斯神经网络(BNNs)提供了一种通过将权重视为随机变量来估计神经网络预测中的不确定性的原则方式。通过贝叶斯推断,BNNs不仅可以提供点估计,还可以提供置信区间,使其在不确定性估计至关重要的应用中变得非常有价值。使用MNIST数据集,我们展示了如何使用PyTorch构建和训练BNN,展示了贝叶斯方法在深度学习中的强大能力。
通过理解和利用贝叶斯技术,我们可以创造出更稳健和可解释的机器学习模型,使其更好地应对现实世界场景中的不确定性。