Keras 教学 – 利用二元分类器训练 IMDB 深度学习演算法
前一篇文章介绍了机器学习最基本的 Hello World = MNIST 模型训练。今天换一个 IMDB Reviews 的 Play Data 例子玩看看,对于如何透过 Keras 训练神经网路的基本概念,可以参考前一篇「 Keras MNIST 手写辨识 x 深度学习的 HelloWorld 」文章介绍。
二元分类或称两类分类可能是在机器学习中应用最广泛问题。只要处理的问题只有两个结果,就可以适用。在这个例子中,我们将根据 IMDB 评论的文本内容将电影评论分为「正面」评论和「负面」评论。
关于 IMDB Dataset 资料集
IMDB Dataset 是来自 Internet 电影数据库 50,000 条评论文字。他们分为 25,000 条训练数据和 25,000 条测试数据,每组皆包含包括 50% 的负面评论和 50% 的正面评论。
我们可以直接透过 Keras Datasets 函式库载入已经整理好的资料集。这些资料集已经经过处理,会将评论依据单词顺序,排列为整数序列,其中每个整数代表字典中的特定单词。如下:
from keras.datasets import imdb (train_data, train_labels), (test_data, test_labels) = imdb.load_data(num_words=10000)
num_words=10000 表示我们只採用前 10000 个常出现的字词,此外在 label 中 0 表示负评 1 表示正评。
max([max(sequence) for sequence in train_data])
也可以透过字典档,将资料组合回评论文字。
word_index = imdb.get_word_index() reverse_word_index = dict([(value, key) for (key, value) in word_index.items()]) decoded_review = ' '.join([reverse_word_index.get(i - 3, '?') for i in train_data[0]]) # show decoded_review
IMDB Reviews 资料前处理
我们无法将代表字典档索引位置的整数资料直接送进网路进行训练,因此需要对资料进行转换。由于我们只採用前 10000 常用字词作为资料集,因此输入资料可以转换为 10000 维度的 one-hot-encode,例如 [3, 18] 表示一个全部都是 0 的阵列,只有 index 3, 18 是 1。我们会将这样的资料格式作为张量进行训练,转换如下:
import numpy as np def vectorize_sequences(sequences, dimension=10000): # Create an all-zero matrix of shape (len(sequences), dimension) results = np.zeros((len(sequences), dimension)) for i, sequence in enumerate(sequences): results[i, sequence] = 1. # set specific indices of results[i] to 1s return results # Our vectorized training data x_train = vectorize_sequences(train_data) # Our vectorized test data x_test = vectorize_sequences(test_data)
将结果标记进行正规化
# Our vectorized labels y_train = np.asarray(train_labels).astype('float32') y_test = np.asarray(test_labels).astype('float32')
建立 CNN 网路架构
这里我们预计使用三层网路,全连接层仅使用两层 16 个神经元的网路,启动函数设定为 relu,连接最后使用一个神经元输出(表示正评或负评),并使用 sigmoid 作为最后输出的启动函数。由下而上网路架构如下:
预计採用的 CNN 详细定义如下:
训练 Dataset:25,000
测试 Dataset:25,000
优化器:RMSprop
损失函数:二元分类
Batch Size:512
Epochs:100
from keras import models from keras import layers model = models.Sequential() model.add(layers.Dense(16, activation='relu', input_shape=(10000,))) model.add(layers.Dense(16, activation='relu')) model.add(layers.Dense(1, activation='sigmoid'))
将优化器设定为 rmsprop,损失函数使用 binary_crossentropy,将网路进行 Compile
model.compile(optimizer='rmsprop', loss='binary_crossentropy', metrics=['accuracy'])
训练 CNN 模型
先将準备训练的 25000 笔资料集,抽出 10000 笔资料集用在训练时期的验证资料,好让我们监控训练过程的準确性变化。如下:
x_val = x_train[:10000] partial_x_train = x_train[10000:] y_val = y_train[:10000] partial_y_train = y_train[10000:]
开始训练模型
history = model.fit(partial_x_train, partial_y_train, epochs=100, batch_size=512, validation_data=(x_val, y_val))
如果是是透过 Colab 的 GPU 进行训练,每一个 Epoch 差不多 3 秒多一点,训练结果如下:
训练的过程会把相关资讯存放在 history,透过事后分析训练过程的资讯可以帮助我们优化参数。
history_dict = history.history history_dict.keys()
训练结果图表分析
透过上面的方法可以取得训练 History 包含的资讯,然后我们将资讯绘製成为图表,首先观察 loss 变化,如下:
#@title import matplotlib.pyplot as plt acc = history.history['accuracy'] val_acc = history.history['val_accuracy'] loss = history.history['loss'] val_loss = history.history['val_loss'] epochs = range(1, len(acc) + 1) # "bo" is for "blue dot" plt.plot(epochs, loss, 'bo', label='Training loss') # b is for "solid blue line" 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_values = history_dict['accuracy'] val_acc_values = history_dict['val_accuracy'] 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()
执行结果:
由上面的数据可以看出来,以现在的网路架构,其实在第 3 次 Epoch 就已经获得最佳的结果,之后的训练已经造成过度拟合 (Over Fitting),因此在这个案例中将 Epoch 设定为 3 或 4 是取得最佳训练模型的方法。今天的範例程式可以在 GitHub 取得 ipynb 档案 ,需要请自行下载。
原来算比较久不一定比较好 R~实务上,建议透过训练过程的监控,可以选择某一个 Epoch 获得表现最好的 Model 作为最佳解。此外,模型的参数也是很重要的,下一篇文章我们就来介绍一下各种参数的使用情境,敬请期待…….
Be First to Comment