【Python深度学习系列】基于贝叶斯神经网络模型实现图像分类(案例+源码)

这是我的第390篇原创文章。

一、引言

        贝叶斯神经网络的结构与传统神经网络相似,但将权重参数视为随机变量,并赋予先验分布。例如,对于一个简单的用于股票价格预测的多层感知机神经网络,其输出(预测的股票价格)与输入(如历史价格、成交量、宏观经济指标等特征)的关系可以表示为:,其中 是神经网络的前向传播函数, 是权重参数向量,服从某种先验分布,如高斯分布。先验分布的选择依据对股票市场的先验知识或假设,例如认为权重不应过大,以避免过拟合或不稳定的预测。

        在贝叶斯神经网络中,目标是确定神经网络参数的后验分布,而不是像传统神经网络那样确定固定的参数值。在贝叶斯神经网络中,预测不再是一个确定的值,而是一个概率分布。本文实现了训练一个基于贝叶斯神经网络(Bayesian Neural Network,BNN)的小型多层感知机(MLP),并在 MNIST 数据集上进行分类任务。通过使用贝叶斯推断和变分推断方法,模型会进行多次采样,以估计预测的分布,从而为每个样本计算出预测的均值和方差。

二、实现过程

2.1 数据准备

代码:

full_ds = MNIST(data_dir, train=True, download=True, transform=Compose([ToTensor(), Normalize((0.1307,), (0.3081,))]))
train_ds, val_ds = random_split(full_ds, [55000, 5000])
test_ds = MNIST(data_dir, train=False, download=True,transform=Compose([ToTensor(), Normalize((0.1307,), (0.3081,))]))

train_dl = DataLoader(train_ds, batch_size=batch_size, shuffle=True)
val_dl = DataLoader(val_ds, batch_size=batch_size, shuffle=False)
test_dl = DataLoader(test_ds, batch_size=batch_size, shuffle=False)

in_dim = train_ds[0][0].numel() # 输入维度
out_dim = 10                    # 输入维度
batch_num=len(train_dl)         # 小批量总数

下载 MNIST 数据集,训练集和验证集的变换操作包括转换为 Tensor 格式,并进行归一化处理。

random_split将完整的训练数据 full_ds 分为 55000 张训练图片和 5000 张验证图片。

下载测试集,并创建训练集、验证集和测试集的 DataLoaderDataLoader 用于将数据分批次传递给模型。

in_dim获取单个输入图像的维度,out_dim是输出的类别数(MNIST 共有10个数字类别)。

batch_num 计算一个 epoch 中的批次总数。

2.2模型定义

代码:

# 模型
model = BayesMLP(in_dim, out_dim, hidden_dims=[128, 64]).to(device)
# 优化器
opt = Adam(model.parameters(), lr=0.001)
# 损失
loss_fn = ClassificationELBOLoss(batch_num=batch_num)

BayesMLP 是一个自定义的贝叶斯多层感知机模型,具有输入维度 in_dim,输出类别数 out_dim,隐藏层大小 [128, 64]。这个模型使用贝叶斯推断进行多次采样。

使用 Adam 优化器来优化模型参数,学习率为 0.001。

ClassificationELBOLoss 是一个自定义损失函数,可能是用于变分推断的损失函数(ELBO:Evidence Lower Bound)。它在每次迭代时根据变分推断的损失进行优化。

2.3 模型训练

代码:

epochs = 3
for epoch in range(epochs):                                                         # 算法2:第2行    
    for batch, (batch_x, batch_y) in enumerate(train_dl):        
        opt.zero_grad()        
        model_out = model(batch_x, sample_num)        
        loss = loss_fn(model_out, batch_y)        
        loss.backward()                                                             # 算法2:第9行        
        opt.step()                                                                  # 算法2:第12行        
        if batch % 100 == 0:            
            acc = evaluate(model, val_dl, sample_num)            
            print(f'epoch: {epoch+1:>2}/{epochs:<2} batch: {batch+1:>4}/{batch_num:<4} Loss: {loss.item():.8}\tValid Acc: {acc: .6}')            
            model.train()

训练循环:训练 3 个 epoch。

batch_x是当前批次的输入数据,batch_y 是标签。

model(batch_x, sample_num)表示使用贝叶斯神经网络模型对输入数据进行预测,并进行 sample_num 次采样

loss_fn(model_out, batch_y)计算模型输出和标签之间的损失。

通过 loss.backward() 和 opt.step() 进行反向传播和参数更新。

2.4 测试可视化

代码:

test_acc = evaluate(model, test_dl, sample_num)
print(f"Test acc: {test_acc:.6}")
# 从测试集中随机选择一个数据
img = random.choice(test_ds)imgs = rotate(img[0], angle=30)      # 旋转图像
model.eval()preds = model(imgs, sample_num=50)                    # 多次采样
preds = torch.stack(preds, dim=-1).softmax(dim=1)
mean_preds = preds.mean(dim=-1).argmax(dim=1)           # 预测的均值
preds_var = preds.var(dim=-1).mean(dim=1)               # 预测的方差
infos = [f'predict: {p.item()}  var: {v.item():.3}' for p, v in zip(mean_preds, preds_var)]grid_show_imgs(imgs, infos=infos)

测试和可视化部分通过旋转图像并多次采样,展示了模型对未知样本的预测分布。

从测试集随机选择一个样本 img。将该图像旋转 30 度。使用模型对旋转后的图像进行预测,进行 50 次采样。计算每个预测的均值(mean_preds)和方差(preds_var)。

构建一个字符串列表 infos,记录每个预测的类别和对应的方差。使用 grid_show_imgs 函数可视化图像和预测信息。

结果:

图片

作者简介:

读研期间发表6篇SCI数据挖掘相关论文,现在某研究院从事数据算法相关科研工作,结合自身科研实践经历不定期分享关于Python、机器学习、深度学习、人工智能系列基础知识与应用案例。致力于只做原创,以最简单的方式理解和学习,关注我一起交流成长。需要数据集和源码的小伙伴可以关注底部公众号添加作者微信。