keras_新闻多分类问题

https://www.jianshu.com/p/46162c8a66a0

Python深度学习
‰ 如果要对 N 个类别的数据点进行分类,网络的最后一层应该是大小为 N 的 Dense 层。
‰ 对于单标签、多分类问题,网络的最后一层应该使用 softmax 激活,这样可以输出在 N
个输出类别上的概率分布。
‰ 这种问题的损失函数几乎总是应该使用分类交叉熵。它将网络输出的概率分布与目标的
真实分布之间的距离最小化。
‰ 处理多分类问题的标签有两种方法。
ƒ 通过分类编码(也叫 one-hot 编码)对标签进行编码,然后使用 categorical_
crossentropy 作为损失函数。
ƒ 将标签编码为整数,然后使用 sparse_categorical_crossentropy 损失函数。
‰ 如果你需要将数据划分到许多类别中,应该避免使用太小的中间层,以免在网络中造成
信息瓶颈。

import keras
keras.__version__
# 本节你会构建一个网络,将路透社新闻划分为 46 个互斥的主题。因为有多个类别,所以
# 这是多分类(multiclass classification)问题的一个例子。因为每个数据点只能划分到一个类别,
# 所以更具体地说,这是单标签、多分类(single-label, multiclass classification)问题的一个例
# 子。如果每个数据点可以划分到多个类别(主题),那它就是一个多标签、多分类(multilabel,
# multiclass classification)问题

from keras.datasets import reuters

(train_data, train_labels), (test_data, test_labels) = reuters.load_data(num_words=10000)

# 本节使用路透社数据集,它包含许多短新闻及其对应的主题,由路透社在 1986 年发布。它
# 是一个简单的、广泛使用的文本分类数据集。它包括 46 个不同的主题:某些主题的样本更多,
# 但训练集中每个主题都有至少 10 个样本。
# 与 IMDB 和 MNIST 类似,路透社数据集也内置为 Keras 的一部分。我们来看一下。

len(train_data)

8982
len(test_data)
2246
As with the IMDB reviews, each example is a list of integers (word indices):

train_data[10]
[1,
 245,
 273,
 207,
 156,
 53,
 74,


注意,索引减去了 3,因为 012 是为“padding”(填充)、“start of
# sequence”(序列开始)、“unknown”(未知词)分别保留的索引
# 我们可以将索引解码为新闻文本
# 注意,索引减去了 3,因为 0、 1、 2 是为“padding”(填充)、“start of
# sequence”(序列开始)、“unknown”(未知词)分别保留的索引
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]])
decoded_newswire
'? ? ? said as a result of its december acquisition of space co it expects earnings per share in 1987 of 1 15 to 1 30 dlrs per share up from 70 cts in 1986 the company said pretax net should rise to nine to 10 mln dlrs from six mln dlrs in 1986 and rental operation revenues to 19 to 22 mln dlrs from 12 5 mln dlrs it said cash flow per share this year should be 2 50 to three dlrs reuter 3'
The label associated with an example is an integer between 0 and 45: a topic index.

train_labels[10]
3

Preparing the data
We can vectorize the data with the exact same code as in our previous example:

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)
To vectorize the labels, there are two possibilities: we could just cast the label list as an integer tensor, or we could use a "one-hot" encoding. One-hot encoding is a widely used format for categorical data, also called "categorical encoding". For a more detailed explanation of one-hot encoding, you can refer to Chapter 6, Section 1. In our case, one-hot encoding of our labels consists in embedding each label as an all-zero vector with a 1 in the place of the label index, e.g.:

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)
Note that there is a built-in way to do this in Keras, which you have already seen in action in our MNIST example:

from keras.utils.np_utils import to_categorical
​
one_hot_train_labels = to_categorical(train_labels)
one_hot_test_labels = to_categorical(test_labels)

Building our network


