神经网络入门:分类与回归(3)
在代码清单4-8和代码清单4-9中,我们将使用Matplotlib在同一张图上绘制训练损失和验证损失(见图4-4),以及训练精度和验证精度(见图4-5)。由于模型的随机初始值不同,得到的结果可能会略有不同。
图4-4 训练损失和验证损失
图4-5 训练精度和验证精度
代码清单4-8 绘制训练损失和验证损失
import matplotlib.pyplot as plt
history_dict = history.history
loss_values = history_dict[“loss”]
val_loss_values = history_dict[“val_loss”]
epochs = range(1, len(loss_values) + 1)
plt.plot(epochs, loss_values, “bo”, label=“Training loss”) ←---- "bo"表示“蓝色圆点”
plt.plot(epochs, val_loss_values, “b”, label=“Validation loss”) ←---- "b"表示“蓝色实线”
plt.title(“Training and validation loss”)
plt.xlabel(“Epochs”)
plt.ylabel(“Loss”)
plt.legend()
plt.show()
代码清单4-9 绘制训练精度和验证精度
plt.clf() ←----清空图像
acc = history_dict[“accuracy”]
val_acc = history_dict[“val_accuracy”]
plt.plot(epochs, acc, “bo”, label=“Training acc”)
plt.plot(epochs, val_acc, “b”, label=“Validation acc”)
plt.title(“Training and validation accuracy”)
plt.xlabel(“Epochs”)
plt.ylabel(“Accuracy”)
plt.legend()
plt.show()
训练损失每轮都在减小,训练精度每轮都在提高。这正是梯度下降优化的预期结果——我们想要最小化的量随着每次迭代变得越来越小。但验证损失和验证精度并非如此,它们似乎在第4轮达到峰值。模型在训练数据上的表现越来越好,但在前所未见的数据上不一定表现得越来越好。准确地说,这种现象叫作过拟合(overfit):在第4轮之后,你对训练数据过度优化,最终学到的表示仅针对于训练数据,无法泛化到训练集之外的数据。在这种情况下,为防止过拟合,可以在4轮之后停止训练。一般来说,可以用多种方法来降低过拟合。我们从头开始训练一个新模型,训练4轮,然后在测试数据上评估模型,如代码清单4-10所示。
代码清单4-10 从头开始训练一个模型
model = keras.Sequential([
layers.Dense(16, activation="relu"),
layers.Dense(16, activation="relu"),
layers.Dense(1, activation="sigmoid")
])
model.compile(optimizer=“rmsprop”,
loss="binary_crossentropy",
metrics=["accuracy"])
model.fit(x_train, y_train, epochs=4, batch_size=512)
results = model.evaluate(x_test, y_test)
最终结果如下所示。
results
[0.2929924130630493, 0.88327999999999995] ←----第一个数字是测试损失,第二个数字是测试精度
这种相当简单的方法得到了约88%的精度。利用最先进的方法,你应该可以得到接近95%的精度。
利用训练好的模型对新数据进行预测
训练好一个模型之后,最好用于实践。可以用predict方法来计算评论为正面的可能性。
model.predict(x_test)
array([[ 0.98006207]
[ 0.99758697]
[ 0.99975556]
...,
[ 0.82167041]
[ 0.02885115]
[ 0.65371346]], dtype=float32)
模型对某些样本的结果非常确信(大于等于0.99,或小于等于0.01),但对其他样本不那么确信(0.6或0.4)。4.1.6
进一步实验
通过以下实验,可以确信前面选择的神经网络架构是非常合理的,不过仍有改进的空间。我们在最后的分类层之前使用了两个表示层。可以尝试使用一个或三个表示层,然后观察这么做对验证精度和测试精度的影响。
尝试使用更多或更少的单元,比如32个或64个。
尝试使用mse损失函数代替binary_crossentropy。
尝试使用tanh激活函数(这种激活函数在神经网络早期非常流行)代替relu。
小结
下面是这个例子中的要点。
通常需要对原始数据进行大量预处理,以便将其转换为张量输入到神经网络中。
单词序列可以被编码为二进制向量,但也有其他编码方式。
带有relu激活函数的Dense层堆叠,可以解决很多问题(包括情感分类)。你可能会经常用到这种模型。对于二分类问题(两个输出类别),模型的最后一层应该是只有一个单元并使用sigmoid激活函数的Dense层,模型输出应该是一个0到1的标量,表示概率值。对于二分类问题的sigmoid标量输出,应该使用binary_crossentropy损失函数。
无论问题是什么,rmsprop优化器通常都是一个足够好的选择。无须为此费神。随着神经网络在训练数据上的表现越来越好,模型最终会过拟合,并在前所未见的数据上得到越来越差的结果。一定要一直监控模型在训练集之外的数据上的性能。
多分类问题示例
前面介绍了如何用密集连接神经网络将向量输入划分为两个互斥的类别。但如果类别不止两个,要怎么做呢?我们将构建一个模型,把路透社新闻划分到46个互斥的主题中。由于有多个类别,因此这是一个多分类(multiclass classification)问题。由于每个数据点只能划分到一个类别中,因此更具体地说,这是一个单标签、多分类(single-label, multiclass classification)问题。如果每个数据点可以划分到多个类别(主题)中,那就是多标签、多分类(multilabel, multiclass classification)问题。
路透社数据集
接下来将使用路透社数据集,它包含许多短新闻及其对应的主题,由路透社于1986年发布。它是一个简单且广泛使用的文本分类数据集,其中包括46个主题。某些主题的样本相对较多,但训练集中的每个主题都有至少10个样本。
与IMDB数据集和MNIST数据集类似,路透社数据集也内置为Keras的一部分。我们来看一下这个数据集,如代码清单4-11所示。
代码清单4-11 加载路透社数据集
from tensorflow.keras.datasets import reuters
(train_data, train_labels), (test_data, test_labels) = reuters.load_data(
num_words=10000)
与IMDB数据集一样,参数num_words=10000将数据限定为前10 000个最常出现的单词。我们有8982个训练样本和2246个测试样本。
len(train_data)
8982
len(test_data)
2246
与IMDB影评一样,每个样本都是一个整数列表(表示单词索引)。
train_data[10]
[1, 245, 273, 207, 156, 53, 74, 160, 26, 14, 46, 296, 26, 39, 74, 2979,
3554, 14, 46, 4689, 4329, 86, 61, 3499, 4795, 14, 61, 451, 4329, 17, 12]
可以用代码清单4-12所示的代码将样本解码为单词。
代码清单4-12 将新闻解码为文本
word_index = reuters.get_word_index()
reverse_word_index = dict(
[(value, key) for (key, value) in word_index.items()])
decoded_newswire = " ".join(
[reverse_word_index.get(i - 3, "?") for i in train_data[0]]) ←----注意,索引减去了3,因为0、1、2分别是为“padding”(填充)、“start of sequence”(序列开始)、“unknown”(未知词)保留的索引
样本对应的标签是一个介于0和45之间的整数,即话题索引编号
train_labels[10]
3