Skip to content

1.17. 神经网络模型(有监督)

校验者:         @tiantian1412         @火星         @Loopy         @N!no 翻译者:         @A

警告

  • 此实现不适用于大规模数据应用。 特别是 scikit-learn 不支持 GPU。如果想要提高运行速度并使用基于 GPU 的实现以及为构建深度学习架构提供更多灵活性的框架,请参阅 Related Projects

1.17.1. 多层感知器

多层感知器(MLP) 是一种监督学习算法,通过在数据集上训练来学习函数 f(\cdot): R^m \rightarrow R^o,其中 m 是输入的维数,o 是输出的维数。 给定一组特征 X = {x_1, x_2, ..., x_m} 和标签 y ,它可以学习用于分类或回归的非线性函数。 与逻辑回归不同的是,在输入层和输出层之间,可以有一个或多个非线性层,称为隐藏层。 图1 展示了一个具有标量输出的单隐藏层 MLP。

http://sklearn.apachecn.org/cn/0.19.0/_images/multilayerperceptron_network.png

图1:单隐藏层MLP.

最左层的输入层由一组代表输入特征的神经元 {x_i | x_1, x_2, ..., x_m} 组成。 每个隐藏层中的神经元将前一层的值进行加权线性求和转换 w_1x_1 + w_2x_2 + ... + w_mx_m ,再通过非线性激活函数 g(\cdot):R \rightarrow R - 比如双曲正切函数 tanh 。 输出层接收到的值是最后一个隐藏层的输出经过变换而来的。

该模块包含公共属性 coefs_intercepts_coefs_ 是一系列权重矩阵,其中下标为 i 的权重矩阵表示第 i 层和第 i+1 层之间的权重。 intercepts_ 是一系列偏置向量,其中的下标为 i 的向量表示添加到第 i+1 层的偏置值。

多层感知器的优点:

  • 可以学习得到非线性模型。
  • 使用partial_fit 可以学习得到实时模型(在线学习)。

多层感知器的缺点:

  • 具有隐藏层的 MLP 具有非凸的损失函数,它有不止一个的局部最小值。 因此不同的随机初始化权重会导致不同的验证集准确率。
  • MLP 需要调试一些超参数,例如隐藏层神经元的数量、层数和迭代轮数。
  • MLP 对特征归一化很敏感。

克服这些缺点的方法请参阅 实用使用技巧 部分。

1.17.2. 分类

MLPClassifier 类实现了通过 Backpropagation 进行训练的多层感知器(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)]

目前, MLPClassifier 只支持交叉熵损失函数,它通过运行 predict_proba 方法进行概率估计。

MLP 算法使用的是反向传播的方式。 更准确地说,它使用了某种形式的梯度下降来进行训练,其中的梯度是通过反向传播计算得到的。 对于分类问题而言,它最小化了交叉熵损失函数,为每个样本 x 给出一个向量形式的概率估计 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]])

MLPClassifier 通过应用 Softmax 作为输出函数来支持多分类。

此外,该模型支持 多标签分类 ,其中一个样本可以属于多个类别。 对于每个种类,原始输出经过 logistic 函数变换后,大于或等于 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]])

更多内容请参阅下面的示例和文档 MLPClassifier.fit

示例

1.17.3. 回归

MLPRegressor 类实现了多层感知器(MLP),它使用反向传播进行训练,输出层没有使用激活函数,也可以被看作是使用恒等函数(identity function)作为激活函数。 因此,它使用平方误差作为损失函数,输出是一组连续值。

MLPRegressor 还支持多输出回归,其中一个样本可以有多个目标值。

1.17.4. 正则化

MLPRegressor 类和 MLPClassifier 类都使用参数 alpha 作为正则化( L2 正则化)系数,正则化通过惩罚大数量级的权重值以避免过拟合问题。 下面的图表展示了不同的 alpha 值下的决策函数的变化。

http://sklearn.apachecn.org/cn/master/_images/sphx_glr_plot_mlp_alpha_0011.png

详细信息,请参阅下面的示例。

示例

1.17.5. 算法

MLP 使用 随机梯度下降(Stochastic Gradient Descent)Adam,或者 L-BFGS 进行训练。 随机梯度下降(SGD)使用损失函数相对于需要调整的参数的梯度来更新参数,即