# 这个主题分类问题与前面的电影评论分类问题类似,两个例子都是试图对简短的文本片段
# 进行分类。但这个问题有一个新的约束条件:输出类别的数量从 2 个变为 46 个。输出空间的维
# 度要大得多。
# 对于前面用过的 Dense 层的堆叠,每层只能访问上一层输出的信息。如果某一层丢失了与
# 分类问题相关的一些信息,那么这些信息无法被后面的层找回,也就是说,每一层都可能成为
# 信息瓶颈。上一个例子使用了 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 的 Dense 层。这意味着,对于每个输入样本,网络都会输
# 出一个 46 维向量。这个向量的每个元素(即每个维度)代表不同的输出类别。
# • 最后一层使用了 softmax 激活。你在 MNIST 例子中见过这种用法。网络将输出在 46
# 个不同输出类别上的概率分布——对于每一个输入样本,网络都会输出一个 46 维向量,
# 其中 output[i] 是样本属于第 i 个类别的概率。 46 个概率的总和为 1。
# 对于这个例子,最好的损失函数是 categorical_crossentropy(分类交叉熵)。它用于
# 衡量两个概率分布之间的距离,这里两个概率分布分别是网络输出的概率分布和标签的真实分
# 布。通过将这两个分布的距离最小化,训练网络可使输出结果尽可能接近真实标签


model.compile(optimizer='rmsprop',
              loss='categorical_crossentropy',
              metrics=['accuracy'])
## Validating our approach
​
Let's set apart 1,000 samples in our training data to use as a validation set:
# 验证你的方法
# 我们在训练数据中留出 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:]
Now let's train our network for 20 epochs:

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

keras_新闻多分类问题_第1张图片

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

keras_新闻多分类问题_第2张图片


It seems that the network starts overfitting after 8 epochs. Let's train a new network from scratch for 8 epochs, then let's evaluate it on the test set:


# 网络在训练 9 轮后开始过拟合。我们从头开始训练一个新网络,共 9 个轮次,然后在测试
# 集上评估模型。
# 代码清单 3-21 从头开始重新训练一个模型# 网络在训练 9 轮后开始过拟合。我们从头开始训练一个新网络,共 9 个轮次,然后在测试
# 集上评估模型。
# 代码清单 3-21 从头开始重新训练一个模型
​
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)

results
[0.98764628548762257, 0.77693677651807869]
Our approach reaches an accuracy of ~78%. With a balanced binary classification problem, the accuracy reached by a purely random classifier would be 50%, but in our case it is closer to 19%, so our results seem pretty good, at least when compared to a random baseline:

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

# 在新数据上生成预测结果
# 你可以验证,模型实例的 predict 方法返回了在 46 个主题上的概率分布。我们对所有测
# 试数据生成主题预测。

# predictions 中的每个元素都是长度为 46 的向量。
# >>> predictions[0].shape
# (46,)
# 这个向量的所有元素总和为 1。
# >>> np.sum(predictions[0])
# 1.0
# 最大的元素就是预测类别,即概率最大的类别。
# >>> np.argmax(predictions[0])

predictions = model.predict(x_test)


predictions[0].shape
(46,)
The coefficients in this vector sum to 1:

np.sum(predictions[0])
0.99999994
The largest entry is the predicted class, i.e. the class with the highest probability:

np.argmax(predictions[0])
3



处理标签和损失的另一种方法

前面提到了另一种编码标签的方法,就是将其转换为整数张量,如下所示。
y_train = np.array(train_labels)
y_test = np.array(test_labels)
对于这种编码方法,唯一需要改变的是损失函数的选择。对于代码清单 3-21 使用的损失
函数 categorical_crossentropy,标签应该遵循分类编码。对于整数标签,你应该使用
sparse_categorical_crossentropy。
model.compile(optimizer='rmsprop',
loss='sparse_categorical_crossentropy',
metrics=['acc'])
这个新的损失函数在数学上与 categorical_crossentropy 完全相同,二者只是接口不同

中间层维度足够大的重要性

中间层维度足够大的重要性
前面提到,最终输出是 46 维的,因此中间层的隐藏单元个数不应该比 46 小太多。现在来
看一下,如果中间层的维度远远小于 46(比如 4 维),造成了信息瓶颈,那么会发生什么?
代码清单 3-23 具有信息瓶颈的模型
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'))66  第 3 章 神经网络入门
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%。导致这一下降的主要原因在于,你
试图将大量信息(这些信息足够恢复 46 个类别的分割超平面)压缩到维度很小的中间空间。网
络能够将大部分必要信息塞入这个四维表示中,但并不是全部信息

你可能感兴趣的:(神经网络)