目录:
机器学习介绍
机器学习进阶
深度学习-神经网络
AI技术基于计算机科学、心理学、神经科学、统计学等多个领域的知识,通过算法、大数据、机器学习、深度学习等方法,让计算机系统实现类似于人类的智能。AI技术已经在多个领域得到了广泛应用,比如医疗、金融、制造业、教育等。
近年来,人工智能领域取得的令人印象深刻的进展很多都是在深度学习方面。
在自然语言翻译,图像识别,玩游戏等方面,深度学习模型取得了不逊色甚至超过人类的水平。
那么什么是深度学习呢?深度学习是一种以深度计算为特征的机器学习的方法。这种深度计算能够让深度学习模型理清从现实世界数据集中发现的各种复杂、分层的模式。
神经网络凭借其强大的能力和伸缩性成为了深度学习的模型。
神经网络由神经元组成,每个神经元都进行独自的计算。神经网络的强大能力来源于由神经元组成的错综复杂的网络。
通过本文,你将学习到以下内容:
神经元是神经网络的基础,将不同的神经元组织在一起就构成了神经网络。
首先来看一下神经网络的基础单元:单个的神经元。
在这个神经网络中,输入是x,它到神经元的连接权重为w。当一个值从这个连接经过时,需要将该值乘以权重。如果输入值为x,到达神经元的则是w*x
。通过调整权重值让神经网络不断学习,最终得到我们想要的结果。
b是特殊的权重值,称之为偏差。偏差没有与之关联的输入,为了方便可以将输入看成1,这样到达神经元的仍然还是b(1*b=b
)。偏差值可以让神经元对输出值做一些调整。
y是这个神经元最终的输出。神经元将所有接收到的值相加就是其输出,在本例中就是y=w*x + b
, 或者写成公式 y = wx + b。这是一个线性函数,所以单个神经元也可以说是线性单元。
尽管单个神经元只是大型网络的一部分,但是了解神经元是进一步认识大型神经网络的基础。
下面以80种早餐麦片这个数据集为例,来学习如何使用神经元。
首先训练一个模型,该模型以糖分含量(单位为克)为输入,卡路里(单位为卡)为输出。如果偏差值为90,权重为2.5,则每份含5克糖分的麦片可以提供的热量的评估模型为:
计算公式为:卡路里=2.5 x 5 + 90 = 102.5.
在上面这个数据集中,除了糖分以外,还有很多其他的特征。如果想将纤维或者蛋白质之类的特征也纳入到模型中进行评估呢?其实也是很简单的。
我们可以在这个神经元上加入多条输入连接,每一条连接接受一个额外的特征输入。将每个输入与其连接权重相乘,然后相加,就得到了最终输出。模型如下:
一个具有两个输入的线性单元将拟合一个平面,而一个具有更多输入的线性单元将拟合一个超平面。
接下来将使用Keras来创建线性模型,接受三个输入特征,并输出结果。
Keras 是一个高级的开源机器学习库,它是用 Python 语言编写的,并基于 TensorFlow、CNTK 或 Theano 等后端引擎运行。Keras 的设计目标是使深度学习模型的构建过程变得简单、快速和可扩展,从而使机器学习技术更易于使用。
数据集为kaggle的红酒质量数据集,该数据集1599行,12列。其中quality这一列为预测目标,其余列为特征值。
代码如下:
from tensorflow import keras
from tensorflow.keras import layers
model = keras.Sequential([layers.Dense(units=1, input_shape=[11])])
# look at the weights
w, b = model.weights
print("Weights\n{}\n\nBias\n{}".format(w, b))
神经网络是由多个神经元构成的。
神经网络将神经元组织成层。当我们将一组具有相同输入的神经元放在一起,就得到了一个密集层(Dense)。如下所示:
神经网络是由多个层组成的,每一层都执行一些简单的转换操作,例如线性变换和非线性变换。通过将多个层堆叠在一起,神经网络可以逐步将输入数据转换为更复杂的表示形式,以更好地适应不同的任务和数据。
在训练良好的神经网络中,每一层都是一个转换操作,使我们可以更接近解决方案,从而实现高性能的模型。
密集层本身只能执行线性变换,不能有效地处理非线性关系。如果我们在两个密集层之间没有添加任何非线性层,则它们的组合仍然只能表示线性模型。
因此,我们需要引入一些非线性变换,以实现更复杂的模型。这可以通过添加激活函数来实现。
激活函数是一种非线性函数,能够对传入的数据进行非线性变换,从而使神经网络能够更好地处理非线性关系。通过在密集层和激活函数之间交替堆叠,我们可以构建具有更高表达能力和更强泛化能力的神经网络。
常见的整流函数就是激活函数,如max(0, x),该函数将小于0的输入值整流成0:
当我们将整流函数关联到线性单元时,我们就获得了一个修正线性单元(rectified linear unit),简称ReLU。如下图所示:
ReLU函数在输入为正数时返回输入值本身,在输入为负数时返回0。因此,ReLU对输入进行了一种线性变换和非线性变换的组合。
现在我们有了非线性,接下来看看如何通过堆叠层来获得更为复杂的数据转换。
在输出层之前的层有时候被叫做隐藏层,因为我们看不到这一层的输出结果。
我们注意到最后的输出层是一个线性单元,没有激活函数,这使得整个网络趋近于一个回归处理模型。如果想要获得一个其他类型的模型,如分类模型,则可能需要在输出层设置激活函数。
构建有序模型
在上文中,将密集层堆叠到一起,从输入层开始到输出层结束,上一层的结果作为下一层的输入。这种模型也可以叫做有序模型。下面用代码创建有序模型:
from tensorflow import keras
from tensorflow.keras import layers
model = keras.Sequential([
# the hidden ReLU layers
layers.Dense(units=4, activation='relu', input_shape=[2]),
layers.Dense(units=3, activation='relu'),
# the linear output layer
layers.Dense(units=1),
])
所有的密集层都在一个list中,每个密集层关联的激活函数通过activation参数来指定。
在上文创建的神经网络中,所有的权重值都是随机设置的,神经网络对权重值的设置是不知情的。
接下来将介绍如何训练神经网络。
和所有的机器学习任务一样,首先需要一个数据集。将特征值作为输入,目标作为输出。训练一个神经网络,就是要调整每条边的权重值,最终将特征值转换成目标。
还是以上文中的80种早餐麦片数据集为例,我们需要一个神经网络,将糖分,纤维,蛋白质等(特征值)作为输入,通过转换,最终得到热量值(目标)为输出。在这个神经网络中,每条边的权重值代表着每个特征值和目标的某种关系。
除此之外,我们还需要干两件事:一是需要一个损失函数以衡量神经网络预测结果的好坏;二是需要优化器来告诉我们怎么变改权重值。
损失函数用来衡量模型预测结果和实际目标值的差异。
不同的问题需要不同的损失函数。
之前介绍过一些模型训练,基本都是用来预测数据类型的结果的。如80种早餐麦片的热量,房子的价格等,此类问题称为回归问题。
对于回归问题,常用的损失函数为平均绝对误差MAE。对于每个预测,MAE衡量其与实际值的差异的绝对值。如下所示:
除了MAE之外,其他的可以用于回归问题的损失函数还有平方误差MSE或Huber损失函数。
优化器是一种算法,可以告诉我们如何调整权重值。
事实上,在深度学习中,所有的优化算法都属于同一个大类,就是所谓的随机梯度下降。
随机梯度下降是一种通过迭代的方式来训练神经网络的算法。其中迭代过程如下:
使用随机梯度下降训练神经网络的示意图如下所示:
每次迭代的训练数据样本叫做小批(通常直接叫批-batch),而一轮完整的训练叫做epoch。上图的动画显示了线性模型使用 SGD 进行训练的过程。淡红点描述了整个训练集,而实心红点即是小批,每次 SGD 看到一个新的 minibatch 时,它都会将权重(w 斜率和 b 截距)移向该批次的正确值。通过一批又一批的迭代,线最终收敛到最佳拟合。可以看到,随着权重越来越接近其真实值,损失越来越小。
请注意,线条仅在每个批次的方向上进行小幅移动(而不是一直移动)。这些变化的大小由学习率(learning rate)决定。较小的学习率意味着网络在其权重收敛到最佳值之前需要看到更多的小批量。
学习率和批量的大小是对 SGD 训练过程影响最大的两个参数。它们的相互作用通常是微妙的,选择正确的参数很困难。
幸运的是,对于大多数工作,我们不需要进行大量的超参数搜索来获得满意的结果。 Adam 是一种 SGD 算法,具有自适应学习率,它可以适用于大多数问题而无需任何参数调整(从某种意义上说,它是“自调整”)。 Adam 是一个很棒的通用优化器。
接下来我们将损失函数和优化器应用到神经网络中,如下所示:
model.compile(optimizer="adam", loss="mae")
可以看到损失函数和优化器可以直接通过字符串指定。当然,如果你想进一步调整参数的话,你也可以通过Keras的API来设置。
了解完损失函数和优化器之后,就可以开始训练深度学习模型了。
下面将以红酒质量数据集为例进行介绍。这个数据集由1600种葡萄牙红酒组成,以不同的指标来对红酒进行衡量。当然,也包括通过品尝盲测得到的质量评价指标。
首先对数据集做一下处理:
import pandas as pd
from IPython.display import display
red_wine = pd.read_csv('../input/dl-course-data/red-wine.csv')
# Create training and validation splits
df_train = red_wine.sample(frac=0.7, random_state=0)
df_valid = red_wine.drop(df_train.index)
display(df_train.head(4))
# Scale to [0, 1]
max_ = df_train.max(axis=0)
min_ = df_train.min(axis=0)
df_train = (df_train - min_) / (max_ - min_)
df_valid = (df_valid - min_) / (max_ - min_)
# Split features and target
X_train = df_train.drop('quality', axis=1)
X_valid = df_valid.drop('quality', axis=1)
y_train = df_train['quality']
y_valid = df_valid['quality']
print(X_train.shape)
注意,这里拆分数据集是用的sample
函数,与以前介绍过的train_test_split
函数是不同的。
sample 函数用于从给定的数据集中进行随机抽样,以获取一个样本。该函数通常用于从整个数据集中获取一个小而具有代表性的子集,以便进行数据探索、可视化或快速建模等任务。
同时,本例中还对数据集特征值进行了重新缩放,以便提高算法性能和收敛速度。常见的特征值重新缩放方法包括标准化和归一化, 标准化将特征值转换成均值为0,标准差为1的分布,而归一化将特征值缩放到0到1的范围。这里是归一化处理。
运行以上代码,可以从结果中看出缩放前的训练数据是什么样的,最终训练集有1119行,11列。
接下来使用一个三层神经网络来训练,并设置优化器和损失函数:
from tensorflow import keras
from tensorflow.keras import layers
model = keras.Sequential([
layers.Dense(512, activation='relu', input_shape=[11]),
layers.Dense(512, activation='relu'),
layers.Dense(512, activation='relu'),
layers.Dense(1),
])
model.compile(
optimizer='adam',
loss='mae',
)
然后开始训练,批大小取256,一共10轮:
history = model.fit(
X_train, y_train,
validation_data=(X_valid, y_valid),
batch_size=256,
epochs=10,
)
结果如下:
Epoch 1/10
5/5 [==============================] - 1s 66ms/step - loss: 0.2470 - val_loss: 0.1357
Epoch 2/10
5/5 [==============================] - 0s 21ms/step - loss: 0.1349 - val_loss: 0.1231
Epoch 3/10
5/5 [==============================] - 0s 23ms/step - loss: 0.1181 - val_loss: 0.1173
Epoch 4/10
5/5 [==============================] - 0s 21ms/step - loss: 0.1117 - val_loss: 0.1066
Epoch 5/10
5/5 [==============================] - 0s 22ms/step - loss: 0.1071 - val_loss: 0.1028
Epoch 6/10
5/5 [==============================] - 0s 20ms/step - loss: 0.1049 - val_loss: 0.1050
Epoch 7/10
5/5 [==============================] - 0s 20ms/step - loss: 0.1035 - val_loss: 0.1009
Epoch 8/10
5/5 [==============================] - 0s 20ms/step - loss: 0.1019 - val_loss: 0.1043
Epoch 9/10
5/5 [==============================] - 0s 19ms/step - loss: 0.1005 - val_loss: 0.1035
Epoch 10/10
5/5 [==============================] - 0s 20ms/step - loss: 0.1011 - val_loss: 0.0977
可以看到,随着训练的进行,损失函数越来越小。
用图形化的方法来展示损失函数的变化过程:
import pandas as pd
# convert the training history to a dataframe
history_df = pd.DataFrame(history.history)
# use Pandas native plot method
history_df['loss'].plot();
在本系列之前的相关文章中提到过,使用决策树训练机器学习模型时容易出现拟合不足或拟合过度的情况。
同样地,使用随机梯度下降(Stochastic Gredient Descent)来训练深度学习模型时也会出现拟合不足和拟合过度的情况。
训练数据可以分为有意义的数据和噪声数据。
所谓有意义的数据,就是有助于模型作出准确预测的数据;而噪声数据是来自真实世界的所有随机波动数据,或者是无意义的、不能帮助模型进行预测的数据。
当我们训练一个模型时,我们可以绘制训练集上每轮训练的损失曲线。为了更全面评估模型,还可以绘制验证集上的损失曲线。这些曲线被称为学习曲线。如下图所示:
训练损失是用于评估深度学习模型在训练集上的误差。类似的,验证损失是用于评估深度学习模型在验证集上的误差。验证损失类似于训练损失,是根据验证集中的每个示例的误差总和计算得出的。
无论训练模型的数据是有意义的数据还是噪声数据,训练集上的损失曲线都会下降。而验证集上的损失函数则不同,当训练数据是有意义的数据时,验证集上的损失曲线会下降,而当训练数据是噪声数据时,验证集上的损失曲线会上升。
通过训练集和验证集上的学习曲线的不同走势,我们可以了解到有多少噪声数据。
训练数据全部都是有意义的数据肯定是最好的,但这几乎不可能。因此需要去权衡,只要噪声数据带来的负面影响小于有意义的数据带来的正面影响,就是可以接受的。换句话说,只要训练集损失曲线和验证集损失曲线都在下降,那就说明还可以继续迭代,直到验证集上的损失曲线开始上升为止。
这个过程中有一个合适的点,在这个点之前,验证集损失曲线是下降的,在这个点之后,验证集损失曲线是上升的。在这个点之前,用于训练的有意义的数据还不够多,训练出来的模型误差大,这叫拟合不足;在这个点之后,训练数据中的噪声数据太多了,影响了模型的性能,这叫拟合过度。
深度学习的一个重点,就是找到拟合不足和拟合过度的平衡点。
模型容量指的是该模型可以学习的模式大小和复杂度。对于神经网络而言,这极大程度上由神经元的数量和它们的连接方式而决定。如果你的神经网络模型出现拟合不足的情况,你可以尝试增加模型容量。
可以从两方面来增加神经网络的容量,一是扩展宽度,即增加密集层的神经元数量,二是扩展深度,即增加密集层本身的数量。当处理线性关系比较强的数据集时,增加神经网络的宽度比较合适;当处理非线形关系比较强的数据集时,增加神经网络的深度更合适。因此,选择更宽还是更深的神经网络取决于数据集的特征和复杂性。
上文介绍过拟合不足和拟合过度的情况,当用于训练模型的数据中包含太多噪声数据时,验证损失将会增大。为了阻止这种情况,我们可以简单地停止训练即可。这种做法叫做提前停止(Early Stopping)。换句话说,一旦我们检测到验证损失开始增加,就将权重值重置为验证损失最小的时候的值,以此保证模型不会出现过拟合的情况。
可以通过回调函数将提前停止添加到训练过程中。回调函数将在每轮训练完成之后执行一次。Keras中有很多内置的回调函数,当然,你也可以自定义回调函数。
看看回调是如何使用的:
from tensorflow.keras.callbacks import EarlyStopping
early_stopping = EarlyStopping(
min_delta = 0.001, # 一次提升的最小改变值
patience = 20, # 在停止之前等待多少轮
retore_best_weights = True, # 重置为最佳权重值
)
这个回调函数意义是:如果在前20轮训练中,如果没有最少0.001的损失提升,那么就停止训练并将神经网络的权重值重置为最佳权重值。有时候很难判断验证损失升高是由过度拟合导致的还是仅仅由于随机批次变化导致的,所以需要设置轮次参数。
在上文的训练模型过程中添加了优化器和损失函数,下面我们继续将回调函数添加进来。
数据准备和预处理的过程还是依照上文,先加载红酒数据集,然后进行归一化缩放。真正需要修改的就是训练的过程:
from tensorflow import keras
from tensorflow.keras import layers, callbacks
# 定义深度学习模型
model = keras.Sequential([
layers.Dense(512, activation='relu', input_shape=[11]),
layers.Dense(512, activation='relu'),
layers.Dense(512, activation='relu'),
layers.Dense(1),
])
# 添加优化器和损失函数
model.compile(
optimizer='adam',
loss='mae',
)
# 定义回调函数
early_stopping = callbacks.EarlyStopping(
min_delta=0.001, # minimium amount of change to count as an improvement
patience=20, # how many epochs to wait before stopping
restore_best_weights=True,
)
# 拟合模型
history = model.fit(
X_train, y_train,
validation_data = (X_valid, y_valid),
batch_size = 256,
epochs = 500,
callbacks = [early_stopping],
verbose = 0, # 关闭训练日志
)
history_df = pd.DataFrame(history.history)
history_df.loc[:, ['loss', 'val_loss']].plot();
print("Minimum validation loss: {}".format(history_df['val_loss'].min()))
运行结果如下:
Dropout是在《ImageNet Classification with Deep Convolutional》这篇论文里提出来为了防止神经网络的过拟合,其主要思想是在每次迭代时随机丢弃一部分输入单元,避免对某个节点的强依赖,让反向传播的修正值可以更加平衡的分布到各个参数上。
通过dropout,神经网络可以不依赖于任何特定的权重组合,从而减少过拟合的风险。它强制网络学习到更广泛、更一般的模式,这些模式对于不同的权重组合都具有鲁棒性。
下面这个动图演示了每轮迭代在两个隐藏层中丢弃50%输入单元,即所谓的dropout的过程:
你可以将dropout机制看作是创建一类神经网络的组合。在这种方式下,预测结果将不再是由一个大的神经网络提供,而是由一组小的神经网络组合而来。在这一组小的神经网络中,单个的神经网络可能会有一些错误预测,但将所有的结果综合起来考虑后反而是比单个大的神经网络的更好。
这跟之前介绍过的决策树和决策森林是相同的道理。
下面是dropout的代码示例
keras.Sequential([
# ...
layers.Dropout(rate=0.3), # 将传给下一层的输入单元丢弃30%
layers.Dense(16),
# ...
])
在神经网络中,除了密集层之外,还有其他的层,比如这里的dropout层,它是一个功能层。
在上文中我们介绍过深度学习的数据处理手段,比如归一化和标准化,这里介绍批量标准化。
所谓批量标准化,就是对神经网络中的每层进行标准化处理,其目的是通过规范化每层的输入,使其具有零均值和单位方差,而从加速网络的训练速度和提高性能。
在神经网络中,我们有一个专门的层来做批量标准化,可以将其放到某一层之后:
layers.Dense(16, activation='relu'),
layers.BatchNormalization(),
也可以将其放到密集层和激活函数之间:
layers.Dense(16),
layers.BatchNormalization(),
layers.Activation('relu'),
如果将批量标准化层作为神经网络的第一层,它还可以作为某种适应性预处理器,比如scikit-learn
里面的标准化缩放器。
下面是代码示例,首先对数据集进行预处理:
# Setup plotting
import matplotlib.pyplot as plt
plt.style.use('seaborn-whitegrid')
# Set Matplotlib defaults
plt.rc('figure', autolayout=True)
plt.rc('axes', labelweight='bold', labelsize='large',
titleweight='bold', titlesize=18, titlepad=10)
import pandas as pd
red_wine = pd.read_csv('../input/dl-course-data/red-wine.csv')
# Create training and validation splits
df_train = red_wine.sample(frac=0.7, random_state=0)
df_valid = red_wine.drop(df_train.index)
# Split features and target
X_train = df_train.drop('quality', axis=1)
X_valid = df_valid.drop('quality', axis=1)
y_train = df_train['quality']
y_valid = df_valid['quality']
接下来进行dropout和批量准备化处理。注意,当添加了dropout层时,可以适当增大每层的神经元数量。
rom tensorflow import keras
from tensorflow.keras import layers
model = keras.Sequential([
layers.Dense(1024, activation='relu', input_shape=[11]),
layers.Dropout(0.3),
layers.BatchNormalization(),
layers.Dense(1024, activation='relu'),
layers.Dropout(0.3),
layers.BatchNormalization(),
layers.Dense(1024, activation='relu'),
layers.Dropout(0.3),
layers.BatchNormalization(),
layers.Dense(1),
])
然后指定优化器和损失函数,并进行拟合:
model.compile(
optimizer='adam',
loss='mae',
)
history = model.fit(
X_train, y_train,
validation_data=(X_valid, y_valid),
batch_size=256,
epochs=100,
verbose=0,
)
# Show the learning curves
history_df = pd.DataFrame(history.history)
history_df.loc[:, ['loss', 'val_loss']].plot();
结果如下所示:
如果在将数据用于训练之前对其进行标准化,通常会获得更好的性能。然而,我们能够使用原始数据,这表明批量标准化在更难的数据集上是多么有效。
在前文中我们用神经网络处理的问题都是回归问题,接下来将介绍机器学习中另一类很常见的问题,那就是分类。
用神经网络处理回归问题与分类问题的主要区别在于所使用的损失函数不同,再就是输出层不一样。
分类问题是一类很常见的问题,比如预测一个用户是否喜欢购物,或者一张信用卡的交易是否是正常交易等等。
在原始数据集中,类别可能用“是”或者“否”来表示,又或是“猫”或者“狗”。我们需要将其转换成类别标签,一类为“0”,另一类为“1”,以便于神经网络处理。
准确率就是正确预测的比例,就不用多说了。准确性得分的范围在0到1之间,其中1表示模型的预测完全准确,0表示模型的预测完全错误。
在给定的数据集中,如果不用类别的样本出现频率大致相同,即类别分布平衡,那么准确率将是一个合理的评估指标。这是因为类别的平衡意味着每个类别对于准确性的贡献相当,模型需要在各个类别上都有良好的预测表现。
如果数据集中的类别分布不平衡时,准确性可能会产生误导。举个例子来说,某个类别中的样本数量远远超过其他类别时,那么一个简单的模型只需要将所有样本都预测为该类别,也能获得很高的准确性,但实际上这个模型并没有学到其他类别的预测能力。
在这种情况下,准确性就不再是一个合适的指标了,可能需要考虑其他的评估指标,比如召回率(Recall)或F1分数(F1-score)。
但在分类问题上,准确率无法直接用作损失函数。在SGD中,损失函数需要平滑的连续变化,而准确率是一个基于计数的比率,它在每个样本的预测结果发生变化时会出现跳跃式的改变。这使得准确率无法满足梯度下降算法等优化方法的要求。
因此我们需要选择一个替代的损失函数来作为优化算法的目标函数。交叉墒就是一种常见的替代损失函数,特别适用于分类任务。
交叉墒损失函数将模型的预测概率分布与真实标签之间的差异量化为一个实数值。它能够提供平滑的连续变化,并具有良好的可微性,使得可以使用梯度下降等优化算法进行模型参数的训练和更新。
下面以无线电信号数据集lonosphere为例,需要训练一个模型,预测该信号代表是代表某种物体还是真空。
首先初始化数据集:
import pandas as pd
from IPython.display import display
ion = pd.read_csv('../input/dl-course-data/ion.csv', index_col=0)
display(ion.head())
df = ion.copy()
df['Class'] = df['Class'].map({'good': 0, 'bad': 1})
df_train = df.sample(frac=0.7, random_state=0)
df_valid = df.drop(df_train.index)
max_ = df_train.max(axis=0)
min_ = df_train.min(axis=0)
df_train = (df_train - min_) / (max_ - min_)
df_valid = (df_valid - min_) / (max_ - min_)
df_train.dropna(axis=1, inplace=True) # drop the empty feature in column 2
df_valid.dropna(axis=1, inplace=True)
X_train = df_train.drop('Class', axis=1)
X_valid = df_valid.drop('Class', axis=1)
y_train = df_train['Class']
y_valid = df_valid['Class']
接下来创建深度学习模型并指定优化器和损失函数,与之前不同的是,损失函数不能用mae了,而是用交叉墒:
from tensorflow import keras
from tensorflow.keras import layers
model = keras.Sequential([
layers.Dense(4, activation='relu', input_shape=[33]),
layers.Dense(4, activation='relu'),
layers.Dense(1, activation='sigmoid'),
])
model.compile(
optimizer='adam',
loss='binary_crossentropy',
metrics=['binary_accuracy'],
)
为了防止过度拟合,我们使用提前终止回调函数:
early_stopping = keras.callbacks.EarlyStopping(
patience=10,
min_delta=0.001,
restore_best_weights=True,
)
history = model.fit(
X_train, y_train,
validation_data=(X_valid, y_valid),
batch_size=512,
epochs=1000,
callbacks=[early_stopping],
verbose=0, # hide the output because we have so many epochs
)
最后看一下损失函数曲线和准确率:
history_df = pd.DataFrame(history.history)
# Start the plot at epoch 5
history_df.loc[5:, ['loss', 'val_loss']].plot()
history_df.loc[5:, ['binary_accuracy', 'val_binary_accuracy']].plot()
print(("Best Validation Loss: {:0.4f}" +\
"\nBest Validation Accuracy: {:0.4f}")\
.format(history_df['val_loss'].min(),
history_df['val_binary_accuracy'].max()))
结果如下:
Best Validation Loss: 0.3534
Best Validation Accuracy: 0.8857
[1].https://www.kaggle.com/learn/intro-to-deep-learning