受限玻尔兹曼机的python参考实现

简介

众所周知,玻尔兹曼机好是好,但是太复杂了,所以在实际应用中不会使用。而受限玻尔兹曼机(Restricted Boltzmann machine, RBM)是它的一个带约束的版本,因此模型变得更加简单。受限玻尔兹曼机是一种生成式的机器学习模型,能够学习样本的概率分布。根据学到的概率分布,可生成符合分布的样本。例如可以用它学习人脸图片的概率分布,然后用它学到的概率分布来生成一张这个世界不存在的人脸。

总而言之,RBM是通过输入数据集学习概率分布的随机生成神经网络。它把网络中的节点分为两层:

  • 可见层
  • 隐藏层

每层有若干节点。
可见层 v = ( v 1 , v 2 , . . . , v i , . . . , v n ) v = (v_1,v_2,...,v_i,...,v_n) v=(v1,v2,...,vi,...,vn),隐藏层 h = ( h 1 , h 2 , . . . , h j , . . . h m ) h=(h_1,h_2,...,h_j,...h_m) h=(h1,h2,...,hj,...hm),层内的节点没有连接。层间结点两两相连。每一条连接都有一个权重 w i j w_{ij} wij。每个节点都是二值的随机变量 v i , h j ∈ { 0 , 1 } v_i,h_j \in \{0,1\} vi,hj{01}

请添加图片描述

RBM是一种随机的动力系统,因此用联合组态能量 表示系统的一种总体状态。定义如下:
E ( v , h ; θ ) = − ∑ i j w i j v i h j − ∑ i b i v i − ∑ j a j h j E(v,h;\theta) = -\sum_{ij}w_{ij}v_ih_j-\sum_{i}b_iv_i-\sum_ja_jh_j E(v,h;θ)=ijwijvihjibivijajhj
其中模型参数 θ = { w i j , a j , b i } \theta = \{w_{ij},a_j,b_i\} θ={wij,aj,bi} a j , b i a_j,b_i aj,bi分别是偏置单元。

那么根据联合组态能量,可定义 v , h v,h v,h这两组随机向量的联合概率分布:
p θ ( v , h ) = 1 z ( θ ) e x p ( − E ( v , h ; θ ) ) = 1 z ( θ ) ∏ i j e w i j v i h j ∏ i e b i v i ∏ j e a j h j \begin{aligned} p_\theta(v,h) &= \frac{1}{z(\theta)}exp(-E(v,h;\theta)) \\ &= \frac{1}{z(\theta)} \prod _{ij}e^{w_{ij}v_ih_j} \prod_i e^{b_iv_i} \prod_j e^{a_jh_j} \end{aligned} pθ(v,h)=z(θ)1exp(E(v,h;θ))=z(θ)1ijewijvihjiebivijeajhj
其中 Z ( θ ) = ∑ v , h e x p ( − E ( v , h ; θ ) ) Z(\theta)=\sum_{v,h}exp(-E(v,h;\theta)) Z(θ)=v,hexp(E(v,h;θ))

RBM通过最大化似然函数来找到最优的参数 W , a , b W,a,b W,a,b

对于给定的训练样本 D = { v ^ ( 1 ) , v ^ ( 3 ) , . . . , v ^ ( N ) } D=\{\hat{v}^{(1)},\hat{v}^{(3)},...,\hat{v}^{(N)}\} D={v^(1),v^(3),...,v^(N)},其对数似然函数为:
L ( D ; θ ) = 1 N ∑ n = 1 N log ⁡ p ( v ^ ( N ) ; θ ) L(D;\theta)=\frac{1}{N}\sum_{n=1}^{N}\log p(\hat{v}^{(N)};\theta) L(D;θ)=N1n=1Nlogp(v^(N);θ)
p ( v ^ ( N ) ) p(\hat{v}^{(N)}) p(v^(N))可在联合分布的基础上求边缘分布得到。然后求偏导,使用梯度下降法求解参数。

log ⁡ p ( v ) = log ⁡ ∑ h exp ⁡ ( − E ( v , h ) ) − log ⁡ ∑ v ′ , h ′ exp ⁡ ( − E ( v ′ h ′ ) ) \log p(v) = \log \sum_h\exp(-E(v,h))-\log\sum_{v',h'}\exp(-E(v'h')) logp(v)=loghexp(E(v,h))logv,hexp(E(vh))
∂ log ⁡ p ( v ) ∂ θ = . . . = E p ( h ∣ v ) [ − ∂ E ( v , h ) ∂ θ ] − E p ( v ′ , h ′ ) [ − ∂ E ( v ′ , h ′ ) ∂ θ ] \frac{\partial \log p(v)}{\partial\theta}=...=E_{p(h|v)}\left[\frac{-\partial E(v,h)}{\partial \theta} \right] -E_{p(v',h')}\left[\frac{-\partial E(v',h')}{\partial \theta} \right] θlogp(v)=...=Ep(hv)[θE(v,h)]Ep(v,h)[θE(v,h)]

