美文网首页《Deep Learning with Python》中文版
《Deep Learning with Python》第三章 3

《Deep Learning with Python》第三章 3

作者: 神机喵算 | 来源:发表于2018-06-09 22:51 被阅读8次
    3.5 新闻分类:多分类

    在上一小节,学习了如何使用全联接神经网络将向量输入分为二类。但是,当需要多分类时该咋办呢?

    在本小节,你将学习构建神经网络,把路透社新闻分为互不相交的46类主题。很明显,这个问题是多分类问题,并且每个数据点都只归为一类,那么该问题属于单标签、多分类;如果每个数据点可以属于多个分类,那么你面对的将是多标签、多分类问题。

    3.5.1 路透社新闻数据集

    路透社新闻数据集是由路透社1986年发布的短新闻和对应主题的集合,它常被用作文本分类的练手数据集。该数据集有46个不同的新闻主题,在训练集中每个主题包含至少10个新闻。

    和IMDB和MNIST数据集一样,路透社新闻数据集也打包作为Keras的一部分,下面简单看下:

    #Listing 3.12 Loading the Reuters dataset
    from keras.datasets import reuters
    (train_data, train_labels), (test_data, test_labels) = reuters.load_data(
        num_words=10000)
    

    设置参数num_words=10000,保留训练集中词频为top 10000的单词。

    你有8982条训练样本数据和2246条测试样本数据:

    >>> len(train_data)
    8982
    >>> len(test_data)
    2246
    

    从上述返回的结果看,每个样本都是整数列表(词索引):

    >>> 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]
    

    下面代码可以把词索引解码成词:

    #Listing 3.13 Decoding newswires back to text
    word_index = reuters.get_word_index()
    reverse_word_index = dict([(value, key) for (key, value) in word_index.items()])
    #Note that the indices are 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]])
    

    样本的label是0到45的整数(主题索引):

    >>> train_labels[10]
    3
    
    3.5.2 准备数据

    使用和上一小节同样的代码进行数据向量化。

    #Listing 3.14 Encoding the data
    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
    
    #Vectorized training data
    x_train = vectorize_sequences(train_data)
    #Vectorized test data
    x_test = vectorize_sequences(test_data)
    

    向量化label有两种方法:一种是,将label列表转成整数张量,另一种是,使用one-hot编码。one-hot编码广泛适用于分类数据,也称为分类编码。它的更详细介绍在6.1小节。在本例中,label的one-hot编码是将每个label映射到label索引位置为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
    #Vectorized training labels
    one_hot_train_labels = to_one_hot(train_labels)
    #Vectorized test labels
    one_hot_test_labels = to_one_hot(test_labels)
    

    上述label向量化的方式在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)
    
    3.5.3 构建神经网络

    这个主题分类问题和前一个影评分类类似:两类问题都是将短文本分类。但是这里有个新的限制:输出分类的数量由过去的2个变为46个。所以输出空间的维度更大。

    使用一系列的Dense layer时,每个layer只能访问上一个layer的输出信息。如果某一个layer丢失一些与分类相关的信息时,接下来的layer不可能再恢复这些信息,所以每个layer都可能成为潜在的信息瓶颈。在前面的例子中,选用的16维中间layer,但是16维空间并不能学习到46个不同的分类。

    考虑到上面的情况,这里使用更大的layer,隐藏单元设为64。

    #Listing 3.15 Model definition
    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'))
    

    上述代码中的神经网络架构需要注意两个事情:

    • 最后一个Dense layer大小为46。这意味着每个输入样本,神经网络模型输出一个46维向量。其中每个项代表不同的分类;
    • 最后一个layer使用softmax激活函数。这意味着神经网络模型输出一个46维的概率分布。对于每个输入样本,模型将输出一个46维的输出向量,每个output[i]是样本属于类别 i 的概率,且46个分数之和为1。

    对于本例最适合的损失函数是categorical_crossentropy。该函数度量两个概率分布的距离,意即,模型输出的概率分布与label真实分布之间的距离。为了最小化两个分布的距离,训练模型使得其输出更接近真实label。

    #Listing 3.16 Compiling the model
    model.compile(optimizer='rmsprop',
                  loss='categorical_crossentropy',
                  metrics=['accuracy'])
    
    3.5.4 验证模型

    下面从训练数据中分出1000个样本作为验证集。

    #Listing 3.17 Setting aside a validation set
    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个epoch。

    #Listing 3.18 Training the model
    history = model.fit(partial_x_train,
                        partial_y_train,
                        epochs=20,
                        batch_size=512,
                        validation_data=(x_val, y_val))
    

    最后,显示损失函数和准确度的曲线,见图3.9和3.10。

    #Listing 3.19 Plotting the training and validation loss
    import matplotlib.pyplot as pet
    
    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()
    
    #Listing 3.20 Plotting the training and validation accuracy
    #Clears the figure
    plt.clf()
    
    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()
    
    image

    图3.9 训练集和验证集的损失曲线

    image

    图3.10 训练集和验证集的准确度曲线

    从上面的图可以看出,神经网络模型训练在第9个epoch开始过拟合。接着从头开始训练9个epoch,然后在测试集赏进行评估。

    #Listing 3.21 Retraining a model from scratch
    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=9,
              batch_size=512,
              validation_data=(x_val, y_val))
    results = model.evaluate(x_test, one_hot_test_labels)
    

    下面是最终训练结果:

    >>> results
    [0.9565213431445807, 0.79697239536954589]
    

    上面的方法达到约80%的准确度。在二分类问题中,纯随机分类的准确度是50%。而在本例中,纯随机分类的准确度将近19%,所以本例的模型结果还是不错的,至少超过随机基准线:

    >>> import copy
    >>> test_labels_copy = copy.copy(test_labels)
    >>> np.random.shuffle(test_labels_copy)
    >>> hits_array = np.array(test_labels) == np.array(test_labels_copy)
    >>> float(np.sum(hits_array)) / len(test_labels)
    0.18655387355298308
    
    3.5.5 模型预测

    你可以用模型实例的predict方法验证返回的46个主题分类的概率分布。下面对所有的测试集生成主题预测。

    #Listing 3.22 Generating predictions for new data
    predictions = model.predict(x_test)
    

    predictions的每项是一个长度为64的向量:

    >>> predictions[0].shape
    (46,)
    

    这些向量的系数之和为1:

    >>> np.sum(predictions[0])
    1.0
    

    下面从预测分类中找出概率最大的项:

    >>> np.argmax(predictions[0])
    4
    
    3.5.6 处理label和loss的不同方法

    前面提到过label编码的两外一种方法,将其转化为整数张量,比如:

    y_train = np.array(train_labels)
    y_test = np.array(test_labels)
    

    上述处理label的方法唯一需要改变的是损失函数。在listing 3.21代码中使用的损失函数,categorical_crossentropy,期望label是一个分类编码。对于整数label,你应该选用sparse_categorical_crossentropy损失函数:

    model.compile(optimizer='rmsprop',
                  loss='sparse_categorical_crossentropy',
                  metrics=['acc'])
    

    上面这个新的损失函数在数学上是和categorical_crossentropy相同的,不同之处在于接口不同。

    3.5.7 中间layer的重要性

    前面提过,因为最后的输出是46维,你应该避免中间layer小于46个hidden unit。下面为你展示中间layer小于46维导致的信息瓶颈问题,以4个hidden unit为例。

    #Listing 3.23 A model with an information bottleneck
    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))
    

    现在新的模型达到最大约71%的验证准确度,丢失了8%。这种情况主要是压缩许多信息到一个低维度的空间,导致没有足够多的信息可以恢复。

    3.5.8 延伸实验
    • 尝试使用更大或者更小的layer:32个unit、128个unit等等;
    • 本例使用两个隐藏层。可以尝试一个或者三个隐藏层。
    3.5.9 总结

    从本例应该学习到的知识点:

    • 如果你想将数据分为N类,那神经网络模型最后一个Dense layer大小为N;
    • 在单标签、多分类的问题中,模型输出应该用softmax激活函数,输出N个分类的概率分布;
    • 分类交叉熵是分类问题合适的损失函数。它最小化模型输出的概率分布和真实label的概率分布之间的距离;
    • 处理多分类中label的两种方法:
      • 通过one-hot编码编码label,并使用categorical_crossentropy作为损失函数;
      • 通过整数张量编码label,并使用sparse_categorical_crossentropy损失函数
    • 对于数据分类的类别较多的情况,应该避免创建较小的中间layer,导致信息瓶颈。

    未完待续。。。

    Enjoy!

    翻译本书系列的初衷是,觉得其中把深度学习讲解的通俗易懂。不光有实例,也包含作者多年实践对深度学习概念、原理的深度理解。最后说不重要的一点,François Chollet是Keras作者。
    声明本资料仅供个人学习交流、研究,禁止用于其他目的。如果喜欢,请购买英文原版。


    侠天,专注于大数据、机器学习和数学相关的内容,并有个人公众号:bigdata_ny分享相关技术文章。

    若发现以上文章有任何不妥,请联系我。

    image

    相关文章

      网友评论

        本文标题:《Deep Learning with Python》第三章 3

        本文链接:https://www.haomeiwen.com/subject/uqvxeftx.html