公司动态

零基础人工智能-第三篇 TensorFlow2.0 巧用 IMDB数据集影评好坏分类

2020-04-21


IMDB数据集介绍 

 
    IMDB数据集包含来自互联网的50000条严重两极分化的评论,该数据被分为用于训练的25000条评论和用于测试的25000条评论,训练集和测试集都包含50%的正面评价和50%的负面评价。该数据集已经经过预处理:评论(词语序列)已经被转换为整数序列,其中每个整数代表该单词在字典中的位置。


IMDB影评数据集使用


    我想建立一个神经模型,对影评进行分析,来推断它是正面还是负面的评论。

1. 使用tf.keras框架,和数学库numpy


import tensorflow as tf
from tensorflow import keras
import numpy as np
print(tf.__version__)


2. 下载imdb



imdb=keras.datasets.imdb(train_x, train_y), (test_x, text_y)=keras.datasets.imdb.load_data(num_words=10000)

    参数num_words = 10000 表示数据集将会保留出现频率在前10000的单词,有些稀有单词将会被抛弃以保证数据的可处理性。

3. 查看数据


    已知数据集已经被预处理,每个影评都长串整数数字,代表字典位置,每个影评都有标签数字0或1代表负面或正面评论。

print("Training entries: {}, labels: {}".format(len(train_x), len(train_y)))
print(train_x[0])
print('len: ',len(train_x[0]), len(train_x[1]))


    电影评论基本上长度都不会相等。但是输入神经网络的数据必须是相等的,我们稍后会填充。

4.创建数字id和词的匹配字典


    数字id转化成词方便查看

word_index = imdb.get_word_index()
word2id = {k:(v+3for k, v in word_index.items()}
word2id['<PAD>'] = 0
word2id['<START>'] = 1
word2id['<UNK>'] = 2
word2id['<UNUSED>'] = 3
id2word = {v:k for k, v in word2id.items()}
def get_words(sent_ids):
return ' '.join([id2word.get(i, '?'for i in sent_ids])
sent = get_words(train_x[0])
print(sent)


    PAD 长度不同,用PAD填充pad一般选用一种“无损”的方法,比如CNN里用0向量,一般keras会自动补0。
    unk: 如果输入一个在语料库中不存在的词,“未登录词”,当作unk处理

5. 准备数据


    影视评价文本想要输入到神经网络中必须将其转化为向量,需要填充数组,使他们相同长度,然后创建一个num_examples * max_length的向量。可以使用能够处理这种形状的嵌入层作为我们网络中的第一层。

    使用pad_sequences函数来标准化长度

train_x = keras.preprocessing.sequence.pad_sequences(
train_x, value=word2id['<PAD>'],
padding='post', maxlen=256
)
test_x = keras.preprocessing.sequence.pad_sequences(
test_x, value=word2id['<PAD>'],
padding='post', maxlen=256
)
print(train_x[0])
print('len: ',len(train_x[0]), len(train_x[1]))


6.  建立模型


    神经网络层是由层的堆叠来实现的,需要两个架构参数

模型需要多少层?

每层多少隐藏神经元?

    因为我们输入层是包含单词索引的数组,预测的标签是0或1,那么可以这样建立模型

import tensorflow.keras.layers as layers
vocab_size = 10000
model = keras.Sequential()
model.add(layers.Embedding(vocab_size, 16))
model.add(layers.GlobalAveragePooling1D())
model.add(layers.Dense(16, activation='relu'))
model.add(layers.Dense(1, activation='sigmoid'))
model.summary()
model.compile(optimizer='adam',
loss='binary_crossentropy',
metrics=['accuracy'])


模型堆叠方式
    第一层是嵌入层。该层采用整数编码的词汇表,并查找每个词索引的嵌入向量。这些向量是作为模型训练学习的。向量为输出数组添加维度。生成的维度为:(batch, sequence, embedding)。
    第二层是GlobalAveragePooling1D层。通过对序列维度求平均,为每个示例返回固定长度的输出向量。这允许模型以最简单的方式处理可变长度的输入。
    第三层是16个神经元全连接层。
    第四层是输出层。激活函数采用sigmoid,输出一个0和1之间的浮点数,用来表示置信度。
    优化器和损失函数和校准函数为常见的。


7.  模型验证与校准


    通过从原训练集数据分离10000组数据做校验数据集,验证网络精度。

x_val = train_x[:10000]
x_train = train_x[10000:]
y_val = train_y[:10000]
y_train = train_y[10000:]
history = model.fit(x_train,y_train,
epochs=40, batch_size=512,
validation_data=(x_val, y_val),
verbose=1)
result = model.evaluate(test_x, text_y)
print(result)



    训练该模型使用15000个样本,batch_size 为512,共训练了40个epoch。
在训练的同时会记录与验证数据集的对比结果。
    

    最终打印出模型结果。    
    精确度已经达到了87%,随着训练次数的增加,这个值可能最终会逼近95%。

8.  查看准确率时序图

    model.fit()这个函数会返回在训练过程中的历史数据,在TensorFlow中会以History 对象的形式存在,它是一个dictionary,我们可以打印它来观察它的结构,绘制拟合图。

import matplotlib.pyplot as plt
history_dict = history.history
history_dict.keys()
acc = history_dict['accuracy']
val_acc = history_dict['val_accuracy']
loss = history_dict['loss']
val_loss = history_dict['val_loss']
epochs = range(1len(acc)+1)
plt.plot(epochs, loss, 'bo', label='train loss')
plt.plot(epochs, val_loss, 'b', label='val loss')
plt.title('Train and val loss')
plt.xlabel('Epochs')
plt.xlabel('loss')
plt.legend()
plt.show()




plt.clf() # clear figure
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()


    上图中,点表示训练过程中的损失值和精确度,实线表示验证数据集的损失值和精确度。
    可以看出,训练损失值随着每个epoch而减少,并且训练精确度随epoch增加而增加。这在使用梯度下降优化时是符合预期的。
    验证精确度和训练精确度在20个epoch之后稍微有一些“分道扬镳”。
    这是过度拟合的一个例子:模型在训练数据上的表现比在以前从未见过的数据上表现得更好。在此之后,模型由于过度优化,无法将结果更精准的适用于测试数据了。
    对于这种特殊情况,我们可以通过在二十个左右的epoch之后停止训练来防止过度拟合。


以后的教程中,我们将进一步学习如何使用回调自动执行此操作。

一起来期待一下吧~