Press "Enter" to skip to content

Keras 教学 – 训练 IMDB Reviews CNN 网路模型

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

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注