# 1.17. 神经网络模型(有监督) 校验者:         [@tiantian1412](https://github.com/tiantian1412)         [@火星](https://github.com/apachecn/scikit-learn-doc-zh)         [@Loopy](https://github.com/loopyme)         [@N!no](https://github.com/lovelybuggies) 翻译者:         [@A](https://github.com/apachecn/scikit-learn-doc-zh) >**警告** > >此实现不适用于大规模数据应用。 特别是 scikit-learn 不支持 GPU。如果想要提高运行速度并使用基于 GPU 的实现以及为构建深度学习架构提供更多灵活性的框架,请参阅 [Related Projects](https://scikit-learn.org/stable/related_projects.html#related-projects) 。 ## 1.17.1. 多层感知器 **多层感知器(MLP)** 是一种监督学习算法,通过在数据集上训练来学习函数 ![f(\cdot): R^m \rightarrow R^o](img/fd6f65ce4fb7491d7628d1ce576c19d4.jpg),其中 ![m](img/94156b879a7455cb0d516efa9c9c0991.jpg) 是输入的维数,![o](img/f7f0b321634c8d80ceacdc75ee3c68b6.jpg) 是输出的维数。 给定一组特征 ![X = {x_1, x_2, ..., x_m}](img/d7228aff11bb03497e40badd984560a6.jpg) 和标签 ![y](img/0775c03fc710a24df297dedcec515aaf.jpg) ,它可以学习用于分类或回归的非线性函数。 与逻辑回归不同的是,在输入层和输出层之间,可以有一个或多个非线性层,称为隐藏层。 图1 展示了一个具有标量输出的单隐藏层 MLP。 [![http://sklearn.apachecn.org/cn/0.19.0/_images/multilayerperceptron_network.png](img/a060693e746caf8e0ff030ed5411520f.jpg)](http://sklearn.apachecn.org/cn/0.19.0/_images/multilayerperceptron_network.png) **图1:单隐藏层MLP.** 最左层的输入层由一组代表输入特征的神经元 ![\{x_i | x_1, x_2, ..., x_m\}](img/3cc550ecff73666ed35ae1efee48b4f4.jpg) 组成。 每个隐藏层中的神经元将前一层的值进行加权线性求和转换 ![w_1x_1 + w_2x_2 + ... + w_mx_m](img/f1fb5834480bfa9770be94da12bbd514.jpg) ,再通过非线性激活函数 ![g(\cdot):R \rightarrow R](img/80a5660d27392922e501744cab3623da.jpg) - 比如双曲正切函数 tanh 。 输出层接收到的值是最后一个隐藏层的输出经过变换而来的。 该模块包含公共属性 `coefs_` 和 `intercepts_` 。 `coefs_` 是一系列权重矩阵,其中下标为 ![i](img/43e13b580daefe5ba754b790dfbd216c.jpg) 的权重矩阵表示第 ![i](img/43e13b580daefe5ba754b790dfbd216c.jpg) 层和第 ![i+1](img/4aafe42b7f9cf8d06d93b9246d01bbfd.jpg) 层之间的权重。 `intercepts_` 是一系列偏置向量,其中的下标为 ![i](img/43e13b580daefe5ba754b790dfbd216c.jpg) 的向量表示添加到第 ![i+1](img/4aafe42b7f9cf8d06d93b9246d01bbfd.jpg) 层的偏置值。 多层感知器的优点: * 可以学习得到非线性模型。 * 使用``partial_fit`` 可以学习得到实时模型(在线学习)。 多层感知器的缺点: * 具有隐藏层的 MLP 具有非凸的损失函数,它有不止一个的局部最小值。 因此不同的随机初始化权重会导致不同的验证集准确率。 * MLP 需要调试一些超参数,例如隐藏层神经元的数量、层数和迭代轮数。 * MLP 对特征归一化很敏感。 克服这些缺点的方法请参阅 [实用使用技巧](#1178-实用技巧) 部分。 ## 1.17.2. 分类 [`MLPClassifier`](https://scikit-learn.org/stable/modules/generated/sklearn.neural_network.MLPClassifier.html#sklearn.neural_network.MLPClassifier "sklearn.neural_network.MLPClassifier") 类实现了通过 [Backpropagation](http://ufldl.stanford.edu/wiki/index.php/Backpropagation_Algorithm) 进行训练的多层感知器(MLP)算法。 MLP 在两个数组上进行训练:大小为 (n_samples, n_features) 的数组 X,用来储存表示训练样本的浮点型特征向量; 大小为 (n_samples,) 的数组 y,用来储存训练样本的目标值(类别标签): ```python >>> 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') ``` 拟合(训练)后,该模型可以预测新样本的标签: ```python >>> clf.predict([[2., 2.], [-1., -2.]]) array([1, 0]) ``` MLP 可以为训练数据拟合一个非线性模型。 `clf.coefs_` 包含了构成模型参数的权值矩阵: ```python >>> [coef.shape for coef in clf.coefs_] [(2, 5), (5, 2), (2, 1)] ``` 目前, [`MLPClassifier`](https://scikit-learn.org/stable/modules/generated/sklearn.neural_network.MLPClassifier.html#sklearn.neural_network.MLPClassifier "sklearn.neural_network.MLPClassifier") 只支持交叉熵损失函数,它通过运行 `predict_proba` 方法进行概率估计。 MLP 算法使用的是反向传播的方式。 更准确地说,它使用了某种形式的梯度下降来进行训练,其中的梯度是通过反向传播计算得到的。 对于分类问题而言,它最小化了交叉熵损失函数,为每个样本 ![x](img/5c82dbae35dc43d2f556f9f284d9d184.jpg) 给出一个向量形式的概率估计 ![P(y|x)](img/3cca81fd08a4732dc7061cd246b323ed.jpg): ```python >>> clf.predict_proba([[2., 2.], [1., 2.]]) array([[1.967...e-04, 9.998...-01], [1.967...e-04, 9.998...-01]]) ``` [`MLPClassifier`](https://scikit-learn.org/stable/modules/generated/sklearn.neural_network.MLPClassifier.html#sklearn.neural_network.MLPClassifier "sklearn.neural_network.MLPClassifier") 通过应用 [Softmax](https://en.wikipedia.org/wiki/Softmax_activation_function) 作为输出函数来支持多分类。 此外,该模型支持 [多标签分类](multiclass.html#multiclass) ,其中一个样本可以属于多个类别。 对于每个种类,原始输出经过 logistic 函数变换后,大于或等于 0.5 的值将为 1,否则为 0。 对于样本的预测输出,值为 1 的索引表示该样本的分类类别: ```python >>> 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]]) ``` 更多内容请参阅下面的示例和文档 [`MLPClassifier.fit`](https://scikit-learn.org/stable/modules/generated/sklearn.neural_network.MLPClassifier.html#sklearn.neural_network.MLPClassifier.fit "sklearn.neural_network.MLPClassifier.fit") 。 > **示例**: > >* [Compare Stochastic learning strategies for MLPClassifier](https://scikit-learn.org/stable/auto_examples/neural_networks/plot_mlp_training_curves.html#sphx-glr-auto-examples-neural-networks-plot-mlp-training-curves-py) >* [Visualization of MLP weights on MNIST](https://scikit-learn.org/stable/auto_examples/neural_networks/plot_mnist_filters.html#sphx-glr-auto-examples-neural-networks-plot-mnist-filters-py) ## 1.17.3. 回归 [`MLPRegressor`](https://scikit-learn.org/stable/modules/generated/sklearn.neural_network.MLPRegressor.html#sklearn.neural_network.MLPRegressor "sklearn.neural_network.MLPRegressor") 类实现了多层感知器(MLP),它使用反向传播进行训练,输出层没有使用激活函数,也可以被看作是使用恒等函数(identity function)作为激活函数。 因此,它使用平方误差作为损失函数,输出是一组连续值。 [`MLPRegressor`](https://scikit-learn.org/stable/modules/generated/sklearn.neural_network.MLPRegressor.html#sklearn.neural_network.MLPRegressor "sklearn.neural_network.MLPRegressor") 还支持多输出回归,其中一个样本可以有多个目标值。 ## 1.17.4. 正则化 [`MLPRegressor`](https://scikit-learn.org/stable/modules/generated/sklearn.neural_network.MLPRegressor.html#sklearn.neural_network.MLPRegressor "sklearn.neural_network.MLPRegressor") 类和 [`MLPClassifier`](https://scikit-learn.org/stable/modules/generated/sklearn.neural_network.MLPClassifier.html#sklearn.neural_network.MLPClassifier "sklearn.neural_network.MLPClassifier") 类都使用参数 `alpha` 作为正则化( L2 正则化)系数,正则化通过惩罚大数量级的权重值以避免过拟合问题。 下面的图表展示了不同的 alpha 值下的决策函数的变化。 [![http://sklearn.apachecn.org/cn/0.21.3/_images/sphx_glr_plot_mlp_alpha_0011.png](img/cfe45a2d171ae9c5933cd6d48cd48cb0.jpg)](https://scikit-learn.org/stable/_images/sphx_glr_plot_mlp_alpha_0011.png) 详细信息,请参阅下面的示例。 > **示例**: > > * [Varying regularization in Multi-layer Perceptron](https://scikit-learn.org/stable/auto_examples/neural_networks/plot_mlp_alpha.html#sphx-glr-auto-examples-neural-networks-plot-mlp-alpha-py) ## 1.17.5. 算法 MLP 使用 [随机梯度下降(Stochastic Gradient Descent)](https://en.wikipedia.org/wiki/Stochastic_gradient_descent),[Adam](http://arxiv.org/abs/1412.6980),或者 [L-BFGS](https://en.wikipedia.org/wiki/Limited-memory_BFGS) 进行训练。 随机梯度下降(SGD)使用损失函数相对于需要调整的参数的梯度来更新参数,即 ![w \leftarrow w - \eta (\alpha \frac{\partial R(w)}{\partial w}+ \frac{\partial Loss}{\partial w})](img/cdc5ef75d769259ef0537940296ab0b4.jpg) 其中 ![\eta](img/fe1d79339349f9b6263e123094ffce7b.jpg) 是学习率(learning rate),用来控制训练过程参数更新的步长。 ![Loss](img/16622481c2bbb001363e20660b549ae9.jpg) 是用于这个网络的损失函数(loss function)。 更多细节可以在 [SGD](6) 的文档中找到 。 Adam 在某种意义上来说类似于 SGD,因为它是随机优化器(stochastic optimizer),但它可以根据低阶矩的自适应估计自动调整参数更新的量。 使用 SGD 或 Adam ,训练过程可以支持在线学习模式和小批量学习模式。 L-BFGS 是一种粗略估计表示函数二阶偏导数的 Hessian 矩阵的求解器,它粗略估计 Hessian 矩阵的逆来进行参数更新。该实现使用 Scipy 版本的 [L-BFGS](http://docs.scipy.org/doc/scipy/reference/generated/scipy.optimize.fmin_l_bfgs_b.html)。 如果你所选择的方法是 ‘L-BFGS’,训练过程不支持在线学习模式和小批量学习模式。 ## 1.17.6. 复杂度 假设有 ![n](img/c87d9110f3d32ffa5fa08671e4af11fb.jpg) 个训练样本, ![m](img/94156b879a7455cb0d516efa9c9c0991.jpg) 个特征, ![k](img/f93871977da52a6d11045d57c3e18728.jpg) 个隐藏层,每个包含 ![h](img/c5f49595b56010ad04fce358940848e5.jpg) 个神经元 - 为简单起见, ![o](img/f7f0b321634c8d80ceacdc75ee3c68b6.jpg) 个输出神经元。 反向传播的时间复杂度是 ![O(n\cdot m \cdot h^k \cdot o \cdot i)](img/9ef5bf146675caa32b298d7e8318fc43.jpg) ,其中 ![i](img/43e13b580daefe5ba754b790dfbd216c.jpg) 是迭代次数。 由于反向传播具有高时间复杂性,最好以较少隐藏层神经元和较少隐藏层开始训练。 ## 1.17.7. 数学公式 给出一组训练样本 ![(x_1, y_1), (x_2, y_2), \ldots, (x_n, y_n)](img/ad854ab6b0056f9b521d823a98548d3f.jpg) 其中 ![x_i \in \mathbf{R}^n](img/1f1667a67d885f419222cbd85c70dd56.jpg) , ![y_i \in \{0, 1\}](img/6a8621a4ada40acd48b43436ca6a4527.jpg) ,一个单隐藏层单神经元 MLP 学习到的函数是 ![f(x) = W_2 g(W_1^T x + b_1) + b_2](img/d2fed0ae8e2b987a781ee01a92c31dfb.jpg) ,其中 ![W_1 \in \mathbf{R}^m](img/77eee205b1d286584f4002a39c9b32a3.jpg) 和 ![W_2, b_1, b_2 \in \mathbf{R}](img/855d4e5dae2b0286042ee7eef0c91ab5.jpg) 是模型参数。![W_1, W_2](img/4609693b88f682790da8203535625471.jpg) 分别是输入层与隐藏层之间和隐藏层与输出层之间的权重,![b_1, b_2](img/09ed5f467366506cf3b8d425d00db588.jpg) 分别是隐藏层和输出层的偏置值。![g(\cdot) : R \rightarrow R](img/f7129cf20abc58eaa0e261335a7606a6.jpg) 是激活函数,默认为双曲正切函数。 具体形式如下: ![g(z)= \frac{e^z-e^{-z}}{e^z+e^{-z}}](img/f72a2f9f160a11abc8568b72386776fe.jpg) 对于二分类, ![f(x)](img/da2ce2d49bbab0c389600d1c82fccf9b.jpg) 经过 logistic 函数 ![g(z)=1/(1+e^{-z})](img/05c3632395ec8941c82954de930b9d3e.jpg) 得到 0 到 1 之间的输出值。 阈值设置为 0.5 ,输出大于等于 0.5 的样本分到正类(positive class),其他的分为负类(negative class)。 如果多于两类,则 ![f(x)](img/da2ce2d49bbab0c389600d1c82fccf9b.jpg) 本身将是一个大小为 (n_classes,) 的向量。 它需要经过 softmax 函数而不是 logistic 函数进行变换,具体形式如下: ![\text{softmax}(z)_i = \frac{\exp(z_i)}{\sum_{l=1}^k\exp(z_l)}](img/2d4c303729e327500afa8bdb343713ff.jpg) 其中 ![z_i](img/e0532dc18cc4c92c2b39f4b29d33cd13.jpg) 表示 softmax 函数的第 ![i](img/43e13b580daefe5ba754b790dfbd216c.jpg) 个输入的元素,它对应于第 ![i](img/43e13b580daefe5ba754b790dfbd216c.jpg) 类, ![K](img/e279b8169ddd6581c5606c868ba52fae.jpg) 是类别的数量。 计算结果是样本 ![x](img/5c82dbae35dc43d2f556f9f284d9d184.jpg) 属于每个类别的概率的向量。 最终输出的分类结果是具有最高概率的类别。 在回归问题中,输出依然是 ![f(x)](img/da2ce2d49bbab0c389600d1c82fccf9b.jpg) ;因此,输出激活函数就是恒等函数。 MLP 根据特定问题使用不同的损失函数。 分类问题的损失函数的是交叉熵,在二分类情况中具体形式如下, ![Loss(\hat{y},y,W) = -y \ln {\hat{y}} - (1-y) \ln{(1-\hat{y})} + \alpha ||W||_2^2](img/a201561ab545f4fd9cba5a2e0eae9a94.jpg) 其中 ![\alpha ||W||_2^2](img/1f7b275b5002d3772b809055d9199f91.jpg) 是 L2 正则化的模型复杂度惩罚项;![\alpha > 0](img/11cde057716cf1a820780a60c8ffa8e4.jpg) 这个非负的超参数控制惩罚的程度。 对于回归问题,MLP 使用平方误差损失函数,具体形式如下, ![Loss(\hat{y},y,W) = \frac{1}{2}||\hat{y} - y ||_2^2 + \frac{\alpha}{2} ||W||_2^2](img/929e25fd2cb34bf9709d68d266786fd3.jpg) 从随机初始化权重开始,多层感知器(MLP)不断更新这些权重值来最小化损失函数。计算完损失之后,从输出层到前面各层进行反向传播,更新权重参数的值以减小损失函数。 在梯度下降中,计算得到损失函数关于每个权重的梯度 ![\nabla Loss_{W}](img/9ca39b9e9aa5f1a4660e45f3c9b5ef7b.jpg) 并从权重 ![W](img/369b6e6bd43ee84fe99e14c8d78cdc9f.jpg) 中减掉。用公式表示为: ![W^{i+1} = W^i - \epsilon \nabla {Loss}_{W}^{i}](img/92e5a41435bd53653e9ad36f030cbd61.jpg) 其中 ![i](img/43e13b580daefe5ba754b790dfbd216c.jpg) 是当前迭代步数, ![\epsilon](img/58ef9e1b5d2ee139dcb588a3879ca1a6.jpg) 是大于 0 学习率。 算法停止的条件或者是达到预设的最大迭代次数,或者是损失函数低于某个特定的较小值。 ## 1.17.8. 实用技巧 * 多层感知器对特征的缩放是敏感的,所以它强烈建议您归一化您的数据。 例如,将输入向量 X 的每个属性放缩到到 [0, 1] 或 [-1,+1] ,或者将其标准化使它具有 0 均值和方差 1。注意,为了得到有意义的结果,您必须对测试集也应用*相同的*尺度缩放。 您可以使用 `StandardScaler` 进行标准化。 ```python >>> from sklearn.preprocessing import StandardScaler # doctest: +SKIP >>> scaler = StandardScaler() # doctest: +SKIP >>> # Don't cheat - fit only on training data >>> scaler.fit(X_train) # doctest: +SKIP >>> X_train = scaler.transform(X_train) # doctest: +SKIP >>> # apply same transformation to test data >>> X_test = scaler.transform(X_test) # doctest: +SKIP ``` 另一个推荐的方法是在 `Pipeline` 中使用的 `StandardScaler` 。 * 最好使用 `GridSearchCV` 找到一个合理的正则化参数 ![\alpha](img/d8b3d5242d513369a44f8bf0c6112744.jpg) ,通常范围是在 `10.0 ** -np.arange(1, 7)` 。 * 据经验可知,我们观察到 *L-BFGS* 是收敛速度更快的且在小数据集上表现更好的解决方案。对于规模相对比较大的数据集,*Adam* 是非常鲁棒的。 它通常会迅速收敛,并得到相当不错的表现。另一方面,如果学习率调整得正确, 使用 momentum 或 nesterov’s momentum 的 *SGD* 可能比这两种算法更好。 ## 1.17.9. 使用 warm_start 的更多控制 如果您希望更多地控制 SGD 中的停止标准或学习率,或者想要进行额外的监视,使用 `warm_start=True` 和 `max_iter=1` 并且自身迭代可能会有所帮助: ```python >>> 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) ... # additional monitoring / inspection MLPClassifier(... ``` > **参考资料**: > >* [“Learning representations by back-propagating errors.”](http://www.iro.umontreal.ca/~pift6266/A06/refs/backprop_old.pdf) Rumelhart, David E., Geoffrey E. Hinton, and Ronald J. Williams. >* [“Stochastic Gradient Descent”](http://leon.bottou.org/projects/sgd) L. Bottou - Website, 2010. >* [“Backpropagation”](http://ufldl.stanford.edu/wiki/index.php/Backpropagation_Algorithm) Andrew Ng, Jiquan Ngiam, Chuan Yu Foo, Yifan Mai, Caroline Suen - Website, 2011. >* [“Efficient BackProp”](http://yann.lecun.com/exdb/publis/pdf/lecun-98b.pdf) Y. LeCun, L. Bottou, G. Orr, K. Müller - In Neural Networks: Tricks of the Trade 1998. >* [“Adam: A method for stochastic optimization.”](http://arxiv.org/pdf/1412.6980v8.pdf) Kingma, Diederik, and Jimmy Ba. arXiv preprint arXiv:1412.6980 (2014).