在sklearn中,堆叠受限玻尔兹曼机以创建深度置信网络

8
根据这个网站,深度置信网络只是将多个RBM堆叠在一起,使用前一个RBM的输出作为下一个RBM的输入。 enter image description here 在scikit-learn文档中,有一个使用RBM对MNIST数据集进行分类的示例。他们将RBMLogisticRegression放在管道中以获得更好的准确性。
因此,我想知道是否可以将多个RBM添加到该管道中,以创建如下代码所示的深度置信网络。
from sklearn.neural_network import BernoulliRBM
import numpy as np
from sklearn import linear_model, datasets, metrics
from sklearn.model_selection import train_test_split
from sklearn.pipeline import Pipeline

digits = datasets.load_digits()
X = np.asarray(digits.data, 'float32')
Y = digits.target
X = (X - np.min(X, 0)) / (np.max(X, 0) + 0.0001)  # 0-1 scaling

X_train, X_test, Y_train, Y_test = train_test_split(X, Y,
                                                    test_size=0.2,
                                                    random_state=0)

logistic = linear_model.LogisticRegression(C=100)
rbm1 = BernoulliRBM(n_components=100, learning_rate=0.06, n_iter=100, verbose=1, random_state=101)
rbm2 = BernoulliRBM(n_components=80, learning_rate=0.06, n_iter=100, verbose=1, random_state=101)
rbm3 = BernoulliRBM(n_components=60, learning_rate=0.06, n_iter=100, verbose=1, random_state=101)
DBN3 = Pipeline(steps=[('rbm1', rbm1),('rbm2', rbm2), ('rbm3', rbm3), ('logistic', logistic)])

DBN3.fit(X_train, Y_train)

print("Logistic regression using RBM features:\n%s\n" % (
    metrics.classification_report(
        Y_test,
        DBN3.predict(X_test))))

然而,我发现在管道中添加更多的RBM会降低准确性。

1个RBM在管道中--> 95%

2个RBM在管道中--> 93%

3个RBM在管道中--> 89%

下面的训练曲线显示,100次迭代是收敛的最佳选择。过多的迭代会导致过拟合,可能性再次降低。

批处理大小=10

enter image description here

批次大小为256或更高

我注意到一件有趣的事情。如果我使用更高的批量大小,网络的性能会大大恶化。当批量大小超过256时,准确度下降到不到10%。对我来说,训练曲线似乎没有意义,第一和第二个RBM学习不多,但第三个RBM突然学得很快。 enter image description here

看起来89%是具有3个RBM的网络的瓶颈。

我想知道我是否做错了什么。我的深度置信网络理解正确吗?


2
请注意,您叠加的RBM越多,需要估计的参数就越多。100次迭代可能不够。您是否检查过模型是否收敛?您是否检查过验证损失?它通常应该在一段时间内下降,然后在某个点上开始过拟合,并开始上升。 - Eskapp
6
RBM的堆栈是以贪婪的方式进行训练的,即首先完全训练最底层,然后收集一些样本编码来训练下一层,然后训练下一层,以此类推。我不熟悉sklearn.pipeline,不过鉴于它似乎是一个用于优化模型/转换链的通用工具,我会假设它尝试同时训练/拟合所有模型,而不是逐个顺序地进行。 - Paul Brodersen
4
这是一堆受限玻尔兹曼机(RBM),不是反向传播。 RBM是通过某种形式的对比散度进行训练的,它会一次处理两个层。因此,在这里没有梯度传播,这是期望的(也是所期望的)行为。 - Paul Brodersen
1
@Paul Brodersen 我已经尝试减少连续层的单元数量,但准确性基本相同。 - Raven Cheuk
1
@Eskapp 我已经检查过模型收敛了。更多的迭代会导致过拟合(验证准确率下降),而较少的迭代则不能得到最佳准确度。100次迭代是目前我能够得到的最佳结果。 - Raven Cheuk
显示剩余11条评论
1个回答

