06_神经网络

描述

神经网络也称为“深度学习”,虽然深在许多机器学习应用中都有巨大的潜力,但算法往往经过精确调整,只适用于特定的使用场景。这里只讨论一些相对简单的方法,即用于分类和回归的多层感知机(MLP)。MLP 也被称为前馈神经网络,有时也简称为神经网络。

MLP 可以被视为广义的线性模型,执行多层处理后得到结论。

线性回归的预测公式为:

$ \widehat y = w[0]*x[0]+w[1]*x[1]+…+w[p]*x[p]+b$

ŷ 是输入特征 x[0] 到 x[p] 的加权求和,权重为学到的系数 w[0] 到 w[p]。

display(mglearn.plots.plot_logistic_regression_graph()) # 公式可视化

执行上例,图中左边的每个结点代表一个输入特征,连线代表学到的系数,右边的结点代表输出,是输入的加权求和。

display(mglearn.plots.plot_single_hidden_layer_graph()) 

在 MLP 中,多次重复这个计算加权求和的过程,首先计算代表中间过程的隐单元(hidden unit)然后再计算这些隐单元的加权求和并得到最终结果。

MLP 需要学习更多的系数(也叫作权重):在每个输入与每个隐单元(隐单元组成了隐层)之间有一个系数,在每个隐单元与输出之间也有一个系数。

从数学的角度看,计算一系列加权求和与只计算一个加权求和是完全相同的,因此,为了让这个模型真正比线性模型更为强大在计算完每个隐单元的加权求和之后,对结果再应用一个非线性函数(通常是校正非线性或者正切双曲线)。然后将这个函数的结果用于加权求和,计算得到输出 ŷ。

对于简单模型,隐层可以设置10,对于非常复杂的模型,隐层值可以大到10000甚至更多。

神经网络

调参

将MLPClassifier应用到two_moons数据集上

import numpy as np
import matplotlib.pyplot as plt
import mglearn
from sklearn.neural_network import MLPClassifier
from sklearn.datasets import make_moons
from sklearn.model_selection import train_test_split

X,Y=make_moons(n_samples=100,noise=0.25,random_state=3)
X_train,X_test,Y_train,y_test = train_test_split(X,Y,stratify=Y,random_state=42)

mlp  = MLPClassifier(solver='lbfgs',random_state=0).fit(X_train,Y_train)
mglearn.plots.plot_2d_separator(mlp,X_train,fill=True,alpha=0.3)
mglearn.discrete_scatter(X_train[:,0],X_train[:,1],Y_train)
plt.xlabel('Feature 0')
plt.ylabel('Feature 1')

执行上例可以看到,神经网络学到的决策边界完全是非线性的,但相对平滑。默认情况下,MLP 使用 100 个隐结点(一个隐层中的计算节点,MLP默认情况下只有一个隐层),这对于这个小型数据集来说已经相当多了,可以减少其数量(从而降低了模型复杂度)。通过hidden_layer_sizes参数调解隐层节点的大小。

mlp  = MLPClassifier(solver='lbfgs',random_state=0,hidden_layer_sizes=[10]).fit(X_train,Y_train)
mglearn.plots.plot_2d_separator(mlp,X_train,fill=True,alpha=0.3)
mglearn.discrete_scatter(X_train[:,0],X_train[:,1],Y_train)
plt.xlabel('Feature 0')
plt.ylabel('Feature 1')

执行上例,可以明显看出只有 10 个隐单元时,决策边界看起来更加参差不齐。如果使用单隐层,那么决策函数将由 10 个直线段组成。如果想得到更加平滑的决策,可以添加更多的隐单元、添加第二个隐层或者使用tanh非线性(激活函数:默认的非线性是 relu)。

调节隐层参数是hidden_layer_sizes,上例中可以看到hidden_layer_sizes接收的是一个数组。数组长度是隐层数,值为隐藏节点数

mlp  = MLPClassifier(solver='lbfgs',random_state=0,hidden_layer_sizes=[10,10]).fit(X_train,Y_train)
mglearn.plots.plot_2d_separator(mlp,X_train,fill=True,alpha=0.3)
mglearn.discrete_scatter(X_train[:,0],X_train[:,1],Y_train)
plt.xlabel('Feature 0')
plt.ylabel('Feature 1')

