新闻分类:多个类别的分类范例
本节,你将搭建网络把路透社的新闻按主题区分成 46 个不同类,每个数据点只能归入一个类别,因此,这种问题叫做单标签、多类别的分类。如果每个数据点可以属于多个类别(即主题),你所面临的问题就成了多标签、多类别的分类。
路透社数据集
这个数据集是路透社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)
# words=10000 限制数据集最常用的词汇数目
# 训练样本 8,982 个,测试样本 2,246 个:
>>> 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]
把新闻数据解码成文本
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]])
# 样本的标签是个整数,值为 0 - 45,表示主题的索引:
>>> train_labels[10]
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
x_train = vectorize_sequences(train_data)
x_test = vectorize_sequences(test_data)
# 二值编码:
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
one_hot_train_labels = to_one_hot(train_labels)
one_hot_test_labels = to_one_hot(test_labels)
# Keras 内建的二值编码:
from keras.utils.np_utils import to_categorical
one_hot_train_labels = to_categorical(train_labels)
one_hot_test_labels = to_categorical(test_labels)
搭建网络
定义模型
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 维向量。每一维编码表示不同的输出类别
# 网络最后一层的激活函数是 softmax。这意味着输出是 46 个不同类别的概率分布,并且其和等于 1。
在此最后的损失函数是 categorical_crossentropy。它测算网络输出的概率分布与标签实际分布的差距。将两种分布的差距最小化,你训练的网络输出就最大限度地接近真正的标签。
编译模型
model.compile(optimizer='rmsprop',
loss='categorical_crossentropy',metrics=['accuarcy']
验证你的训练方法
建立验证数据集
# 从训练数据中分出 1,000 样本,用于验证。
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()
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()
在 9 次迭代后,网络开始过度拟合。我们来从头写个迭代 9 次的新网络,再用测试数据进行评估。
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]
根据新数据进行预测
你可以验证,模型预测的返回结果是对 46 个主题的概率分布。
predictions = model.predict(x_test)
# 每条预测是长度为 46 的向量:
>>> predictions[0].shape
(46,)
# 这个向量各系数之和为 1:
>>> np.sum(predictions[0])
1.0
# 向量系数(概率值)中的最大值是预测出的类别:
>>> np.argmax(predictions[0])
4
处理标签和损失的另一种办法
# 标签编码的另一种办法是将其转成整数张量:
y_train = np.array(train_labels)
y_test = np.array(test_labels)
# 使用数字标签,应当相应地改变损失函数为 sparse_categorical_crossentropy:
model.compile(optimizer='rmsprop',
loss='sparse_categorical_crossentropy',
metrics=['acc'])
大空间中间层很重要
本例的中间层不能少于 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))
这个网络现在的验证精确度最大约 71%,下降了 8%。