.. _neural_networks_supervised: ================================== 神经网络模型(监督学习) ================================== .. currentmodule:: sklearn.neural_network .. warning:: 此实现不适用于大规模应用。特别是,scikit-learn 不提供 GPU 支持。对于更快速、基于 GPU 的实现,以及提供更多灵活性来构建深度学习架构的框架,请参阅 :ref:`related_projects` 。 .. _multilayer_perceptron: 多层感知器 ====================== **多层感知器 (MLP)** 是一种监督学习算法,通过在数据集上训练来学习函数 :math:`f: R^m \rightarrow R^o` ,其中 :math:`m` 是输入的维度,:math:`o` 是输出的维度。给定一组特征 :math:`X = {x_1, x_2, ..., x_m}` 和目标 :math:`y` ,它可以学习用于分类或回归的非线性函数逼近器。与逻辑回归不同,在输入层和输出层之间可以有一个或多个非线性层,称为隐藏层。图1展示了一个具有标量输出的单隐藏层 MLP。 .. figure:: ../images/multilayerperceptron_network.png :align: center :scale: 60% **图1:单隐藏层 MLP。** 最左边的层,称为输入层,由一组神经元 :math:`\{x_i | x_1, x_2, ..., x_m\}` 表示输入特征。隐藏层中的每个神经元使用加权线性求和 :math:`w_1x_1 + w_2x_2 + ... + w_mx_m` 对来自前一层的值进行变换,然后通过非线性激活函数 :math:`g(\cdot):R \rightarrow R` (如双曲正切函数)进行处理。输出层接收最后一个隐藏层的值,并将它们转换为输出值。 该模块包含公共属性 ``coefs_`` 和 ``intercepts_`` 。 ``coefs_`` 是一个权重矩阵列表,其中索引为 :math:`i` 的权重矩阵表示层 :math:`i` 和层 :math:`i+1` 之间的权重。 ``intercepts_`` 是一个偏置向量列表,其中索引为 :math:`i` 的向量表示添加到层 :math:`i+1` 的偏置值。 .. dropdown:: 多层感知器的优缺点 多层感知器的优点包括: + 能够学习非线性模型。 + 能够实时(在线学习)使用 ``partial_fit`` 学习模型。 多层感知器(MLP)的缺点包括: + 具有隐藏层的 MLP 具有非凸损失函数,其中存在多个局部最小值。因此,不同的随机权重初始化可能导致不同的验证准确性。 + MLP 需要调整许多超参数,例如隐藏神经元、层数和迭代次数。 + MLP 对特征缩放敏感。 请参阅 :ref:`实用使用技巧 ` 部分,该部分解决了一些这些缺点。 分类 ==== 类 :class:`MLPClassifier` 实现了使用 `反向传播 `_ 训练的多层感知器(MLP)算法。 MLP 在两个数组上进行训练:大小为 (n_samples, n_features) 的数组 X,它包含表示为浮点特征向量的训练样本;以及大小为 (n_samples,) 的数组 y,它包含训练样本的目标值(类别标签):: >>> from sklearn.neural_network import MLPClassifier >>> X = [[0., 0.], [1., 1.]] >>> y = [0, 1] >>> clf = MLPClassifier(solver='lbfgs', alpha=1e-5, ... hidden_layer_sizes=(5, 2), random_state=1) ... >>> clf.fit(X, y) MLPClassifier(alpha=1e-05, hidden_layer_sizes=(5, 2), random_state=1, solver='lbfgs') 训练(拟合)后,模型可以为新样本预测标签:: >>> clf.predict([[2., 2.], [-1., -2.]]) array([1, 0]) MLP 可以拟合一个非线性模型到训练数据。 ``clf.coefs_`` 包含了构成模型参数的权重矩阵:: >>> [coef.shape for coef in clf.coefs_] [(2, 5), (5, 2), (2, 1)] 目前,:class:`MLPClassifier` 仅支持交叉熵损失函数,该函数允许通过运行 ``predict_proba`` 方法来估计概率。 MLP 使用反向传播进行训练。更准确地说,它使用某种形式的梯度下降进行训练,并且梯度是通过反向传播计算的。对于分类,它最小化交叉熵损失函数,为每个样本 :math:`x` 提供概率估计向量 :math:`P(y|x)` :: >>> clf.predict_proba([[2., 2.], [1., 2.]]) array([[1.967...e-04, 9.998...-01], [1.967...e-04, 9.998...-01]]) :class:`MLPClassifier` 通过应用 `Softmax `_ 作为输出函数来支持多类分类。 此外,该模型支持 :ref:`多标签分类 ` ,其中一个样本可以属于多个类别。对于每个类别,原始输出通过逻辑函数传递。大于或等于 `0.5` 的值被四舍五入为 `1` ,否则为 `0` 。对于一个样本的预测输出,值为 `1` 的索引表示该样本被分配的类别:: >>> X = [[0., 0.], [1., 1.]] >>> y = [[0, 1], [1, 1]] >>> clf = MLPClassifier(solver='lbfgs', alpha=1e-5, ... hidden_layer_sizes=(15,), random_state=1) ... >>> clf.fit(X, y) MLPClassifier(alpha=1e-05, hidden_layer_sizes=(15,), random_state=1, solver='lbfgs') >>> clf.predict([[1., 2.]]) array([[1, 1]]) >>> clf.predict([[0., 0.]]) array([[0, 1]]) 请参阅下面的示例和文档字符串。 :meth:`MLPClassifier.fit` 了解更多信息。 .. rubric:: 示例 * :ref:`sphx_glr_auto_examples_neural_networks_plot_mlp_training_curves.py` * 参见 :ref:`sphx_glr_auto_examples_neural_networks_plot_mnist_filters.py` 以获取训练权重的可视化表示。 回归 ==== 类 :class:`MLPRegressor` 实现了一个多层感知器(MLP),该感知器使用反向传播进行训练,输出层没有激活函数,也可以看作是使用恒等函数作为激活函数。因此,它使用平方误差作为损失函数,输出是一组连续值。 :class:`MLPRegressor` 还支持多输出回归,其中一个样本可以有多个目标。 正则化 ====== :class:`MLPRegressor` 和 :class:`MLPClassifier` 都使用参数 ``alpha`` 进行正则化(L2正则化),这有助于通过惩罚具有较大幅度的权重来避免过拟合。下面的图显示了随着 alpha 值变化的不同决策函数。 .. figure:: ../auto_examples/neural_networks/images/sphx_glr_plot_mlp_alpha_001.png :target: ../auto_examples/neural_networks/plot_mlp_alpha.html :align: center :scale: 75 参见下面的示例以获取更多信息。 .. rubric:: 示例 * :ref:`sphx_glr_auto_examples_neural_networks_plot_mlp_alpha.py` 算法 ==== MLP 使用 `随机梯度下降 `__、:arxiv:`Adam <1412.6980>` 或 `L-BFGS `__ 进行训练。随机梯度下降(SGD)通过损失函数相对于需要调整的参数的梯度来更新参数,即 .. math:: w \leftarrow w - \eta (\alpha \frac{\partial R(w)}{\partial w} + \frac{\partial Loss}{\partial w}) 其中 :math:`\eta` 是学习率,它控制着步长。 参数空间搜索。:math:`Loss` 是用于网络的损失函数。 更多细节可以在 `SGD `_ 的文档中找到。 Adam 在某种意义上类似于 SGD,因为它是一种随机优化器,但它可以根据对低阶矩的自适应估计自动调整更新参数的量。 使用 SGD 或 Adam,训练支持在线学习和 mini-batch 学习。 L-BFGS 是一种求解器,它近似表示函数二阶偏导数的 Hessian 矩阵,并进一步近似 Hessian 矩阵的逆来进行参数更新。该实现使用了 Scipy 版本的 `L-BFGS `_ 。 如果选择的求解器是 'L-BFGS',则训练不支持在线学习或 mini-batch 学习。 复杂度 ====== 假设有 :math:`n` 个训练样本,:math:`m` 个特征,:math:`k` 个隐藏层,每个包含 :math:`h` 个神经元(为简单起见),以及 :math:`o` 个输出神经元。反向传播的时间复杂度为 :math:`O(i \cdot n \cdot (m \cdot h + (k - 1) \cdot h \cdot h + h \cdot o))` ,其中 :math:`i` 是迭代次数。由于反向传播具有较高的时间复杂度,建议从较少的隐藏神经元和较少的隐藏层开始训练。 .. dropdown:: 数学公式 给定一组训练样本 :math:`(x_1, y_1), (x_2, y_2), \ldots, (x_n, y_n)` ,其中 :math:`x_i \in \mathbf{R}^n` 且 :math:`y_i \in \{0, 1\}` ,一个包含一个隐藏层和一个隐藏神经元的 MLP 学习函数 :math:`f(x) = W_2 g(W_1^T x + b_1) + b_2` ,其中 :math:`W_1 \in \mathbf{R}^m` 且 :math:`W_2, b_1, b_2 \in \mathbf{R}` 是模型参数。:math:`W_1, W_2` 分别表示输入层和隐藏层的权重;:math:`b_1, b_2` 表示添加到隐藏层和输出层的偏置。 分别对应隐藏层和输出层。 :math:`g(\cdot) : R \rightarrow R` 是激活函数,默认设置为双曲正切函数。其表达式为: .. math:: g(z)= \frac{e^z-e^{-z}}{e^z+e^{-z}} 对于二分类问题,:math:`f(x)` 通过逻辑函数 :math:`g(z)=1/(1+e^{-z})` 处理,以获得介于零和一之间的输出值。设定阈值为0.5,输出值大于或等于0.5的样本将被分配到正类,其余样本则分配到负类。 如果有两个以上的类别,:math:`f(x)` 本身将是一个大小为 (n_classes,) 的向量。它不是通过逻辑函数,而是通过 softmax 函数处理,其表达式为: .. math:: \text{softmax}(z)_i = \frac{\exp(z_i)}{\sum_{l=1}^k\exp(z_l)} 其中 :math:`z_i` 表示输入到 softmax 的第 :math:`i` 个元素,对应于类别 :math:`i` ,而 :math:`K` 是类别的数量。结果是一个向量,包含样本 :math:`x` 属于每个类别的概率。输出是具有最高概率的类别。 在回归问题中,输出保持为 :math:`f(x)` ;因此,输出激活函数就是恒等函数。 MLP 根据问题类型使用不同的损失函数。分类问题的损失函数是平均交叉熵,在二分类情况下表达式为: .. math:: Loss(\hat{y},y,W) = -\dfrac{1}{n}\sum_{i=0}^n(y_i \ln {\hat{y_i}} + (1-y_i) \ln{(1-\hat{y_i})}) + \dfrac{\alpha}{2n} ||W||_2^2 其中 :math:`\alpha ||W||_2^2` 是 L2 正则化项(即惩罚项),用于惩罚复杂模型;而 :math:`\alpha > 0` 是一个非负超参数,控制惩罚项的大小。 对于回归问题,MLP 使用均方误差损失函数,其表达式为: .. math:: Loss(\hat{y},y,W) = \frac{1}{2n}\sum_{i=0}^n||\hat{y}_i - y_i ||_2^2 + \frac{\alpha}{2n} ||W||_2^2 从初始随机权重开始,多层感知器(MLP)通过反复更新这些权重来最小化损失函数。在计算损失后,反向传播从输出层传递到先前的层,为每个权重参数提供一个旨在减少损失的更新值。 在梯度下降中,损失相对于权重的梯度 :math:`\nabla Loss_{W}` 被计算并从 :math:`W` 中减去。更正式地,这表示为: .. math:: W^{i+1} = W^i - \epsilon \nabla {Loss}_{W}^{i} 其中 :math:`i` 是迭代步骤,而 :math:`\epsilon` 是学习率,其值大于 0。 当达到预设的最大迭代次数,或者损失的改进低于某个小数值时,算法停止。 .. _mlp_tips: 实用使用技巧 ============== * 多层感知器对特征缩放敏感,因此强烈建议对数据进行缩放。例如,将输入向量 X 中的每个属性缩放到 [0, 1] 或 [-1, +1],或者标准化为均值为 0 和方差为 1。请注意,必须对测试集应用相同的缩放以获得有意义的结果。可以使用 :class:`~sklearn.preprocessing.StandardScaler` 进行标准化。 >>> from sklearn.preprocessing import StandardScaler # doctest: +SKIP >>> scaler = StandardScaler() # doctest: +SKIP >>> # 不要作弊 - 仅在训练数据上进行拟合 >>> scaler.fit(X_train) # doctest: +SKIP >>> X_train = scaler.transform(X_train) # doctest: +SKIP >>> # 对测试数据应用相同的变换 >>> X_test = scaler.transform(X_test) # doctest: +SKIP 另一种推荐的方法是使用 :class:`~sklearn.preprocessing.StandardScaler` 在 :class:`~sklearn.pipeline.Pipeline` 中。 * 找到一个合理的正则化参数 :math:`\alpha` 最好使用 :class:`~sklearn.model_selection.GridSearchCV` ,通常在范围 ``10.0 ** -np.arange(1, 7)`` . * 根据经验,我们观察到 `L-BFGS` 在小型数据集上收敛更快且能得到更好的解决方案。然而,对于相对较大的数据集, `Adam` 非常稳健。它通常能快速收敛并提供相当不错的性能。另一方面,带有动量或 Nesterov 动量的 `SGD` 如果在学习率正确调整的情况下,可以表现得比这两种算法更好。 更多控制与 warm_start ===================== 如果你想对 SGD 的停止标准或学习率有更多控制,或者想要进行额外的监控,使用 ``warm_start=True`` 和 ``max_iter=1`` 并自行迭代可能会有所帮助:: >>> X = [[0., 0.], [1., 1.]] >>> y = [0, 1] >>> clf = MLPClassifier(hidden_layer_sizes=(15,), random_state=1, max_iter=1, warm_start=True) >>> for i in range(10): ... clf.fit(X, y) ... # 额外的监控 / 检查 MLPClassifier(... .. dropdown:: 参考文献 * `"Learning representations by back-propagating errors." `_ Rumelhart, David E., Geoffrey E. Hinton, and Ronald J. Williams. * `"Stochastic Gradient Descent" `_ L. Bottou - 网站, 2010. * `"Backpropagation" `_ Andrew Ng, Jiquan Ngiam, Chuan Yu Foo, Yifan Mai, Caroline Suen - 网站, 2011. * `"Efficient BackProp" `_ Y. LeCun, L. Bottou, G. Orr, K. Müller - 在 Neural Networks: Tricks of the Trade 1998. * :arxiv:`"Adam: A method for stochastic optimization." <1412.6980>` Kingma, Diederik, and Jimmy Ba (2014)