精华内容
下载资源
问答
  • 近期学习总结前言玻尔兹曼机(BM)受限玻尔兹曼机(RBM)深度玻尔兹曼机(DBM)深度置信网络(DBN) 前言 前段时间,团队被老师安排了一个涉及玻尔兹曼机及相关变体的任务。具体内容就是学习相关理论知识及代码实现。 所以...

    前言

    前段时间,团队被老师安排了一个涉及玻尔兹曼机及相关变体的任务。具体内容就是学习相关理论知识及代码实现。
    所以就想着写一篇博客来总结一下繁杂的知识点(尤其是背后的数学推导公式),如果你是玻尔兹曼机及相关变体的初学者,并对这些知识非常感兴趣,那么恭喜你,发现了这篇文章。我会用非常白话、通俗易懂的方式向读者解释这些知识逻辑。
    在此特别感谢王志强、董倩等研究生,对此篇博客做出的贡献。

    玻尔兹曼机(BM)

    玻尔兹曼机(Boltzman Machine)是一个随机动力系统,每个变量的状态都以一定的概率受到其他变量的影响。
    玻尔兹曼机可以用概率无向图模型来描述,一个具有K个节点的玻尔兹曼机满足以下三个性质:

    1. 二值化。每个节点的状态值只有0和1。
    2. 一个玻尔兹曼机包括两类节点,一类是可观察的节点有N个,一类是不可观察的节点,即隐藏节点,有(K-N)个。
    3. 节点之间是全连接的。每个节点都和其他节点连接。
    4. 每两个变量之间的互相影响是对称的。这里的对称和上面无向其实是一个概念,说白了就是已知A点的状态值,那么求B的状态值和已知B的状态值,求A的状态值的影响是相等的。如果你还是没有理解这句话,碰巧你又了解过一点概率论的知识,那么你可以将上述理解为 P(B|A) = P(A|B)

    在这里插入图片描述
    上图就是一个有六个节点的玻尔兹曼机。其中有三个可观察的节点,我已经标了黄色,还有三个不可观测的节点,即隐藏节点,我已经标了灰色。

    波尔兹曼分布推导过程

    玻尔兹曼机中,随机向量X的联合概率,也就是节点的状态值,是满足玻尔兹曼分布的。

    玻尔兹曼分布是描述粒子处于特定状态下的概率,是关于状态能量E(x)与系统温度T的函数。一个粒子处于状态α的概率P(α)是关于状态能量E(x)与系统温度T的函数。

    别急,我相信你读完这个定义一定是懵逼,心里已经开始呐喊,TMD啥叫特定状态啊?啥叫特定状态下的概率啊?啥是状态能量啊?…
    不急,我这就用 “人话” 翻译一遍。
    特定状态就是说,节点的状态值为1,还是0。x=α
    特定状态就是说,节点状态值为1或者0时的概率。P(x=α)
    状态能量就是说,粒子本身具有的能量。
    玻尔兹曼分布就是计算P(x=α)时具体的概率函数与系统的状态能量E(x)和系统温度T的函数有关。具体表达式为:
    在这里插入图片描述
    通常玻尔兹曼分布还有另一个表达式,实则是上式的等价处理,如下:
    在这里插入图片描述
    E(x)为能量函数,T为系统温度,Z为配分函数,实则就是一个归一化因子。
    从玻尔兹曼分布的定义,我们可以发现系统的两个不同状态的概率之比仅与系统能量有关:
    P(a)/P(b)=exp((E1E2)/KT) P(a)/P(b) = exp((E1−E2)/KT)
    具体的推导公式如下图:
    在这里插入图片描述

    吉布斯采样

    我采用的是Python下的tensooflow语言来实现的玻尔兹曼机。请注意,我们使用的版本是tensorflow2。
    在讲代码之前,有必要讲一下玻尔兹曼机的训练过程。
    在玻尔兹曼机中,配分函数Z通常很难计算,因此,联合概率分布P(x)一般通过马尔科夫链蒙特卡洛方法(MCMC方法)来做近似计算。
    玻尔兹曼机采用了基于吉布斯采样的样本生成方法来训练的。

    吉布斯采样
    玻尔兹曼机的吉布斯采样过程为:随机选择一个变量Xi,然后根据其全条件概率P(Xi|X-i)来设置其状态值,即以P(Xi=1|X-i)的概率将变量Xi设为1,否则为0。在固定的温度T下,运行足够时间后,玻尔兹曼机会达到热平衡状态。此时,任何全局状态的概率都服从玻尔兹曼分布P(x),只和系统的能量有关,和初始状态无关。

    如果你听不懂上述定义,(针对没有概率论和热力学基础的读者)。那你只需要了解,因为玻尔兹曼机的联合概率函数中配分函数Z很难处理,就采用另一种方法来将节点的状态值概率趋近于玻尔兹曼分布。

    玻尔兹曼机可以解决两类问题,一类是搜索问题:当给定变量之间的连接权重时,需要找到一组二值向量,使得整个网络的能量最低。另一类是学习问题,当给定变量的多组观测值时,学习网络的最优权重。

    受限玻尔兹曼机(RBM)

    全连接的玻尔兹曼机在理论上固然有趣,但是由于其复杂性,目前为止并没有广泛运用。实际应用中,用得更多的是基于玻尔兹曼机改造的一个版本——受限玻尔兹曼机(RBM),其网络架构如下:
    在这里插入图片描述
    玻尔兹曼机没有层的概念,它所有的节点都是全连接的。
    但受限玻尔兹曼机有层的概念。它有两层,一层称为显层,用于观测和输入,一层为隐藏层,用于提取特征。
    受限玻尔兹曼机相比玻尔兹曼机,层间的节点还是采用了对称的全连接的方式连接,但是层内的节点相互独立,相互不受影响。
    因为层内节点相互独立,那么由Bayes条件独立定理,受限玻尔兹曼机可以并行地对所有的显层变量或隐藏层变量同时进行采样,从而更快达到热平衡。

    能量函数

    在这里插入图片描述
    由于受限玻尔兹曼机变成了层的结构,所以受限玻尔兹曼机的能量函数也变成了由三部分组成,
    一个是显层节点偏置乘以显层随机可观测变量部分,
    一个是连接权重与显层随机可观测变量和隐层随机可观测变量相乘部分,
    一个是隐层节点偏置乘以隐层随机可观测变量偏置部分。

    CD学习算法

    由于受限玻尔兹曼机的特殊结构,G·Hinton提出了一种比吉布斯采样更加有效的学习算法,即对比散度学习算法,又称为CD学习算法。
    通过对CD学习算法的学习,我发现这个CD算法就是在吉布斯采样的基础上作出的一点改进,即在处理玻尔兹曼机时,运行无穷次的吉布斯采样改进为运行K次即可。
    以前处理玻尔兹曼机时,吉布斯采样是一直对这个玻尔兹曼机处理,直到这个波尔兹曼机收敛。G·Hinton提出,在受限玻尔兹曼机中,不需要等到受限玻尔兹曼机完全收敛,只需要K步吉布斯采样,这时模型就非常好了。所以CD算法又称K步吉布斯采样法。

    代码实现受限玻尔兹曼机

    受限玻尔兹曼机有两个偏置项,隐藏层的偏置项有助于RBM在前向传递中获得非零激活值,而可见层的偏置项有助于受限玻尔兹曼机学习后向传递中的重建。
    在正向传递中,每个输入数据乘以一个独立的权重,然后相加后再加上一个偏置项,最后将结果传递到激活函数来产生输出。
    用对比散度计算正反向梯度,然后更新偏置和权重
    因为最开始受限玻尔兹曼机权重是随机初始化的,所以重建结果和原始输入差距通常会比较大,这个差距可看作是重建误差,训练受限玻尔兹曼机是通过在可见层和隐藏层之间迭代学习不断正向反向传播,直至达到某个误差的最小值
    我们采用了Python环境下Numpy工具库来撰写代码,具体注释已在代码中标注,不做过多讲解。

    import numpy
    class RBM:
        def __init__(self, n_visible, n_hidden):
            self.n_visible = n_visible
            self.n_hidden = n_hidden
            self.bias_a = np.zeros(self.n_visible)  # 可视层偏移量
            self.bias_b = np.zeros(self.n_hidden)  # 隐藏层偏移量
            self.weights = np.random.normal(0, 0.01, size=(self.n_visible, self.n_hidden))
            self.n_sample = None
    
        def encode(self, v):
            # 编码,即基于v计算h的条件概率:p(h=1|v)
            return sigmoid(self.bias_b + v @ self.weights)
    
        def decode(self, h):
            # 解码(重构):即基于h计算v的条件概率:p(v=1|h)
            return sigmoid(self.bias_a + h @ self.weights.T)
    
        def gibbs_sample(self, v0, max_cd):
            # gibbs采样, 返回max_cd采样后的v以及h值
            v = v0
            for _ in range(max_cd):
                # 首先根据输入样本对每个隐藏层神经元采样。二项分布采样,决定神经元是否激活
                ph = self.encode(v)
                h = np.random.binomial(1, ph, (self.n_sample, self.n_hidden))
                # 根据采样后隐藏层神经元取值对每个可视层神经元采样
                pv = self.decode(h)
                v = np.random.binomial(1, pv, (self.n_sample, self.n_visible))
            return v
            
        def update(self, v0, v_cd, eta):
            # 根据Gibbs采样得到的可视层取值(解码或重构),更新参数
            ph = self.encode(v0)
            ph_cd = self.encode(v_cd)
            self.weights += eta * (v0.T @ ph - v_cd.T @ ph)  # 更新连接权重参数
            self.bias_b += eta * np.mean(ph - ph_cd, axis=0)  # 更新隐藏层偏移量b
            self.bias_a += eta * np.mean(v0 - v_cd, axis=0)  # 更新可视层偏移量a
            return
    
        def train(self, data, max_step, max_cd=2, eta=0.1):
            # 训练主函数,采用对比散度算法(CD算法)更新参数
           
            assert data.shape[1] == self.n_visible, "输入数据维度与可视层神经元数目不相等"
            self.n_sample = data.shape[0]
            for i in range(max_step):
                v_cd = self.gibbs_sample(data, max_cd)
                self.update(data, v_cd, eta)
                error = np.sum((data - v_cd) ** 2) / self.n_sample / self.n_visible * 100
                if i == (max_step-1):  # 将重构后的样本与原始样本对比计算误差
                    print("可视层(隐藏层)状态误差比例:{0}%".format(round(error, 2)))
        def predict(self, v):
            # 输入训练数据,预测隐藏层输出
            ph = self.encode(v)[0]
            states = ph >= np.random.rand(len(ph))
            return states.astype(int)
    

    深度玻尔兹曼机(DBM)

    在这里插入图片描述

    深度玻尔兹曼机实际上是由多个受限玻尔兹曼机堆栈构成,我们构建一个简单的三层深度玻尔兹曼机,就首先需要进行预训练,通过对比散度算法训练出两个受限玻尔兹曼机,对于每个受限玻尔兹曼机,都是根据可见层的数据,来学习到隐层的数据,对于第二个受限玻尔兹曼机,可以把求得的第一个隐层看作是可见层来求第二个隐层的数据,然后再combine两个受限玻尔兹曼机进行微调,通过CD算法来更新相应的数据,最后实现一个简单的三层深度玻尔兹曼机。
    对于受限玻尔兹曼机每新增的隐藏层,权重都会通过迭代学习反复调整,直至该层能够逼近前一层的输入,这是贪婪的、逐层的无监督的预训练。
    深度玻尔兹曼机网络结构如下:
    在这里插入图片描述

    代码实现深度玻尔兹曼机

    # Implementation of 3 layer ( 2 hidden layer ) Deep Boltzmann Machine
    import numpy as np     
    from scipy.special import expit   #explit函数也称为logistic sigmoid函数,expit(x)= 1 /(1 + exp(-x))
    from matplotlib import pylab as plt    
    
    def binary_cross_entropy(data, reconst):    #交叉熵损失函数(交叉熵就是用来判定实际的输出与期望的输出的接近程度)
        return - np.mean( np.sum( data * np.log(reconst) + (1-data) * np.log(1 - reconst), axis=1) )
    
    def reconstruct_data(data, b, c1, c2, w_vh1, w_h1h2, num_sample=100): #重建数据
        m_h1 = expit( np.dot(data, w_vh1) + c1 )       #自下而上求得第一个隐层的数据,dot()函数用来做矩阵乘法
        for i in range(num_sample):
            m_h2 = expit( np.dot(m_h1, w_h1h2) + c2 )  #自下而上求得第二个隐层的数据
            m_h1 = expit( np.dot(w_h1h2, m_h2.T).T + c1 )  #第一个隐层的重建数据
        return expit( np.dot(w_vh1, m_h1.T).T + b )     #返回可视层的重建数据
    
    def popup(data, c1, w_vh1):     #编码函数
        return expit(np.dot(data, w_vh1) + c1)
    
    def rbm_contrastive_divergence(data, b, c, w, num_sample=100):  #RBM的对比散度算法(CD算法)
        # Mean field可视层参数和隐藏层参数定义
        m_vis = data
        m_hid = expit( np.dot(data, w) + c )
         
        s_vis = m_vis
        for i in range(num_sample):
            #Gibbs采样,决定神经元是否被激活
            sm_hid = expit( np.dot(s_vis, w) + c )
            s_hid = np.random.binomial(1, sm_hid)       #吉布斯采样后的隐藏层数据
            sm_vis = expit( np.dot(w, s_hid.T).T + b )
            s_vis = np.random.binomial(1, sm_vis)	#吉布斯采样后的可见层数据
        return np.mean(m_vis - s_vis, axis=0), np.mean(m_hid - s_hid, axis=0), \
                        (np.dot(m_vis.T, m_hid) - np.dot(s_vis.T, s_hid)) / len(data)    #返回原始数据和最后一次采样数据梯度的平均值
    
    def dbm_contrastive_divergence(data, b, c1, c2, w_vh1, w_h1h2, num_sample=100):		#相比于RBM多一个隐藏层的DBM的对比散度算法(CD算法)
        # Mean field相应参数的定义
        m_vis = data
        m_h1 = np.random.uniform(size=(len(data), len(c1)))    
        m_h2 = np.random.uniform(size=(len(data), len(c2)))
        for i in range(num_sample):
            m_h1 = expit( np.dot(m_vis, w_vh1) + np.dot(w_h1h2, m_h2.T).T + c1 )
            m_h2 = expit( np.dot(m_h1, w_h1h2) + c2 )
        # Gibbs sample吉布斯采样
        s_vis = np.random.binomial(1, m_vis)
        s_h1 = np.random.binomial(1, 0.5, size=(len(data), len(c1)))
        s_h2 = np.random.binomial(1, 0.5, size=(len(data), len(c2)))
        for i in range(num_sample):
            sm_vis = expit( np.dot(w_vh1, s_h1.T).T + b )
            s_vis = np.random.binomial(1, sm_vis)
            sm_h1 = expit( np.dot(s_vis, w_vh1) + np.dot(w_h1h2, s_h2.T).T + c1 )
            s_h1 = np.random.binomial(1, sm_h1)
            sm_h2 = expit( np.dot(s_h1, w_h1h2) + c2 )
            s_h2 = np.random.binomial(1, sm_h2)
        return np.mean(m_vis - s_vis, axis=0), np.mean(m_h1 - s_h1, axis=0), np.mean(m_h2 - s_h2, axis=0), \
                    ( np.dot(m_vis.T, m_h1) - np.dot(s_vis.T, s_h1) ) / len(data), ( np.dot(m_h1.T, m_h2) - np.dot(s_h1.T, s_h2) ) / len(data)
    
    # Assign structural parameters 分配结构参数
    num_visible = 784   #可视层神经元节点个数
    num_hidden1 = 500   #第一个隐藏层神经元节点个数
    num_hidden2 = 1000  #第二个隐藏层神经元节点个数
    
    # Assign learning parameters分配学习参数
    pretrain_epochs = 100           #预训练的迭代次数
    pretrain_learning_rate = 0.1    #预训练学习率
    train_epochs = 100              #训练迭代次数
    train_learning_rate = 0.1       #训练学习率
    
    # Initialize weights and biases  初始化权重,偏置
    b = np.zeros((num_visible, ))      
    c1 = np.zeros((num_hidden1, ))
    c2 = np.zeros((num_hidden2, ))
     #随机初始化权重
    w_vh1 = np.random.normal(scale=0.01, size=(num_visible, num_hidden1))     
    w_h1h2 = np.random.normal(scale=0.01, size=(num_hidden1, num_hidden2))
    
    # Load data, data needs to be in range [0, 1]   
    data = np.load("./imgs.npy").reshape(10, 28*28)[[3, 7, 9]]
    
    # Pretraining预训练
    for i in range(pretrain_epochs):
        # Calculate gradient 计算梯度
        update_b, update_c1, update_w_vh1 = rbm_contrastive_divergence(data, b, c1, w_vh1)
        # Upate parameters 更新可视层和第一个隐层的偏置以及连接权
        b += pretrain_learning_rate * update_b
        c1 += pretrain_learning_rate * update_c1
        w_vh1 += pretrain_learning_rate * update_w_vh1
    
    pseudo_data = popup(data, c1, w_vh1) #保存由可见层编码学习到的第一个隐层的数据
    for i in range(pretrain_epochs):
        # Calculate gradient计算梯度
        update_c1, update_c2, update_w_h1h2 = rbm_contrastive_divergence(pseudo_data, c1, c2, w_h1h2)
        # Upate parameters更新两个隐层的偏置以及连接权
        c1 += pretrain_learning_rate * update_c1
        c2 += pretrain_learning_rate * update_c2
        w_h1h2 += pretrain_learning_rate * update_w_h1h2
    
    # Show current cost 打印原始数据与重建结果之间的误差
    cost = binary_cross_entropy(data, reconstruct_data(data, b, c1, c2, w_vh1, w_h1h2))
    print( "Reconstruction cost is %.2f"%cost )
    
    # Fine tuning 微调
    for i in range(train_epochs):
        # Calculate gradient
        update_b, update_c1, update_c2, update_w_vh1, update_w_h1h2 \
                                        = dbm_contrastive_divergence(data, b, c1, c2, w_vh1, w_h1h2)
        # Update parameters更新所有的偏置以及连接权
        b += train_learning_rate * update_b
        c1 += train_learning_rate * update_c1
        c2 += train_learning_rate * update_c2
        w_vh1 += train_learning_rate * update_w_vh1
        w_h1h2 += train_learning_rate * update_w_h1h2
    
    # Show fine tuning result 打印微调后的原始数据与重建结果之间的误差
    cost = binary_cross_entropy(data, reconstruct_data(data, b, c1, c2, w_vh1, w_h1h2))
    print( "Reconstruction cost is %.2f"%cost )
    
    # Show result images 显示.npy文件最后形成的灰度图
    plt.matshow(reconstruct_data(data, b, c1, c2, w_vh1, w_h1h2)[0].reshape(28, 28))
    plt.gray()
    plt.show()
    

    深度置信网络(DBN)

    玻尔兹曼机和深度置信网络是生成模型,借助隐变量来描述复杂的数据分布。
    深度置信网络是神经网络的一种。既可以用于非监督学习,也可以用于监督学习。
    深度置信网络组成元件是受限玻尔兹曼机。通过下图的网络结构,我们可以看出深度置信网络和受限玻尔兹曼机的关系:
    在这里插入图片描述
    训练深度置信网络由两部分组成。
    一是单独训练每一个受限玻尔兹曼机,使它们收敛。
    二是将每一个受限玻尔兹曼机深入展开,构成一个前向传播的深层网络。然后使用BP算法微调参数,从而使整个网络收敛。

    代码实现深度置信网络

    我们采用了Python环境下Numpy工具库来和tensorflow2来撰写代码,具体注释已在代码中标注,不做过多讲解。

    import numpy as np
    import tensorflow as tf
    
    def sigmoid(z):
        return 1 / (1 + np.exp(-z))
    
    def DBN(epoch, test_data, rbm_list,  lr=0.001):
        # 构建深度置信网络
    
        RBM1_w = tf.Variable(tf.convert_to_tensor(rbm_list[0].weights))     # 将数据转换为Varible类型,方便梯度跟踪
        RBM1_vb = tf.Variable(tf.convert_to_tensor(rbm_list[0].bias_a))     # 将数据转换为Varible类型,方便梯度跟踪
        RBM1_hb = tf.Variable(tf.convert_to_tensor(rbm_list[0].bias_b))     # 将数据转换为Varible类型,方便梯度跟踪
    
        RBM1to2_w = tf.Variable(tf.random.normal([6, 6],dtype=tf.float64))  # 将数据转换为Varible类型,方便梯度跟踪
    
        RMB2_w = tf.Variable(tf.convert_to_tensor(rbm_list[1].weights))     # 将数据转换为Varible类型,方便梯度跟踪
        RBM2_vb = tf.Variable(tf.convert_to_tensor(rbm_list[1].bias_a))     # 将数据转换为Varible类型,方便梯度跟踪
        RBM2_hb = tf.Variable(tf.convert_to_tensor(rbm_list[1].bias_b))     # 将数据转换为Varible类型,方便梯度跟踪
    
        BP_w = tf.Variable(tf.random.normal([6,6],dtype=tf.float64))        # 将数据转换为Varible类型,方便梯度跟踪
        BP_b = tf.Variable(tf.random.normal([6],dtype=tf.float64))          # 将数据转换为Varible类型,方便梯度跟踪
    
        test_data = tf.Variable(tf.convert_to_tensor(test_data,dtype=tf.float64))
    
        # 微调迭代次数
        for step in range(epoch):
            # 计算当前批次样本的网络前向传播
            with tf.GradientTape() as tape:
                # 前向传播
                # 第一层计算 第一个RBM v-h
                out1 = tf.nn.sigmoid(RBM1_hb + test_data @ RBM1_w)
                # 第二层计算 h-v
                out2 = tf.nn.sigmoid(RBM2_vb + out1 @ RBM1to2_w)
                # 第三层输出也就是第二个RBM v-h
                out3 = tf.nn.sigmoid(RBM2_hb + out2 @ RMB2_w)
                out = tf.nn.relu(BP_b + out3 @ BP_w)
                # 反向传播
                # 计算损失函数
                loss = tf.reduce_mean(tf.square(test_data - out))   # 均方差损失函数
                # 手动梯度更新参数
                grads = tape.gradient(loss, [RBM1_w, RBM1_vb, RBM1_hb,
                                             RBM1to2_w,
                                             RMB2_w, RBM2_vb, RBM2_hb,
                                             BP_b,BP_w])
                # 参数更新
                RBM1_w.assign_sub(lr * grads[0])
                # RBM1_vb = RBM1_vb - lr * grads[1]
                RBM1_hb.assign_sub(lr * grads[2])
                RBM1to2_w.assign_sub(lr * grads[3])
                RMB2_w.assign_sub(lr * grads[4])
                RBM2_vb.assign_sub(lr * grads[5])
                RBM2_hb.assign_sub(lr * grads[6])
                BP_b.assign_sub(lr * grads[7])
                BP_w.assign_sub(lr * grads[8])
    
                print(step,": loss : ", loss)
    
    class RBM:
        def __init__(self, n_visible, n_hidden):
            self.n_visible = n_visible          # 可视层节点个数
            self.n_hidden = n_hidden            # 隐藏层节点个数
            self.bias_a = np.zeros(self.n_visible)  # 可视层偏移量
            self.bias_b = np.zeros(self.n_hidden)  # 隐藏层偏移量
            self.weights = np.random.normal(0, 0.01, size=(self.n_visible, self.n_hidden))  # 连接权重w
            self.n_sample = None
    
        def encode(self, v):
            # 编码,即基于v计算h的条件概率:p(h=1|v)
            return sigmoid(self.bias_b + v @ self.weights)
    
        def decode(self, h):
            # 解码(重构):即基于h计算v的条件概率:p(v=1|h)
            return sigmoid(self.bias_a + h @ self.weights.T)
    
        # gibbs采样, 返回max_cd采样后的v以及h值
        def gibbs_sample(self, v0, max_cd):
            v = v0
            for _ in range(max_cd):
                # 首先根据输入样本对每个隐藏层神经元采样。二项分布采样,决定神经元是否激活
                ph = self.encode(v)
                h = np.random.binomial(1, ph, (self.n_sample, self.n_hidden))
                # 根据采样后隐藏层神经元取值对每个可视层神经元采样
                pv = self.decode(h)
                v = np.random.binomial(1, pv, (self.n_sample, self.n_visible))
            return v
    
        # 根据Gibbs采样得到的可视层取值(解码或重构),更新参数
        def update(self, v0, v_cd, eta):
            ph = self.encode(v0)
            ph_cd = self.encode(v_cd)
            self.weights += eta * (v0.T @ ph - v_cd.T @ ph)  # 更新连接权重参数
            self.bias_b += eta * np.mean(ph - ph_cd, axis=0)  # 更新隐藏层偏移量b
            self.bias_a += eta * np.mean(v0 - v_cd, axis=0)  # 更新可视层偏移量a
            return
    
        # 训练函数 采用对比散度算法更新参数
        def fit(self, data, max_step, max_cd=2, eta=0.1):
            # data 训练数据集
            # max_cd 采样步数
            # max_step: 最大迭代次数 iter
            # eta: 学习率
            assert data.shape[1] == self.n_visible, "输入数据维度与可视层神经元数目不相等"
            self.n_sample = data.shape[0]
    
            for i in range(max_step):
                v_cd = self.gibbs_sample(data, max_cd)
                self.update(data, v_cd, eta)
                error = np.sum((data - v_cd) ** 2) / self.n_sample / self.n_visible * 100
                if i == (max_step-1):  # 将重构后的样本与原始样本对比计算误差
                    print("可视层(隐藏层)状态误差比例:{0}%".format(round(error, 2)))
    
        # 预测
        def predict(self, v):
            # 输入训练数据,预测隐藏层输出
            ph = self.encode(v)[0]
            states = ph >= np.random.rand(len(ph))
            return states.astype(int)
    
    
    if __name__ == '__main__':
        # 迭代次数
        iter = 100
        # 学习率
        lr = 0.001
        # N代表组成DBN的RBM的层数
        N = 2
    
        # 创建多个RBM层
        rbm_model_list = []
        # 用于两个RBM的连接权重,h-v
        for i in range(N):
            rbm_model_list.append(RBM(n_visible=6, n_hidden=6))
    
        # 训练集数据
        V = np.array([[1, 1, 1, 0, 0, 0], [1, 0, 1, 0, 0, 0], [1, 1, 1, 0, 0, 0],
                      [0, 0, 1, 1, 1, 0], [0, 0, 1, 1, 0, 0], [0, 0, 1, 1, 1, 0],
                      [1, 0, 0, 1, 1, 0], [0, 0, 0, 1, 0, 0], [0, 0, 1, 0, 1, 0]])
    
        # 单独训练每一个RBM层的参数
        for epoch in range(iter):   # 迭代次数
            for i in range(N):      # 每一次迭代 单独训练每一个RBM层
                rbm_model_list[i].fit(V, max_step=iter, max_cd=1, eta=lr)
    
        # 将两层RBM串联起来进行预测
        user = np.array([[1,0,0,1,0,0]])
        temp = rbm_model_list[0].predict(user)
        out = rbm_model_list[1].predict([temp])
        print(out)
    
        DBN(iter,V,rbm_model_list)
    
    
    展开全文
  • RBM 与 DBN 学习笔记

    2016-06-22 22:21:00
    2006 年,Hinton 等人基于受限波尔兹曼机(Re- stricted Boltzmann Machines, RBMs)提出的深度信念 网络(Deep Belief Networks, DBNs)是深度学习理论在 机器学习领域打响的第一枪,并成为了其后至今深度学 习算法的主要...

    2006 年,Hinton 等人基于受限波尔兹曼机(Re- stricted Boltzmann Machines, RBMs)提出的深度信念 网络(Deep Belief Networks, DBNs)是深度学习理论在 机器学习领域打响的第一枪,并成为了其后至今深度学 习算法的主要框架。在该算法中,DBN 由若干层 RBM 级联而成,得益于对比散度(Contrastive Divergence, CD)的高效近似算法,DBN 绕过了多隐层神经网络整 体训练的难题,将其简化为多个 RBM 的训练问题,这 样使得多层神经网络的识别效果和计算性能得到显著提 升。理论和实践经验也表明,DBN 能够较好的提取出训 练数据中的层次化结构性特征,为机器学习中数据特征 选取提供了新的思路。本文主要梳理 RBM 和 DBN 的 算法原理和实现过程,并给出示例程序。

     

    笔记全文见http://pan.baidu.com/s/1hspszfm。需要说明的是,这篇笔记没有写完,因近来接连收到询问这篇笔记的邮件,才想起这篇差点被我遗忘的两年前写的文稿。

    转载于:https://www.cnblogs.com/newerly/p/5608822.html

    展开全文
  • 深入研究了城市物流效率分析的研究现状,结合深度学习相关理论,针对具体问题构建三隐层连续型深度信念网络(DBN),对网络知识集进行了定义,提出了自适应DBN算法,分析了算法的收敛性。利用Iris数据集和Wine数据集...
  • 深度学习之DBN(一)

    千次阅读 2015-09-15 20:26:16
    第一篇博客,想直入主题的,直接从第四段开始。想长期关注此博客。烦请过一下下面的两段 ... 接触的第一个深度学习理论应该是DBN了吧,看了吴恩达和网上的各种大牛的独到讲解。我将自己的一些小简介说一下啊

         第一篇博客,想直入主题的,直接从第四段开始。想长期关注此博客。烦请过一下下面的两段  

        最近深度学习很火,个人也在学。但是学了之后总是将各种理解体会忘记了。幸好以前在学习深度学习的时候保存了不少笔记。写下博客为自己的学习做一个记录和总结。方便和大家交流和自己查阅。

        接触的第一个深度学习理论应该是DBN了吧,看了吴恩达和网上的各种大牛的独到讲解。我将自己的一些小简介说一下啊吧!第一篇博客,难免有不足,希望大家指出。一般来说。我每讲一个算法都会深究一下数学原理还有代码实现。有资源会和大家共享,一些公式的推到比较习惯用纸写。然后拍照上传。言归正传。

       话说DBN(深度置信网络),首先就得说一下物理当中的统计热力学的能量模型。这里关于能量模型的介绍转载一下whiteinblue《深度学习 读书笔记之RBM》当中的一段话:

       能量模型是个什么样的东西呢?直观上的理解就是,把一个表面粗糙又不太圆的小球,放到一个表面也比较粗糙的碗里,就随便往里面一扔,看看小球停在碗的哪个地方。一般来说停在碗底的可能性比较大,停在靠近碗底的其他地方也可能,甚至运气好还会停在碗口附近(这个碗是比较浅的一个碗);能量模型把小球停在哪个地方定义为一种状态,每种状态都对应着一个能量,这个能量由能量函数来定义,小球处在某种状态的概率(如停在碗底的概率跟停在碗口的概率当然不一样)可以通过这种状态下小球具有的能量来定义(换个说法,如小球停在了碗口附近,这是一种状态,这个状态对应着一个能量E,而发生“小球停在碗口附近”这种状态的概率p,可以用E来表示,表示成p=f(E),其中f是能量函数),这就是我认为的能量模型。

      好了,有了能量模型的一个简单明了的了解,接下来为大家介绍一下能量函数。能量函数是从玻尔兹曼分布中来的,所以首先简单介绍一下玻尔兹曼分布,具体介绍参见维基百科

         首先拉出玻尔兹曼分布当中的能量概率分布公式来,这个公式也是从维基百科当中弄过来的。如下:

       \frac{N_i}{N} = \frac{g_i \exp\left(-E_i/kT \right) } { \sum_{j}^{} g_j \,{\exp\left(-E_j/kT\right)} }\qquad\qquad (1)

        这个公式描绘了处于各个状态的系统能量分布,其中Ni 是平衡温度T 时,处于状态i 的粒子数目,具有能量Ei 和简并度giN 是系统中的总粒子数目,k玻尔兹曼常数。(注意有时在上面的方程中不写出简并度gi。在这个情况下,指标i 将指定了一个单态,而不是具有相同能量Eigi 的多重态。)

         接下来在引入分子热力学的一段话,这个是从博主转载过来的   ,这个博主也是大神,写得文章字字珠玑。关于DBN写得很不错。

        分子在高温中运动剧烈,能够克服局部约束(分子之间的一些物理约束,比如键值吸引力等),在逐步降到低温时,分子最终会排列出有规律的结构,此时也是低能量状态。受此启发,早期的模拟退火算法就是在高温中试图跳出局部最小。随机场作为物理模型之一,也引入了此方法。 

      大家在看到上面的这段话的时候,有一句话很重要。逐步降到低温时,分子最终会排列出有规律的结构,此时也是低能量状态。这个意思也就是说。低温状态是比较整个系统比较稳定的状态。是众多状态出现概率比较高的状态。如果放在机器学习的目标函数当中引用过来的话。能量最低的状态就是目标函数的极值

      但是如果要这样认为,还要印证一下此时真的是上面能量分布概率公式真的是否存在一个极值。如果存在这个极值,那么这个时候极值附近又有什么样的性质。我们怎么将这个想法迁移到神经网络当中。并进行求解,追究数学公式的原因就是,只要数学形式一样,后面的想法自然成立。只不过这个数学表达式或者说是思想只是从统计热力学当中迁移到了深度学习当中的DBN当中。为此,在维基百科能量均分定理当中找到了这样的一个图:


        上面的这个图是速率分布图,纵坐标代表的是概率密度,横坐标代表的是各种速率,也就是各种状态,但是如果熟悉大学物理统计热力学的都应该知道。和能量概率模型图差不多。这个图形状和上面公式的图的形状很想象。所以看出,函数的确存在极值,并且在极值上导数是0,导数意味着此时状态变化率不大

       好了,上面讲的是DBN的物理意义还有数学原理,这样的话我们就可以讲注意力转移到深度学习上的DBN的 RBM上来了。不过,在此之前,还要啰嗦一下。RBM的一些其他的意义:首先是看到某大牛csdn上的一段话,当时对这段话有点感觉,看了好久,由于水平有限,无法领悟作者所在的这个层次说的这段话。
       统计模式识别主要工作之一就是捕获变量之间的相关性,同样能量模型也要捕获变量之间的相关性,变量之间的相关程度决定了能量的高低。把变量的相关关系用图表示出来,并引入概率测度方式就够成了概率图模型的能量模型,其实实际中也可以不用概率表示,比如立体匹配中直接用两个像素点的像素差作为能量,所有像素对之间的能量和最小时的配置即为目标解。RBM作为一种概率图模型,引入概率就是为了方便采样,因为在CD(contrastive divergence)算法中采样部分扮演着模拟求解梯度的角色。

       其实这段话说的是下面将要引入的RBM一个迁移的理由,写得字字珠玑,各位自行领悟吧!

       不罗嗦,接下来为介绍一下RBM的网络结构还有能量函数

       首先是RBM的网络结构,直接上盗的图:

          其中v表示实际样本节点,以下叫可视节点,h表示隐藏节点数据,theta={W,b,a},W表示网络权重,b表示可视节点偏移,a表示隐藏节点偏移。这些参数是需要我们求解的,一般开始时我们随机初始化这些参数

          接下来引入能量函数,RBM能量函数如下,也是盗图:

    {\rm{E}}\left( {{\rm{v}},{\rm{h}}} \right) =  -  \sum \limits_{i = 1}^n  \sum \limits_{j = 1}^m {w_{ij}}{h_i}{v_j} -  \sum \limits_{j = 1}^m {b_j}{v_j} -  \sum \limits_{i = 1}^n {c_i}{h_i}

        个人的简介,分析一下这个函数,首先等式右边第一项有点类似于能量的“互能量”的概念,放在神经网络当中,这个就是神经网络的Hebbian  theory。从网络的观点看,这一点是十分重要的,因为正是这一项代表了可视层和隐藏层之间的连接关系。另外两项之间就是各个节点各自的能量,有点类似于重力势能。这个是很有趣的一个现象。如果我们放在物理当中的分子能量当中去看的话,第一项代表了可以看做分子之间的引力势能,后面的两项的可以看做分子的重力势能 。从这点可以看出这个函数定义很有意义,也很有道理。发现了学科之间的迁移了有木有,有种大道值简的赶脚啊。

         其实关于能量函数的看法,还可以从专家乘积系统这个角度的解读来看这个问题。

        专家乘积系统主要是为了用简单的混合模型来逼近复杂的高维度模型。但是同时用简单的混合模型有着来逼近复杂的高维度模型有两种方法,一种是概率模型,这种模型本质是"加法和概率"的思想相结合的一种模型.比较典型的模型就是混合高斯模型,当时这种模型有缺陷,表现在高维度的时候,每个单个模型都要fine tune高维度的数据,但是每个个模型的表达能力是有限的(low-resolution).但是也有其他的方法,借鉴乘法的的独立的想法,这样每个专家只需要逼近高维度的一部分维度就行了。当然这些维度之间最好是相互独立的(事实上也是这样的,在RBM当中每一层都是不相互连接的)。这样的话用乘法才能起到比较好的效果。也许在进行数据预处理和特征选择的时候可以用FA和特殊的特征选择方法进行选择。关于专家乘积系统,写了一篇相关的论文。

        然后专家乘积系统怎样和能量函数扯上关系的呢,这位大牛当中的一句话:不错  !

        能量函数的意义是有一个解释的,叫做专家乘积系统(POE,product of expert),这个理论也是hinton发明的,他把每个隐藏节点看做一个“专家”,每个“专家”都能对可视节点的状态分布产生影响,可能单个“专家”对可视节点的状态分布不够强,但是所有的“专家”的观察结果连乘起来就够强了。具体我也看不太懂,各位有兴趣看hinton的论文吧,中文的也有,叫《专家乘积系统的原理及应用,孙征,李宁》。

       这样介绍完了能量函数在物理和专家乘积系统中的概念之后,接下来就是推到数学公式了。

       将上面讲的能量函数类比下面这个公式

      \frac{N_i}{N} = \frac{g_i \exp\left(-E_i/kT \right) } { \sum_{j}^{} g_j \,{\exp\left(-E_j/kT\right)} }\qquad\qquad (1)

       就可以得到关于RBM状态的概率模型:

    {\rm{p}}\left( {{\rm{v}},{\rm{h}}} \right) = \frac{{{e^{ - E\left( {v,h} \right)}}}}{{ \sum \nolimits_{v,h} {e^{ - E\left( {v,h} \right)}}}}

       这个状态的概率是由能量函数定义的

       现在我们相当于的到了一个概率分布,分母是进行归一化的。其实这个概率分布是可以叫做Gibbs分布,当然不是一个标准的Gibbs分布,而是一个特殊的Gibbs分布,这个分布是有一组参数的,就是能量函数的那几个参数w,b,c。关于Gibbs分布,这里有一篇博客。

      

        

        

    展开全文
  • 【theano-windows】学习笔记十六——深度信念网络DBN

    千次阅读 热门讨论 2017-11-27 23:26:34
    前言前面学习了受限玻尔兹曼机(RBM)的理论和搭建方法, 如果稍微了解过的人, 肯定知道利用RBM可以堆叠构成深度信念网络(deep belief network, DBN)和深度玻尔兹曼机(deep Boltzmann machine), 这里就先学习一下DBN....

    前言

    前面学习了受限玻尔兹曼机(RBM)的理论和搭建方法, 如果稍微了解过的人, 肯定知道利用RBM可以堆叠构成深度信念网络(deep belief network, DBN)和深度玻尔兹曼机(deep Boltzmann machine), 这里就先学习一下DBN.

    国际惯例, 参考博文:

    Deep Belief Networks

    A fast learning algorithm for deep belief nets

    理论

    DBN的网络结构就是简单地将RBM堆叠起来, 样子长得就跟全连接一样.

    这里写图片描述

    由于它也是图模型, 所以很容易写出所有层的联合分布, 需要注意的是训练方法是逐层训练, 也就是说每两层是作为一个RBM训练的, 与其它层无关, 也即遇到类似于P(h2|x,h1)的可以直接写成P(h2|h1), 表示h2的训练与x无关, 了解到这, 我们就可以写联合分布了, 注意为了保持与教程一致, 此处采用的是以隐层为条件的可见层概率分布, 其实常规思维是以可见层为条件的隐层概率分布, 依据我前面介绍过的图模型联合概率分布计算方法:

    P(x,h1,h2,,hl)=P(hl)P(hl1|hl)P(hl2|hl1,hl)P(x|hl,hl1,,h1)=P(hl)P(hl1|hl)P(hl2|hl1)P(x|h1)=P(hl,hl1)Πl2k=0(P(hk|hk+1))

    上式中最后一步是将h0=x, 进而整个DBN的训练方法就显而易见了:

    • 将第一层作为一个RBM训练, 输入是x=h(0), 作为可见层
    • 将第一层得到是输入表示作为第二层的数据输入, 有两种方案, 可以使用P(h(1)=1|h(0))的平均激活, 或者是从P(h(1)|h(0))采样, 个人觉得前者是针对二值输入, 后者是真对实值输入
    • 将第二层作为RBM训练, 将变换数据(样本或均值激活)作为训练样本
    • 重复第二步和第三步
    • 最后就是微调整个网络参数,就是传说中的fine-tuning, 两种方法, 第一种是使用负对数似然损失的代理方法, 其实论文里面说的就是从上之下的一个算法, 也是Hinton大佬发明的wake-sleep算法, 这个算法经常被用于预训练AE(详细请看《视觉机器学习20讲》); 另一种方法就是有监督学习算法, 在模型顶上加个分类器作为误差传递的来源.

    在教程中, 主要关注监督梯度下降的微调算法, 具体来说就是使用Logistic回归分类器基于DBN最后一层隐单元的输出对输入x分类. 随后通过负对数似然损失来执行有监督梯度下降, 由于有监督梯度对于权重和隐单元偏置非零(对每层RBM的可见层是零), 这就等价于通过无监督的方式, 为一个深层多层感知器(MLP)进行权重和隐单元偏置的初始化.

    为什么这种训练方法有效?

    拿具有两个隐层(h(1),h(2))的DBN来说, 权重分别为W(1),W(2), Hinton论文 中建立了一个式子logp(x), 如果不懂这个符号的含义, 建议去看看RBM的证明, 戳这里有一个非常非常好的RBM证明文档, 极为清晰, 极力推荐去看看.

    logp(x)=KL(Q(h(1)|x)p(h(1)|x))+HQ(h(1)|x)+hQ(h(1)|x)(logp(h(1))+logp(x|h(1)))

    细心点会发现教程提供的这个式子与Hinton的论文中不同, 但是可以转化过去的, 你需要了解KL散度的知识, 我原来写过一篇博文, 戳这里, 然后我们对上式进行变换, 得到论文的表达形式
    logp(x)=KL(Q(h(1)|x)p(h(1)|x))+HQ(h(1)|x)+hQ(h(1)|x)(logp(h(1))+logp(x|h(1)))=hH(Q(h(1)|x),p(h(1)|x))H(Q(h(1)|x))+HQ(h(1)|x)+hQ(h(1)|x)[logp(h(1))+logp(x|h(1))]=hQ(h(1)|x)logp(h(1)|x)+hQ(h(1)|x)[logp(h(1))+logp(x|h(1))]

    边界情况就是KL(Q(h(1)|x)|p(h(1)|x))=0, 即由模型根据输入向量得到的特征向量与原始数据本来的真正的特征向量相等, 那么Q(h(1)|x)=p(h(1)|x), 最终上式结果可以写成
    logp(x)=hQ(h(1)|x)logQ(h(1)|x)+hQ(h(1)|x)[logp(h(1))+logp(x|h(1))]

    可以发现这个式子与论文的式子完全相同.

    正如论文说的, h(0)是第一个隐层的二值向量, p(h(0))是当前模型h(0)的先验概率, Q(|x)是基于第一层隐层为条件的任意概率分布, 边界就是当且仅当Q(|x)是真实的后验分布. 当所有的权重被绑定在一起的时候, 将WT1应用到数据向量计算得到基于h(1)的因子分布是真实的后验分布. 因而在贪婪算法的第二步logp(x)是等于边界的. 第二步将Q(|x)p(x|h(1))固定, 边界的导数与下式的导数一样

    Q(h(1)|x)logp(h(1))

    因而最大化高层的比如说权重的边界实际上等价于最大化数据集的对数概率, 其中h(1)是由Q(h(1)|x)产生的. 如果边界变得更加紧密, logp(x)有极大可能降低, 即使基于它的较低的边界增加, 但是logp(x)不会低于第二步贪婪算法得到的它的值, 因为边界是很紧密的而且经常处于上升趋势

    代码实现

    可以发现DBN与SdA非常相似, 因为他们都是无监督的层级训练, 主要区别就是DBN使用层级训练基础是RBM, 而SdA使用的层级训练基础是dA. 最好复习一下dA的梯度是由什么损失函数得到的, 这样有助于与RBM做一个区分.

    因为采用DBN做无监督训练, 然后采用MLP微调, 所以直接先建立一个DBN类去作为MLP的层. 因而还是需要用到我们之前的代码, 包含:

    • 引入各种包

      import numpy as np
      import theano
      import theano.tensor as T
      from theano.tensor.shared_randomstreams import RandomStreams
      import cPickle,gzip
      from PIL import Image
      import pylab
      import os
    • 读取数据的函数

      
      #定义读数据的函数,把数据丢入到共享区域
      
      def load_data(dataset):
        data_dir,data_file=os.path.split(dataset)
        if os.path.isfile(dataset):
            with gzip.open(dataset,'rb') as f:
                train_set,valid_set,test_set=cPickle.load(f)
        #共享数据集
        def shared_dataset(data_xy,borrow=True):
            data_x,data_y=data_xy
            shared_x=theano.shared(np.asarray(data_x,dtype=theano.config.floatX),borrow=borrow)
            shared_y=theano.shared(np.asarray(data_y,dtype=theano.config.floatX),borrow=borrow)
            return shared_x,T.cast(shared_y,'int32')
        #定义三个元组分别存储训练集,验证集,测试集
        train_set_x,train_set_y=shared_dataset(train_set)
        valid_set_x,valid_set_y=shared_dataset(valid_set)
        test_set_x,test_set_y=shared_dataset(test_set)
        rval=[(train_set_x,train_set_y),(valid_set_x,valid_set_y),(test_set_x,test_set_y)]
        return rval
    • 定义RBM作为预训练的基础, 主要有positive phasenegative phase, 构成Gibbs sampling, 还有能量函数的定义以及使用能量函数做梯度更新的方法

      
      #定义RBM
      
      class RBM(object):
        def __init__(self,
                     rng=None,
                     trng=None,
                     input=None,
                     n_visible=784,
                     n_hidden=500,
                     W=None,
                     hbias=None,
                     vbias=None):
            self.n_visible=n_visible
            self.n_hidden=n_hidden
      
            if rng is None:
                rng=np.random.RandomState(1234)
            if trng is None:
                trng=RandomStreams(rng.randint(2**30))
            #初始化权重和偏置    
            if W is None:
                initW=np.asarray(rng.uniform(low=-4*np.sqrt(6./(n_hidden+n_visible)),
                                        high=4*np.sqrt(6./(n_hidden+n_visible)),
                                        size=(n_visible,n_hidden)),
                            dtype=theano.config.floatX)
                W=theano.shared(value=initW,name='W',borrow=True)
            if hbias is None:
                inithbias=np.zeros(n_hidden,dtype=theano.config.floatX)
                hbias=theano.shared(value=inithbias,name='hbias',borrow=True)
            if vbias is None:
                initvbias=np.zeros(n_visible,dtype=theano.config.floatX)
                vbias=theano.shared(value=initvbias,name='vbias',borrow=True)
            self.input=input
            if not input:
                self.input=T.matrix('input')
            self.W=W
            self.hbias=hbias
            self.vbias=vbias
            self.trng=trng
            self.params=[self.W,self.hbias,self.vbias]
      
        ##########前向计算,从可见层到隐层################
        #激活概率
        def propup(self,vis):
            pre_sigmoid_activation=T.dot(vis,self.W)+self.hbias
            return [pre_sigmoid_activation,T.nnet.sigmoid(pre_sigmoid_activation)]
        #二值激活
        def sample_h_given_v(self,v0_samples):
            pre_sigmoid_h1,h1_mean=self.propup(v0_samples)
            h1_sample=self.trng.binomial(size=h1_mean.shape,
                                            n=1,
                                            p=h1_mean,
                                            dtype=theano.config.floatX)
            return [pre_sigmoid_h1,h1_mean,h1_sample]
      
        ##########反向计算,从隐层到可见层################
        #激活概率
        def propdown(self,hid):
            pre_sigmoid_activation=T.dot(hid,self.W.T)+self.vbias
            return [pre_sigmoid_activation,T.nnet.sigmoid(pre_sigmoid_activation)]
        #二值激活
        def sample_v_given_h(self,h0_samples):
            pre_sigmoid_v1,v1_mean=self.propdown(h0_samples)
            v1_sample=self.trng.binomial(size=v1_mean.shape,
                                             n=1,
                                             p=v1_mean,
                                             dtype=theano.config.floatX)
            return [pre_sigmoid_v1,v1_mean,v1_sample]
      
        ##########吉布斯采样################
        #可见层->隐层->可见层
        def gibbs_vhv(self,v0_samples):
            pre_sigmoid_h1,h1_mean,h1_sample=self.sample_h_given_v(v0_samples)
            pre_sigmoid_v1,v1_mean,v1_sample=self.sample_v_given_h(h1_sample)
            return [pre_sigmoid_v1,v1_mean,v1_sample,
                    pre_sigmoid_h1,h1_mean,h1_sample]
      
        ############自由能量函数###############
        def free_energy(self,v_samples):
            wx_b=T.dot(v_samples,self.W)+self.hbias
            vbias_term=T.dot(v_samples,self.vbias)#第一项
            hidden_term=T.sum(T.log(1+T.exp(wx_b)),axis=1)#第二项
            return -hidden_term-vbias_term
      
        ############梯度更新#################
        def get_cost_updates(self,lr=0.1,k=1):
            ([pre_sigmoid_nvs,nv_means,nv_samples,pre_sigmoid_nhs,nh_means,nh_samples],updates)=\
            theano.scan(self.gibbs_vhv,
                           outputs_info=[None,None,self.input,None,None,None],
                           n_steps=k,
                           name='gibbs_vhv')
            chain_end=nv_samples[-1]
            cost=T.mean(self.free_energy(self.input))-T.mean(self.free_energy(chain_end))
            gparams=T.grad(cost,self.params,consider_constant=[chain_end])
            for gparam,param in zip(gparams,self.params):
                updates[param]=param-gparam*T.cast(lr,dtype=theano.config.floatX)
      
            ##################期望看到交叉熵损失##############
            monitor_cost=self.get_reconstruction_cost(pre_sigmoid_nvs[-1])
            return monitor_cost,updates
      
        ########非持续性对比散度,重构误差#########
        def get_reconstruction_cost(self,pre_sigmoid_nv):
            cross_entropy=T.mean(T.sum(self.input*T.log(T.nnet.sigmoid(pre_sigmoid_nv))+\
                                          (1-self.input)*T.log(1-T.nnet.sigmoid(pre_sigmoid_nv)),
                                          axis=1))
            return cross_entropy
    • 搭建MLP需要的隐层定义

      
      #定义多层感知器的隐层单元相关操作
      
      class HiddenLayer(object):
        def __init__(self,rng,input,n_in,n_out,W=None,b=None,activation=T.tanh):
            self.input=input
            if W is None:
                W_values=np.asarray(rng.uniform(low=- np.sqrt(6./(n_in+n_out)),
                                               high= np.sqrt(6./(n_in+n_out)),
                                               size=(n_in,n_out)),dtype=theano.config.floatX)
                if activation==T.nnet.sigmoid:
                    W_values *= 4
                W=theano.shared(value=W_values,name='W',borrow=True)
            if b is None:
                b_vaules=np.zeros((n_out,),dtype=theano.config.floatX)
                b=theano.shared(value=b_vaules,name='b',borrow=True)
      
            self.W=W
            self.b=b
      
            lin_output=T.dot(input,self.W)+self.b#未被激活的线性操作
            self.output=(lin_output if activation is None else activation(lin_output))
            self.params=[self.W,self.b]
    • 最后微调需要softmax

      
      #定义最后一层softmax
      
      class LogisticRegression(object):
        def __init__(self,input,n_in,n_out):
            #共享权重
            self.W=theano.shared(value=np.zeros((n_in,n_out),dtype=theano.config.floatX),
                                name='W',
                                borrow=True)
            #共享偏置
            self.b=theano.shared(value=np.zeros((n_out,),dtype=theano.config.floatX),
                                name='b',
                                borrow=True)
            #softmax函数
            self.p_y_given_x=T.nnet.softmax(T.dot(input,self.W)+self.b)
            #预测值
            self.y_pred=T.argmax(self.p_y_given_x,axis=1)
            self.params=[self.W,self.b]#模型参数
            self.input=input#模型输入
      
        #定义负对数似然
        def negative_log_likelihood(self,y):
            return -T.mean(T.log(self.p_y_given_x)[T.arange(y.shape[0]),y])
      
        #定义误差
        def errors(self, y):
      
            # check if y has same dimension of y_pred
            if y.ndim != self.y_pred.ndim:
                raise TypeError(
                    'y should have the same shape as self.y_pred',
                    ('y', y.type, 'y_pred', self.y_pred.type)
                )
            # check if y is of the correct datatype
            if y.dtype.startswith('int'):
                # the T.neq operator returns a vector of 0s and 1s, where 1
                # represents a mistake in prediction
                return T.mean(T.neq(self.y_pred, y))
            else:
                raise NotImplementedError()

    准备工作完成以后, 可以进行DBN的定义了, 首先定义结构, 主要包含多个隐层, RBM的逐层训练, 由于MLP和多个RBM是共享隐单元的, 所以无需重复定义, 但是最后需要使用softmax层作为微调梯度的来源.

    class DBN(object):
        def __init__(self,
                     rng=None,
                     trng=None,
                     n_visible=784,
                     n_hidden=[500,500],
                     n_out=10):
            self.sigmoid_layers=[]
            self.rbm_layers=[]
            self.params=[]
            self.n_layers=len(n_hidden)
            assert self.n_layers>0
            if not trng:
                trng=RandomStreams(rng.randint(2**30))
            self.x=T.matrix('x')#输入
            self.y=T.ivector('y')#标签
            for i in range(self.n_layers):
                #初始化各隐层
                if i==0:
                    input_size=n_visible
                else:
                    input_size=n_hidden[i-1]
    
                if i==0:
                    layer_input=self.x
                else:
                    layer_input=self.sigmoid_layers[-1].output
                #建立隐层
                sigmoid_layer=HiddenLayer(rng=rng,
                                           input=layer_input,
                                           n_in=input_size,
                                           n_out=n_hidden[i],
                                           activation=T.nnet.sigmoid)
    
                self.sigmoid_layers.append(sigmoid_layer)
                self.params.extend(sigmoid_layer.params)
                #逐层预训练
                rbm_layer=RBM(rng=rng,
                              trng=trng,
                              input=layer_input,
                              n_visible=input_size,
                              n_hidden=n_hidden[i],
                              W=sigmoid_layer.W,
                              hbias=sigmoid_layer.b)
                self.rbm_layers.append(rbm_layer)
            #微调分类层
            self.logLayer=LogisticRegression(input=self.sigmoid_layers[-1].output,
                                                 n_in=n_hidden[-1],
                                                 n_out=n_out)
            self.params.extend(self.logLayer.params)
            self.finetune_cost=self.logLayer.negative_log_likelihood(self.y)
            self.errors=self.logLayer.errors(self.y)

    这里一定要注意微调分类层不是包含在for循环中的, 虽然大家都知道, 但是写代码就是容易发生这个对齐情况, 我当时就写错了, 找了半天错误, 错误提示是

    构建DBN
    预训练开始
    第0层第0次迭代, 损失-981层第0次迭代, 损失-3322层第0次迭代, 损失-52
    开始微调
    ---------------------------------------------------------------------------
    DisconnectedInputError                    Traceback (most recent call last)
    <ipython-input-13-1ad031bf1afb> in <module>()
    ----> 1 test_DBN()
    
    <ipython-input-12-1ea97c3e407d> in test_DBN(pretrain_lr, k, pretrain_epoches, finetune_lr, train_epoch, dataset, batch_size)
         18             print('第%d层第%d次迭代, 损失%d' %(i,epoch,np.mean(c,dtype='float64')))
         19     print('开始微调')
    ---> 20     train_fn,validate_model,test_model=dbn.finetune(datasets=datasets,batch_size=batch_size,learning_rate=finetune_lr)
         21     patience=4*n_train_batches
         22     patience_inc=2.0
    
    <ipython-input-11-f24396c0dd18> in finetune(self, datasets, batch_size, learning_rate)
         78 
         79         index=T.lscalar('index')
    ---> 80         gparams=T.grad(self.finetune_cost,self.params)
         81         updates=[]
         82         for param,gparam in zip(self.params,gparams):
    
    C:\ProgramData\Anaconda2\lib\site-packages\theano\gradient.pyc in grad(cost, wrt, consider_constant, disconnected_inputs, add_names, known_grads, return_disconnected, null_gradients)
        537         if elem not in var_to_app_to_idx and elem is not cost \
        538                 and elem not in grad_dict:
    --> 539             handle_disconnected(elem)
        540             grad_dict[elem] = disconnected_type()
        541 
    
    C:\ProgramData\Anaconda2\lib\site-packages\theano\gradient.pyc in handle_disconnected(var)
        524             elif disconnected_inputs == 'raise':
        525                 message = utils.get_variable_trace_string(var)
    --> 526                 raise DisconnectedInputError(message)
        527             else:
        528                 raise ValueError("Invalid value for keyword "
    
    DisconnectedInputError:  
    Backtrace when that variable is created:
    
      File "C:\ProgramData\Anaconda2\lib\site-packages\ipykernel\zmqshell.py", line 533, in run_cell
        return super(ZMQInteractiveShell, self).run_cell(*args, **kwargs)
      File "C:\ProgramData\Anaconda2\lib\site-packages\IPython\core\interactiveshell.py", line 2718, in run_cell
        interactivity=interactivity, compiler=compiler, result=result)
      File "C:\ProgramData\Anaconda2\lib\site-packages\IPython\core\interactiveshell.py", line 2828, in run_ast_nodes
        if self.run_code(code, result):
      File "C:\ProgramData\Anaconda2\lib\site-packages\IPython\core\interactiveshell.py", line 2882, in run_code
        exec(code_obj, self.user_global_ns, self.user_ns)
      File "<ipython-input-13-1ad031bf1afb>", line 1, in <module>
        test_DBN()
      File "<ipython-input-12-1ea97c3e407d>", line 10, in test_DBN
        dbn=DBN(rng=rng,trng=RandomStreams(rng.randint(2**30)),n_visible=28*28,n_hidden=[1000, 500, 400],n_out=10)
      File "<ipython-input-11-f24396c0dd18>", line 49, in __init__
        n_out=n_out)
      File "<ipython-input-5-22c6bb9a49a7>", line 7, in __init__
        borrow=True)

    然后就可以定义预训练过程了,逐层更新, 依旧是使用for循环

    def pretrain(self,train_set,batch_size,k):
            index=T.lscalar('index')
            learning_rate=T.scalar('lr')
            batch_begin=index*batch_size
            batch_end=batch_begin+batch_size
            pretrain_fns=[]
            for rbm in self.rbm_layers:
                cost,updates=rbm.get_cost_updates(learning_rate,k=k)
                fn=theano.function(inputs=[index,theano.In(learning_rate,value=0.1)],
                                   outputs=cost,
                                   updates=updates,
                                   givens={
                                       self.x:train_set[batch_begin:batch_end]
                                   })
                pretrain_fns.append(fn)
            return pretrain_fns

    微调阶段与MLP的构建一样, 利用训练集更新参数, 利用验证集和测试集查看模型效果

    def finetune(self,datasets,batch_size,learning_rate):
            (train_set_x,train_set_y)=datasets[0]
            (valid_set_x,valid_set_y)=datasets[1]
            (test_set_x,test_set_y)=datasets[2]
    
            n_valid_batches=valid_set_x.get_value(borrow=True).shape[0]
            n_valid_batches//=batch_size
            n_test_batches=test_set_x.get_value(borrow=True).shape[0]
            n_test_batches//=batch_size
    
            index=T.lscalar('index')
            gparams=T.grad(self.finetune_cost,self.params)
            updates=[]
            for param,gparam in zip(self.params,gparams):
                updates.append((param,param-gparam*learning_rate))
    
            train_fn=theano.function(inputs=[index],
                                     outputs=self.finetune_cost,
                                     updates=updates,
                                     givens={
                                         self.x:train_set_x[index*batch_size:(index+1)*batch_size],
                                         self.y:train_set_y[index*batch_size:(index+1)*batch_size]
                                     })
            valid_score=theano.function(inputs=[index],
                                        outputs=self.errors,
                                        givens={
                                            self.x:valid_set_x[index*batch_size:(index+1)*batch_size],
                                            self.y:valid_set_y[index*batch_size:(index+1)*batch_size]
                                        })
            test_score=theano.function(inputs=[index],
                                       outputs=self.errors,
                                       givens={
                                           self.x:test_set_x[index*batch_size:(index+1)*batch_size],
                                           self.y:test_set_y[index*batch_size:(index+1)*batch_size]
                                       })

    对于验证集和测试集, 我们希望得到准确率信息

    def valid():
        return [valid_score(i) for i in range(n_valid_batches)]
    
    def test():
        return [test_score(i) for i in range(n_test_batches)]
    
    return train_fn,valid,test

    最终运行阶段, 首先初始化一个DBN网络

        datasets=load_data(dataset)
        train_set_x,train_set_y=datasets[0]
        n_train_batches=train_set_x.get_value(borrow=True).shape[0]//batch_size
        print('构建DBN')
        rng=np.random.RandomState(123)
        trng=RandomStreams(rng.randint(2**30))
        dbn=DBN(rng=rng,trng=RandomStreams(rng.randint(2**30)),n_visible=28*28,n_hidden=[500, 200, 100],n_out=10)

    然后正式逐层RBM预训练

        print('预训练开始')
        pretrain_fns=dbn.pretrain(train_set=train_set_x,batch_size=batch_size,k=k)
        for i in range(dbn.n_layers):
            for epoch in range(pretrain_epoches):
                c=[]
                for batch_index in range(n_train_batches):
                    c.append(pretrain_fns[i](index=batch_index,lr=pretrain_lr))
                print('第%d层第%d次迭代, 损失%d' %(i,epoch,np.mean(c,dtype='float64')))

    提前终止算法微调

     print('开始微调')
        train_fn,validate_model,test_model=dbn.finetune(datasets=datasets,batch_size=batch_size,learning_rate=finetune_lr)
        patience=4*n_train_batches
        patience_inc=2.0
        imp_threshold=0.995
        valid_frequence=min(n_train_batches,patience/2)
    
        best_loss=np.inf
        test_socre=0.0
        done_loop=False
        epoch=0
        while(epoch<train_epoch) and (not done_loop):
            epoch=epoch+1
            for minibatch_index in range(n_train_batches):
                train_fn(minibatch_index)
                iter=(epoch-1)*n_train_batches+minibatch_index
                if (iter+1)%valid_frequence==0:
                    valid_loss=validate_model()
                    this_valid_loss=np.mean(valid_loss,dtype='float64')
                    print('第%d次迭代, 第%d个批次,验证误差为%f %%' %(epoch,minibatch_index+1,this_valid_loss*100))
                    if this_valid_loss<best_loss:
                        if this_valid_loss<best_loss*imp_threshold:
                            patience=max(patience,iter*patience_inc)
                        best_loss=this_valid_loss
                        best_iter=iter
                        test_loss=test_model()
                        test_score=np.mean(test_loss,dtype='float64')
                        print('第%d次训练, 第%d批数据,测试误差为%f %%' %(epoch,minibatch_index+1,test_score*100.0))
                if patience<=iter:
                    done_loop=True
                    break

    模型的保存方法就不写了, 和MLP的差不多, 主要还是因为我python不是特别好, 搞不好又出一堆错误, 训练结果如下:

    构建DBN
    预训练开始
    第0层第0次迭代, 损失-1061层第0次迭代, 损失-1802层第0次迭代, 损失-38
    开始微调
    第1次迭代, 第5000个批次,验证误差为5.360000 %
    第1次训练, 第5000批数据,测试误差为6.100000 %
    第2次迭代, 第5000个批次,验证误差为4.100000 %
    第2次训练, 第5000批数据,测试误差为4.510000 %
    第3次迭代, 第5000个批次,验证误差为3.490000 %
    第3次训练, 第5000批数据,测试误差为4.030000 %
    第4次迭代, 第5000个批次,验证误差为3.250000 %
    第4次训练, 第5000批数据,测试误差为3.560000 %
    第5次迭代, 第5000个批次,验证误差为3.020000 %
    第5次训练, 第5000批数据,测试误差为3.320000 %
    第6次迭代, 第5000个批次,验证误差为2.830000 %
    第6次训练, 第5000批数据,测试误差为3.220000 %
    第7次迭代, 第5000个批次,验证误差为2.790000 %
    第7次训练, 第5000批数据,测试误差为2.990000 %
    第8次迭代, 第5000个批次,验证误差为2.650000 %
    第8次训练, 第5000批数据,测试误差为2.800000 %
    第9次迭代, 第5000个批次,验证误差为2.600000 %
    第9次训练, 第5000批数据,测试误差为2.690000 %
    第10次迭代, 第5000个批次,验证误差为2.620000 %
    第11次迭代, 第5000个批次,验证误差为2.570000 %
    第11次训练, 第5000批数据,测试误差为2.580000 %
    第12次迭代, 第5000个批次,验证误差为2.480000 %
    第12次训练, 第5000批数据,测试误差为2.580000 %
    第13次迭代, 第5000个批次,验证误差为2.460000 %
    第13次训练, 第5000批数据,测试误差为2.590000 %
    第14次迭代, 第5000个批次,验证误差为2.440000 %
    第14次训练, 第5000批数据,测试误差为2.520000 %
    第15次迭代, 第5000个批次,验证误差为2.370000 %
    第15次训练, 第5000批数据,测试误差为2.500000 %
    第16次迭代, 第5000个批次,验证误差为2.320000 %
    第16次训练, 第5000批数据,测试误差为2.460000 %
    第17次迭代, 第5000个批次,验证误差为2.310000 %
    第17次训练, 第5000批数据,测试误差为2.510000 %
    第18次迭代, 第5000个批次,验证误差为2.310000 %
    第19次迭代, 第5000个批次,验证误差为2.260000 %
    第19次训练, 第5000批数据,测试误差为2.430000 %
    第20次迭代, 第5000个批次,验证误差为2.230000 %
    第20次训练, 第5000批数据,测试误差为2.360000 %

    后记

    自己写代码的时候主要就是刚才提到的那个对齐错误, 导致整个程序的错误日志有点看不懂, 大概意思就是梯度更新的位置出现了问题, 但是导致梯度出问题的原因可能有很多, 当代码量较大的时候就不太好查找了, 所以大家写代码一定要仔细仔细仔细.

    博文代码:链接: https://pan.baidu.com/s/1gfaTR6z 密码: fhe2

    展开全文
  •  理论上来说, 隐藏层越多, 模型的表达能力应该越强。但是, 当隐藏层数多于一层时, 如果我们使用随机值来初始化权重, 使用梯度下降来优化参数就会出现许多问题[1]: 如果初始权重值设置的过大, 则训练过程中权重值会...
  • 深度信念神经网络DBN最通俗易懂的教程

    万次阅读 多人点赞 2017-08-06 21:45:22
    前几天我把受限玻尔兹曼机给讲完了,这次我接着讲一下它的进阶理论,还是那句话,全是白话文,你们肯定能听懂 深度信念网络,它的英文名叫作Deep Belief Network,先解释一下这个名词: Deep意思是深度,大家应该...
  • 目录 1 绪论 1 1.1 选题背景及意义 1 1.1.1 选题背景 1 1.1.2 研究意义 3 1.2 国内外研究现状 3 1.3 本文主要工作 4 2 相关理论基础 9 2.1 入侵检测 9 2.1.1 入侵检测概述 9 2.1.2 入侵检测分类 11 .1.3 四大类网络...
  • 深度信念网络曾照亮了深度学习的前进之路,今天,Hinton 通过远程视频演讲,再一次为我们展现了DBN的思想与理论之美。 2006年,研究者对深度神经网络的兴趣再度复兴,而这主要归功于Hinton等人发现的高效新方法。...
  • 针对因线路分布复杂、终端数目庞大等带来的低压台区理论线损计算困难的问题,提出一种基于深度置信网络(DBN)的低压台区理论线损计算新方法。在训练过程中,先利用贪婪算法对DBN模型中的神经网络层进行逐层无监督的预...
  • 主要讲了玻尔兹曼机的推导以及二值RBM,这一部分程序很简单但理论感觉不是很好讲,涉及到挺多随机过程和概率的知识。Bengio那篇文章其实讲的很详细,不过他是从自由能的角度讲的,物理意义挺清楚但推导过程不如网上...
  • 大家好,走过路过不要错过,想系统地学习深度神经网络算法吗?...还在为没有代码没有理论知识发愁吗?还怕毕 业不了吗?不用怕了,现在博主推出自己的家当了,有兴趣的加博 主qq:1274323974 ...
  • 动态贝叶斯网络推理学习理论及应用;动态系统;可靠性评估;故障诊断;贝叶斯网络建模;DBN
  • 本套餐从深度学习的理论基础知识开始讲解,其中包括深度置信网络DBN,卷积神经网络CNN,深度残差网络RES及长短时记忆网络LSTM等知识点;还包括python的入门知识,numpy,pandas,matplotlib的使用。后通过如今热门的...
  • 本文章从深度神经网络(DNN)入手,对深度学习...内容包括:卷积神经网络(CNN)、循环神经网络(RNN)、长时记忆(LSTM)和门控递归单元(GRU)、自动编码器(AE)、深度信念网络(DBN)、生成对抗性网络(GAN)和深度强化学习(DRL)。
  • 介绍深度置信网络(DBN理论基础的发展,对比分析深层结构DBN与浅层网络结构的差异,最后引用多篇文献分析研究DBN在文字检测、人脸及表情识别领域和遥感图像领域的应用效果。全面介绍了深度学习模型DBN,深入分析...
  • 为了增强电力调度的智能化程度,文中基于深度学习理论对用电负荷的分析与预测方法进行研究。对玻尔兹曼机(RBM)中的能量传递机制进行研究,将低层次的RBM作为高层次的输入搭建深度置信网络(DBN),实现电力负荷...
  • 变结构动态贝叶斯网络(DBN)描述的是一个非稳态随机过程,...在定义了单隐变量变结构离散动态贝叶斯模型和单元的基础上,提出了算法的基本思想,并从理论上对算法进行了推导。仿真实验验证了该算法的正确性和有效性。
  • deep learn toolbox的问题

    2015-06-29 16:14:00
    用自己的数据集,输入到deep learning toolbox的dbn下,理论应该是分3类,但每次全都分到一类,求帮忙
  • 我们提出了 GlobalMIT,这是一个工具箱,用于使用最近引入的基于信息理论的评分指标互信息测试 (MIT) 来学习全局最优 DBN 结构。 在 MIT 下,可以在多项式时间内高效地实现全局最优 DBN 的学习。 该工具箱是在 ...
  • 深度学习研究和进展

    千次阅读 2017-01-27 11:22:04
    1.研究背景和理论基础1958年,Rosenblatt提出感知机模型(ANN) 1986年,Hinton提出多隐层构造深层神经网络(MNN) 2006年,Hinton提出深度置信网络(DBN),成为深度学习的主要框架 然后,此算法的高效性由...
  • 结合水下声纳图像的特点,研究深度学习相关理论与研究方法后,为水下声纳图像的分类构建深度信念网络(Deep Belief Network, DBN)和卷积神经网络(Convolutional Neural Network, CNN),并进行相应的分类实验。...
  • Matlab与神经网络

    2018-12-15 19:54:19
    这是第一个,关于神经网络工具箱,深度学习,以及人工神经网络的内容,包含25中人工神经网络的matlab代码实现,Matlab神经网络用户手册,面前Matlab工具箱的神经网络理论与应用,深度学习CNN+DBN+RBM等资料。
  • 本套餐包含深度学习的理论基础,包括深度置信网络DBN,卷积神经网络CNN,深度残差网络RES以及长短时记忆网络LSTM的讲解,然后通过如今热门的深度学习框架Tensorflow来进行深度学习实践。学完本套餐后既可以掌握如今...
  • 本套餐包含深度学习的理论基础,包括深度置信网络DBN,卷积神经网络CNN,深度残差网络RES以及长短时记忆网络LSTM的讲解,然后通过如今热门的深度学习框架Caffe来进行深度学习实践。学完本套餐后既可以掌握如今核心的...
  • Training Products of Experts by Minimizing Contrastive Divergence(以下简称 PoE)是 DBN 和深度学习理论的 肇始之篇,最近在爬梳和学习 RBM 的相关知识时,发 现训练 RBM 时使用的对比散度算法在各种中英文资料 ...
  • 这段时间在看RBM得东西,很多材料都罗列了很多共识、理论,显得自己很牛B的样子。不过到具体实现,get hands dirty,就没有了。BS这种装B资料!张春霞《受限波尔兹曼机简介》(收录于...讲了讲历史,RBM对DBN的模拟
  • WORD WORD 格式可编辑版 与标准的傅里叶变换相比小波分析中使用到的小波函数具有不唯一性即...方法处理信号的结果与理论结果的误差来判定小波基的好坏由此决定小波基常用小波 基有 Haar 小波 Daubechies(dbN) 小波 Mex
  • 我对目前GAN经典的及最新的较有影响力的论文进行了阅读与整理,目前仅完成了论文梗概的总结。后续将会分篇详细介绍。...发明GAN,生成器与判别器是较简单的多层感知机,对比了RBM、MCMC、DBN、CAE、GSN等工作,给...

空空如也

空空如也

1 2 3
收藏数 42
精华内容 16
关键字:

dbn理论