7
以下并不是一个十分明确的答案,因为它缺乏任何统计学严谨性。然而,必要的参数优化和评估仍需要数天的CPU时间。在那之前,我将提交下面的原理证明作为答案。
Tl;dr 更大的层 + 更长的训练 => 逻辑回归本身的性能 < + 1个RBM层 < + RBM堆叠 / DBN
介绍
正如我在对OP帖子的评论中所述,使用堆叠的RBM / DBN进行无监督预训练已经在Erhan et al. (2010)中得到系统地探索。准确地说,他们的设置与OP的设置不同,在训练DBN之后,他们添加了一个最终的输出神经元层,并使用反向传播微调整个网络。OP通过逻辑回归在最终层的输出上增加了一个或多个RBM层的效益。此外,Erhan等人还没有使用scikit-learn中的64像素digits数据集,而是使用784像素的MNIST图像(及其变体)。
话虽如此,这些相似之处足以将他们的发现作为评估scikit-learn实现DBN的起点,这正是我所做的:我也使用MNIST数据集,并使用Erhan等人报告的最佳参数(如果有)。这些参数与OP示例中给出的参数有很大不同,并且可能是OP模型表现不佳的原因:特别是,层大小要大得多,训练样本数量要大得多。但是,像OP一样,在管道的最后一步中使用逻辑回归来评估RBM或一堆RBM / DBN的图像转换是否改善了分类。

顺便说一句,在RBM层(800个单元)中有大约与原始图像(784个像素)相同数量的单元,这也使得在原始图像像素上进行纯逻辑回归成为合适的基准模型。

因此,我比较以下3个模型:

  1. 仅逻辑回归(即基线/基准模型),

  2. 在RBM输出上进行逻辑回归,和

  3. 在一堆RBM / DBN的输出上进行逻辑回归。

结果

与先前的文献一致,我的初步结果确实表明,使用RBM的输出进行逻辑回归可以提高性能,而仅使用原始像素值则无法达到此效果。DBN转换虽然比RBM有所改进,但改进幅度较小。

仅使用逻辑回归:

Model performance:
             precision    recall  f1-score   support

        0.0       0.95      0.97      0.96       995
        1.0       0.96      0.98      0.97      1121
        2.0       0.91      0.90      0.90      1015
        3.0       0.90      0.89      0.89      1033
        4.0       0.93      0.92      0.92       976
        5.0       0.90      0.88      0.89       884
        6.0       0.94      0.94      0.94       999
        7.0       0.92      0.93      0.93      1034
        8.0       0.89      0.87      0.88       923
        9.0       0.89      0.90      0.89      1020

avg / total       0.92      0.92      0.92     10000

在RBM输出上进行逻辑回归:

Model performance:
             precision    recall  f1-score   support

        0.0       0.98      0.98      0.98       995
        1.0       0.98      0.99      0.99      1121
        2.0       0.95      0.97      0.96      1015
        3.0       0.97      0.96      0.96      1033
        4.0       0.98      0.97      0.97       976
        5.0       0.97      0.96      0.96       884
        6.0       0.98      0.98      0.98       999
        7.0       0.96      0.97      0.97      1034
        8.0       0.96      0.94      0.95       923
        9.0       0.96      0.96      0.96      1020

avg / total       0.97      0.97      0.97     10000

在一堆受限玻尔兹曼机/深度置信网络的输出上进行逻辑回归:

Model performance:
             precision    recall  f1-score   support

        0.0       0.99      0.99      0.99       995
        1.0       0.99      0.99      0.99      1121
        2.0       0.97      0.98      0.98      1015
        3.0       0.98      0.97      0.97      1033
        4.0       0.98      0.97      0.98       976
        5.0       0.96      0.97      0.97       884
        6.0       0.99      0.98      0.98       999
        7.0       0.98      0.98      0.98      1034
        8.0       0.98      0.97      0.97       923
        9.0       0.96      0.97      0.96      1020

avg / total       0.98      0.98      0.98     10000

代码

#!/usr/bin/env python

"""
Using MNIST, compare classification performance of:
1) logistic regression by itself,
2) logistic regression on outputs of an RBM, and
3) logistic regression on outputs of a stacks of RBMs / a DBN.
"""

import numpy as np
import matplotlib.pyplot as plt

