基于tensorflow2的CNN实现mnist数据集分类

前言:看以前的博客很多都是基于tensorflow1.x版本的教程,当我用2.2版本的时候发现各种报错,而且tensorflow2里面的keras直接调库很方便,所以做个笔记记录。本人是深度学习初学者,一些理解不到位的地方希望大家指出。在看本文章前可以先了解一下一些概念。

CNN的介绍
卷积神经网络(CNN)介绍
CNN(卷积神经网络)详解
通俗理解卷积神经网络

激活函数解释https://www.zhihu.com/question/22334626

理解一些参数
深度学习中的batch、epoch、iteration的含义
CNN中feature map、卷积核、卷积核个数、filter、channel

一、 数据处理

导入包,读入数据

import numpy as np
import matplotlib.pyplot as plt
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Conv2D, MaxPooling2D, Dense, Dropout, Flatten

(x_train, y_train), (x_test, y_test) = keras.datasets.mnist.load_data()

数据可视化

def image(images, row, col):
    im = images[:col * row]
    hs = np.hstack(im)
    sp = np.split(hs, row, axis=1)
    show_image = np.vstack(sp)
    plt.imshow(show_image, cmap='gray')
    plt.axis("off")
    plt.show()

def draw_bar(y_numbers):
    digits = range(len(y_numbers))
    digits = [str(i) for i in digits]
    plt.bar(digits, y_numbers)
    plt.xlabel('digits', color='b')
    plt.ylabel('numbers of digits', color='b')
    for x, y in zip(range(len(y_numbers)), y_numbers):
        plt.text(x + 0.05, y + 0.05, '%.2d' % y, ha='center', va='bottom')
    plt.show()


"""  画柱形图  """
temp1 = np.unique(y_train)
temp2 = np.unique(y_test)
y_train_numbers = []
y_test_numbers = []
for v in temp1:
    y_train_numbers.append(np.sum(y_train == v))
for v in temp2:
    y_test_numbers.append(np.sum(y_test == v))
plt.title("The number of each digit in the train data")
draw_bar(y_train_numbers)
plt.title("The number of each digit in the test data")
draw_bar(y_test_numbers)

print('x_train:', x_train.shape, '\ny_train:', y_train.shape, '\nx_test:', x_test.shape, '\ny_test:', y_test)

"""  显示出训练集数字图像  """
image(x_train, 10, 10)

数据可视化结果:

  • 训练集数字分布
    训练集数字分布

  • 测试集数字分布
    测试集数字分布

  • 前100个测试集数据图片灰度图
    前100个测试集数据图片灰度图

数据基本处理

def normalize(x):
    x = x.astype(np.float32) / 255.0
    return x

"""  把数据reshape成四维,用作模型的输入并归一化数据  """
x_train = x_train.reshape(x_train.shape[0], 28, 28, 1).astype('float32')
x_test = x_test.reshape(x_test.shape[0], 28, 28, 1).astype('float32')
x_train = normalize(x_train)
x_test = normalize(x_test)

二、 模型训练

模型的情况

# 创建模型,输入28*28*1个神经元,输出10个神经元
model = Sequential()
"""
第一个卷积层
input_shape输入平面
kernel——size 卷积窗口大小
padding padding方法 same or valid
activation 激活函数
"""
model.add(Conv2D(filters=8, kernel_size=(5, 5), padding='same',
                 input_shape=(28, 28, 1), activation='relu'))
