【翻译自 : Develop a Neural Network for Cancer Survival Dataset】
【说明:Jason Brownlee PhD大神的文章个人很喜欢,所以闲暇时间里会做一点翻译和学习实践的工作,这里是相应工作的实践记录,希望能帮到有需要的人!】
为新数据集开发神经网络预测模型可能具有挑战性。一种方法是首先检查数据集并为可能使用的模型开发思路,然后探索数据集上简单模型的学习动态,然后最后使用健壮的测试工具为数据集开发和调整模型。此过程可用于为分类和回归预测建模问题开发有效的神经网络模型。
在本教程中,您将发现如何为癌症生存二进制分类数据集开发多层Perceptron神经网络模型。完成本教程后,您将知道:
如何加载和总结癌症生存数据集以及如何使用结果建议要使用的数据准备和模型配置。
如何探索数据集上简单MLP模型的学习动态。
如何开发出对模型性能的可靠估计,调整模型性能以及对新数据进行预测。
教程概述
本教程分为4个部分。他们是:
哈伯曼乳腺癌生存数据集
神经网络学习动力学
稳健的模型评估
最终模型和做出预测
哈伯曼乳腺癌生存数据集
第一步是定义和探索数据集。我们将使用“ haberman”标准二进制分类数据集。该数据集描述了乳腺癌患者的数据,其结果是患者的生存率。特别是患者是否存活了五年或更长时间,或者患者是否没有存活。这是用于不平衡分类研究的标准数据集。根据数据集描述,这些操作是在1958年至1970年之间在芝加哥大学比林斯医院进行的。
数据集中有306个示例,并且有3个输入变量。他们是:
手术时患者的年龄。
两位数的年份。
检测到的“阳性腋窝淋巴结”的数量,衡量癌症是否扩散。
因此,除了数据集中可用的内容外,我们无法控制组成数据集的案例的选择或在这些案例中要使用的特征。尽管该数据集描述了乳腺癌患者的生存情况,但鉴于数据集的规模很小,并且事实证明该数据是基于数十年前的乳腺癌诊断和手术,因此,基于此数据集构建的任何模型都不会得到普遍推广。
注意:要明确地说,我们不是在“解决乳腺癌”。我们正在探索标准分类数据集。
以下是数据集的前5行的示例
30,64,1,1
30,62,3,1
30,65,0,1
31,59,2,1
31,65,4,1
您可以在此处了解有关数据集的更多信息:
哈伯曼生存数据集(haberman.csv)
Haberman生存数据集详细信息(haberman.names)
我们可以直接从URL将数据集作为pandas DataFrame加载; 例如:
# load the haberman dataset and summarize the shape
from pandas import read_csv
# define the location of the dataset
url = 'https://raw.githubusercontent.com/jbrownlee/Datasets/master/haberman.csv'
# load the dataset
df = read_csv(url, header=None)
# summarize shape
print(df.shape)
运行示例将直接从URL加载数据集并报告数据集的形状。
在这种情况下,我们可以确认该数据集具有4个变量(3个输入和一个输出),并且该数据集具有306行数据。
对于神经网络来说,这不是很多数据行,这表明一个小型的网络(可能带有正则化)将是合适的。
这也表明,使用k折交叉验证是一个好主意,因为与训练/测试拆分相比,它可以提供更可靠的模型性能估算,并且因为单个模型的拟合时间为数秒而不是数小时或数天。 最大的数据集。
(306, 4)
接下来,我们可以通过查看摘要统计信息和数据图来了解有关数据集的更多信息。
# show summary statistics and plots of the haberman dataset
from pandas import read_csv
from matplotlib import pyplot
# define the location of the dataset
url = 'https://raw.githubusercontent.com/jbrownlee/Datasets/master/haberman.csv'
# load the dataset
df = read_csv(url, header=None)
# show summary statistics
print(df.describe())
# plot histograms
df.hist()
pyplot.show()
运行示例之前,先加载数据,然后输出每个变量的摘要统计信息。
我们可以看到,值随均值和标准偏差的不同而变化,在建模之前可能需要进行一些归一化或标准化。
0 1 2 3
count 306.000000 306.000000 306.000000 306.000000
mean 52.457516 62.852941 4.026144 1.264706
std 10.803452 3.249405 7.189654 0.441899
min 30.000000 58.000000 0.000000 1.000000
25% 44.000000 60.000000 0.000000 1.000000
50% 52.000000 63.000000 1.000000 1.000000
75% 60.750000 65.750000 4.000000 2.000000
max 83.000000 69.000000 52.000000 2.000000
然后为每个变量创建一个直方图。我们可以看到,第一个变量可能具有类似高斯的分布,而接下来的两个输入变量可能具有指数分布。我们可以在每个变量上使用幂变换来降低概率分布的偏斜,这可能会改善模型性能,这可能会带来一些好处。
我们可以在两个类之间的示例分布中看到一些偏差,这意味着分类问题是不平衡的。 它是不平衡的。了解数据集实际的不平衡程度可能会有所帮助。我们可以使用Counter对象对每个类中的示例数进行计数,然后使用这些计数来汇总分布。
下面列出了完整的示例。
# summarize the class ratio of the haberman dataset
from pandas import read_csv
from collections import Counter
# define the location of the dataset
url = 'https://raw.githubusercontent.com/jbrownlee/Datasets/master/haberman.csv'
# define the dataset column names
columns = ['age', 'year', 'nodes', 'class']
# load the csv file as a data frame
dataframe = read_csv(url, header=None, names=columns)
# summarize the class distribution
target = dataframe['class'].values
counter = Counter(target)
for k,v in counter.items():
per = v / len(target) * 100
print('Class=%d, Count=%d, Percentage=%.3f%%' % (k, v, per))
运行示例总结了数据集的类分布。我们可以看到,生存类别1的示例最多,为225,约占数据集的74%。 我们可以看到,非生存类的第2类在81个样本中所占的比例较少,约占数据集的26%。类的分布是不对称的,但是并没有严重失衡。
Class=1, Count=225, Percentage=73.529%
Class=2, Count=81, Percentage=26.471%
这是有帮助的,因为如果我们使用分类精度,那么任何达到精度低于约73.5%的模型都不会具有该数据集的技能。现在我们已经熟悉了数据集,让我们探讨如何开发神经网络模型。
神经网络学习动力学
我们将使用TensorFlow为数据集开发一个多层感知器(MLP)模型。
我们不知道学习超参数的哪种模型架构对该数据集将是好的还是最好的,所以我们必须进行实验并发现什么是行之有效的。
假设数据集很小,则小批量可能是个好主意,例如16或32行。入门时,使用Adam版本的随机梯度下降法是个好主意,因为它会自动适应学习率,并且在大多数数据集上都能很好地工作。在我们认真评估模型之前,最好回顾一下学习动态并调整模型体系结构和学习配置,直到我们拥有稳定的学习动态,然后再充分利用模型。我们可以使用简单的训练/测试数据拆分并查看学习曲线图来实现此目的。这将帮助我们了解我们是学习过度还是学习不足;然后我们可以相应地调整配置。
首先,我们必须确保所有输入变量都是浮点值,并将目标标签编码为整数值0和1。
# ensure all data are floating point values
X = X.astype('float32')
# encode strings to integer
y = LabelEncoder().fit_transform(y)
接下来,我们可以将数据集分为输入和输出变量,然后分为67/33训练和测试集。
我们必须确保按类别对拆分进行分层,以确保训练集和测试集具有与主数据集相同的类别标签分布。
# split into input and output columns
X, y = df.values[:, :-1], df.values[:, -1]
# split into train and test datasets
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.5, stratify=y, random_state=3)
我们可以定义一个最小的MLP模型。 在这种情况下,我们将使用一个包含10个节点的隐藏层和一个输出层(任意选择)。 我们将在隐藏层中使用ReLU激活功能和“ he_normal”权重初始化,因为它们在一起是一种很好的做法。
该模型的输出是用于二进制分类的S型激活,我们将使二进制交叉熵损失最小化。
# determine the number of input features
n_features = X.shape[1]
# define model
model = Sequential()
model.add(Dense(10, activation='relu', kernel_initializer='he_normal', input_shape=(n_features,)))
model.add(Dense(1, activation='sigmoid'))
# compile the model
model.compile(optimizer='adam', loss='binary_crossentropy')
由于该数据集很小,因此我们将其拟合为200个训练时期(任意选择)的模型,批处理大小为16。我们正在原始数据上拟合模型,我们认为这可能是个好主意,但这是一个重要的起点。
# fit the model
history = model.fit(X_train, y_train, epochs=200, batch_size=16, verbose=0, validation_data=(X_test,y_test))
训练结束时,我们将在测试数据集上评估模型的性能,并将性能报告为分类准确性。
# predict test set
yhat = model.predict_classes(X_test)
# evaluate predictions
score = accuracy_score(y_test, yhat)
print('Accuracy: %.3f' % score)
最后,我们将在训练过程中在训练和测试集上绘制交叉熵损失的学习曲线。
# plot learning curves
pyplot.title('Learning Curves')
pyplot.xlabel('Epoch')
pyplot.ylabel('Cross Entropy')
pyplot.plot(history.history['loss'], label='train')
pyplot.plot(history.history['val_loss'], label='val')
pyplot.legend()
pyplot.show()
综上所述,下面列出了在癌症生存数据集上评估我们的第一个MLP的完整示例。
# fit a simple mlp model on the haberman and review learning curves
from pandas import read_csv
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import LabelEncoder
from sklearn.metrics import accuracy_score
from tensorflow.keras import Sequential
from tensorflow.keras.layers import Dense
from matplotlib import pyplot
# load the dataset
path = 'https://raw.githubusercontent.com/jbrownlee/Datasets/master/haberman.csv'
df = read_csv(path, header=None)
# split into input and output columns
X, y = df.values[:, :-1], df.values[:, -1]
# ensure all data are floating point values
X = X.astype('float32')
# encode strings to integer
y = LabelEncoder().fit_transform(y)
# split into train and test datasets
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.5, stratify=y, random_state=3)
# determine the number of input features
n_features = X.shape[1]
# define model
model = Sequential()
model.add(Dense(10, activation='relu', kernel_initializer='he_normal', input_shape=(n_features,)))
model.add(Dense(1, activation='sigmoid'))
# compile the model
model.compile(optimizer='adam', loss='binary_crossentropy')
# fit the model
history = model.fit(X_train, y_train, epochs=200, batch_size=16, verbose=0, validation_data=(X_test,y_test))
# predict test set
yhat = model.predict_classes(X_test)
# evaluate predictions
score = accuracy_score(y_test, yhat)
print('Accuracy: %.3f' % score)
# plot learning curves
pyplot.title('Learning Curves')
pyplot.xlabel('Epoch')
pyplot.ylabel('Cross Entropy')
pyplot.plot(history.history['loss'], label='train')
pyplot.plot(history.history['val_loss'], label='val')
pyplot.legend()
pyplot.show()
运行示例首先使模型适合训练数据集,然后报告测试数据集的分类准确性。
在这种情况下,我们可以看到该模型的性能要优于无技能模型,因为该模型的准确度约为73.5%以上。
Accuracy: 0.765
然后创建训练集-测试集上的损耗线图。我们可以看到,该模型可以快速找到适合数据集的模型,并且看起来不会过度拟合或拟合不足。
现在,我们对数据集上的简单MLP模型的学习动力学有了一些了解,我们可以着眼于对数据集上的模型性能进行更健壮的评估。
稳健的模型评估
k折交叉验证过程可以提供更可靠的MLP性能估计,尽管它可能非常慢。这是因为必须拟合和评估k个模型。 当数据集大小较小时(例如癌症生存数据集),这不是问题。我们可以使用StratifiedKFold类并手动枚举每个折叠,对模型进行拟合,评估,然后在过程结束时报告评估得分的平均值。
# prepare cross validation
kfold = KFold(10)
# enumerate splits
scores = list()
for train_ix, test_ix in kfold.split(X, y):
# fit and evaluate the model...
...
...
# summarize all scores
print('Mean Accuracy: %.3f (%.3f)' % (mean(scores), std(scores)))
我们可以使用此框架通过我们的基本配置,甚至使用各种不同的数据准备,模型架构和学习配置,来开发对MLP模型性能的可靠估计。
重要的是,在使用k-fold交叉验证来评估性能之前,我们首先需要对上一部分中的数据集模型的学习动态有一个了解。 如果我们开始直接调整模型,我们可能会获得良好的结果,但是如果没有,我们可能不知道为什么,例如 模型超出或不足。
如果我们再次对模型进行较大更改,则最好返回并确认模型在适当收敛。下面列出了用于评估上一节中的基本MLP模型的此框架的完整示例。
# k-fold cross-validation of base model for the haberman dataset
from numpy import mean
from numpy import std
from pandas import read_csv
from sklearn.model_selection import StratifiedKFold
from sklearn.preprocessing import LabelEncoder
from sklearn.metrics import accuracy_score
from tensorflow.keras import Sequential
from tensorflow.keras.layers import Dense
from matplotlib import pyplot
# load the dataset
path = 'https://raw.githubusercontent.com/jbrownlee/Datasets/master/haberman.csv'
df = read_csv(path, header=None)
# split into input and output columns
X, y = df.values[:, :-1], df.values[:, -1]
# ensure all data are floating point values
X = X.astype('float32')
# encode strings to integer
y = LabelEncoder().fit_transform(y)
# prepare cross validation
kfold = StratifiedKFold(10, random_state=1)
# enumerate splits
scores = list()
for train_ix, test_ix in kfold.split(X, y):
# split data
X_train, X_test, y_train, y_test = X[train_ix], X[test_ix], y[train_ix], y[test_ix]
# determine the number of input features
n_features = X.shape[1]
# define model
model = Sequential()
model.add(Dense(10, activation='relu', kernel_initializer='he_normal', input_shape=(n_features,)))
model.add(Dense(1, activation='sigmoid'))
# compile the model
model.compile(optimizer='adam', loss='binary_crossentropy')
# fit the model
model.fit(X_train, y_train, epochs=200, batch_size=16, verbose=0)
# predict test set
yhat = model.predict_classes(X_test)
# evaluate predictions
score = accuracy_score(y_test, yhat)
print('>%.3f' % score)
scores.append(score)
# summarize all scores
print('Mean Accuracy: %.3f (%.3f)' % (mean(scores), std(scores)))
运行示例将在评估过程的每次迭代中报告模型性能,并在运行结束时报告分类精度的平均值和标准偏差。
在这种情况下,我们可以看到MLP模型的平均准确度约为75.2%,这与上一节中的粗略估计非常接近。
这证实了我们的期望,即对于该数据集,基本模型配置可能比纯朴的模型更有效
>0.742
>0.774
>0.774
>0.806
>0.742
>0.710
>0.767
>0.800
>0.767
>0.633
Mean Accuracy: 0.752 (0.048)
这是一个好结果吗?
实际上,这是一个具有挑战性的分类问题,得分高于74.5%很好。接下来,让我们看看如何拟合最终模型并使用它进行预测。
最终模型和做出预测
选择模型配置后,我们可以在所有可用数据上训练最终模型,并使用它对新数据进行预测。在这种情况下,我们将使用具有辍学和小批量的模型作为最终模型。尽管可以在整个数据集而不是数据集的训练子集上进行操作,但我们仍可以像以前一样准备数据并拟合模型。
# split into input and output columns
X, y = df.values[:, :-1], df.values[:, -1]
# ensure all data are floating point values
X = X.astype('float32')
# encode strings to integer
le = LabelEncoder()
y = le.fit_transform(y)
# determine the number of input features
n_features = X.shape[1]
# define model
model = Sequential()
model.add(Dense(10, activation='relu', kernel_initializer='he_normal', input_shape=(n_features,)))
model.add(Dense(1, activation='sigmoid'))
# compile the model
model.compile(optimizer='adam', loss='binary_crossentropy')
然后,我们可以使用此模型对新数据进行预测。首先,我们可以定义一行新数据。
# define a row of new data
row = [30,64,1]
注意:我从数据集的第一行中提取了这一行,期望的标签是“ 1”。然后我们可以做出预测。
# make prediction
yhat = model.predict_classes([row])
然后对预测进行转换,这样我们就可以在正确的标签中使用或解释结果(对于该数据集而言,这只是一个整数)。
# invert transform to get label for class
yhat = le.inverse_transform(yhat)
在这种情况下,我们将仅报告预测。
# report prediction
print('Predicted: %s' % (yhat[0]))
综上所述,下面列出了为haberman数据集拟合最终模型并使用其对新数据进行预测的完整示例。
# fit a final model and make predictions on new data for the haberman dataset
from pandas import read_csv
from sklearn.preprocessing import LabelEncoder
from sklearn.metrics import accuracy_score
from tensorflow.keras import Sequential
from tensorflow.keras.layers import Dense
from tensorflow.keras.layers import Dropout
# load the dataset
path = 'https://raw.githubusercontent.com/jbrownlee/Datasets/master/haberman.csv'
df = read_csv(path, header=None)
# split into input and output columns
X, y = df.values[:, :-1], df.values[:, -1]
# ensure all data are floating point values
X = X.astype('float32')
# encode strings to integer
le = LabelEncoder()
y = le.fit_transform(y)
# determine the number of input features
n_features = X.shape[1]
# define model
model = Sequential()
model.add(Dense(10, activation='relu', kernel_initializer='he_normal', input_shape=(n_features,)))
model.add(Dense(1, activation='sigmoid'))
# compile the model
model.compile(optimizer='adam', loss='binary_crossentropy')
# fit the model
model.fit(X, y, epochs=200, batch_size=16, verbose=0)
# define a row of new data
row = [30,64,1]
# make prediction
yhat = model.predict_classes([row])
# invert transform to get label for class
yhat = le.inverse_transform(yhat)
# report prediction
print('Predicted: %s' % (yhat[0]))
运行示例可以使模型适合整个数据集,并为单行新数据做出预测。
Predicted: 1