激活函数:activation参数修改激活函数,取值:‘identity’, ‘logistic’, ‘tanh’, ‘relu’,默认是relu

mlp  = MLPClassifier(solver='lbfgs',random_state=0,hidden_layer_sizes=[10],activation='tanh').fit(X_train,Y_train)
mglearn.plots.plot_2d_separator(mlp,X_train,fill=True,alpha=0.3)
mglearn.discrete_scatter(X_train[:,0],X_train[:,1],Y_train)
plt.xlabel('Feature 0')
plt.ylabel('Feature 1')

可以利用 L2 惩罚使权重趋向于 0,从而控制神经网络的复杂度,通过参数alpha(与线性回归模型中的相同)它的默认值很小(弱正则化)。

下面的例子是不同 alpha 值对 two_moons 数据集的影响,用的是 2 个隐层的神经网络,每层包含 10 个或 100 个单元:

fig,axes = plt.subplots(2,4,figsize=(20,8))
for axx,n_hidden_nodes in zip(axes,[10,100]):
    for ax,alpha in zip(axx,[0.0001,0.001,0.1,1]):
        mlp = MLPClassifier(solver='lbfgs',random_state=0,
                            hidden_layer_sizes=[n_hidden_nodes,n_hidden_nodes],
                            alpha=alpha)
        mlp.fit(X_train,Y_train)
        mglearn.plots.plot_2d_separator(mlp,X_train,fill=True,alpha=0.3,ax=ax)
        mglearn.discrete_scatter(X_train[:,0],X_train[:,1],Y_train,ax=ax)
        ax.set_title('n_hidden=[{},{}]\nalpha={:.4f}'.format(n_hidden_nodes,n_hidden_nodes,alpha))
        

神经网络的一个重要性质是,在开始学习之前其权重是随机设置的,这种随机初始化会影响学到的模型。也就是说,即使使用完全相同的参数,如果随机种子不同的话,我们也可能得到非常不一样的模型。如果网络很大,并且复杂度选择合理的话,那么这应该不会对精度有太大影响。

模型权重

将 MLPClassifier 应用在乳腺癌数据集上

from sklearn.datasets import load_breast_cancer

cancer = load_breast_cancer()
X_train,X_test,Y_train,Y_test= train_test_split(cancer.data,cancer.target,random_state=0)

mlp = MLPClassifier(random_state=42).fit(X_train,Y_train)
print(mlp.score(X_train,Y_train),mlp.score(X_test,Y_test))

执行上例,可以看到MLP在没有调参的情况下得到一个相当好精度(但没有其他模型好),原因可能在于数据的缩放(与SVC例子差不多)。神经网络也要求所有输入特征的变化范围相似,最理想的情况是均值为 0、方差为1。

min_on_training = X_train.min(axis=0) # 计算训练集中每个特征的最小值
range_on_training = (X_train-min_on_training).max(axis=0) # 计算训练集中每个特性的范围(最大值-最小值)
# 减去最小值,然后除以范围
# 这样每个特征都是min=0和max=1
X_train_scaled = (X_train-min_on_training)/range_on_training
print("Minimum for each feature\n{}".format(X_train_scaled.min(axis=0)))
print("Maximum for each feature\n {}".format(X_train_scaled.max(axis=0)))

# 利用训练集的最小值和范围对测试集做相同的变换
X_test_scaled = (X_test - min_on_training) / range_on_training
mlp = MLPClassifier(random_state=42).fit(X_train_scaled,Y_train)
print(mlp.score(X_train_scaled,Y_train),mlp.score(X_test_scaled,Y_test))

执行上例,可以明显看到精度又所提高。缩放之后的结果要好得多,而且也相当有竞争力。

增大 alpha 参数,降低模型的复杂性来得到更好的泛化性能

mlp = MLPClassifier(random_state=42,alpha=0.1).fit(X_train_scaled,Y_train)
print(mlp.score(X_train_scaled,Y_train),mlp.score(X_test_scaled,Y_test))

通过查看模型的权重,来了解模型学到了什么

sns.set_context({'figure.figsize':[12,6]})
sns.heatmap(mlp.coefs_[0],yticklabels=cancer.feature_names,cmap='viridis')