# 第一个池化层
model.add(MaxPooling2D(pool_size=(2, 2)))  # 出来的特征图为14*14大小
# 第二个卷积层 16个滤波器(卷积核),卷积窗口大小为5*5
# 经过第二个卷积层后,有16个feature map,每个feature map为14*14
model.add(Conv2D(filters=16, kernel_size=(5,5), padding='same',
                 activation='relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))  # 出来的特征图为7*7大小

# 平坦层,把第二个池化层的输出扁平化为1维 16*7*7
model.add(Flatten())
# 第一个全连接层
model.add(Dense(100, activation='relu'))
# Dropout,用来放弃一些权值,防止过拟合
model.add(Dropout(0.25))
# 第二个全连接层,由于是输出层,所以使用softmax做激活函数
model.add(Dense(10, activation='softmax'))
# 打印模型
print(model.summary())

可以看到各层的输出和参数。
在这里插入图片描述

模型训练过程

开始训练

# 训练配置
model.compile(loss='sparse_categorical_crossentropy',
              optimizer='adam', metrics=['accuracy'])
# 开始训练
history = model.fit(x=x_train, y=y_train, validation_split=0.2,
                    epochs=10, batch_size=200, verbose=2)

在这里插入图片描述

TensorFlow中loss与val_loss、accuracy和val_accuracy含义
(这个好多文章里面都有,我找不到原出处只能盗一下了)

  • loss:训练集损失值
  • accuracy:训练集准确率
  • val_loss:测试集损失值
  • val_accruacy:测试集准确率

以下5种情况可供参考:

  • train loss 不断下降,test loss不断下降,说明网络仍在学习;(最好的)
  • train loss 不断下降,test loss趋于不变,说明网络过拟合;(max pool或者正则化)
  • train loss 趋于不变,test loss不断下降,说明数据集100%有问题;(检查dataset)
  • train loss 趋于不变,test loss趋于不变,说明学习遇到瓶颈,需要减小学习率或批量数目;(减少学习率)
  • train loss 不断上升,test oss不断上升,说明网络结构设计不当,训练超参数设置不当,数据集经过清洗等问题。(最不好的情况)

这里的 test loss 是我上面分类的训练集中的一部分用来测试的数据,不是x_test , y_test。

测试集预测结果

画 accuracy 和 loss 图

plt.plot(history.history['accuracy'], label='Accuracy')
plt.plot(history.history['val_accuracy'], label='Validation accuracy')
plt.xlabel('Epoch')
plt.ylabel('Accuracy')
plt.ylim([0.75, 1])
plt.legend(loc='lower right')
plt.show()

plt.plot(history.history['loss'], label='Loss')
plt.plot(history.history['val_loss'], label='Validation loss')
plt.xlabel('Epoch')
plt.ylabel('loss')
plt.ylim([0, 0.6])
plt.legend(loc='upper right')
plt.show()

test_loss, test_acc = model.evaluate(x_test, y_test, verbose=2)
print("test loss - ", round(test_loss, 3), " - test accuracy - ", round(test_acc, 3))

在这里插入图片描述
在这里插入图片描述

这个才是 x_test , y_test 的预测。
在这里插入图片描述
准确率达到了0.99,还是不错的,当然模型的评价不只是看准确率。

三、 完整代码

import numpy as np
import matplotlib.pyplot as plt
from tensorflow import keras
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Conv2D, MaxPooling2D, Dense, Dropout, Flatten

def image(images, row, col):
    im = images[:col * row]
    hs = np.hstack(im)
    sp = np.split(hs, row, axis=1)
    show_image = np.vstack(sp)
    plt.imshow(show_image, cmap='gray')
    plt.axis("off")
    plt.show()


def normalize(x):
    x = x.astype(np.float32) / 255.0
    return x


(x_train, y_train), (x_test, y_test) = keras.datasets.mnist.load_data()


def draw_bar(y_numbers):
    digits = range(len(y_numbers))
    digits = [str(i) for i in digits]
    plt.bar(digits, y_numbers)
    plt.xlabel('digits', color='b')
    plt.ylabel('numbers of digits', color='b')
    for x, y in zip(range(len(y_numbers)), y_numbers):
        plt.text(x + 0.05, y + 0.05, '%.2d' % y, ha='center', va='bottom')
    plt.show()


"""  画柱形图  """
temp1 = np.unique(y_train)
temp2 = np.unique(y_test)
y_train_numbers = []
y_test_numbers = []
for v in temp1:
    y_train_numbers.append(np.sum(y_train == v))
for v in temp2:
    y_test_numbers.append(np.sum(y_test == v))
plt.title("The number of each digit in the train data")
draw_bar(y_train_numbers)
plt.title("The number of each digit in the test data")
draw_bar(y_test_numbers)

# x_train = x_train[:30000, ]
# y_train = y_train[:30000]
print('x_train:', x_train.shape, '\ny_train:', y_train.shape, '\nx_test:', x_test.shape, '\ny_test:', y_test)

"""  显示出训练集数字图像  """
image(x_train, 10, 10)
plt.imshow(x_train[0], cmap='binary')
plt.axis('off')
plt.show()

x_train = x_train.reshape(x_train.shape[0], 28, 28, 1).astype('float32')
x_test = x_test.reshape(x_test.shape[0], 28, 28, 1).astype('float32')
x_train = normalize(x_train)
x_test = normalize(x_test)


# 创建模型,输入28*28*1个神经元,输出10个神经元
model = Sequential()
# 第一个卷积层
# input_shape输入平面
# kernel——size 卷积窗口大小
# padding padding方法 same or valid
# activation 激活函数
model.add(Conv2D(filters=8, kernel_size=(5, 5), padding='same',
                 input_shape=(28, 28, 1), activation='relu'))
# 第一个池化层
model.add(MaxPooling2D(pool_size=(2, 2)))  # 出来的特征图为14*14大小
# 第二个卷积层 16个滤波器(卷积核),卷积窗口大小为5*5
# 经过第二个卷积层后,有16个feature map,每个feature map为14*14
model.add(Conv2D(filters=16, kernel_size=(5,5), padding='same',
                 activation='relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))  # 出来的特征图为7*7大小

# 平坦层,把第二个池化层的输出扁平化为1维 16*7*7
model.add(Flatten())
# 第一个全连接层
model.add(Dense(100, activation='relu'))
# Dropout,用来放弃一些权值,防止过拟
model.add(Dropout(0.25))
# 第二个全连接层,由于是输出层,所以使用softmax做激活函数
model.add(Dense(10, activation='softmax'))
# 打印模型
print(model.summary())

# 训练配置
model.compile(loss='sparse_categorical_crossentropy',
              optimizer='adam', metrics=['accuracy'])
# 开始训练
history = model.fit(x=x_train, y=y_train, validation_split=0.2,
                    epochs=10, batch_size=200, verbose=2)


plt.plot(history.history['accuracy'], label='Accuracy')
plt.plot(history.history['val_accuracy'], label='Validation accuracy')
plt.xlabel('Epoch')
plt.ylabel('Accuracy')
plt.ylim([0.75, 1])
plt.legend(loc='lower right')
plt.show()

plt.plot(history.history['loss'], label='Loss')
plt.plot(history.history['val_loss'], label='Validation loss')
plt.xlabel('Epoch')
plt.ylabel('loss')
plt.ylim([0, 0.6])
plt.legend(loc='upper right')
plt.show()

test_loss, test_acc = model.evaluate(x_test, y_test, verbose=2)
print("test loss - ", round(test_loss, 3), " - test accuracy - ", round(test_acc, 3))

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值