系列文章目录
深度学习入门系列1:多层感知器概述 深度学习入门系列2:用TensorFlow构建你的第一个神经网络 深度学习入门系列3:深度学习模型的性能评价方法 深度学习入门系列4:用scikit-learn找到最好的模型 深度学习入门系列5项目实战:用深度学习识别鸢尾花种类 深度学习入门系列6项目实战:声纳回声识别 待更新: 深度学习入门系列7:项目实战:波士顿房屋价格回归 深度学习入门系列8:保存模型以便稍后进行序列化 深度学习入门系列9:训练期间用检查点保存最好模型 深度学习入门系列10:从绘制记录中理解训练期间的模型行为 深度学习入门系列11:用Dropout正则减少过拟合 深度学习入门系列12:使用学习规划来提升性能 深度学习入门系列13:卷积神经网络 深度学习入门系列14项目实战:手写数字识别 深度学习入门系列15用图像增强改善模型性能 深度学习入门系列16:图像中对象识别项目 深度学习入门系列17:项目实战:从电影评论预测情感 深度学习入门系列18:递归神经网络
在这个项目教程中,你将学习如何在自己的机器学习项目中有效的使用Keras库,并逐步完成一个二分类项目。一步步完成教程之后,你将学到:
- 如何加载训练数据并在Keras可用。
- 如何为指标数据设计并训练神经网络。
- 如何在未知数据上评价Keras神经网络模型的性能。
- 在使用神经网络时,如何进行数据准备来提高技能。
- 如何调节Keras中的神经网络的结构和配置。
让我们开始
6.1 声呐目标分类数据集
我们将要在这个教程中使用的数据集是声呐数据集。这个数据集描述了不同曲面的声呐回声。60个输入变量是不同角度的回声强度。这个二分类问题需要模型从金属圆柱中区分岩石。
这个数据集很好理解。所有的变量都是连续的而且一般在0-1之间。输出变量M代表矿物质,R代表岩石,这需要将它们转化为1和0。数据集包含208个观察对象。这本书的源码中也提供了这个数据集,或者你可以下载数据集,并以sonar.csv名字放在你的工作目录中。
使用这个数据集的好处是它是个标准化的基准问题。这意味着我们知道一个好的模型所期望的技巧是什么。使用交叉验证情况下,一个神经网络模型能够获得大约84%的准确率,对于自定义模型精度上限大概在88%。你可以在UCI机器学习仓库上了解更多关于这个数据集的信息。
6.2 基准神经网络模型性能
对于这个问题,让我们创建基准模型和结果。我们从导入我们需要的类和函数开始。
import numpy
from pandas import read_csv
from keras.models import Sequential
from keras.layers import Dense
from keras.wrappers.scikit_learn import KerasClassifier from sklearn.model_selection import cross_val_score from sklearn.preprocessing import LabelEncoder
from sklearn.model_selection import StratifiedKFold from sklearn.preprocessing import StandardScaler
from sklearn.pipeline import Pipeline
接下来,我初始化随机数生成器,以确保当我们执行这个代码时总能得到相同的结果。
seed = 7 numpy.random.seed(seed)
现在,我们使用pandas加载数据并把列划分为60输入变量(X)和1个输出变量(Y)。我们使用Pandas加载数据,因为它很容易处理字符串(输出变量),然而尝试使用NumPy直接加载数据比较困难。
dataframe = read_csv("sonar.csv", header=None)
dataset = dataframe.values
X = dataset[:,0:60].astype(float)
Y = dataset[:,60]
输出变量是字符串,我们必须把他们转成整型。我们使用scikit-learn 中 LabelEnCoder类进行处理。这个类通过 fit() 函数建立需要的编码模型,然后使用 transform() 函数把编码模型应用到创建的新输出变量上。
encoder = LabelEncoder() encoder.fit(Y)
encoded_Y = encoder.transform(Y)
现在我们准备使用Keras创建神经网络模型。我们使用scikit-learn库评价使用了多层K折交叉验证的模型。这是个重采样技术,它将提供了模型性能的估计。为了使用scikit-learn中的Keras模型,我们必须使用KerasClassifier封装器。这个类入参是我们创建和返回神经网络模型的函数。它将以fit()函数中参数顺序传入参数,如迭代次数和批量大小。让我们从定义基准模型函数开始。我们有一个单层模型的隐藏层,它的神经元数量和输出层一样多的神经元。当在新问题上创建神经网络时,这将是一个好的默认起始点。
使用小的高斯随机数来初始化权重。使用整流器激活函数。为了做预测,输出层只含有一个神经元,并使用sigmoid激活函数,为了得到0-1之间的输出概率,又能轻松自动转成清晰的类值。最后,训练期间,我们使用对数损失函数(binary_crossentropy),对于二分类问题它是首选损失函数。模型的梯度下降算法采用Adam优化算法并在训练时收集了精度。
def create_baseline():
model = Sequential()
model.add(Dense(60, input_dim=60, kernel_initializer='normal', activation='relu'))
model.add(Dense(1, kernel_initializer='normal', activation='sigmoid'))
model.compile(loss='binary_crossentropy', optimizer='adam', metrics=['accuracy'])
return model
现在是时候使用scikit-learn框架中的多层交叉验证来评估模型了。我们把训练迭代次数传到KerasClassifier中,还可以使用默认值。鉴于10-折交叉验证,模型被创建10次,详细输出被关闭了。
estimator = KerasClassifier(build_fn=create_baseline, epochs=100, batch_size=5, verbose=0) kfold = StratifiedKFold(n_splits=10, shuffle=True, random_state=seed)
results = cross_val_score(estimator, X, encoded_Y, cv=kfold)
print("Baseline: %.2f%% (%.2f%%)" % (results.mean()*100, results.std()*100))
为了完整性,下面提供所有列表的代码。
import numpy
from pandas import read_csv
from keras.models import Sequential
from keras.layers import Dense
from keras.wrappers.scikit_learn import KerasClassifier from sklearn.model_selection import cross_val_score
from sklearn.preprocessing import LabelEncoder
from sklearn.model_selection import StratifiedKFold
seed = 7
numpy.random.seed(seed)
dataframe = read_csv("sonar.csv", header=None)
dataset = dataframe.values
X = dataset[:,0:60].astype(float)
Y = dataset[:,60]
encoder = LabelEncoder()
encoder.fit(Y)
encoded_Y = encoder.transform(Y)
def create_baseline():
model = Sequential()
model.add(Dense(60, input_dim=60, kernel_initializer='normal', activation='relu'))
model.add(Dense(1, kernel_initializer='normal', activation='sigmoid'))
model.compile(loss='binary_crossentropy', optimizer='adam', metrics=['accuracy'])
return model
estimator = KerasClassifier(build_fn=create_baseline, epochs=100, batch_size=5, verbose=0)
kfold = StratifiedKFold(n_splits=10, shuffle=True, random_state=seed)
results = cross_val_score(estimator, X, encoded_Y, cv=kfold)
print("Baseline: %.2f%% (%.2f%%)" % (results.mean()*100, results.std()*100))
运行这段代码,得到下面的结果,显示了在未知数据评估模型精度的平均值和标准差。
Baseline: 81.68% (7.26%)
在没有做任何努力的情况下,这个结果已经很棒了。
6.3 用数据预处理来提供性能
在建模之前预处理数据这是个好的习惯。神经网络特别适合输入值在大小和分布都一致的。当构建神经网络模型时表格数据的有效预处理是标准化的。这是重新调整数据的位置,使每个属性的平均值为0,标准差为1。这能保持高斯分布和类高斯分布同时规范化每个属性的中心趋势。
我们可以使用scikit-learn中的StandScaler类来规范化我们声呐数据。而不是对整个数据集进行规范化。在训练集上的一个交叉验证流程之内来训练标准程序并使用标准实例来预处理未知测试数据,这是一个很好的练习。这使得在模型预处理和交叉验证过程标准化。它可以防止算法在评估过程中掌握未知的数据,从而可以从数据预处理方案中获得知识,如更清晰的分布。
我们可以在scikit-learn中只用pipeline类获取这些。pipeline是一个封装类,他能在一个交叉验证程序内运行不止一个的模型。在这,我们能够用StandardScaler定义一个Pipeline,跟在我们的神经网络模型的后面。
import numpy
from pandas import read_csv
from keras.models import Sequential
from keras.layers import Dense
from keras.wrappers.scikit_learn import KerasClassifier from sklearn.model_selection import cross_val_score from sklearn.preprocessing import LabelEncoder
from sklearn.model_selection import StratifiedKFold from sklearn.preprocessing import StandardScaler
from sklearn.pipeline import Pipeline
seed = 7
numpy.random.seed(seed)
dataframe = read_csv("sonar.csv", header=None)
dataset = dataframe.values
X = dataset[:,0:60].astype(float)
Y = dataset[:,60]
encoder = LabelEncoder()
encoder.fit(Y)
encoded_Y = encoder.transform(Y)
def create_baseline():
model = Sequential()
model.add(Dense(60, input_dim=60, kernel_initializer='normal', activation='relu'))
model.add(Dense(1, kernel_initializer='normal', activation='sigmoid'))
model.compile(loss='binary_crossentropy', optimizer='adam', metrics=['accuracy'])
return model
estimators = []
estimators.append(('standardize', StandardScaler()))
estimators.append(('mlp', KerasClassifier(build_fn=create_baseline, epochs=100,
batch_size=5, verbose=0)))
pipeline = Pipeline(estimators)
kfold = StratifiedKFold(n_splits=10, shuffle=True, random_state=seed)
results = cross_val_score(pipeline, X, encoded_Y, cv=kfold)
print("Standardized: %.2f%% (%.2f%%)" % (results.mean()*100, results.std()*100))
运行这个例子,得到下面的结果,我们确实看到了一个很小但很好的平均精度提升。
6.4 调节模型中的层数和神经元
调节神经网络有很多事情可以做,如权重初始化,激活函数,优化程序等等。有很大影响的一方面是网络本身的结构,我们叫做拓扑结构。在这一部分,我们看看在网络结构上的两个实验:使得它小点,使得它大点。当我们在你的问题上调节网络时,可以做一个很好的实验。
6.4.1 评估一个更小规模网络
我怀疑这个问题的输入变量有很大的冗余。数据从不同的角度描述了同样的信号。可能这些角度和其他信号有关联。我们强制某一种网络的特征提取,限制第一个隐藏层的表示空间。我们采用隐藏层中60个神经元的基线模型,并减少到一半30。这将在训练期间对网络施加压力,以挑选输入数据中最重要的结构来建模。
正如前面实验,我们用预处理来标准化数据,并尝试小幅度提升性能。
Standardized: 84.56% (5.74%)
运行上面这个例子,提供了西面的结果。我们能看到模型在均值估计上略微提升,标准差(平均速度)的精度分数显著减少。这是个很棒的结果,因为我们对网络大小为一半的网络做的更好,反过来要花费一半的时间来训练。
import numpy
from pandas import read_csv
from keras.models import Sequential
from keras.layers import Dense
from keras.wrappers.scikit_learn import KerasClassifier from sklearn.model_selection import cross_val_score from sklearn.preprocessing import LabelEncoder
from sklearn.model_selection import StratifiedKFold from sklearn.preprocessing import StandardScaler
from sklearn.pipeline import Pipeline
seed = 7
numpy.random.seed(seed)
dataframe = read_csv("sonar.csv", header=None)
dataset = dataframe.values
X = dataset[:,0:60].astype(float)
Y = dataset[:,60]
encoder = LabelEncoder()
encoder.fit(Y)
encoded_Y = encoder.transform(Y)
def create_smaller():
model = Sequential()
model.add(Dense(30, input_dim=60, kernel_initializer='normal', activation='relu'))
model.add(Dense(1, kernel_initializer='normal', activation='sigmoid'))
model.compile(loss='binary_crossentropy', optimizer='adam', metrics=['accuracy'])
return model
estimators = []
estimators.append(('standardize', StandardScaler()))
estimators.append(('mlp', KerasClassifier(build_fn=create_smaller, epochs=100,
batch_size=5, verbose=0)))
pipeline = Pipeline(estimators)
kfold = StratifiedKFold(n_splits=10, shuffle=True, random_state=seed) results = cross_val_score(pipeline, X, encoded_Y, cv=kfold)
print("Smaller: %.2f%% (%.2f%%)" % (results.mean()*100, results.std()*100))
6.4.2 评估较大规模的网络
一个不止一层的神经网络拓扑结构有更多的机会提取关键特征并且用有效的非线性方法结合它们。我们可以评估是否向网络中添加更多的层并稍微调整创建我们模型的函数来提高性能。在这,我们添加新的一层到网络,在第一隐藏层之后引入有30个神经元隐藏层。我们网络现在有如下的结构。
60 inputs -> [60 -> 30] -> 1 output
这里的想法是出现瓶颈之前,网络有机会建模所有的输入变量,而且被迫减半表示容量,更像上面小规模网络的实验。我们能用额外的层来处理这个过程,而不是压缩输入表示本身。
import numpy
from pandas import read_csv
from keras.models import Sequential
from keras.layers import Dense
from keras.wrappers.scikit_learn import KerasClassifier from sklearn.model_selection import cross_val_score from sklearn.preprocessing import LabelEncoder
from sklearn.model_selection import StratifiedKFold from sklearn.preprocessing import StandardScaler
from sklearn.pipeline import Pipeline
numpy.random.seed(seed)
dataframe = read_csv("sonar.csv", header=None) dataset = dataframe.values
Y = dataset[:,60]
encoder = LabelEncoder() encoder.fit(Y)
encoded_Y = encoder.transform(Y)
def create_larger():
model = Sequential()
model.add(Dense(60, input_dim=60, kernel_initializer='normal', activation='relu'))
model.add(Dense(30, kernel_initializer='normal', activation='relu'))
model.add(Dense(1, kernel_initializer='normal', activation='sigmoid'))
model.compile(loss='binary_crossentropy', optimizer='adam', metrics=['accuracy'])
return model
estimators = []
estimators.append(('standardize', StandardScaler()))
estimators.append(('mlp', KerasClassifier(build_fn=create_larger, epochs=100, batch_size=5,
verbose=0)))
pipeline = Pipeline(estimators)
kfold = StratifiedKFold(n_splits=10, shuffle=True, random_state=seed) results = cross_val_score(pipeline, X, encoded_Y, cv=kfold)
print("Larger: %.2f%% (%.2f%%)" % (results.mean()*100, results.std()*100))
运行这个例子得到下面结果,我们可以看到,我们的确获得不错的性能提升,花很少力气获得了很好结果。
Larger: 83.14% (4.52%)
进一步调节优化算法和训练周期次数等方面,期望尽可能进一步提高。你能在这个数据集上获得最好的分数是多少?
6.5 总结
在这节课,你已经学习如何一步步用Keras解决二分类问题,特别是:
- 如何在Keras中加载并处理数据用于使用。
- 如何创建一个基准神经网络模型。
- 如何用scikit-learn和多层k-fold 交叉验证来评估Keras模型。
- 数据预处理是如何提供你模型性能的。
- 调整网络拓扑结构试验能提高模型性能。
6.5.1 接下来
对于多分类和二分类问题,你现在知道如何开发神经网络模型。在接下来教程中,你将完成一个项目,开发一个神经网络模型解决回归问题。
|