贝叶斯神经网络----从贝叶斯准则到变分推断

本文介绍了贝叶斯神经网络的基本概念,包括极大似然估计、最大后验概率估计以及变分推断。在神经网络中,极大似然估计寻求使样本数据概率最大的权重,而最大后验概率则考虑了权重的先验分布。贝叶斯神经网络则将权重视为随机变量,采用变分推断近似后验概率分布。通过变分推断,文章展示了如何构建和训练一个贝叶斯神经网络模型,并给出了一个具体的PyTorch实现示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前言

在认识贝叶斯神经网络之前,建议先复习联合概率,条件概率,边缘概率,极大似然估计,最大后验估计,贝叶斯估计这些基础

极大似然估计

一个神经网络模型可以视为一个条件分布模型p(y∣x,w)p(y|x,w)p(yx,w),即在x,wx,wx,w已知的条件下求出yyy的分布,如果是分类问题,该分布对应分到各类的概率,如果是回归问题,则认为是高斯分布并取均值作为预测结果,相应地,神经网络的学习可以视作一个最大似然估计(Maximum Likelihood Estimation,MLE)
wMLE=argmaxwlog⁡p(D∣w)=argmaxw∑ilog⁡p(yi∣xi,w) w^{MLE}=\mathop{argmax}\limits_w \log p(D|w) \\ =\mathop{argmax}\limits_w \sum_i\log p(y_i|x_i,w) wMLE=wargmaxlogp(Dw)=wargmaxilogp(yixi,w)
也就是说,我们要寻找一组这样的www,能够在样本集的所有数据上预测到真实值的概率最大。上式中DDD对应训练所使用的数据集,回归问题中带入高斯分布可以得到平均平方误差,分类问题则带入逻辑函数可以推导出交叉熵,这一点会单独分一节来讨论。

最大后验概率

极大似然估计和最大后验概率估计的不同在于,在后验概率中,我们认为www取值的机会不是均等的,而极大似然则单一认为www取每个值的概率相同,其实最大后验概率更符合日常的模型,因为高斯模型这种代表“大部分人都是普通人的思想”几乎可见于数学的各个应用领域。
我们假设www事先服从某种分布,一般为高斯分布,即www在某个区间的取值概率是最大的,而不是可能取所有的值,这样一来,损失函数改写为:
wMAP=argmaxwlog⁡p(D∣w)p(w)=argmaxwlog⁡p(D∣w)+log⁡p(w) w^{MAP}=\mathop{argmax}\limits_w \log p(D|w)p(w)\\ =\mathop{argmax}\limits_w \log p(D|w)+ \log p(w) wMAP=wargmaxlogp(Dw)p(w)=wargmaxlogp(Dw)+logp(w)
最大后验概率准则将www的先验分布考虑进去,寻找这样的www能够获得上式的最小值。

贝叶斯神经网络

传统的神经网络认为每一层的权重是一个“固定”的值,这个固定不是不变,而是在每次前向传播时,权重都只是一个值,而不是一种分布;而贝叶斯神经网络则认为每一个参数都是服从某种分布的,整个过程是建立在分布的基础上进行前向,反向传播的计算,网络的实际参数是根据参数的分布采样得到,我们需要更新的值实际上是参数所对应的分布。
贝叶斯要做的不是确定www,而是求解能否根据观测数据,推测模型参数服从什么分布?即如何确定p(w∣D)p(w|D)p(wD)
举个形象一点的例子,如果DDD是我们养的一批猪,现在我们想根据这批猪的生长状况推测猪平时过得怎么样,如果猪看起来无精打采的,可能生活环境不太好,比如居住环境过于潮湿,饲料供应不新鲜,如果猪活蹦乱跳的,大概率住的生活环境也比较优渥,这里的生活环境就可以认为是参数www。我们通过数据DDD来推测www的过程就是“inference
根据贝叶斯准则,我们有:
p(w∣D)=p(D,w)p(D)=p(D∣w)p(w)p(D)posterior=likelihood∗priorevidence p(w|D)=\frac{p(D,w)}{p(D)}=\frac{p(D|w)p(w)}{p(D)}\\ posterior=\frac{likelihood*prior}{evidence} p(wD)=p(D)p(D,w)=p(D)p(Dw)p(w)posterior=evidencelikelihoodprior
上式里面p(D∣w)p(D|w)p(Dw)是可计算的,在参数www确定时,可以通过∑ip(yi∣xi,w)\sum_{i}p(y_i|x_i,w)ip(yixi,w)求解,p(w)p(w)p(w)也是可计算的,因为我们事先假设其服从某种分布。
但是难解的地方在于p(D)p(D)p(D),因为p(D)p(D)p(D)的计算需要知道所有潜在的www所对应的p(D,w)p(D,w)p(D,w),即:
p(D)=∫w0...∫wN−1p(D,w)dw0...dwN−1 p(D)=\int_{w_0}...\int_{w_{N-1}}p(D,w)dw_0...dw_{N-1} p(D)=w0...wN1p(D,w)dw0...dwN1
其中NNN为参数www的维度。这个问题难解是因为我们需要对多个可能的模型计算联合概率,在高维情况下这是很难实现的,由此引入变分推断来解决这个问题。

