作为教程的第一篇文章,讲解的内容从简、从易,采用官方推荐的代码风格(即采用keras)搭建一个简单的卷积神经网络,数据集也采用最简单的手写字符MNIST。
首先,MNIST数据集tensorflow自身提供了加载函数
(train_images, train_labels), (test_images, test_labels) = tf.keras.datasets.mnist.load_data()
第一次运行时会自动下载到本地,如网络情况不好或脱机状态则需要事先手动下载并读取。
搭建网络时,需要用到的模型和各个层均采用keras的实现
from tensorflow.keras import models
from tensorflow.keras.layers import Conv2D, MaxPooling2D, Flatten, Dense, Reshape
然后,我们将卷积层、池化层等顺序放入,就能很轻松的完成一个模型
def Model():
model = models.Sequential()
model.add(Reshape((28, 28, 1), input_shape=(28, 28)))
model.add(Conv2D(32, (3, 3), activation='relu'))
model.add(MaxPooling2D((2, 2)))
model.add(Conv2D(64, (3, 3), activation='relu'))
model.add(MaxPooling2D((2, 2)))
model.add(Conv2D(64, (3, 3), activation='relu'))
model.add(Flatten())
model.add(Dense(64, activation='relu'))
model.add(Dense(10, activation='softmax'))
return model
其中,模型仅第一层需要提供input_shape
参数,后续的网络每一层的输入输出大小都会自动推算。这里我的第一层为Reshape
,因为tensorflow的数据组织形式为(B, H, W, C),依次为图像的批次数量、高度、宽度、通道数,而MNIST数据集为黑白图像,读取时没有通道维度,从而第一层采用Reshape
手动添加通道。Flatten
层为将多维数据拉平,从而进行后续的Dense
全连接操作。
模型搭建完毕后,可以在代码中加入model.summary()
,从而在执行时打印各个层的信息,如
Model: "sequential"
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
reshape (Reshape) (None, 28, 28, 1) 0
_________________________________________________________________
conv2d (Conv2D) (None, 26, 26, 32) 320
_________________________________________________________________
max_pooling2d (MaxPooling2D) (None, 13, 13, 32) 0
_________________________________________________________________
conv2d_1 (Conv2D) (None, 11, 11, 64) 18496
_________________________________________________________________
max_pooling2d_1 (MaxPooling2 (None, 5, 5, 64) 0
_________________________________________________________________
conv2d_2 (Conv2D) (None, 3, 3, 64) 36928
_________________________________________________________________
flatten (Flatten) (None, 576) 0
_________________________________________________________________
dense (Dense) (None, 64) 36928
_________________________________________________________________
dense_1 (Dense) (None, 10) 650
=================================================================
Total params: 93,322
Trainable params: 93,322
Non-trainable params: 0
_________________________________________________________________
可以看到,每经过一个卷积层特征图的大小会减2,keras默认卷积的padding形式是valid,如果希望经过卷积层特征图的大小不变(实际上绝大多数情况都是如此),可以设置参数padding='same'
来控制。
然后,设置模型训练时的优化器、损失函数,这里我们采用adam作为优化器,损失函数为交叉熵。
model.compile(optimizer='adam', loss='sparse_categorical_crossentropy', metrics=['accuracy'])
更多的内容如学习率、l2正则会在下篇文章介绍。
最后我们就可以开始训练模型了,这里我们让数据的batchsize为128,网络训练5轮,再在测试机上进行测试。
model.compile(optimizer='adam', loss='sparse_categorical_crossentropy', metrics=['accuracy'])
model.fit(train_images,
train_labels,
batch_size=128,
epochs=5)
test_loss, test_acc = model.evaluate(test_images, test_labels)
print(test_acc)
最终训练集准确率约99%,测试集约98.5%。
完整的代码在
https://github.com/Apm5/tensorflow_2.0_tutorial/blob/master/CNN/simple_example.py
额外说明一点,完整代码中最先执行的gpu设置
physical_devices = tf.config.experimental.list_physical_devices('GPU')
tf.config.experimental.set_memory_growth(device=physical_devices[0], enable=True)
是让显存的占用按需增长,否则无论多小的网络都会将一张显卡的显存全部占满。如果是cpu版本或不想用gpu训练,可以注释掉这两行。