原文地址:https://github.com/fchollet/deep-learning-with-python-notebooks/blob/master/3.6-classifying-newswires.ipynb
在本节中,我们将建立一个网络,将路透新闻专线分为46个不同的互斥专题。 由于我们有很多类,这个问题是“多类分类”的一个实例,由于每个数据点应该只被分类到一个类别中,所以问题更具体的是“单标签,多类分类”的实例,。 如果每个数据点可能属于多个类别(在我们的案例中是主题),那么我们将面临“多标签,多类分类”问题。
与IMDB和MNIST一样,路透数据集也是Keras的一部分。 让我们马上看看:
from keras.datasets import reuters
(train_data, train_labels), (test_data, test_labels) = reuters.load_data(num_words=10000)
与IMDB数据集一样,参数num_words = 10000将数据限制为在数据中找到的10,000个最常出现的单词。
我们有8,982个训练样例和2,246个测试例子:
In[3]:len(train_data)
Out[3]:8982
In [4]:len(test_data)
Out[4]:2246
与IMDB评论一样,每个示例都是一个整数列表(单词索引):
train_data[10]
以下是您可以将其解码回单词的方式,以防万一您好奇:
word_index = reuters.get_word_index()
reverse_word_index = dict([(value, key) for (key, value) in word_index.items()])
# Note that our indices were offset by 3
# because 0, 1 and 2 are reserved indices for "padding", "start of sequence", and "unknown".
decoded_newswire = ' '.join([reverse_word_index.get(i - 3, '?') for i in train_data[0]])
In [7]:decoded_newswire
与示例关联的标签是0到45之间的整数:主题索引。
In [8]:train_labels[10]
Out[8]:3
我们可以使用与前面示例中完全相同的代码对数据进行矢量化处理:
import numpy as np
def vectorize_sequences(sequences, dimension=10000):
results = np.zeros((len(sequences), dimension))
for i, sequence in enumerate(sequences):
results[i, sequence] = 1.
return results
# Our vectorized training data
x_train = vectorize_sequences(train_data)
# Our vectorized test data
x_test = vectorize_sequences(test_data)
为了对标签进行矢量化,有两种可能性:我们可以将标签列表作为整数张量,或者我们可以使用“单热”编码。 单热编码是广泛使用的分类数据格式,也称为“分类编码”。 有关单热编码的更详细解释,请参阅第6章第1节。在我们的情况中,我们标签的热门编码包括将每个标签作为全零矢量嵌入,其中1代表 标签索引,例如:
def to_one_hot(labels, dimension=46):
results = np.zeros((len(labels), dimension))
for i, label in enumerate(labels):
results[i, label] = 1.
return results
# Our vectorized training labels
one_hot_train_labels = to_one_hot(train_labels)
# Our vectorized test labels
one_hot_test_labels = to_one_hot(test_labels)
请注意,在Keras中有一种内置的方法可以实现,您在我们的MNIST示例中已经看到了这一点:
from keras.utils.np_utils import to_categorical
one_hot_train_labels = to_categorical(train_labels)
one_hot_test_labels = to_categorical(test_labels)
出于这个原因,我们将使用更大的图层。我们去64个单位:
from keras import models
from keras import layers
model = models.Sequential()
model.add(layers.Dense(64, activation='relu', input_shape=(10000,)))
model.add(layers.Dense(64, activation='relu'))
model.add(layers.Dense(46, activation='softmax'))
还有两件事你应该注意这个架构:
在这种情况下使用的最佳损失函数是categorical_crossentropy。它测量两个概率分布之间的距离:在本例中,我们网络输出的概率分布与标签的真实分布之间的距离。通过最小化这两个分布之间的距离,我们训练我们的网络输出尽可能接近真实标签的东西。
model.compile(optimizer='rmsprop',
loss='categorical_crossentropy',
metrics=['accuracy'])
我们在训练数据中设置1000个样本作为验证集:
x_val = x_train[:1000]
partial_x_train = x_train[1000:]
y_val = one_hot_train_labels[:1000]
partial_y_train = one_hot_train_labels[1000:]
现在让我们训练我们的网络20代:
history = model.fit(partial_x_train,
partial_y_train,
epochs=20,
batch_size=512,
validation_data=(x_val, y_val))
让我们来显示其损失和准确度曲线:
import matplotlib.pyplot as plt
loss = history.history['loss']
val_loss = history.history['val_loss']
epochs = range(1, len(loss) + 1)
plt.plot(epochs, loss, 'bo', label='Training loss')
plt.plot(epochs, val_loss, 'b', label='Validation loss')
plt.title('Training and validation loss')
plt.xlabel('Epochs')
plt.ylabel('Loss')
plt.legend()
plt.show()
plt.clf() # clear figure
acc = history.history['acc']
val_acc = history.history['val_acc']
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('Loss')
plt.legend()
plt.show()
看起来网络在8个时期后开始过度配合。 让我们从头开始训练一个新的网络8个时期,然后让我们在测试集上评估它:
model = models.Sequential()
model.add(layers.Dense(64, activation='relu', input_shape=(10000,)))
model.add(layers.Dense(64, activation='relu'))
model.add(layers.Dense(46, activation='softmax'))
model.compile(optimizer='rmsprop',
loss='categorical_crossentropy',
metrics=['accuracy'])
model.fit(partial_x_train,
partial_y_train,
epochs=8,
batch_size=512,
validation_data=(x_val, y_val))
results = model.evaluate(x_test, one_hot_test_labels)
In [28]:results
Out[28]:[0.98764628548762257, 0.77693677651807869]
我们的方法达到〜78%的准确度。 对于一个平衡的二元分类问题,纯随机分类器达到的精度为50%,但在我们的例子中,它接近于19%,所以我们的结果看起来相当不错,至少与随机基线相比:
import copy
test_labels_copy = copy.copy(test_labels)
np.random.shuffle(test_labels_copy)
float(np.sum(np.array(test_labels) == np.array(test_labels_copy))) / len(test_labels)
我们可以验证模型实例的预测方法返回所有46个主题的概率分布。 让我们为所有测试数据生成主题预测:
predictions = model.predict(x_test)
预测中的每个条目都是长度为46的矢量:
predictions[0].shape
该向量中的系数总和为1:
np.sum(predictions[0])
最大的条目是预测的类别,即具有最高概率的类别:
np.argmax(predictions[0])
我们前面提到,另一种对标签进行编码的方法是将它们转换为整数张量,如下所示:
y_train = np.array(train_labels)
y_test = np.array(test_labels)
它唯一会改变的是损失函数的选择。 我们以前的损失categorical_crossentropy期望标签遵循分类编码。 使用整数标签,我们应该使用sparse_categorical_crossentropy:
model.compile(optimizer='rmsprop', loss='sparse_categorical_crossentropy', metrics=['acc'])
这个新的损失函数仍然在数学上与categorical_crossentropy相同; 它只是有不同的界面。
我们之前提到,由于我们的最终输出是46维的,我们应该避免隐藏单元少于46个的中间层。 现在让我们尝试看看当我们通过使中间层显着小于46维时引入信息瓶颈时会发生什么,例如,4维。
model = models.Sequential()
model.add(layers.Dense(64, activation='relu', input_shape=(10000,)))
model.add(layers.Dense(4, activation='relu'))
model.add(layers.Dense(46, activation='softmax'))
model.compile(optimizer='rmsprop',
loss='categorical_crossentropy',
metrics=['accuracy'])
model.fit(partial_x_train,
partial_y_train,
epochs=20,
batch_size=128,
validation_data=(x_val, y_val))