from sklearn.datasets import fetch_mldata
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LogisticRegression
from sklearn.neural_network import BernoulliRBM
from sklearn.base import clone
from sklearn.pipeline import Pipeline
from sklearn.metrics import classification_report


def norm(arr):
    arr = arr.astype(np.float)
    arr -= arr.min()
    arr /= arr.max()
    return arr


if __name__ == '__main__':

    # load MNIST data set
    mnist = fetch_mldata('MNIST original')
    X, Y = mnist.data, mnist.target

    # normalize inputs to 0-1 range
    X = norm(X)

    # split into train, validation, and test data sets
    X_train, X_test, Y_train, Y_test = train_test_split(X,       Y,       test_size=10000, random_state=0)
    X_train, X_val,  Y_train, Y_val  = train_test_split(X_train, Y_train, test_size=10000, random_state=0)

    # --------------------------------------------------------------------------------
    # set hyperparameters

    learning_rate = 0.02 # from Erhan et el. (2010): median value in grid-search
    total_units   =  800 # from Erhan et el. (2010): optimal for MNIST / only slightly worse than 1200 units when using InfiniteMNIST
    total_epochs  =   50 # from Erhan et el. (2010): optimal for MNIST
    batch_size    =  128 # seems like a representative sample; backprop literature often uses 256 or 512 samples

    C = 100. # optimum for benchmark model according to sklearn docs: https://scikit-learn.org/stable/auto_examples/neural_networks/plot_rbm_logistic_classification.html#sphx-glr-auto-examples-neural-networks-plot-rbm-logistic-classification-py)

    # TODO optimize using grid search, etc

    # --------------------------------------------------------------------------------
    # construct models

    # RBM
    rbm = BernoulliRBM(n_components=total_units, learning_rate=learning_rate, batch_size=batch_size, n_iter=total_epochs, verbose=1)

    # "output layer"
    logistic = LogisticRegression(C=C, solver='lbfgs', multi_class='multinomial', max_iter=200, verbose=1)

    models = []
    models.append(Pipeline(steps=[('logistic', clone(logistic))]))                                              # base model / benchmark
    models.append(Pipeline(steps=[('rbm1', clone(rbm)), ('logistic', clone(logistic))]))                        # single RBM
    models.append(Pipeline(steps=[('rbm1', clone(rbm)), ('rbm2', clone(rbm)), ('logistic', clone(logistic))]))  # RBM stack / DBN

    # --------------------------------------------------------------------------------
    # train and evaluate models

    for model in models:
        # train
        model.fit(X_train, Y_train)

        # evaluate using validation set
        print("Model performance:\n%s\n" % (
            classification_report(Y_val, model.predict(X_val))))

    # TODO: after parameter optimization, evaluate on test set

1
你为什么使用了两个相同大小的RBM? - Ahmad
@Ahmad Erhan等人,请保持层的大小恒定(对于任何给定的实验)。由于我正在使用他们的超参数,我也在这方面遵循了他们的例子。但是,从第一原理上讲,随着层次逐渐变小,应该会更好,因为这会强制进行更大的泛化。 - Paul Brodersen
@ Ahmad 此外,一个恒定大小为800的层具有优势,即我们不会改变逻辑回归的输入大小。因此,我们保持基准(纯逻辑回归)和带有1或2个RBM层的设置之间的可比性。如果层逐渐变小,逻辑回归可能会更容易地完成任务,这可能会扭曲结果(无论是否通过增加RBM层本身来提高性能)。 - Paul Brodersen
非常感谢!一些评论提到使用sci-kitpipeline方法并不是实现DBN的正确方式。您确定这种实现方法是构建DBN的正确方法吗?因为我从未见过如此简洁的DBN代码。 - Ahmad
@Ahmad 如果你仔细看那个关于“Pipeline”的评论,你会注意到那是我当时不知情的观点。然后我回去查看了“Pipeline”的源代码,发现它确实在做正确的事情,我撤回了之前的声明。所以,是的,据我所知,这个RBM堆栈是以正确的方式训练的,而且使用一个或两个RBM层时性能的提高也是支持正确性的有力证据。 - Paul Brodersen
显示剩余3条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接