Keras是一个用于深度学习的Python库,它封装了高效的数值库TensorFlow和Theano。
Keras允许你快速而简单地设计和训练神经网络和深度学习模型。
在这篇文章中,你将通过逐步完成一个二进制分类项目,发现如何在你的机器学习项目中有效使用Keras库。
完成本教程后,你将知道。
- 如何加载训练数据并将其提供给Keras。
- 如何为表格数据设计和训练一个神经网络。
- 如何评估Keras中的神经网络模型在未见过的数据上的性能。
- 在使用神经网络时,如何进行数据准备以提高技能。
- 如何调整Keras中神经网络的拓扑结构和配置。
1.数据集的描述
我们在本教程中要使用的数据集是Sonar数据集。
这是一个描述在不同服务上反弹的声纳啁啾声回报的数据集。60个输入变量是不同角度下的回报强度。这是一个二元分类问题,需要一个模型来区分岩石和金属圆柱体。
这是一个被充分理解的数据集。所有的变量都是连续的,一般都在0到1的范围内。输出变量是一个字符串 "M",代表矿山,"R "代表岩石,这需要转换成整数1和0。
使用这个数据集的一个好处是,它是一个标准的基准问题。这意味着我们对一个好模型的预期技能有一定的了解。使用交叉验证,一个神经网络应该能够达到84%左右的性能,自定义模型的准确率上限为88%左右。
2.基线神经网络模型性能
让我们为这个问题创建一个基线模型和结果。
我们将首先导入所有我们需要的类和函数。
import pandas as pd
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense
from scikeras.wrappers 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
...
现在我们可以使用pandas加载数据集,并将列分成60个输入变量(X)和1个输出变量(Y)。我们使用pandas来加载数据,因为它很容易处理字符串(输出变量),而试图直接使用NumPy来加载数据会比较困难。
...
# load dataset
dataframe = pd.read_csv("sonar.csv", header=None)
dataset = dataframe.values
# split into input (X) and output (Y) variables
X = dataset[:,0:60].astype(float)
Y = dataset[:,60]
输出变量是字符串值。我们必须将它们转换为整数值0和1。
我们可以使用scikit-learn中的LabelEncoder类来做这件事。这个类将通过fit()函数使用整个数据集建立所需的编码模型,然后使用transform()函数应用编码来创建一个新的输出变量。
...
# encode class values as integers
encoder = LabelEncoder()
encoder.fit(Y)
encoded_Y = encoder.transform(Y)
现在我们已经准备好使用Keras创建我们的神经网络模型。
我们将使用scikit-learn来评估使用分层k-fold交叉验证的模型。这是一种再抽样技术,将提供对模型性能的估计。它的做法是将数据分成k个部分,在所有部分上训练模型,只有一个部分作为测试集来评估模型的性能。这个过程要重复K次,所有构建的模型的平均得分被用作性能的稳健估计。它是分层的,这意味着它将查看输出值,并试图平衡属于数据的k分片中每个类别的实例数量。
为了在scikit-learn中使用Keras模型,我们必须使用SciKeras模块的KerasClassifier包装器。这个类需要一个函数来创建并返回我们的神经网络模型。它还接受参数,这些参数将传递给fit()的调用,如epochs的数量和批次大小。
让我们从定义创建基线模型的函数开始。我们的模型将有一个单一的全连接隐藏层,其神经元的数量与输入变量相同。这是创建神经网络时一个很好的默认起点。
权重是用一个小的高斯随机数初始化的。使用整流器激活函数。输出层包含一个单一的神经元,以便进行预测。它使用sigmoid激活函数,以便在0到1的范围内产生一个概率输出,可以很容易地自动转换为清晰的类值。
最后,我们在训练期间使用对数损失函数(binary_crossentropy),这是二元分类问题的首选损失函数。该模型还使用了高效的Adam优化算法进行梯度下降,在模型训练时将收集准确度指标。
# baseline model
def create_baseline():
# create model
model = Sequential()
model.add(Dense(60, input_shape=(60,), activation='relu'))
model.add(Dense(1, activation='sigmoid'))
# Compile model
model.compile(loss='binary_crossentropy', optimizer='adam', metrics=['accuracy'])
return model
现在是时候使用scikit-learn框架中的分层交叉验证来评估这个模型了。
我们将训练历时的数量传递给KerasClassifier,同样使用合理的默认值。考虑到模型将被创建10次以进行10倍交叉验证,我们也关闭了粗略的输出。
...
# evaluate model with standardized dataset
estimator = KerasClassifier(model=create_baseline, epochs=100, batch_size=5, verbose=0)
kfold = StratifiedKFold(n_splits=10, shuffle=True)
results = cross_val_score(estimator, X, encoded_Y, cv=kfold)
print("Baseline: %.2f%% (%.2f%%)" % (results.mean()*100, results.std()*100))
把这些联系起来,完整的例子列在下面。
# Binary Classification with Sonar Dataset: Baseline
from pandas import read_csv
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense
from scikeras.wrappers import KerasClassifier
from sklearn.model_selection import cross_val_score
from sklearn.preprocessing import LabelEncoder
from sklearn.model_selection import StratifiedKFold
# load dataset
dataframe = read_csv("sonar.csv", header=None)
dataset = dataframe.values
# split into input (X) and output (Y) variables
X = dataset[:,0:60].astype(float)
Y = dataset[:,60]
# encode class values as integers
encoder = LabelEncoder()
encoder.fit(Y)
encoded_Y = encoder.transform(Y)
# baseline model
def create_baseline():
# create model
model = Sequential()
model.add(Dense(60, input_shape=(60,), activation='relu'))
model.add(Dense(1, activation='sigmoid'))
# Compile model
model.compile(loss='binary_crossentropy', optimizer='adam', metrics=['accuracy'])
return model
# evaluate model with standardized dataset
estimator = KerasClassifier(model=create_baseline, epochs=100, batch_size=5, verbose=0)
kfold = StratifiedKFold(n_splits=10, shuffle=True)
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%)
这是一个很好的分数,没有做任何艰苦的工作。
3.通过数据准备重新运行基线模型
在建模之前准备好你的数据是一个很好的做法。
神经网络模型特别适合有一致的输入值,无论是规模还是分布。
在建立神经网络模型时,对表格数据的有效数据准备方案是标准化。这就是对数据进行重新标定,使每个属性的平均值为0,标准差为1。这就保留了高斯和类高斯分布,同时使每个属性的中心趋势正常化。
我们可以使用scikit-learn的StandardScaler类来对我们的Sonar数据集进行标准化处理。
与其在整个数据集上执行标准化,不如在交叉验证运行的过程中对训练数据进行标准化程序训练,并使用训练好的标准化来准备 "未见过的 "测试折页。这使得标准化成为交叉验证过程中模型准备的一个步骤,它可以防止算法在评估过程中拥有 "未见过的 "数据的知识,这些知识可能会从数据准备方案中传递出来,比如更清晰的分布。
我们可以在scikit-learn中使用管道来实现这一点。管道是一个包装器,在交叉验证程序的一个通道中执行一个或多个模型。在这里,我们可以用StandardScaler定义一个管道,然后是我们的神经网络模型。
...
# evaluate baseline model with standardized dataset
estimators = []
estimators.append(('standardize', StandardScaler()))
estimators.append(('mlp', KerasClassifier(model=create_baseline, epochs=100, batch_size=5, verbose=0)))
pipeline = Pipeline(estimators)
kfold = StratifiedKFold(n_splits=10, shuffle=True)
results = cross_val_score(pipeline, X, encoded_Y, cv=kfold)
print("Standardized: %.2f%% (%.2f%%)" % (results.mean()*100, results.std()*100))
把这些联系起来,完整的例子列在下面。
# Binary Classification with Sonar Dataset: Standardized
from pandas import read_csv
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense
from scikeras.wrappers 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
# load dataset
dataframe = read_csv("sonar.csv", header=None)
dataset = dataframe.values
# split into input (X) and output (Y) variables
X = dataset[:,0:60].astype(float)
Y = dataset[:,60]
# encode class values as integers
encoder = LabelEncoder()
encoder.fit(Y)
encoded_Y = encoder.transform(Y)
# baseline model
def create_baseline():
# create model
model = Sequential()
model.add(Dense(60, input_shape=(60,), activation='relu'))
model.add(Dense(1, activation='sigmoid'))
# Compile model
model.compile(loss='binary_crossentropy', optimizer='adam', metrics=['accuracy'])
return model
# evaluate baseline model with standardized dataset
estimators = []
estimators.append(('standardize', StandardScaler()))
estimators.append(('mlp', KerasClassifier(model=create_baseline, epochs=100, batch_size=5, verbose=0)))
pipeline = Pipeline(estimators)
kfold = StratifiedKFold(n_splits=10, shuffle=True)
results = cross_val_score(pipeline, X, encoded_Y, cv=kfold)
print("Standardized: %.2f%% (%.2f%%)" % (results.mean()*100, results.std()*100))
运行这个例子的结果如下。
注意:鉴于算法或评估程序的随机性,或数字精度的差异,你的结果可能会有所不同。考虑多运行几次这个例子,比较一下平均结果。
我们确实看到在平均精度上有一个小的但非常好的提升。
Standardized: 84.56% (5.74%)
4.调整模型中的层数和神经元的数量
在神经网络中,有很多东西需要调整,比如权重初始化、激活函数、优化程序等等。
有一个方面可能会产生巨大的影响,那就是网络本身的结构,即网络拓扑结构。在本节中,我们看一下关于网络结构的两个实验:把它变小和把它变大。
在对你的问题进行神经网络调整时,这些都是很好的实验。
4.1.评估一个较小的网络
我怀疑这个问题的输入变量中存在很多冗余的东西。
这些数据从不同的角度描述了同一个信号。也许其中一些角度比其他角度更相关。我们可以通过限制第一隐藏层的表征空间来迫使网络进行某种类型的特征提取。
在这个实验中,我们把隐蔽层有60个神经元的基线模型,减少一半到30个。这将在训练中给网络带来压力,使其在输入数据中挑选出最重要的结构来建模。
我们还将像之前的实验中的数据准备一样对数据进行标准化,并尝试利用性能的小幅提升。
...
# smaller model
def create_smaller():
# create model
model = Sequential()
model.add(Dense(30, input_shape=(60,), activation='relu'))
model.add(Dense(1, activation='sigmoid'))
# Compile model
model.compile(loss='binary_crossentropy', optimizer='adam', metrics=['accuracy'])
return model
estimators = []
estimators.append(('standardize', StandardScaler()))
estimators.append(('mlp', KerasClassifier(model=create_smaller, epochs=100, batch_size=5, verbose=0)))
pipeline = Pipeline(estimators)
kfold = StratifiedKFold(n_splits=10, shuffle=True)
results = cross_val_score(pipeline, X, encoded_Y, cv=kfold)
print("Smaller: %.2f%% (%.2f%%)" % (results.mean()*100, results.std()*100))
把这些联系起来,完整的例子列在下面。
# Binary Classification with Sonar Dataset: Standardized Smaller
from pandas import read_csv
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense
from scikeras.wrappers 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
# load dataset
dataframe = read_csv("sonar.csv", header=None)
dataset = dataframe.values
# split into input (X) and output (Y) variables
X = dataset[:,0:60].astype(float)
Y = dataset[:,60]
# encode class values as integers
encoder = LabelEncoder()
encoder.fit(Y)
encoded_Y = encoder.transform(Y)
# smaller model
def create_smaller():
# create model
model = Sequential()
model.add(Dense(30, input_shape=(60,), activation='relu'))
model.add(Dense(1, activation='sigmoid'))
# Compile model
model.compile(loss='binary_crossentropy', optimizer='adam', metrics=['accuracy'])
return model
estimators = []
estimators.append(('standardize', StandardScaler()))
estimators.append(('mlp', KerasClassifier(model=create_smaller, epochs=100, batch_size=5, verbose=0)))
pipeline = Pipeline(estimators)
kfold = StratifiedKFold(n_splits=10, shuffle=True)
results = cross_val_score(pipeline, X, encoded_Y, cv=kfold)
print("Smaller: %.2f%% (%.2f%%)" % (results.mean()*100, results.std()*100))
运行这个例子可以得到以下结果。我们可以看到,我们的平均估计准确度有了非常小的提升,模型的准确度得分的标准差(平均传播)也有了重要的减少。
注意:鉴于算法或评估程序的随机性,或数字精度的差异,你的结果可能会有所不同。考虑将这个例子运行几次,并比较平均结果。
这是一个很好的结果,因为我们用一半大小的网络做得稍好,而这又需要一半的时间来训练。
Smaller: 86.04% (4.00%)
4.2.评估一个更大的网络
有更多层的神经网络拓扑结构为网络提供了更多的机会来提取关键特征并以有用的非线性方式重新组合它们。
我们可以通过对用于创建我们的模型的函数进行另一个小的调整来评估向网络添加更多的层是否能轻松地提高性能。在这里,我们在网络中增加了一个新的层(一条线),在第一个隐藏层之后引入了另一个有30个神经元的隐藏层。
我们的网络现在有了拓扑结构。
60 inputs -> [60 -> 30] -> 1 output
这里的想法是,在遇到瓶颈和被迫将表示能力减半之前,网络有机会对所有输入变量进行建模,就像我们在上面的实验中对较小的网络所做的那样。
我们没有挤压输入本身的表示,而是有一个额外的隐藏层来帮助这个过程。
...
# larger model
def create_larger():
# create model
model = Sequential()
model.add(Dense(60, input_shape=(60,), activation='relu'))
model.add(Dense(30, activation='relu'))
model.add(Dense(1, activation='sigmoid'))
# Compile model
model.compile(loss='binary_crossentropy', optimizer='adam', metrics=['accuracy'])
return model
estimators = []
estimators.append(('standardize', StandardScaler()))
estimators.append(('mlp', KerasClassifier(model=create_larger, epochs=100, batch_size=5, verbose=0)))
pipeline = Pipeline(estimators)
kfold = StratifiedKFold(n_splits=10, shuffle=True)
results = cross_val_score(pipeline, X, encoded_Y, cv=kfold)
print("Larger: %.2f%% (%.2f%%)" % (results.mean()*100, results.std()*100))
把这些联系起来,完整的例子列在下面。
# Binary Classification with Sonar Dataset: Standardized Larger
from pandas import read_csv
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense
from scikeras.wrappers 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
# load dataset
dataframe = read_csv("sonar.csv", header=None)
dataset = dataframe.values
# split into input (X) and output (Y) variables
X = dataset[:,0:60].astype(float)
Y = dataset[:,60]
# encode class values as integers
encoder = LabelEncoder()
encoder.fit(Y)
encoded_Y = encoder.transform(Y)
# larger model
def create_larger():
# create model
model = Sequential()
model.add(Dense(60, input_shape=(60,), activation='relu'))
model.add(Dense(30, activation='relu'))
model.add(Dense(1, activation='sigmoid'))
# Compile model
model.compile(loss='binary_crossentropy', optimizer='adam', metrics=['accuracy'])
return model
estimators = []
estimators.append(('standardize', StandardScaler()))
estimators.append(('mlp', KerasClassifier(model=create_larger, epochs=100, batch_size=5, verbose=0)))
pipeline = Pipeline(estimators)
kfold = StratifiedKFold(n_splits=10, shuffle=True)
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%)
随着优化算法和训练历时数等方面的进一步调整,预计有可能进一步改善。你在这个数据集上能达到的最佳分数是多少?
总结
在这篇文章中,你发现了Python中的Keras深度学习库。