今天我们来讲一讲神经网络的数据预处理。
1. 数据预处理
数据预处理的方法通常有三种,假设数据矩阵X是一个N*D维的矩阵,N表示样本数目,D表示数据的维度。
- 0均值 是最常用的预处理方法,就是把数据的每一维-每一维的均值,这样数据就变成0均值的了。在numpy中,这个操作可以写成:X -= np.mean(X, axis = 0)。对于图片来讲,我们可以更简单地对所有pixel减去同一个均值(如 X -= np.mean(X)),当然也可以对RGB三个通道分别减均值。
- 归一化(Normalization) 是指将数据归一化到相同的尺度。通常有两种归一化的方法。第一种是0均值以后的数据的每一维/每一维的标准差(X /= np.std(X, axis = 0));另一种是将数据归一化到每一维的最大最小值为1和-1。这种归一化只适用于当你认为数据的不同维度应该具有相同的重要性时。对于图片来说,不同像素的尺度是基本一致的(0-255),因此我们并不需要对它进行归一化操作。
- PCA和白化 是另一种形式的预处理方法。首先我们将数据变成0均值的,然后计算数据的协方差矩阵来得到数据不同维度之间的相关性:
# Assume input data matrix X of size [N x D]
X -= np.mean(X, axis = 0) # zero-center the data (important)
cov = np.dot(X.T, X) / X.shape[0] # get the data covariance matrix
协方差矩阵的第(i,j)个元素表示数据第i维和第j维特征的相关性,特别地,对角线上的元素表示方差。另外,协方差矩阵是对称并且半正定的。我们可以对上述协方差矩阵进行SVD分解:
U,S,V = np.linalg.svd(cov)
其中U矩阵的列为特征向量,S对角线上的元素是奇异值(等同于特征值的平方)。为了对数据去相关,我们首先将数据(0均值后的)投影到特征向量上:
Xrot = np.dot(X, U) # decorrelate the data
注意U的列是相互正交的向量,也因此它们可以被看成基向量。这种投影相当于将数据X旋转、投影到新的基向量轴上。如果我们再去计算Xrot的协方差矩阵的话,我们会发现它是一个对角阵,说明不同维度之间不再相关。np.linalg.svd的一个很好的特性在于返回的U是按照其特征值的大小排序的,排在前面的就是主方向,因此我们可以通过选取前几个特征向量来减少数据的维度,这就是PCA的降维方法:
Xrot_reduced = np.dot(X, U[:,:100]) # Xrot_reduced becomes [N x 100]
经过上述操作后,我们就能将原始数据从N*D维转变成N*100维,这100维保留了数据最大的方差。通常你可以在经过PCA降维的数据上训练线性分类器或神经网络并且得到很好的效果,同时又节省了时间和空间。
最后一种常用的变形是白化。白化的意思就是(PCA)去相关后的数据方差从对角阵再变成单位阵。
# whiten the data:
# divide by the eigenvalues (which are square roots of the singular values)
Xwhite = Xrot / np.sqrt(S + 1e-5)
注意我们在分母中加了一个很小的常数(1e-5)防止除以0。白化的一个缺点在于它会夸大数据的噪声,因为它将所有的维度拉伸到相同的大小。这可以通过更强的平滑来减轻(如加大1e-5等)。
我们可以对CIFAR-10的图片进行上述预处理来可视化这些预处理。CIFAR-10的训练集是50,000*3072,其中每张图片被拉伸成3072维的行向量。我们可以计算出一个[3072*3072]的协方差矩阵并且进行SVD分解(计算复杂度很高),最后计算得到的特征向量都长什么样子呢?如下图:
In practice. 我们在这里提到PCA和白化是为了completeness,但是这些变换在CNN中并不会用到。但是,对于数据进行0均值处理是非常重要的,归一化也是常见的。
常见陷阱. 在进行数据的预处理时(比如计算数据均值),我们只能在训练数据上进行,然后应用到验证/测试数据上。如果我们对整个数据集-整个数据集的均值,然后再进行训练/验证/测试数据的分割的话,这样是不对的。正确做法是计算训练数据的均值,然后分别把它从训练/验证/测试数据中减去。