变分推断(Variational Inference)

既然我们通过解析的方式不可能得到后验概率p(w∣D)p(w|D)p(wD)的分布,我们能否寻找一个替代品来取代p(w∣D)p(w|D)p(wD)
在这里插入图片描述
为什么可以这样考虑?
我们实际上的分布p(w∣D)p(w|D)p(wD)可能是一个很复杂的曲线,他可能是不对称的,多峰的,但是在某些特征上,比如最值点,我们仍然可以使用高斯分布来近似他,因为最值点代表了我们实际上最为关注的信息,所以现在退而求其次,我们要找的不是p(w∣D)p(w|D)p(wD),而是寻找什么样的高斯分布能够最接近这个分布
现在的问题变成:
我们在所有可能的高斯分布中找出一个分布q(w),q(w)∈Qq(w), q(w) \in Qq(w),q(w)Q,使得这个分布能够最大程度上拟合后验分布p(w∣D)p(w|D)p(wD),常用来衡量两个分布相似性的评价指标为KL散度
KL(q(w)∣∣p(w∣D))=Ew∈q(w)[log⁡q(w)p(w∣D)]=∫wq(w)log⁡q(w)p(w∣D)dw KL(q(w)||p(w|D))=E_{w\in q(w)}[\log \frac{q(w)}{p(w|D)}]\\ =\int_w q(w)\log \frac{q(w)}{p(w|D)}dw KL(q(w)p(wD))=Ewq(w)[logp(wD)q(w)]=wq(w)logp(wD)q(w)dw
我们要找的是:
q∗(w)=arg⁡minq(w)∈Q(KL(q(w)∣∣p(w∣D))) q^*(w)=\mathop{\arg min} \limits_{q(w)\in Q}(KL(q(w)||p(w|D))) q(w)=q(w)Qargmin(KL(q(w)p(wD)))
为什么称上面的问题为变分(variational)问题?
从可能的函数集合中寻找一个满足条件的函数,这称为“变分”,所以我们现在想要寻找一个函数,以便能够进行推理,所以称为变分推断(Variational Inference)
接着来看上面公式的求解,由于我们不知道后验概率,所以将原始的后验概率用贝叶斯准则进行替换:
KL(q(w)∣∣p(w∣D))=∫wq(w)log⁡q(w)p(w∣D)dw=∫wq(w)log⁡q(w)⋅p(D)p(w,D)dw=∫wq(w)log⁡q(w)p(w,D)dw+∫wq(w)log⁡p(D)dw=Ew∈q(w)log⁡[q(w)p(w,D)]+Ew∈q(w)log⁡p(D)=−Ew∈q(w)log⁡[p(w,D)q(w)]+log⁡p(D)=−Ew∈q(w)log⁡[p(w,D)]+Ew∈q(w)log⁡[q(w)]+log⁡p(D) KL(q(w)||p(w|D))\\ =\int_w q(w)\log \frac{q(w)}{p(w|D)}dw\\ =\int_w q(w)\log \frac{q(w) \cdot p(D)}{p(w,D)}dw \\ =\int_w q(w)\log \frac{q(w)}{p(w,D)}dw + \int_w q(w)\log p(D)dw \\ =E_{w\in q(w)}\log [\frac{q(w)}{p(w,D)}]+E_{w\in q(w)}\log p(D)\\ =-E_{w\in q(w)}\log [\frac{p(w,D)}{q(w)}] + \log p(D)\\ =-E_{w\in q(w)}\log [p(w,D)] + E_{w\in q(w)}\log [q(w)] + \log p(D) KL(q(w)p(wD))=wq(w)logp(wD)q(w)dw=wq(w)logp(w,D)q(w)p(D)dw=wq(w)logp(w,D)q(w)dw+wq(w)logp(D)dw=Ewq(w)log[p(w,D)q(w)]+Ewq(w)logp(D)=Ewq(w)log[q(w)p(w,D)]+logp(D)=Ewq(w)log[p(w,D)]+Ewq(w)log[q(w)]+logp(D)
上式中最后一项log⁡p(D)\log p(D)logp(D)是一个固定值(p(D)p(D)p(D)是通过观测得到的,他不随参数的改变而改变,他是所有可能参数的情况的期望),同时KL散度≥0,因此前面的需要满足:Ew∈q(w)log⁡[p(w,D)]−Ew∈q(w)log⁡[q(w)]≤log⁡P(D)E_{w\in q(w)}\log [p(w,D)] - E_{w\in q(w)}\log [q(w)] \leq \log P(D)Ewq(w)log[p(w,D)]Ewq(w)log[q(w)]logP(D)
当等号满足的时候,KL散度为零,但是实际上很难成立,所以退而求其次,我们需要让前面的这一项越大越好,反过来说,我们希望−Ew∈q(w)log⁡[p(w,D)]+Ew∈q(w)log⁡[q(w)]-E_{w\in q(w)}\log [p(w,D)] + E_{w\in q(w)}\log [q(w)]Ewq(w)log[p(w,D)]+Ewq(w)log[q(w)]越小越好,这称之为Evidence Lower Bound (ELBO),其中evidence指的是后面的log⁡p(D)\log p(D)logp(D),且是前面那一项的下界(Lower Bound)。
我们称:
L(q)=Ew∈q(w)log⁡[p(w,D)]−Ew∈q(w)log⁡[q(w)] \mathcal{L(q)}=E_{w\in q(w)}\log [p(w,D)] - E_{w\in q(w)}\log [q(w)] L(q)=Ewq(w)log[p(w,D)]Ewq(w)log[q(w)]
我们需要最大化L(q)\mathcal{L(q)}L(q),将上式再次使用贝叶斯准则展开,可以写为:
L(q)=Ew∈q(w)log⁡[p(D∣w)]+Ew∈q(w)log⁡[p(w)]−Ew∈q(w)log⁡[q(w)] \mathcal{L(q)}=E_{w\in q(w)}\log [p(D|w)] + E_{w\in q(w)}\log [p(w)] - E_{w\in q(w)}\log [q(w)] L(q)=Ewq(w)log[p(Dw)]+Ewq(w)log[p(w)]Ewq(w)log[q(w)]
所以整个网络的求解过程就是最大化上式,并且上面三项都是可计算的。下面具体解释三项分别怎么计算:

  • Ew∈q(w)log⁡[p(D∣w)]E_{w\in q(w)}\log [p(D|w)]Ewq(w)log[p(Dw)]:
    在模型参数www确定的条件下,计算p(D∣w)p(D|w)p(Dw)的分布
  • Ew∈q(w)log⁡[p(w)]E_{w\in q(w)}\log [p(w)]Ewq(w)log[p(w)]
    计算www的先验p(w)p(w)p(w)
  • Ew∈q(w)log⁡[q(w)]E_{w\in q(w)}\log [q(w)]Ewq(w)log[q(w)]
    计算当前的前向传播过程下,假设的q(w)q(w)q(w)的分布,我们前面讲过,网络实际是优化的是什么样的μ\muμδ\deltaδ形成的高斯分布q(w)q(w)q(w)

