欢迎您访问 最编程 本站为您分享编程语言代码,编程技术文章!
您现在的位置是: 首页

Python多分类模型预测技巧与方法解析

最编程 2024-08-03 16:06:28
...



Python深度学习实例二---新闻分类(多分类问题)

  • 1.路透社数据集
  • 2.准备数据
  • 3.构建网络
  • 4.进行训练和测试
  • 4.1 留出验证集
  • 4.2 训练模型
  • 4.3 绘制训练过程中的损失与精度曲线
  • 5.从头开始重新训练一个模型
  • 6.小结


1.路透社数据集

本节使用路透社数据集,它包含许多短新闻及其对应的主题,由路透社在 1986 年发布。它是一个简单的、广泛使用的文本分类数据集。它包括 46 个不同的主题:某些主题的样本更多,但训练集中每个主题都有至少 10 个样本。与 IMDB 和 MNIST 类似,路透社数据集也内置为Keras的一部分。
加载路透社数据集,与学习实例一的IMDB数据集一样它已经过预处理,新闻(单词序列)已经被转换为整数序列,其中每个整数代表字典中的某个单词。参数num_words=10000 将数据限定为前10000个最常出现的单词,代码实现:

from keras.datasets import reuters
(train_data, train_labels), (test_data, test_labels) = reuters.load_data(num_words=10000)

我们来看一下数据以及标签的样子,代码实现:

len(train_data)    #训练数据的长度也就是训练样本的个数
train_data         #训练数据
train_data.shape   #训练数据的shape
len(test_data)     #测试数据的长度也就是测试样本的个数
train_data[10]     #第十一个测试数据

结果:

8982  #一共有8982个训练样本

array([list([1, 2, 2, 8, 43, 10, 447, 5, 25, 207, 270, 5, 3095, 111, 16, 369, 186, 90, 67, 7, 89, 5, 19, 102, 6, 19, 124, 15, 90, 67, 84, 22, 482, 26, 7, 48, 4, 49, 8, 864, 39, 209, 154, 6, 151, 6, 83, 11, 15, 22, 155, 11, 15, 7, 48, 9, 4579, 1005, 504, 6, 258, 6, 272, 11, 15, 22, 134, 44, 11, 15, 16, 8, 197, 1245, 90, 67, 52, 29, 209, 30, 32, 132, 6, 109, 15, 17, 12]),
       list([1, 3267, 699, 3434, 2295, 56, 2, 7511, 9, 56, 3906, 1073, 81, 5, 1198, 57, 366, 737, 132, 20, 4093, 7, 2, 49, 2295, 2, 1037, 3267, 699, 3434, 8, 7, 10, 241, 16, 855, 129, 231, 783, 5, 4, 587, 2295, 2, 2, 775, 7, 48, 34, 191, 44, 35, 1795, 505, 17, 12]),
       list([1, 53, 12, 284, 15, 14, 272, 26, 53, 959, 32, 818, 15, 14, 272, 26, 39, 684, 70, 11, 14, 12, 3886, 18, 180, 183, 187, 70, 11, 14, 102, 32, 11, 29, 53, 44, 704, 15, 14, 19, 758, 15, 53, 959, 47, 1013, 15, 14, 19, 132, 15, 39, 965, 32, 11, 14, 147, 72, 11, 180, 183, 187, 44, 11, 14, 102, 19, 11, 123, 186, 90, 67, 960, 4, 78, 13, 68, 467, 511, 110, 59, 89, 90, 67, 1390, 55, 2678, 92, 617, 80, 1274, 46, 905, 220, 13, 4, 346, 48, 235, 629, 5, 211, 5, 1118, 7, 2, 81, 5, 187, 11, 15, 9, 1709, 201, 5, 47, 3615, 18, 478, 4514, 5, 1118, 7, 232, 2, 71, 5, 160, 63, 11, 9, 2, 81, 5, 102, 59, 11, 17, 12]),
       ...,
       list([1, 141, 3890, 387, 81, 8, 16, 1629, 10, 340, 1241, 850, 31, 56, 3890, 691, 9, 1241, 71, 9, 5985, 2, 2, 699, 2, 2, 2, 699, 244, 5945, 4, 49, 8, 4, 656, 850, 33, 2993, 9, 2139, 340, 3371, 1493, 9, 2, 22, 2, 1094, 687, 83, 35, 15, 257, 6, 57, 9190, 7, 4, 5956, 654, 5, 2, 6191, 1371, 4, 49, 8, 16, 369, 646, 6, 1076, 7, 124, 407, 17, 12]),
       list([1, 53, 46, 957, 26, 14, 74, 132, 26, 39, 46, 258, 3614, 18, 14, 74, 134, 5131, 18, 88, 2321, 72, 11, 14, 1842, 32, 11, 123, 383, 89, 39, 46, 235, 10, 864, 728, 5, 258, 44, 11, 15, 22, 753, 9, 42, 92, 131, 728, 5, 69, 312, 11, 15, 22, 222, 2, 3237, 383, 48, 39, 74, 235, 10, 864, 276, 5, 61, 32, 11, 15, 21, 4, 211, 5, 126, 1072, 42, 92, 131, 46, 19, 352, 11, 15, 22, 710, 220, 9, 42, 92, 131, 276, 5, 59, 61, 11, 15, 22, 10, 455, 7, 1172, 137, 336, 1325, 6, 1532, 142, 971, 6463, 43, 359, 5, 4, 326, 753, 364, 17, 12]),
       list([1, 227, 2406, 91, 2, 125, 2855, 21, 4, 3976, 76, 7, 4, 757, 481, 3976, 790, 5259, 5654, 9, 111, 149, 8, 7, 10, 76, 223, 51, 4, 417, 8, 1047, 91, 6917, 1688, 340, 7, 194, 9411, 6, 1894, 21, 127, 2151, 2394, 1456, 6, 3034, 4, 329, 433, 7, 65, 87, 1127, 10, 8219, 1475, 290, 9, 21, 567, 16, 1926, 24, 4, 76, 209, 30, 4033, 6655, 5654, 8, 4, 60, 8, 4, 966, 308, 40, 2575, 129, 2, 295, 277, 1071, 9, 24, 286, 2114, 234, 222, 9, 4, 906, 3994, 8519, 114, 5758, 1752, 7, 4, 113, 17, 12])],
      dtype=object)
      
 (8982,)
 
 2246  #一共有2246个测试样本
 