具体地:
∂ log ⁡ p ( v ) ∂ w i j = E p ( h ∣ v ) ( v i h j ) − E p ( v ′ , h ′ ) ( v i ′ h j ′ ) ∂ log ⁡ p ( v ) ∂ a i = E p ( h ∣ v ) ( v i ) − E p ( v ′ , h ′ ) ( v i ′ ) ∂ log ⁡ p ( v ) ∂ b i = E p ( h ∣ v ) ( h j ) − E p ( v ′ , h ′ ) ( h j ′ ) \begin{aligned} \frac{\partial \log p(v)}{\partial w_{ij}}&=E_{p(h|v)}\left(v_ih_j \right) -E_{p(v',h')}\left( v'_i h'_j \right) \\ \frac{\partial \log p(v)}{\partial a_{i}}&=E_{p(h|v)}\left(v_i \right) -E_{p(v',h')}\left( v'_i \right) \\ \frac{\partial \log p(v)}{\partial b_{i}}&=E_{p(h|v)}\left(h_j \right) -E_{p(v',h')}\left( h'_j \right) \\ \end{aligned} wijlogp(v)ailogp(v)bilogp(v)=Ep(hv)(vihj)Ep(v,h)(vihj)=Ep(hv)(vi)Ep(v,h)(vi)=Ep(hv)(hj)Ep(v,h)(hj)

偏导中含有的期望很难计算。因此需要进行采样来估计。采样及优化过程使用CD-k算法(对比散度学习算法)。通常k取1,所以算法过程如下:

在这里插入图片描述
其中的 p ( h = 1 ∣ v ) p(h=1|v) p(h=1v) p ( v = 1 ∣ h ) p(v=1|h) p(v=1h)也可以很容易从联合概率密度函数中推导出来。详见《神经网络与深度学习》P299。

代码

本文对RBM代码进行了封装。对外暴露两个函数:

  • train
  • generate

前者用于从样本学习的一概率分布,后者用于从学到的分布上进行一次随机采样。完整代码 如下。

import numpy as np
from typing import *

class RBM:
    """
    受限玻尔兹曼机参考实现
    """
    def __init__(self, N:int, M:int):
        """
        初始化受限波尔兹曼机的参数
        :param N: 可见层神经元个数
        :param M: 隐藏层神经元个数
        """
        self.W = np.zeros((N, M))
        self.a = np.zeros((N,))
        self.b = np.zeros((M))

    def __sigmoid(self, x:np.ndarray)->np.ndarray:
        """
        非线性激活函数
        :param x: 输入的数组
        :return: 输出的数组
        """
        return 1 / (1 + np.exp(- (x)))

    def __sample(self, p:np.ndarray)->np.ndarray:
        """
        按一定概率采样一个0,1数组
        :param p: 概率向量
        :return: 0/1向量
        """
        values = []
        for i in range(len(p)):
            value = np.random.choice([0,1], p=[1 - p[i], p[i]])
            values.append(value)
        return np.array(values)


    def __sample_hidden(self, v:np.ndarray)->np.ndarray:
        """
        根据的可见状态采样一个隐藏状态,即按照p(h=1|v)的概率进行采样
        :param v: 可见状态向量
        :return: 隐藏状态向量
        """
        WT = np.transpose(self.W, [1,0])
        b = self.b
        return self.__sample(self.__sigmoid(WT.dot(v) + b))

    def __sample_visible(self, h:np.ndarray)->np.ndarray:
        """
        根据隐藏状态采样一个可见状态, 即按照p(v=1|h)的概率进行采样
        :param h: 隐藏状态向量
        :return: 可见状态向量
        """
        W = self.W
        a = self.a
        return self.__sample(self.__sigmoid(W.dot(h) + a))


    def train(self, samples:List[np.ndarray], leaning_rate:float=0.01, epoch:int = 3):
        """
        使用CD-1算法不断优化模型的参数
        :param samples: 训练样本,维度为 (batch-size, N)
        :param leaning_rate: 学习率
        :param epoch: 训练的回合数
        :return: 无
        """
        for i in range(epoch):
            for j in range(len(samples)):
                sample = samples[j]
                h = self.__sample_hidden(sample)
                v1 = self.__sample_visible(h)
                h1 = self.__sample_hidden(v1)
                self.W += leaning_rate * (
                        sample.reshape((len(sample), 1)).dot( h.reshape((1, len(h))) )
                        -
                        v1.reshape((len(v1),1)).dot( h1.reshape((1, len(h1))) )
                )
                self.a += leaning_rate * (sample - v1)
                self.b += leaning_rate * (h - h1)

    def generate(self)->np.ndarray:
        """
        随机生成一个符合模型分布的可观测样本
        :return: 样本
        """
        ramdom_h = [np.random.randint(0,2) for i in range(len(self.b))]
        return self.__sample_visible(np.array(ramdom_h))

