当我将神经网络添加了一个CNN—Layer之后,我发现我的模型无法训练了。我们以经典的Mnist数据来了解一下这个简单的原因。

【深度学习手记】使用全连接神网络训练与卷积训练对数据集的要求有什么不一样
- import os
- import tensorflow as tf
- from tensorflow import keras
- from tensorflow.keras import layers, optimizers, datasets
- os.environ['TF_CPP_MIN_LOG_LEVEL']='2'
-
- (x_train,y_train),(x_test,y_test)=datasets.mnist.load_data()
- model = tf.keras.models.Sequential()
- model.add(tf.keras.layers.Flatten())
- model.add(tf.keras.layers.Dense(128,activation=tf.nn.relu))
- model.add(tf.keras.layers.Dense(10,activation=tf.nn.softmax))
- model.compile(optimizer='adam',loss='sparse_categorical_crossentropy',metrics=['accuracy'])
- model.fit(x_train,y_train,epochs=5) #训练模型

目前一切还是正常的,但是如果我们将模型的网络层小小的添加一个卷积层,一切都不一样了。
- model = tf.keras.models.Sequential([
- tf.keras.layers.Conv2D(filters=64,kernel_size=(7,7),padding='same',input_shape=(28,28,1),activation='relu'),
- tf.keras.layers.MaxPool2D(),
- tf.keras.layers.Flatten(),
- tf.keras.layers.Dense(10,activation='softmax')
- ])
- model.compile(optimizer='adam',loss='sparse_categorical_crossentropy',metrics=['accuracy'])
- model.fit(x_train,y_train,epochs=5) #训练模型
ValueError: Input 0 of layer sequential_2 is incompatible with the layer: expected ndim=4, found ndim=3. Full shape received: [32, 28, 28]ValueError:层sequential_2的输入0与层不兼容:预期的ndim=4,找到的ndim=3。收到的完整形状:[32,28,28]
其实原因非常简单,这不是数据集的错,而是卷积核的因素,我们来看看卷积核的结构究竟是怎么样的?

想必看完这张图,知道卷积核是三维的之后,大家就能够恍然大悟了。
因为CNN层中的卷积的过程在空间表达上正是一个卷积核在图像上不断平移的过程,如下图所示:(一个3x3x1的卷积核提取特征过程)

说白了,当数据集是二维——三维数据集(由于数据集构成时,第一个维度是数据的数量,比如Mnist数据集的训练集维度是(70000,34,34,1),表示7W张(34,34,1)的手写体图片。所以是一个三维数据存储起来变成数据集是4维度数据集,这样子做其实也是为了方便训练。数据存储在后面的维度上,所以数据的实际维度是数据集减去第一个维度后的样子),三维的卷积核没有办法在二维的数据(三维数据集)上进行空间平移,试想一下一个三维的人是不可能踩在一个没有高度(厚度)的平面上的,大概就能够理解了。
值得一提的是:对于投喂给模型(fit操作)的X(数据),Y(标签)中的X,不论是深度学习还是机器学习格式都是这样的:第一个维度都是数量,后面的维度才是数据的维度,也就是X.shape=(数据数量+数据维度)的格式,比如Mnist数据中的X.shape=(70000, 28, 28, 1)正是说有7W个(28,28, 1)的数据。最后有一点差点忘记讲的是:数据集一般都是np.array的格式。
那好,想必我已经解释的非常清楚了,那么我需要怎么样修改数据集的维度,使得我们的数据集可以投入卷积神经网络训练呢?如下:
- x_train4D = x_train.reshape(x_train.shape[0],28,28,1).astype('float32')
- x_test4D = x_test.reshape(x_test.shape[0],28,28,1).astype('float32')
- x_train,x_test = x_train4D/255.0,x_test4D/255.0 # 归一化,为了更好的训练可以不做

binggo! 运行成功啦!可以看到,卷积核的效果还是超过全连接层的,同样的epoch5次,CNN准确率到达了98.4%,回顾以下DNN层:

只有94.1% !