[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]    #与IMDB评论一样,每个样本都是一个整数列表

2.准备数据

1.对数据集进行 one-hot 编码,将其转换为 0 和 1 组成的向量。举个例子,序列 [3, 5] 将会被转换为 10000 维向量,只有索引为 3 和 5 的元素是 1,其余元素都是 0。
代码实现:

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)    # 将测试数据向量化

2.对标签进行向量化
将标签向量化有两种方法:你可以将标签列表转换为整数张量,或者使用 one-hot 编码。one-hot 编码是分类数据广泛使用的一种格式,也叫分类编码(categorical encoding)。
(1)Keras 内置方法可以实现one-hot 编码。(本节我们采用这个方法)
代码实现:

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)将标签转换为整数张量
需要注意的是,对于这种编码方法,唯一需要改变是在编译模型时的损失函数的选择,应该使用sparse_categorical_crossentropy,这个新的损失函数在数学上与 categorical_crossentropy 完全相同。
代码实现:

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

3.构建网络

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

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'))
model.compile(optimizer='rmsprop',
                        loss='categorical_crossentropy',
                        metrics=['accuracy'])

4.进行训练和测试

4.1 留出验证集

代码实现:

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

4.2 训练模型

history = model.fit(partial_x_train,
                    partial_y_train,
                    epochs=20,
                    batch_size=512,
                    validation_data=(x_val, y_val))

4.3 绘制训练过程中的损失与精度曲线

代码实现:`

# 绘制训练损失和验证损失
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()

结果:

python用多分类模型预测方法 python多分类问题_深度学习

# 绘制训练精度和验证精度
plt.clf()   
acc = history.history['accuracy']
val_acc = history.history['val_accuracy']
plt.plot(epochs, acc, 'bo', label='Training accuracy')
plt.plot(epochs, val_acc, 'b', label='Validation accuracy')
plt.title('Training and validation accuracy')
plt.xlabel('Epochs')
plt.ylabel('Loss')
plt.legend()
plt.show()

结果:

python用多分类模型预测方法 python多分类问题_人工智能_02

5.从头开始重新训练一个模型

从上面的两幅图可以看出,训练损失每轮都在降低,训练精度每轮都在提升。但是验证损失和验证精度并非如此,它们在第九轮达到最佳值。这种情况表明模型在训练数据上的表现越来越好,但在测试数据上不一定表现的越来越好,这就是过拟合现象。模型在第二轮之后,对训练数据过度优化,最终学到的表示仅针对于训练数据,无法泛化到训练集之外的数据。所以为了防止过拟合,我们从头开始训练一个新的网络,训练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)      #model.evaluate:输入测试数据和测试标签,输出损失和精确度

结果如下:

[0.9776423573493958, 0.7853962779045105]

下面我们利用predict方法在测试数据上生成预测结果

predictions = model.predict(x_test)

predictions中的每一个都是长度为46的向量

predictions.shape

输出:

(2246, 46)

每一个向量的所有元素总和为1

sum(predictions[0])

输出:

0.9999999999428439

每一个向量中最大的元素就是预测类别,即概率最大的类别

np.argmax(predictions[0])  # argmax可以取出中元素最大值所对应的索引

输出:

4

6.小结

(1)如果要对N个类别的数据点进行分类,网络的最后一层应该是大小为N的Dense层。
(2)对于单标签、多分类问题,网络的最后一层应该使用softmax激活,这样可以输出在N个输出类别上的概率分布。
(3)对于单标签、多分类问题的损失函数几乎总是应该使用分类交叉熵(categorical_crossentropy)。它将网络输出的概率分布与目标的真实分布之间的距离最小化。


推荐阅读