Keras学习教程三

原文地址:https://github.com/fchollet/deep-learning-with-python-notebooks/blob/master/3.6-classifying-newswires.ipynb

分类新闻:一个多级分类的例子

    在前一节中,我们看到了如何使用密集连接的神经网络将矢量输入分为两个互斥类。 但是当你有两个以上的课程时会发生什么?

    在本节中,我们将建立一个网络,将路透新闻专线分为46个不同的互斥专题。 由于我们有很多类,这个问题是“多类分类”的一个实例,由于每个数据点应该只被分类到一个类别中,所以问题更具体的是“单标签,多类分类”的实例,。 如果每个数据点可能属于多个类别(在我们的案例中是主题),那么我们将面临“多标签,多类分类”问题。

路透社数据集

    我们将与Reuters数据集一起工作,这是一组由路透社于1986年出版的短篇新闻及其主题,它是一个非常简单,广泛使用的文本分类玩具数据集。 有46个不同的主题; 一些主题比其他主题更具代表性,但每个主题在训练集中至少有10个示例。

    与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)

建立我们的网络

    这个主题分类问题与我们以前的电影评论分类问题看起来非常相似:在这两种情况下,我们都试图对短文本片段进行分类。然而,这里有一个新的约束:输出类的数量从2到46,即输出空间的维数要大得多。
    在像我们使用的密集层的堆栈中,每层只能访问前一层输出中存在的信息。如果一个图层丢弃了与分类问题相关的一些信息,则该信息永远不会被后面的图层恢复:每个图层都有可能成为“信息瓶颈”。在我们之前的例子中,我们使用16维中间层,但16维空间可能太有限,无法分离出46个不同的类:这样的小层可能充当信息瓶颈,永久丢弃相关信息。

    出于这个原因,我们将使用更大的图层。我们去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'))
    还有两件事你应该注意这个架构:
            我们用一个大小为46的密集层来结束网络。这意味着对于每个输入样本,我们的网络将输出一个46维向量。此向量中的每个条目(每个维度)都将编码不同的输出类。
            最后一层使用softmax激活。您已经在MNIST示例中看到过这种模式。这意味着网络将输出46个不同输出类别的概率分布,即对于每个输入样本,网络将产生46维输出向量,其中输出[i]是样本属于类别i的概率。 46分将总计为1。

    在这种情况下使用的最佳损失函数是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))



猜你喜欢

转载自blog.csdn.net/qq_40716944/article/details/80630423