一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第9天,点击查看活动详情。
缩放输入数据集介绍
缩放数据集是一个在网络训练之前提前对数据进行处理的过程,在此过程中,限制数据集中的数据范围,以确保它们不会分布在较大的区间。实现此目的的一种方法是将数据集中的每个数据除以数据集中的最大数据。 通常,缩放输入数据集能够提高神经网络的性能表现,是常用的数据预处理方法之一。
数据集缩放的合理性解释
在本节中,我们将了解缩放数据集能使神经网络性能更好的原因。为了了解缩放输入对输出的影响,我们对比在未缩放输入数据集时模型性能和在缩放输入数据集时的性能情况。 当输入数据未缩放时,在不同权重值的作用下 sigmoid
函数值如下表所示:
输入 | 权重 | 偏置 | sigmoid 值 |
---|---|---|---|
255 | 0.01 | 0 | 0.93 |
255 | 0.1 | 0 | 1.00 |
255 | 0.2 | 0 | 1.00 |
255 | 0.4 | 0 | 1.00 |
255 | 0.8 | 0 | 1.00 |
255 | 1.6 | 0 | 1.00 |
255 | 3.2 | 0 | 1.00 |
255 | 6.4 | 0 | 1.00 |
在上表中,即使权重值在 0.01 到 6.4 之间变化,在经过函数 Sigmoid
后输出变化也不大,为了解释这一现象,我们首先回忆下 Sigmoid
函数的计算方法:
output = 1/(1+np.exp(-(w*x + b))
复制代码
其中 w
是权重,x
是输入,b
是偏置值。
sigmoid
输出不变的原因是由于 w * x
的乘积很大(主要是因为 x
较大),导致 sigmoid
值始终落在 sigmoid
曲线的饱和部分中( sigmoid
曲线的右上角或左下角的值称为饱和部分)。而如果我们将不同的权重值乘以一个较小的输入数字,则结果如下所示:
输入 | 权重 | 偏置 | sigmoid 值 |
---|---|---|---|
1 | 0.01 | 0 | 0.50 |
1 | 0.1 | 0 | 0.52 |
1 | 0.2 | 0 | 0.55 |
1 | 0.4 | 0 | 0.60 |
1 | 0.8 | 0 | 0.69 |
1 | 1.6 | 0 | 0.83 |
1 | 3.2 | 0 | 0.96 |
1 | 6.4 | 0 | 1.00 |
由于输入值较小,因此上表中的 Sigmoid
输出值会随权重的变化发生改变。
通过此示例,我们了解了缩放输入对数据集的影响,当权重(假设权重不具有较大范围)乘以较小输入值时,使输入数据能够对输出产生足够重要的影响。同样当权重值也很大时,输入值对输出的影响将变得不太重要。因此,我们一般将权重值初始化为更接近零的较小数值。同时,为了获得最佳的权重值,通常设置初始化权重的范围变化不大,比如权重在初始化期间采样介于 -1 和 +1 之间的随机值。
接下来,我们对使用的数据集MNIST进行缩放,并比较使用和不使用数据缩放对性能的影响。
使用缩放后的数据集训练模型
- 导入相关的包和
MNIST
数据集:
from keras.datasets import mnist
import numpy as np
from keras.models import Sequential
from keras.layers import Dense, Dropout
from keras.utils import np_utils
import matplotlib.pyplot as plt
(x_train, y_train), (x_test, y_test) = mnist.load_data()
复制代码
- 缩放数据集有多种方法。一种方法是将所有数据点转换为 0 到 1 之间的值(通过将每个数据点除以数据集中的最大值,在本例中最大值为 255),展平输入数据集并对其进行缩放,如下所示:
num_pixels = x_train.shape[1] * x_train.shape[2]
x_train = x_train.reshape(-1, num_pixels).astype('float32')
x_test = x_test.reshape(-1, num_pixels).astype('float32')
x_train = x_train / 255.
x_test = x_test / 255.
复制代码
另一种流行的数据缩放方法是对数据集进行归一化,以使值转换到 -1 和 +1 之间,方法是用数据平均值减去数据点,然后将得到的结果除以原始数据集的标准差:
- 将训练和测试输入的值缩放至
[0, 1]
后,我们将数据集的标签转换为独热编码格式:
y_train = np_utils.to_categorical(y_train)
y_test = np_utils.to_categorical(y_test)
num_classes = y_test.shape[1]
复制代码
- 构建模型并进行编译:
model = Sequential()
model.add(Dense(1000, input_dim=num_pixels, activation='relu'))
model.add(Dense(num_classes, activation='softmax'))
model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['acc'])
复制代码
上述模型与我们在《使用Keras构建神经网络》中构建的模型完全相同,唯一的区别是本节模型将在缩放后的数据集上进行训练。
- 拟合模型,如下所示:
history = model.fit(x_train, y_train,
validation_data=(x_test, y_test),
epochs=50,
batch_size=64,
verbose=1)
复制代码
该模型的准确率约为 98.41%
,而未缩放数据时训练的模型准确率在 97%
左右。绘制不同 epoch
的训练和测试的准确性以及损失值(绘制曲线图的代码与训练原始神经网络方法中使用的代码完全相同):
从上图中可以看出,与非缩放数据集训练的模型相比,训练和测试损失的变化较为平缓。尽管网络能够平稳的降低损失值,但我们看到训练和测试准确率之间存在较大差距,这表明在训练数据集上可能存在过拟合的情况。过拟合是由于模型针对训练数据过度拟合,这导致在测试数据集的性能不如训练数据集,泛化性能较差。
除了通过将值除以最大值来缩放数据集外,其他常用的缩放方法如下:
- 最小-最大归一化
- 均值归一化
- 标准方差归一化