实验结果

本文使用MNIST数据集优化模型参数,以使RBM模型能够学习到数据集的分布。
数据集下载地址 http://yann.lecun.com/exdb/mnist/
实验代码地址 https://gitee.com/clouddea/rbm
下面是实验代码。程序将手写数字的图片数据从文件中读取。原图片是值为0-255单通道的灰度图像,这里为了方便模型处理,将之转换成二值图像。二值图像数据作为RBM模型的可见层的状态。

每100张图像合成一批(即一个batch)送给模型训练一次。 训练结束后,令模型随机生成100张图片。

from RBM import  RBM
import numpy as np
import struct
import cv2
import matplotlib.pyplot as plt
from typing import *

def decode_idx3_ubyte(idx3_ubyte_file):
    # 这部分代码来自: https://blog.csdn.net/panrenlong/article/details/81736754
    # 因此略去
    return images

def image2vector(image:np.ndarray)->np.ndarray:
    image[image < 0] = 0
    image[image > 0] = 1
    # plt.imshow(image * 256)
    # plt.show()
    # plt.pause(0)
    return image.flatten()

def vector2image(vector:np.ndarray)->np.ndarray:
    vector = vector.reshape(28,28)
    vector[vector != 1] = 0
    vector[vector == 1] = 256
    return vector


if __name__ == '__main__':
    #实例化模型
    rbm = RBM(784, 100) # 784 = 28 * 28, 因为这里一张图像有784个像素
    #读取图片数据
    images = decode_idx3_ubyte('./train-images.idx3-ubyte')
    i = 0
    while i < len(images):
        # 构建一批数据
        images_list = images[i:i+100]
        images_batch:List[np.ndarray] = []
        for j in range(len(images_list)):
            image = images_list[j].astype(np.int)
            images_batch.append(image2vector(image))

        #学习一次
        rbm.train(images_batch)
        print("正在处理%s / %s"%(i, len(images)))
        i += 100
        if i >= 10000:
            break


    #模型自动生成一张图片
    for i in range(100):
        v = rbm.generate()
        plt.imshow(vector2image(v), 'gray')
        plt.savefig("./output/%s.png"%(i,))
        print("正在生成 %s.png"%(i,))

下面是上诉实验代码生成的图像。可以发现模型生成的图像与输入的图像具有相似的特征。即比较亮的像素集中在图像中部。部分图像能够看出是某个数字。这说明模型学习到了输入样本的分布特征。
在这里插入图片描述
下面几张是看起来比较明显的:
数字7:
在这里插入图片描述
数字6
在这里插入图片描述

总结

本文实现了RBM,并使用它学习了MNIST数据集的分布。从生成的图像来看模型的确能够学到样本的分布特征。但是生成图像依然不够明显,这可能是因为模型本身不够强大,也有可能训练得不够充分。

  • 6
    点赞
  • 34
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
深度受限玻尔兹曼机(Deep Belief Network, DBN)是一种用于特征融合的机器学习模型,它可以通过学习数据中的特征来进行模式识别和分类。在Python中,我们可以使用现有的库来实现深度受限玻尔兹曼机模型,比如使用TensorFlow或者PyTorch等深度学习框架。 首先,我们需要导入相应的库和模块,比如TensorFlow或者PyTorch。然后,我们需要准备数据集,并对数据进行预处理和特征提取。接下来,我们可以使用深度受限玻尔兹曼机模型来训练数据,并获取特征表示。最后,我们可以利用这些特征表示来进行分类或者其他机器学习任务。 下面是一个简单的使用Python实现深度受限玻尔兹曼机模型的示例: ```python import tensorflow as tf from tensorflow.keras import layers, models # 准备数据集 (train_images, train_labels), (test_images, test_labels) = tf.keras.datasets.mnist.load_data() train_images = train_images.reshape((60000, 28 * 28)).astype('float32') / 255 test_images = test_images.reshape((10000, 28 * 28)).astype('float32') / 255 # 构建深度受限玻尔兹曼机模型 model = models.Sequential([ layers.Dense(128, activation='relu', input_shape=(28 * 28,)), layers.Dense(64, activation='relu'), layers.Dense(10, activation='softmax') ]) # 编译模型 model.compile(optimizer='adam', loss='sparse_categorical_crossentropy', metrics=['accuracy']) # 训练模型 model.fit(train_images, train_labels, epochs=10) # 使用模型进行预测 test_loss, test_acc = model.evaluate(test_images, test_labels) print('Test accuracy:', test_acc) ``` 在这个示例中,我们使用TensorFlow来构建一个简单的深度受限玻尔兹曼机模型,并使用MNIST数据集进行训练和测试。通过这样的方式,我们可以利用深度受限玻尔兹曼机来进行特征融合和模式识别任务。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值