【问题标题】:Stacking RBMs to create Deep belief network in sklearn堆叠 RBM 以在 sklearn 中创建深度信念网络
【发布时间】:2019-02-09 11:42:59
【问题描述】:

根据这个website,深度信念网络只是将多个RBM堆叠在一起,使用前一个RBM的输出作为下一个RBM的输入。

在 scikit-learn documentation 中,有一个使用 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

批量大小 = 256 或以上

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

看起来 89% 在某种程度上是具有 3 个 RBM 的网络的瓶颈。

我想知道我在这里做错了什么。我对深度信念网络的理解正确吗?

【问题讨论】:

  • 请注意,堆叠在一起的 RBM 越多,需要估计的参数就越多。 100 次迭代可能还不够。您是否检查过模型是否收敛?你检查过验证损失吗?它通常应该下降一段时间,然后在某个时候发生过拟合,然后开始上升。
  • RBM 堆栈以贪婪的方式进行训练,即您首先完全训练最低层,然后收集一些样本编码来训练下一层,然后再训练下一层,等等。我是不熟悉sklearn.pipeline,但是,鉴于它似乎是用于优化模型/转换链的通用工具,我认为它会尝试同步而不是顺序训练/拟合所有模型。
  • RBM 堆栈中的另一个更微妙的问题是所有层都具有相同数量的单元,因此您不会强制连续层依次进行更多的泛化。实际上,您并没有跨层“分配”(因为没有更好的词)学习过程,因为一旦学习了第一层的变换,所有后续层都可以简单地学习恒等变换(并且很可能会这样)是最容易学习的变换)。你现在在第 2 层和第 3 层所做的只是添加噪声(由于非线性)。
  • @alvas 这是一堆 RBM,而不是反向传播。 RBM 是通过某种对比散度的变体来训练的,它一次在一对层上运行。所以不,梯度不会在这里传播,这是预期(和期望)的行为。
  • @Paul Brodersen 我已经尝试减少连续层的单元数,但精度或多或少相同。

标签: python machine-learning scikit-learn deep-learning


【解决方案1】:

以下不是一个确定的答案,因为它缺乏任何统计严谨性。 但是,必要的参数优化和评估仍然需要几天的 CPU 时间。在此之前,我提交以下原则证明作为答案。

Tl;博士

更大的层 + 更长的训练 => 逻辑回归本身的性能

简介

正如我在我的一篇 cmets 对 OP 的帖子中所说,在 Erhan et al. (2010) 中系统地探索了使用堆叠 RBM / DBN 进行无监督预训练。准确地说,他们的设置与 OP 的设置不同,因为在训练 DBN 之后,他们添加了最后一层输出神经元并使用反向传播微调整个网络。 OP 使用逻辑回归对最后一层输出的性能来评估添加一个或多个 RBM 层的好处。 此外,Erhan 等人。也不要使用 scikit-learn 中的 64 像素 digits 数据集,而是使用 784 像素的 MNIST 图像(及其变体)。

话虽如此,相似之处足以将他们的发现作为评估 DBN 的 scikit-learn 实现的起点,这正是我所做的:我也使用 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

对一堆 RBM/一个 DBN 的输出进行逻辑回归:

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

【讨论】:

  • 为什么要使用两个大小相同的 RBM?
  • @Ahmad Erhan 等人。保持层的大小不变(对于任何给定的实验)。当我使用他们的超参数时,我也遵循了他们的例子。但是,从第一原则来看,随着您强制进行更大的泛化,具有连续更小的层应该更好。
  • @Ahmad 此外,800 的恒定层大小的优点是我们不会将输入大小更改为逻辑回归。因此,我们保持基准(纯逻辑回归)与具有 1 或 2 个 RBM 层的设置之间的可比性。如果层越来越小,逻辑回归的工作可能会越来越容易,这可能会导致结果出现偏差(与增加 RBM 层本身是否会提高性能无关)。
  • 非常感谢!一些cmets提到使用sci-kitpipeline方法不是实现DBN的正确方法。您确定该实现是构建 DBN 的正确方法吗?因为我从来没有见过这么简洁的 DBN 代码。
  • @Ahmad 如果您仔细查看关于Pipeline 的评论,您会注意到这是我自己的——在当时不知情的情况下——的意见。然后我又回去看了Pipeline的源代码,这说明它确实在做正确的事,我收回了我之前的说法。所以,是的,据我所知,这堆 RBM 以正确的方式进行了训练,使用一两个 RBM 层时性能的提高也是支持正确性的有力证据。
猜你喜欢
  • 2011-03-04
  • 2015-03-30
  • 2014-08-24
  • 2018-06-12
  • 2018-04-20
  • 2014-08-31
  • 2015-04-09
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多