【深度学习】实验二 玻尔兹曼机BM

实验二  玻尔兹曼机BM

一、实验学时: 2学时

二、实验目的

  1. 深入理解玻尔兹曼机的基本原理和结构;
  2. 掌握玻尔兹曼机的实现方法,包括模型构建、参数初始化、训练算法的实现等;
  3. 能够运用玻尔兹曼机对给定的数据进行处理和分析,通过实验结果评估模型性能,从而考察对玻尔兹曼机应用的掌握程度;

三、实验环境

(一)硬件环境:

  1. PC机一台;
  2. 内存要求:不少于2G;
  3. 磁盘空间要求 :不少于50G剩余磁盘空间;

(二)软件环境:

  1. 操作系统要求:Windows(32位/64位)7/10/11等均可;
  2. 文档工具要求:WPS或者office均可;
  3. 编程语言要求:Python;

四、实验内容与结果

  1. 详细阐述玻尔兹曼机的定义、结构特点,包括神经元连接方式、可见层与隐藏层的作用。
  2. 考察学生对玻尔兹曼机模型结构的实现能力,包括如何定义可见层和隐藏层神经元数量、连接权重的初始化方式;
  3. 描述训练算法(如对比散度)的具体实现步骤,包括如何进行采样、计算梯度和更新权重;
  4. 使用Python实现玻尔兹曼机(BM),要求构建一个简单的玻尔兹曼机,并使用对比散度(Contrastive Divergence, CD)算法进行训练。

注意:因为BM算法训练复杂度高,所以准备一点点数据进行训练即可;激活函数使用sigmoid;

