前言:看以前的博客很多都是基于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个测试集数据图片灰度图
数据基本处理
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))