听它爹说他孩儿:Keras 学习笔记 4.3

对电影评论进行分类:二分法的范例

把一批数据分成两类,可能是机器学习应用最为广泛的分类方法。

因特网电影数据库( IMDB )的数据集
该数据集有 50,000 条影评,训练与测试各用 25,000 条,而正面与负面评价各占 50 %。

与数据集 MNIST 相伴,IMDB 数据集也被打包进 Keras。影评已经预处理,文字序列转换成为数字序列,每个数字表示字典中的一个单词。下面的代码首次运行,会把约 80 MB 的数据下载到你的计算机。

 载入数据集 IMDB 

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

变量 train_data 和 test_data 是影评列表。影评是单词索引的列表,单词索引是单词序列的编码。train_labels 和 test_labels 是由数字 0 和 1 组成的列表,0 代表负面评论,1 代表正面评论:

>>> train_data[0]
[1, 14, 22, 16, ... 178, 32]
>>> train_labels[0]
1

影评的词汇量为 10,000 个常用词,其索引最大值不超过 10,000:

>>> max([max(sequence) for sequence in train_data])
9999

把影评解码成英文单词,这样做:

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]])
准备数据

神经网络不能食用数字列表,必须把它们转成张量。有 2 个办法:

  •  把列表变成形状为(样例,单词索引)的张量,作为网络的第一层。
  •  二值编码(One-hot encode 直译“独热编码”不知所云),把列表变成值为 0 或 1 的向量。例如,把序列 [3, 5] 放入维度为 10,000 的向量,索引 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)

# 样本现在的样子:
>>> x_train[0]
array([ 0., 1., 1., ..., 0., 0., 0.])

# 矢量化你的标签:
y_train = np.asarray(train_labels).astype('float32')
y_test = np.asarray(test_labels).astype('float32')

# 现在数据准备进入神经网络。

输入数据是向量,标签是标量(1 和 0):这是最容易的一步。这种网络是简单的多层叠加的全连接网络,用函数 relu 激活: 

Dense(16,activation='relu')。

传递给 Dense 层的参数 16 是隐藏单元的数目。隐藏单元是本层表达空间的维度。Dense 层的激活函数 relu 实现以下张量运算:

output = relu(dot(W, input) + b)

16 个隐藏单元意味着权重矩阵 W 的形状是(输入维度,16):W 的点积运算在 16 维度的表达空间内产生数据(然后,加上偏置向量 b,进行 relu 运算)。更多的表达空间可使网络学习更复杂的表达式,但也要付出更多的计算代价,甚至导致不需要的学习模式(模式可以提高训练成绩,但不能提高测试成绩)。

决定 Dense 层架构的有两个要素:

  • 要用多少层
  • 每个层有多少隐藏单元

在第 4 章,你将学习确定 Dense 层架构的指导原则。目前的选择是:

  • 2 个中间层,每层 16 个隐藏单元
  • 第 3 层产生标量数据,对当前影评的预测

中间层用 relu 作为激活函数,最后一层用 S 型函数激活输出一概率值(0 至 1),表示样本属于正面影评的似然度。

 定义模型 

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'))
为什么激活函数必不可少?

如果没有激活函数,比如 relu,Dense 层成了两个线性运算 — 点积和加法:

output = dot(W, input) + b

于是,Danse 层只会学习对输入数据进行线性转换。多层网络因此失去意义。

最后,你需要选择一个损失函数。因为正在处理的二值分类问题,最好使用损失函数  binary_crossentropy。

 编译模型 

model.compile(optimizer='rmsprop',        # 优化器
              loss='binary_crossentropy', # 损失函数
              metrics=['accuracy'])       # 测控指标

优化器、损失函数、测控指标均可自行设置。例如:

 设置优化器 

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])
验证你的方法

为了测控模型处理数据的精度,从初始的训练数据中分割出 10,000 条样本,作为验证用数据集:

x_val = x_train[:10000]
partial_x_train = x_train[10000:]
y_val = y_train[:10000]
partial_y_train = y_train[10000:]

你将训练模型 20 轮次。也就是说,你要对张量 x_train 和 y_train 中的全部样本,迭代训练 20 次。同时,你将对分割的 10,000 个样本产生的损失和精度进行测控。为此,你要使用变参  validation_data 输入验证用样本。

 训练你的模型 

model.compile(optimizer='rmsprop',
              loss='binary_crossentropy',
              metrics=['acc'])

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

注意, model.fit() 返回的对象 history 是个字典,保存着训练过程中的各种数据:

>>> history_dict = history.history
>>> history_dict.keys()
[u'acc', u'loss', u'val_acc', u'val_loss']

字典包括监测到的 4 个指标:前 2 个是训练的精度和损失;后 2 个是验证的精度和损失。可用 Matplotlib 画出这些指标。

 画出训练和验证的损失 

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(acc) + 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_values = history_dict['acc']
val_acc_values = 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('Loss')
plt.legend()
plt.show()

你会看到,随着每次迭代训练的损失下降,训练的精度上升。这正是你所期待的梯度下降优化。不过,损失和精度的验证是另外一回事:它们似乎在第 4 次迭代时达到峰值。这个例子正是我们曾经警告过的:训练成绩好的模型,使用未曾见过的数据时,表现未必好。确切地说,这种现象是过度拟合。

在此,为了阻止过度拟合,第 3 次迭代后你应该停止训练。可以用些技术减缓过度拟合,具体见第 4 章。

让我们只迭代 4 次,接着用测试数据进行评估:

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

model.fit(x_train, y_train, epochs=4, batch_size=512)

results = model.evaluate(x_test, y_test)

# 最终结果如下:
>>> results
[0.2929924130630493, 0.88327999999999995]

用稚朴的办法可达到 88% 的精度。 用最先进的技术可接近 95% 的精度。

用训练过的网络对新数据进行预测

你可以用 predict 方法预测影评属于正面的可能性:

>>> model.predict(x_test)
array([[ 0.98006207]
       [ 0.99758697]
       [ 0.99975556]
       ...,
       [ 0.82167041]
       [ 0.02885115]
       [ 0.65371346]], dtype=float32)

如你所见,网络对一些样本的确信度高,如 0.99 或 0.01 等;也有的确信度低,如 0.6, 0.4 等。

进一步的实验

以下的实验会帮你确信,以上网络架构虽有改进空间但已相当合理:

  • 使用 2 个隐藏层。试着用 1 或 3 个隐藏层,观察对验证精度和测试精度的影响。
  • 试着用更多或更少的隐藏单元,32 个、64 个等。
  • 试着用损失函数 mse 代替 binary_crossentropy。
  • 试着用激活函数 tanh 代替 relu。(在神经网络早期流行使用 tanh)
小结

以下是你应该从本范例学到的:

  • 通常需要对初始数据做大量预处理,以形成张量供给神经网络。文字序列可以编成二值向量或其他编码形式。
  • 叠加的 Dense 层和激活函数 relu 可解决大量问题(包括情绪分类),你会经常用到它们。
  • 二值分类问题(有两类输出),你的网络末端应该是具有一个单元的 Dense 层,和一个 S 型激活函数;网络输出应该是概率编码后的标量,值从 0 至 1。
  • 用 S 型函数标量输出的二值分类问题,网络的损失函数应该用 binary_crossentropy。
  • 无论你处理何种问题,用优化器 rmsprop 通常是很好的选择,它让你没有后顾之忧。
  • 神经网络训练时会取得好成绩,但最终会出现过度拟合,处理未见过的新数据时效果越来越差。要切实监测网络处理训练集以外的数据时的表现。





  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值