贝叶斯神经网络编程实现

import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
from torch.distributions import Normal
import numpy as np
from scipy.stats import norm
import matplotlib.pyplot as plt


class Linear_BBB(nn.Module):
    """
        Layer of our BNN.
    """
    def __init__(self, input_features, output_features, prior_var=1.):
        """
            先验分布是以均值为0,方差为1的高斯分布
            Initialization of our layer : our prior is a normal distribution
            centered in 0 and of variance 1.
        """
        # initialize layers
        super().__init__()
        # set input and output dimensions
        # 输入和输出的维度
        self.input_features = input_features
        self.output_features = output_features

        # initialize mu and rho parameters for the weights of the layer
        # 初始化该层的权重和偏置  ====》y = w * x + b
        # 该层的每一个权重和偏置都有自己的方差和均值
        self.w_mu = nn.Parameter(torch.zeros(output_features, input_features))
        self.w_rho = nn.Parameter(torch.zeros(output_features, input_features))

        # initialize mu and rho parameters for the layer's bias
        # 网络的参数是权重和偏置的期望与方差
        # 实际的参数,即参与计算的参数是从这个分布里面采样的
        self.b_mu = nn.Parameter(torch.zeros(output_features))
        self.b_rho = nn.Parameter(torch.zeros(output_features))

        # initialize weight samples (these will be calculated whenever the layer makes a prediction)
        self.w = None
        self.b = None

        # initialize prior distribution for all of the weights and biases
        # 假设该层的所有权重和偏置均为正态分布
        self.prior = torch.distributions.Normal(0, prior_var)

    def forward(self, input):
        """
          Optimization process
        """
        # sample weights
        """
            从均值为0,方差为1的高斯分布中采样一些样本点 u + log(1 + exp(p)) * w'
            一种重参数技巧,最早用于VAE中
            对于原本服从 N~(u , p)的随机变量w,先不直接根据这个分布采样,而是先根据标准正态分布采样 w_epsilon
            随后根据 w = u + log(1 + exp(p)) * w' 得到实际的采样值,这样做的目的是为了便于反向传播
        """

        w_epsilon = Normal(0, 1).sample(self.w_mu.shape)
        self.w = self.w_mu + torch.log(1 + torch.exp(self.w_rho)) * w_epsilon

        # sample bias
        b_epsilon = Normal(0, 1).sample(self.b_mu.shape)
        self.b = self.b_mu + torch.log(1 + torch.exp(self.b_rho)) * b_epsilon

        # record log prior by evaluating log pdf of prior at sampled weight and bias
        """
            对已经采样的值,计算其在预先定义的分布上的对数形式得到的值(log_prob(value)是计算value在定义的正态分布(mean,1)中对应的概率的对数)
            损失函数是要最大化elbo下界:L = sum[log(q(w))]- sum(log P(w)) - sum(log P(y_i | w, x_i))
        """
        # 计算 p(w)
        w_log_prior = self.prior.log_prob(self.w)
        b_log_prior = self.prior.log_prob(self.b)
        self.log_prior = torch.sum(w_log_prior) + torch.sum(b_log_prior)

        # record log variational posterior by evaluating log pdf of normal distribution defined by parameters with respect at the sampled values
        # 计算 q(w),也有称其为 p(w|theta)的
        # q(w) 表示根据当前的网络参数 w_mu,w_rho,b_mu,b_rho 计算q(w),也就是说q(w)就是我们损失函数要求解的分布
        self.w_post = Normal(self.w_mu.data, torch.log(1 + torch.exp(self.w_rho)))
        self.b_post = Normal(self.b_mu.data, torch.log(1 + torch.exp(self.b_rho)))
        self.log_post = self.w_post.log_prob(self.w).sum() + self.b_post.log_prob(self.b).sum()

        return F.linear(input, self.w, self.b)