实验内容: 

  1. 玻尔兹曼机:
    (1)定义:玻尔兹曼机是一种随机性神经网络,由节点(神经元)和节点间的连接权重组成。该网络通过模拟神经元之间的随机交互来调整自身的状态,以寻找系统的低能量状态。它的最终目的是学习数据的概率分布,即能够生成新的样本,类似于训练数据的样本。
    (2)特点:

    ① 网络结构:玻尔兹曼机包含可见层(输入层)和隐藏层,层与层之间的节点是全连接的,但同层内的节点不相连。

    ② 能量函数:网络中的状态由一个能量函数定义,该函数衡量了网络中所有节点状态的“能量”。

    ③ 概率分布:网络的平衡状态服从玻尔兹曼分布,这是一种描述粒子在不同状态下的概率分布的函数。

    ④ 学习机制:玻尔兹曼机通过模拟退火算法来调整权重,从而学习数据的分布。

    ⑤ 生成模型:玻尔兹曼机是一种生成模型,能够生成新的数据样本,这使得它在无监督学习中非常有用。
     

    (3)玻尔兹曼机基本组成:

    ① 神经元:Boltzmann机中的神经元可以分为可见神经元和隐藏神经元。可见神经元用于接收输入数据和输出结果,它们与外部环境进行交互。隐藏神经元则在网络内部,用于辅助处理信息和捕捉数据中的复杂关系。每个神经元都有一个状态,通常是二进制的(0或1),不过在一些变体中也可以是连续值。

    ② 连接权重:神经元之间通过连接权重相互连接。设神经元i和神经元j之间的连接权重为{​{W}_{ij}},权重矩阵{​{W}}描述了整个网络的连接情况。权重的大小和正负决定了神经元之间相互影响的强度和性质。在Boltzmann机中,权重是对称的,即{​{W}_{ij}} ={​{W}_{ji}} ,且{​{W}_{ii}} =  0 (无自连接)。这种对称的权重结构对于网络的能量函数和学习算法具有重要意义。

    ③ 偏置项:每个神经元还有一个偏置项,用于调整神经元的激活阈值。偏置项{​{b}_{i}}对于可见神经元和隐藏神经元都存在,它在网络的能量计算和状态更新过程中发挥作用。

    (4)玻尔兹曼机的类型:
    ① 标准玻尔兹曼机(BM):标准玻尔兹曼机是一个全连接的无向图模型,如图4-1所示,其中每个节点都可以与其他节点相连。每个神经元的激活状态取决于周围神经元的状态。由于节点间的连接非常复杂,标准玻尔兹曼机的训练和计算难度非常高,导致其应用受到限制。

    图4-1 六个变量的玻尔兹曼机

    ② 受限玻尔兹曼机(RBM):受限玻尔兹曼机是一种简化版本的玻尔兹曼机,如图4-2所示。它由两层神经元组成,即可见层和隐藏层。RBM中的节点连接受到限制:可见层和隐藏层之间是全连接的,但层内节点之间无连接(即可见层节点彼此独立,隐藏层节点彼此独立)。这种结构的简化使得RBM的训练效率更高,常用于推荐系统、特征学习和数据降维等任务。

     

    图4-2 受限玻尔兹曼机

    ③ 深度玻尔兹曼机(DBM):深度玻尔兹曼机通过堆叠多个RBM层构成,是一种多层结构的深度学习模型。DBM中每一层都可以看作一个RBM,通过逐层训练和调优,这种多层结构可以捕捉数据的复杂特征。DBM在图像处理和自然语言处理领域有一定应用。

    (5)神经元连接方式:标准玻尔兹曼机有全连接结构,包括可见层内部、隐藏层内部以及层间连接。每个神经元都与所有其他神经元双向连接,形成对称权重;受限玻尔兹曼机有层间全连接,但同层内无连接。可见层和隐藏层之间的连接是双向的,但权重对称。

  2. 实现简单的受限玻尔兹曼机,代码如下:
    import numpy as np
    
    
    # 受限玻尔兹曼机(RBM)
    class RBM:
        def __init__(self, num_visible, num_hidden):
            self.num_visible = num_visible  # 可见层神经元数量
            self.num_hidden = num_hidden  # 隐藏层神经元数量
            # 权重矩阵 W, 随机初始化权重,均值为 0,标准差为 0.1
            self.W = np.random.normal(0, 0.1, (num_visible, num_hidden))
            self.b = np.zeros(num_visible)  # 可见层的偏置项
            self.c = np.zeros(num_hidden)  # 隐藏层的偏置项
    
        def energy(self, v, h):
            return -np.sum(self.W * v.reshape(-1, 1) * h.reshape(1, -1)) - np.sum(self.b * v) - np.sum(self.c * h)

    其中能量函数为\mathrm{E(v,h)=-V^TWh-b^Tv-c^Th}
    另外,权重初始化使用均值为0、标准差为0.1的高斯分布初始化权重矩阵{​{W}},偏置初始化可见层偏置{​{b}}和隐藏层偏置{​{c}}初始化为零向量。

  3. 核心思想:通过运行k步吉布斯采样(通常k=1)快速逼近梯度,避免传统吉布斯采样需要达到平稳分布的高计算代价。
    (1)正向传播,计算隐藏层概率,计算隐藏层激活概率:P(h_1=1|v_0)=\sigma(v_0W+c)
    其中为 Sigmoid 函数:\sigma(x)=\frac{1}{(1+e^{-x})}
    代码如下:
    def sample_hidden(self, v):
        # 从可见层采样隐藏层
        h_prob = 1 / (1 + np.exp(-np.dot(v, self.W) - self.c))  # 隐藏层概率
        return np.random.binomial(1, h_prob)  # 二值化采样

    (2)负相采样从隐藏层{h}_{0}重构可见层P(v_1=1|h_0)=\sigma(h_0W^T+b)
    代码如下:

    def sample_visible(self, h):
        # 从隐藏层采样可见层
        v_prob = 1 / (1 + np.exp(-np.dot(h, self.W.T) - self.b))  # 可见层概率
        return np.random.binomial(1, v_prob)  # 二值化采样

    (3)二次正向传播,根据重构的可见层{v}_{1}计算隐藏层概率。P(h_1=1|v_1)=\sigma(v_1W+c)
    (4)通过正相(数据分布)和负相(模型分布)的统计量计算参数梯度。对比散度(CD-k)梯度公式如下:
    \Delta\mathrm{W}=\eta\left(\langle\mathrm{v}_0\mathrm{h}_0^T\rangle_\mathrm{data}-\langle\mathrm{v}_k\mathrm{h}_k^T\rangle_\mathrm{model}\right)-\lambda\mathrm{W}
    \Delta\mathrm{b}=\eta(\langle\mathrm{v}_0\rangle_\mathrm{data}-\langle\mathrm{v}_k\rangle_\mathrm{model})
    \Delta\mathrm{c}=\eta(\langle\mathrm{h}_0\rangle_\mathrm{data}-\langle\mathrm{h}_k\rangle_\mathrm{model})
     

    其中:

    \mathrm{n}为学习率

    \lambda为权重衰减系数(L2正则化)

    \left\langle\cdot\right\rangle表示期望值

    k为吉布斯采样步数(通常k=1)

    完整代码如下:
     

    import numpy as np
    class RBM:
        def __init__(self, num_visible, num_hidden, lr=0.01, weight_decay=1e-4):
            self.num_visible = num_visible
            self.num_hidden = num_hidden
            self.lr = lr
            self.weight_decay = weight_decay
            # 初始化参数
            self.W = np.random.normal(0, 0.01, (num_visible, num_hidden))
            self.b = np.zeros(num_visible)
            self.c = np.zeros(num_hidden)
    
        def sample_hidden(self, v):
            # 从可见层采样隐藏层
            h_prob = 1 / (1 + np.exp(-np.dot(v, self.W) - self.c))  # 隐藏层概率
            return np.random.binomial(1, h_prob)  # 二值化采样
    
        def sample_visible(self, h):
            # 从隐藏层采样可见层
            v_prob = 1 / (1 + np.exp(-np.dot(h, self.W.T) - self.b))  # 可见层概率
            return np.random.binomial(1, v_prob)  # 二值化采样
    
        def train_step(self, v0, k=1):
            # 正相:计算隐藏层
            h0 = self.sample_hidden(v0)
            # 负相:k 步吉布斯采样
            vk = v0.copy()
            for _ in range(k):
                hk = self.sample_hidden(vk)
                vk = self.sample_visible(hk)
            # 计算梯度
            positive_grad = np.dot(v0.T, h0) / v0.shape[0]
            negative_grad = np.dot(vk.T, self.sample_hidden(vk)) / vk.shape[0]
            delta_W = positive_grad - negative_grad
            delta_b = np.mean(v0 - vk, axis=0)
            delta_c = np.mean(h0 - self.sample_hidden(vk), axis=0)
            # 更新参数
            self.W += self.lr * delta_W - self.weight_decay * self.W
            self.b += self.lr * delta_b
            self.c += self.lr * delta_c
  4. Python实现标准玻尔兹曼机,使用对比散度(Contrastive Divergence, CD)算法进行训练,采用sigmoid激活函数,详细代码如下:
    import numpy as np
    
    
    class BoltzmannMachine:
        def __init__(self, num_visible, num_hidden, lr=0.1, weight_decay=1e-4):
            self.nv = num_visible  # 可见层神经元数
            self.nh = num_hidden  # 隐藏层神经元数
            self.lr = lr  # 学习率
            self.wd = weight_decay  # 权重衰减系数
            # 初始化对称权重矩阵 (包含可见层和隐藏层内部的连接)
            self.W = np.random.normal(0, 0.01, (self.nv + self.nh, self.nv + self.nh))
            np.fill_diagonal(self.W, 0)  # 禁止自连接
            self.W = (self.W + self.W.T) / 2  # 强制对称
            # 初始化偏置项
            self.b = np.zeros(self.nv + self.nh)
    
        def sigmoid(self, x):
            return 1 / (1 + np.exp(-x))
    
        def sample_units(self, state, steps=1):
            for _ in range(steps):
                order = np.random.permutation(self.nv + self.nh)
                for unit in order:
                    # 计算激活概率
                    logit = np.dot(self.W[unit, :], state) + self.b[unit]
                    prob = self.sigmoid(logit)
                    state[unit] = np.random.binomial(1, prob)
            return state
    
        def train_step(self, visible_data, k=1):
            """对比散度训练"""
            batch_size = visible_data.shape[0]
            # 构建完整初始状态
            state_0 = np.zeros((batch_size, self.nv + self.nh))
            state_0[:, :self.nv] = visible_data
            # 正相采样
            state_0 = np.array([self.sample_units(s.copy(), steps=3) for s in state_0])
            # 负相采样(k步)
            state_k = state_0.copy()
            state_k = np.array([self.sample_units(s.copy(), steps=k) for s in state_k])
            # 计算梯度
            positive = np.dot(state_0.T, state_0) / batch_size
            negative = np.dot(state_k.T, state_k) / batch_size
            grad_W = positive - negative - self.wd * self.W
            # 更新参数
            self.W += self.lr * (grad_W + grad_W.T) / 2  # 对称更新
            self.b += self.lr * (state_0.mean(axis=0) - state_k.mean(axis=0))
            # 计算可见层重构误差
            error = np.mean((state_0[:, :self.nv] - state_k[:, :self.nv]) ** 2)
            return error
    
    # 测试运行
    if __name__ == "__main__":
        # 3个可见神经元,2个样本
        data = np.array([[1, 0, 1], [0, 1, 0]], dtype=np.float32)
        # 创建模型(可见层3,隐藏层2)
        bm = BoltzmannMachine(num_visible=3, num_hidden=2, lr=0.1)
        # 训练循环
        for epoch in range(50):
            err = bm.train_step(data, k=1)
            if (epoch + 1) % 5 == 0:
                print(f"Epoch {epoch + 1:2d} | Error: {err:.4f}"

五、实验小结(包括问题和解决办法、心得体会、意见与建议等)

1.问题和解决办法:

问题1:程序抛出数值错误,能量函数或概率计算异常。

解决方法:初始化时使用更小的标准差如0.01。

问题2:程序报错ValueError: shapes (5,) and (2,5) not aligned: 5 (dim 0) != 2 (dim 0)。

解决方法:使用公式计算梯度是转置后的行列不对。

2.心得体会:通过本次实验,我不仅掌握了玻尔兹曼机的核心原理和实现技巧,更深刻体会到 理论推导与工程实现的鸿沟。BM 的实现过程像一面镜子,映射出概率图模型的优雅与复杂性。尽管其实用性受限,但手动实现 BM 的经历为我理解更复杂的生成模型(如深度玻尔兹曼机等)奠定了坚实基础。未来,我将继续探索高效的概率建模方法,平衡模型表达力与计算效率,推动其在真实场景中的应用。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

S_yzx

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值