w \leftarrow w - \eta (\alpha \frac{\partial R(w)}{\partial w}+ \frac{\partial Loss}{\partial w})

其中 \eta 是学习率(learning rate),用来控制训练过程参数更新的步长。 Loss 是用于这个网络的损失函数(loss function)。

更多细节可以在 SGD 的文档中找到 。

Adam 在某种意义上来说类似于 SGD,因为它是随机优化器(stochastic optimizer),但它可以根据低阶矩的自适应估计自动调整参数更新的量。

使用 SGD 或 Adam ,训练过程可以支持在线学习模式和小批量学习模式。

L-BFGS 是一种粗略估计表示函数二阶偏导数的 Hessian 矩阵的求解器,它粗略估计 Hessian 矩阵的逆来进行参数更新。该实现使用 Scipy 版本的 L-BFGS

如果你所选择的方法是 ‘L-BFGS’,训练过程不支持在线学习模式和小批量学习模式。

1.17.6. 复杂度

假设有 n 个训练样本, m 个特征, k 个隐藏层,每个包含 h 个神经元 - 为简单起见, o 个输出神经元。 反向传播的时间复杂度是 O(n\cdot m \cdot h^k \cdot o \cdot i) ,其中 i 是迭代次数。 由于反向传播具有高时间复杂性,最好以较少隐藏层神经元和较少隐藏层开始训练。

1.17.7. 数学公式

给出一组训练样本 (x_1, y_1), (x_2, y_2), \ldots, (x_n, y_n) 其中 x_i \in \mathbf{R}^ny_i \in {0, 1} ,一个单隐藏层单神经元 MLP 学习到的函数是 f(x) = W_2 g(W_1^T x + b_1) + b_2 ,其中 W_1 \in \mathbf{R}^mW_2, b_1, b_2 \in \mathbf{R} 是模型参数。W_1, W_2 分别是输入层与隐藏层之间和隐藏层与输出层之间的权重,b_1, b_2 分别是隐藏层和输出层的偏置值。g(\cdot) : R \rightarrow R 是激活函数,默认为双曲正切函数。 具体形式如下:

g(z)= \frac{e^z-e^{-z}}{e^z+e^{-z}}

对于二分类, f(x) 经过 logistic 函数 g(z)=1/(1+e^{-z}) 得到 0 到 1 之间的输出值。 阈值设置为 0.5 ,输出大于等于 0.5 的样本分到正类(positive class),其他的分为负类(negative class)。

如果多于两类,则 f(x) 本身将是一个大小为 (n_classes,) 的向量。 它需要经过 softmax 函数而不是 logistic 函数进行变换,具体形式如下:

\text{softmax}(z)_i = \frac{\exp(z_i)}{\sum_{l=1}^k\exp(z_l)}

其中 z_i 表示 softmax 函数的第 i 个输入的元素,它对应于第 i 类, K 是类别的数量。 计算结果是样本 x 属于每个类别的概率的向量。 最终输出的分类结果是具有最高概率的类别。

在回归问题中,输出依然是 f(x) ;因此,输出激活函数就是恒等函数。

MLP 根据特定问题使用不同的损失函数。 分类问题的损失函数的是交叉熵,在二分类情况中具体形式如下,

Loss(\hat{y},y,W) = -y \ln {\hat{y}} - (1-y) \ln{(1-\hat{y})} + \alpha ||W||_2^2

其中 \alpha ||W||_2^2 是 L2 正则化的模型复杂度惩罚项;\alpha > 0 这个非负的超参数控制惩罚的程度。

对于回归问题,MLP 使用平方误差损失函数,具体形式如下,

Loss(\hat{y},y,W) = \frac{1}{2}||\hat{y} - y ||_2^2 + \frac{\alpha}{2} ||W||_2^2

从随机初始化权重开始,多层感知器(MLP)不断更新这些权重值来最小化损失函数。计算完损失之后,从输出层到前面各层进行反向传播,更新权重参数的值以减小损失函数。

在梯度下降中,计算得到损失函数关于每个权重的梯度 \nabla Loss_{W} 并从权重 W 中减掉。用公式表示为:

W^{i+1} = W^i - \epsilon \nabla {Loss}_{W}^{i}

其中 i 是当前迭代步数, \epsilon 是大于 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=Truemax_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)
...     # additional monitoring / inspection
MLPClassifier(...

参考资料



回到顶部