MINIST手写数字识别——05.卷积神经网络(CNN)
加载 MNIST 数据集
import tensorflow as tf
(x_train, y_train), (x_test, y_test) = tf.keras.datasets.mnist.load_data()
print(x_train.shape, type(x_train))
print(y_train.shape, type(y_train))
数据处理:规范化
channels_last
corresponds to inputs with shape (batch, height, width, channels) while channels_first
corresponds to inputs with shape (batch, channels, height, width).
It defaults to the image_data_format value found in your Keras config file at ~/.keras/keras.json. If you never set it, then it will be channels_last
.
img_rows, img_cols = 28, 28
if tf.keras.backend.image_data_format() == 'channels_first':
x_train = x_train.reshape(x_train.shape[0], 1, img_rows, img_cols)
x_test = x_test.reshape(x_test.shape[0], 1, img_rows, img_cols)
input_shape = (1, img_rows, img_cols)
else:
x_train = x_train.reshape(x_train.shape[0], img_rows, img_cols, 1)
x_test = x_test.reshape(x_test.shape[0], img_rows, img_cols, 1)
input_shape = (img_rows, img_cols, 1)
print(x_train.shape, type(x_train))
print(x_test.shape, type(x_test))
# 将数据类型转换为float32
X_train = x_train.astype('float32')
X_test = x_test.astype('float32')
# 数据归一化
X_train /= 255
X_test /= 255
print(X_train.shape[0], 'train samples')
print(X_test.shape[0], 'test samples')
统计训练数据中各标签数量
import numpy as np
import matplotlib.pyplot as plt
label, count = np.unique(y_train, return_counts=True)
print(label, count)
fig = plt.figure()
plt.bar(label, count, width = 0.7, align='center')
plt.title("Label Distribution")
plt.xlabel("Label")
plt.ylabel("Count")
plt.xticks(label)
plt.ylim(0,7500)
for a,b in zip(label, count):
plt.text(a, b, '%d' % b, ha='center', va='bottom',fontsize=10)
plt.show()
数据处理:one-hot 编码
n_classes = 10
print("Shape before one-hot encoding: ", y_train.shape)
Y_train = tf.keras.utils.to_categorical(y_train, n_classes)
print("Shape after one-hot encoding: ", Y_train.shape)
Y_test = tf.keras.utils.to_categorical(y_test, n_classes)
print(y_train[0])
print(Y_train[0])
使用 Keras sequential model 定义 MNIST CNN 网络
卷积神经网络LeNet-5: 输入的二维图像,首先经过两次卷积层到池化层,再经过全连接层,最后使用以softmax为激活函数的全连接层作为输出层。
Sequential = tf.keras.models.Sequential
Dense = tf.keras.layers.Dense
Dropout = tf.keras.layers.Dropout
Flatten = tf.keras.layers.Flatten
Activation = tf.keras.layers.Activation
Conv2D = tf.keras.layers.Conv2D
MaxPooling2D = tf.keras.layers.MaxPooling2D
model = Sequential()
## Feature Extraction
# 第1层卷积,32个3x3的卷积核 ,激活函数使用 relu
model.add(Conv2D(filters=32, kernel_size=(3, 3), activation='relu',
input_shape=input_shape))
# 第2层卷积,64个3x3的卷积核,激活函数使用 relu
model.add(Conv2D(filters=64, kernel_size=(3, 3), activation='relu'))
# 最大池化层,池化窗口 2x2
model.add(MaxPooling2D(pool_size=(2, 2)))
# Dropout 25% 的输入神经元
model.add(Dropout(0.25))
# 将 Pooled feature map 摊平后输入全连接网络
model.add(Flatten())
## Classification
# 全联接层
model.add(Dense(128, activation='relu'))
# Dropout 50% 的输入神经元
model.add(Dropout(0.5))
# 使用 softmax 激活函数做多分类,输出各数字的概率
model.add(Dense(n_classes, activation='softmax'))
查看 MNIST CNN 模型网络结构
model.summary()
for layer in model.layers:
print(layer.get_output_at(0).get_shape().as_list())
编译模型
compile(optimizer, loss=None, metrics=None, loss_weights=None, sample_weight_mode=None, weighted_metrics=None, target_tensors=None)
model.compile(loss='categorical_crossentropy', metrics=['accuracy'], optimizer='adam')
训练模型,并将指标保存到 history 中
fit(x=None, y=None, batch_size=None, epochs=1, verbose=1, callbacks=None, validation_split=0.0, validation_data=None, shuffle=True, class_weight=None, sample_weight=None, initial_epoch=0, steps_per_epoch=None, validation_steps=None)
history = model.fit(X_train,
Y_train,
batch_size=128,
epochs=5,
verbose=2,
validation_data=(X_test, Y_test))
loss:训练集损失值
accuracy:训练集准确率
val_loss:测试集损失值
val_accruacy:测试集准确率
可视化指标
fig = plt.figure()
plt.subplot(2, 1, 1)
plt.plot(history.history['accuracy'])
plt.plot(history.history['val_accuracy'])
plt.title('Model Accuracy')
plt.ylabel('accuracy')
plt.xlabel('epoch')
plt.legend(['train', 'test'], loc='lower right')
plt.subplot(2, 1, 2)
plt.plot(history.history['loss'])
plt.plot(history.history['val_loss'])
plt.title('Model Loss')
plt.ylabel('loss')
plt.xlabel('epoch')
plt.legend(['train', 'test'], loc='upper right')
plt.tight_layout()
plt.show()
保存模型
You can use model.save(filepath)
to save a Keras model into a single HDF5 file which will contain:
- the architecture of the model, allowing to re-create the model
- the weights of the model
- the training configuration (loss, optimizer)
- the state of the optimizer, allowing to resume training exactly where you left off.
You can then use keras.models.load_model(filepath)
to reinstantiate your model. load_model will also take care of compiling the model using the saved training configuration (unless the model was never compiled in the first place).
import os
gfile = tf.io.gfile
save_dir = "./mnist/cnn-model/"
if gfile.exists(save_dir):
gfile.rmtree(save_dir)
gfile.mkdir(save_dir)
model_name = 'keras_mnist.h5'
model_path = os.path.join(save_dir, model_name)
model.save(model_path)
print('Saved trained model at %s ' % model_path)
加载模型
mnist_model = tf.keras.models.load_model(model_path)
统计模型在测试集上的分类结果
loss_and_metrics = mnist_model.evaluate(X_test, Y_test, verbose=2)
print("Test Loss: {}".format(loss_and_metrics[0]))
print("Test Accuracy: {}%".format(loss_and_metrics[1]*100))
predicted_classes = mnist_model.predict_classes(X_test)
correct_indices = np.nonzero(predicted_classes == y_test)[0]
incorrect_indices = np.nonzero(predicted_classes != y_test)[0]
print("Classified correctly count: {}".format(len(correct_indices)))
print("Classified incorrectly count: {}".format(len(incorrect_indices)))