IMDB 数据集包含来自互联网电影数据库(IMDB)的 50 000 条严重两极分化的评论。数据集被分为用于训练的 25 000 条评论与用于测试的 25 000 条评论,训练集和测试集都包含 50% 的正面评论和 50% 的负面评论。
train_labels 和 test_labels 都是 0 和 1 组成的列表,其中 0代表负面(negative),1 代表正面(positive)
IMDB 数据集内置于 Keras 库。它已经过预处理:评论(单词序列)已经被转换为整数序列,其中每个整数代表字典中的某个单词。
# 这里使用tensorflow作为Keras后端
import tensorflow as tf
from tensorflow import keras
from keras.datasets import imdb
参数 num_words=10000 的意思是仅保留训练数据中前 10 000 个最常出现的单词。低频单词将被舍弃。这样得到的向量数据不会太大,便于处理。
(train_data, train_labels), (test_data, test_labels) = imdb.load_data(num_words=10000)
# 查看样本数量
print("Training entries: {}, labels: {}".format(len(train_data),len(train_labels)))
# 影评文本已转换为整数,其中每个整数都表示字典中的一个特定字词。第一条影评如下所示:
print(train_data[0])
# 第一条影评的标签
print(train_labels[0])
不能将整数序列直接输入神经网络,需要将列表转换为张量,转换方法有以下两种。
下面我们采用后一种方法将数据向量化
# 将整数序列编码为二进制矩阵
import numpy as np
def vectorize_sequences(sequences, dimension=10000):
# 创建一个形状为(len(sequences), dimension) 的零矩阵
results = np.zeros((len(sequences), dimension))
for i, sequence in enumerate(sequences):
results[i, sequence] = 1. # 将 results[i] 的指定索引设为 1
return results
x_train = vectorize_sequences(train_data) # 将训练数据向量化
x_test = vectorize_sequences(test_data) # 将测试数据向量化
y_train = np.asarray(train_labels).astype('float32') # 将训练数据标签向量化
y_test = np.asarray(test_labels).astype('float32') # 将测试数据向量标签化
从原始训练数据中分离出 10000 个样本,创建一个验证集
x_val = x_train[:10000]
partial_x_train = x_train[10000:]
y_val = y_train[:10000]
partial_y_train = y_train[10000:]
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'))
model.summary()
为什么使用激活函数?
如果没有 relu 等激活函数(也叫非线性),Dense 层将只包含两个线性运算——点积和加法:
output = dot(W, input) + b
这样 Dense 层就只能学习输入数据的线性变换(仿射变换):该层的假设空间是从输入数据到 16 位空间所有可能的线性变换集合。这种假设空间非常有限,无法利用多个表示层的优势,因为多个线性层堆叠实现的仍是线性运算,添加层数并不会扩展假设空间。
为了得到更丰富的假设空间,从而充分利用多层表示的优势,你需要添加非线性或激活函数。relu 是深度学习中最常用的激活函数,但还有许多其他函数可选,它们都有类似的奇怪名称,比如 prelu、elu 等。
然后需要选择损失函数和优化器。由于面对的是一个二分类问题,网络输出是一个概率值(网络最后一层使用 sigmoid 激活函数),那么最好使用 binary_crossentropy(二元交叉熵)损失。这并不是唯一可行的选择,比如还可以使用 mean_squared_error(均方误差)。但对于输出概率值的模型,交叉熵(crossentropy)往往是最好的选择。交叉熵是来自于信息论领域的概念,用于衡量概率分布之间的距离,在这个例子中就是真实分布与预测值之间的距离。
下面的步骤是用 rmsprop 优化器和 binary_crossentropy 损失函数来配置模型。注意,我们还在训练过程中监控精度。
model.compile(optimizer='rmsprop',
loss='binary_crossentropy',
metrics=['accuracy'])
上述代码将优化器、损失函数和指标作为字符串传入,这是因为 rmsprop、binary_
crossentropy 和 accuracy 都是 Keras 内置的一部分,有时也可以自定义参数
# 配置优化器
from keras import optimizers
model.compile(optimizer=optimizers.RMSprop(lr=0.001),
loss='binary_crossentropy',
metrics=['accuracy'])
# 使用自定义的损失和指标
from keras import losses
from keras import metrics
model.compile(optimizer=optimizers.RMSprop(lr=0.001),
loss=losses.binary_crossentropy,
metrics=[metrics.binary_accuracy])
现在使用128个样本组成的小批量,将模型训练50个轮次(即对 x_train 和 y_train 两个张量中的所有样本进行 50 次迭代)。与此同时,监控在留出的 10 000 个样本上的损失和精度。可以通过将验证数据传入 validation_data 参数来完成。
history = model.fit(partial_x_train,
partial_y_train,
epochs=50,
batch_size=128,
validation_data=(x_val, y_val))
import matplotlib.pyplot as plt
history_dict = history.history
loss_values = history_dict['loss']
val_loss_values = history_dict['val_loss']
epochs = range(1, len(loss_values) + 1)
plt.plot(epochs, loss_values, 'bo', label='Training loss')
plt.plot(epochs, val_loss_values, 'b', label='Validation loss')
plt.title('Training and validation loss')
plt.xlabel('Epochs')
plt.ylabel('Loss')
plt.legend()
plt.show()
plt.clf() # 清空图像
acc = history_dict['acc']
val_acc = history_dict['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('Accuracy')
plt.legend()
plt.show()
results = model.evaluate(x_test, y_test)
print(results)
predicts=model.predict(x_test)
print(predicts)