class MLP_BBB(nn.Module):
    def __init__(self, hidden_units, noise_tol=.1,  prior_var=1.):

        # initialize the network like you would with a standard multilayer perceptron, but using the BBB layer
        super().__init__()
        self.hidden = Linear_BBB(1, hidden_units, prior_var=prior_var)
        self.out = Linear_BBB(hidden_units, 1, prior_var=prior_var)
        self.noise_tol = noise_tol # we will use the noise tolerance to calculate our likelihood

    def forward(self, x):
        # again, this is equivalent to a standard multilayer perceptron
        x = torch.sigmoid(self.hidden(x))
        x = self.out(x)
        return x

    def log_prior(self):
        # calculate the log prior over all the layers
        return self.hidden.log_prior + self.out.log_prior

    def log_post(self):
        # calculate the log posterior over all the layers
        return self.hidden.log_post + self.out.log_post

    # 损失函数的计算
    def sample_elbo(self, input, target, samples):
        # we calculate the negative elbo, which will be our loss function
        #initialize tensors
        outputs = torch.zeros(samples, target.shape[0])
        log_priors = torch.zeros(samples)
        log_posts = torch.zeros(samples)
        log_likes = torch.zeros(samples)
        # make predictions and calculate prior, posterior, and likelihood for a given number of samples
        # 蒙特卡洛近似,根据给定的样本数采样
        # 蒙特卡洛在这里用来计算前向传播的次数,所以蒙特卡洛可能与模型权重的不确定性有关
        for i in range(samples):
            outputs[i] = self(input).reshape(-1)  # make predictions
            log_priors[i] = self.log_prior() # get log prior
            log_posts[i] = self.log_post() # get log variational posterior
            log_likes[i] = Normal(outputs[i], self.noise_tol).log_prob(target.reshape(-1)).sum() # calculate the log likelihood
        # calculate monte carlo estimate of prior posterior and likelihood
        log_prior = log_priors.mean()
        log_post = log_posts.mean()
        log_like = log_likes.mean()
        # calculate the negative elbo (which is our loss function)
        loss = log_post - log_prior - log_like
        return loss