执行上例,图中的行对应 30 个输入特征,列对应 100 个隐单元。浅色代表较大的正值,而深色代表负值。如果某个特征对所有隐单元的权重都很小,那么这个特征对模型来说就“不太重要”。

优点、缺点和参数

优点

神经网络的主要优点是能够获取大量数据中包含的信息,并构建无比复杂的模型。给定足够的计算时间和数据,并且仔细调节参数,神经网络通常能打败其他机器学习算法。

缺点

功能强大的大型神经网,通常需要很长的训练时间。与 SVM 类似,神经网络在“均匀”数据上的性能最好,其中“均匀”是指所有特征都具有相似的含义。如果数据包含不同种类的特征,那么基于树的模型可能表现得更好。

神经网络调参比较麻烦。

估计神经网络的复杂度

最重要的参数是层数和每层的隐单元个数,首先设置 1 个或 2 个隐层,然后可以逐步增加。每个隐层的结点个数通常与输入特征个数接近。

在考虑神经网络的模型复杂度时,一个有用的度量是学到的权重(或系数)的个数。

如果有一个包含 100 个特征的二分类数据集,模型有 100 个隐单元,那么输入层和第一个隐层之间就有 100 * 100 = 10 000 个权重。在隐层和输出层之间还有 100 * 1 = 100 个权重,总共约 10 100 个权重。如果添加含有 100 个隐单元的第二个隐层,那么在第一个隐层和第二个隐层之间又有 100 * 100 = 10 000 个权重,总数变为约 20 100 个权重。

如果使用包含1000 个隐单元的单隐层,那么在输入层和隐层之间需要学习 100 * 1000 = 100 000 个权重,隐层到输出层之间需要学习 1000 * 1 = 1000 个权重,总共 101 000 个权重。如果再添加第二个隐层,就会增加 1000 * 1000 = 1 000 000 个权重,总数变为巨大的 1 101 000 个权重,这比含有 2 个隐层、每层 100 个单元的模型要大 50 倍。

神经网络调参的常用方法是,首先创建一个大到足以过拟合的网络,确保这个网络可以对任务进行学习。知道训练数据可以被学习之后,要么缩小网络,要么增大 alpha 来增强正则化,这可以提高泛化性能。

如何学习模型或用来学习参数的算法,这一点由 solver ({‘lbfgs’, ‘sgd’, ‘adam’}, default=‘adam’})参数设定。solver 有两个好用的选项。默认选项是 ‘adam’,在大多数情况下效果都很好,但对数据的缩放相当敏感。(因此,始终将数据缩放为均值为 0、方差为 1 是很重要的)。另一个选项是 ‘lbfgs’,其鲁棒性相当好,但在大型模型或大型数据集上的时间会比较长。还有更高级的 ‘sgd’(指随机梯度下降) 选项,配合learning_rate(学习率)参数以便获得最佳结果。

鸢尾花的例子

加载数据:

iris = pd.read_csv(r'..\..\seaborn-data\iris.csv') # 加载数据
iris_class_dic = {'setosa':0, 'versicolor':1, 'virginica':2}
iris_class = ['setosa', 'versicolor', 'virginica']
iris_tz_array=iris.select_dtypes(include='number')
iris_class_array=iris['species'].map(iris_class_dic)
X_train,X_test,Y_train,Y_test = train_test_split(iris_tz_array,iris_class_array,random_state=42)

模型调参

iris_mlp = MLPClassifier().fit(X_train,Y_train)
print(iris_mlp.score(X_train,Y_train),iris_mlp.score(X_test,Y_test))
# 0.9732142857142857 1.0
iris_mlp = MLPClassifier(solver='lbfgs',hidden_layer_sizes=[10,6,3]).fit(X_train,Y_train)
print(iris_mlp.score(X_train,Y_train),iris_mlp.score(X_test,Y_test))
# 0.9821428571428571 1.0
iris_mlp = MLPClassifier(solver='lbfgs',hidden_layer_sizes=[10,6,3],activation='tanh').fit(X_train,Y_train)
print(iris_mlp.score(X_train,Y_train),iris_mlp.score(X_test,Y_test))
# 0.9910714285714286 1.0

经过调参,明显可以看到训练得分在逐步提升,得到一个很棒的模型

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

白杆杆红伞伞

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值