def toy_function(x):
    return -x**4 + 3*x**2 + 1

# toy dataset we can start with
x = torch.tensor([-2, -1.8, -1, 1, 1.8, 2]).reshape(-1,1)
y = toy_function(x)

net = MLP_BBB(32, prior_var=10)
optimizer = optim.Adam(net.parameters(), lr=.1)
epochs = 1000
for epoch in range(epochs):  # loop over the dataset multiple times
    optimizer.zero_grad()
    # forward + backward + optimize
    loss = net.sample_elbo(x, y, 1)
    loss.backward()
    optimizer.step()
    if epoch % 10 == 0:
        print('epoch: {}/{}'.format(epoch+1,epochs))
        print('Loss:', loss.item())
print('Finished Training')


# samples is the number of "predictions" we make for 1 x-value.
# 网络训练完成后,对于随机给定的输入值 x,计算模型的输出值 y
# samples 表示采样次数,即预测多少次,最后对这些次的结果取平均值
samples = 10
x_tmp = torch.linspace(-5, 5, 100).reshape(-1, 1)
y_samp = np.zeros((samples, 100))
print(x_tmp.shape)
for s in range(samples):
    y_tmp = net(x_tmp).detach().numpy()
    y_samp[s] = y_tmp.reshape(-1)
plt.plot(x_tmp.numpy(), np.mean(y_samp, axis=0), label='Mean Posterior Predictive')
plt.fill_between(x_tmp.numpy().reshape(-1), np.percentile(y_samp, 2.5, axis=0),
                 np.percentile(y_samp, 97.5, axis=0), alpha=0.25, label='95% Confidence')
plt.legend()
plt.scatter(x, toy_function(x))
plt.title('Posterior Predictive')
plt.show()

if __name__ == '__main__':
    print("done!")
### 贝叶斯卷积神经网络概述 贝叶斯卷积神经网络(Bayesian Convolutional Neural Networks, BCNN)是一种融合了贝叶斯统计理论与深度学习技术的先进模型。这类网络能够有效地处理不确定性,提供更加稳健的学习机制。 #### 原理 BCNN的核心在于引入了概率分布的概念来描述权重参数,而不是像传统的CNN那样仅使用固定的数值表示。具体来说,在训练过程中,不是寻找一组最优解作为最终的结果,而是估计整个可能值的空间及其对应的置信度。这种做法允许模型表达对于未知情况下的不确定程度,并有助于防止过拟合现象的发生[^2]。 #### 实现方式 构建BCNN的一个重要手段是采用变分推断的方法来进行近似计算。由于直接求解真实的后验分布往往非常困难甚至不可能完成的任务,因此转而寻求一种易于操作的概率密度函数去逼近它。在这个框架下,可以定义一个简单的先验分布给定初始假设条件;接着通过最小化Kullback-Leibler散度或其他相似准则调整这些设定直到找到最佳匹配为止。此过程可以在诸如MindSpore这样的平台上得以实现,该平台提供了丰富的API支持用于创建复杂的贝叶斯结构[^1]。 另外值得注意的是,除了上述提到的技术细节外,还有其他因素也会影响实际开发工作,比如编程环境的选择。例如存在一些专门针对特定语言设计并实现了高效算法的数据科学库可以帮助加速研发进度,如PyTorch-BayesianCNN就是一个很好的例子,该项目不仅深入挖掘了贝叶斯原则与深度学习之间的联系,同时也展示了如何利用Python生态系统的强大功能快速搭建原型系统。 #### 应用场景 在实践中,BCNN被广泛应用于各种需要考虑不确定性的场合: - **医疗诊断**:当面对有限样本量或者噪声干扰较大的医学影像资料时,BCNN能更好地捕捉潜在模式而不至于过分依赖于某些特征; - **自动驾驶汽车感知模块**:车辆行驶环境中存在着许多不可预见的变化源,这就要求传感器解读具备足够的灵活性以应对突发状况; - **金融风险评估**:金融市场本身具有高度波动性特点,运用BCNN可帮助投资者更精准地预估资产价值变动趋势的同时量化投资组合的风险水平。 综上所述,随着硬件设施的进步以及软件工具链的日臻完善,相信未来会有更多创新成果涌现出来推动这一领域向前发展[^5]。 ```python import torch from pytorch_bayesian.nn import BayesianLinear model = torch.nn.Sequential( BayesianLinear(input_dim=784, output_dim=256), torch.nn.ReLU(), BayesianLinear(input_dim=256, output_dim=10) ) print(model) ```
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值