各位同学好,今天和大家分享一下TensorFlow2.0深度学习中卷积神经网络的案例。现在有猫、狗、熊猫图片一千张,构建卷积神经网络实现图像的分类预测。
将训练测试数据划分好后放在同一个文件目录下,使用tf.keras.preprocessing.image_dataset_from_directory()函数构造数据集。函数的具体用法见:tf.keras.preprocessing.image_dataset_from_directory_自在独行的博客-CSDN博客_image_dataset_from_directory
对训练数据和验证数据进行one-hot编码,便于计算损失,读入图像时统一图片大小size为128*128。batch为64,每次迭代从中取64个样本。class_names中保存的是根据文件夹名称生成的标签。
# 三分类,卷积神经网络
import numpy as np
import tensorflow as tf
from tensorflow import keras
from tensorflow.keras import layers,optimizers,datasets,Sequential
import os # 设置一下输出框打印的内容
os.environ['TF_CPP_MIN_LOG_LEVEL'] = '2' # '2'输出栏只打印error信息,其他乱七八糟的信息不打印
#(1)数据获取
# 加载训练集数据
filepath1 = 'C:/Users/admin/.../train'
train_ds = tf.keras.preprocessing.image_dataset_from_directory(
filepath1,
label_mode='categorical', # "int", "categorical"表示onehot, "binary", or None
seed=123,
image_size=(128, 128), # resize图片大小
batch_size=64)
# 加载验证集数据
filepath2 = 'C:/Users/admin/.../new_data/val'
val_ds = tf.keras.preprocessing.image_dataset_from_directory(
filepath2,
label_mode='categorical',
seed=123,
image_size=(128, 128),
batch_size=64)
# 加载测试集数据
filepath3 = 'C:/Users/.../new_data/test'
test_ds = tf.keras.preprocessing.image_dataset_from_directory(
filepath3,
label_mode='int',
seed=123,
image_size=(128, 128),
batch_size=64)
# 类别名称
class_names = train_ds.class_names
print('类别有:',class_names)
# 类别有: ['cats', 'dogs', 'panda']
使用.map()对dataset中的数据进行processing函数中的操作,对每个点的像素值从[0,255]变成[-1,1],.shuffle()对数据集重新洗牌打散,但不改变x和y之间的对应关系。
train_ds.take(1) 是指从训练集数据集中取出1个batch的数据,返回值img存放图像数据,label存放图像的标签。
#(2)数据预处理
def processing(image, label):
image = 2 * tf.cast(image, tf.float32) / 255.0 - 1 #[-1,1]之间
label = tf.cast(label, tf.int32) # 修改数据类型
return (image, label)
train_ds = train_ds.map(processing).shuffle(10000) #洗牌
val_ds = val_ds.map(processing).shuffle(10000)
test_ds = test_ds.map(processing).shuffle(10000)
#(2)数据检查
for img, label in train_ds.take(1): # 取出一个batch的数据,一个batch有64个样本
print('img.shape:', img.shape) # img.shape: (64, 128, 128, 3)
print('label.shape:', label.shape) # label.shape: (64, 3)
# 数据集展示
import matplotlib.pyplot as plt
for img,label in train_ds.take(1): #取一个batch
for i in range(15):
plt.subplot(3,5,i+1)
plt.imshow(img[i]) # 每张图像的shape为(4, 256, 256, 3)
plt.xticks([]) # 不显示xy轴坐标刻度
plt.yticks([])
plt.show()
预处理后的图像展示结果如下:
这里构造一个6层的神经网络,使用Sequential()容器堆叠网络各层,layers.Conv2D()构造卷积层,卷积核size为3*3。layers.MaxPool2D()构造池化层,采用最大池化方法。指定padding='same'填充图像,在传播过程中保证生成的特征图的size不变,只改变其channel。指定layers.Dropout(0.2)每次迭代该层每个神经元都有20%的概率被杀死,防止网络出现过拟合现象。
在卷积池化层和全连接层之间需要指定一个Flatten层layers.Flatten(),输入至全连接层的图像需要是一个二维tensor,假设卷积池化层输出的shape为[b,1,1,64],那么传入全连接层的shape需要w是[b,64]
#(3)网络构建
# ==1== 卷积和池化层,2次卷积1次池化
network = Sequential()
# unit1
network.add(layers.Conv2D(32, kernel_size=[3,3], strides=1, padding='same', activation=tf.nn.relu))
network.add(layers.Conv2D(32, kernel_size=[3,3], strides=1, padding='same', activation=tf.nn.relu))
network.add(layers.MaxPool2D(pool_size=[2,2], strides=2, padding='same'))
# unit2
network.add(layers.Conv2D(64, kernel_size=[3,3], strides=1, padding='same', activation=tf.nn.relu))
network.add(layers.Conv2D(64, kernel_size=[3,3], strides=1, padding='same', activation=tf.nn.relu))
network.add(layers.MaxPool2D(pool_size=[2,2], strides=2, padding='same'))
# dropout层
network.add(layers.Dropout(0.2)) #每个神经元都有0.2的概率被杀死
# ==2== Flatten层,连接卷积池化层和全连接层
network.add(layers.Flatten())
# ==3== 全连接层
network.add(layers.Dense(128, activation=tf.nn.relu))
network.add(layers.Dense(3)) # 输出层logits层
# ==4== 指定输入层
network.build(input_shape=[None, 128, 128, 3])
# ==5== 查看网络结构
network.summary()
网络结构如下,param代表该层网络拥有的参数个数
Model: "sequential"
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
conv2d_1 (Conv2D) (None, 128, 128, 32) 896
conv2d_2 (Conv2D) (None, 128, 128, 32) 9248
max_pooling2d_1 (MaxPoolin (None, 64, 64, 32) 0
g2D)
conv2d_3 (Conv2D) (None, 64, 64, 64) 18496
conv2d_4 (Conv2D) (None, 64, 64, 64) 36928
max_pooling2d_2 (MaxPoolin (None, 32, 32, 64) 0
g2D)
dropout_1 (Dropout) (None, 32, 32, 64) 0
flatten_1 (Flatten) (None, 65536) 0
dense_1 (Dense) (None, 128) 8388736
dense_2 (Dense) (None, 3) 387
=================================================================
Total params: 8,454,691
Trainable params: 8,454,691
Non-trainable params: 0
_________________________________________________________________
采用学习率指数衰减的方法方法,tf.keras.optimizers.schedules.ExponentialDecay(),起初梯度变化大一点能更快接近目标,后续梯度变化不断减小,越来越逼近最优点。设置早停策略,因为神经网络在不断迭代的过程中,准确率不会一直在上升,如果找到某一极值点,且后续多次迭代过程中,网络效果没有变的更优的迹象,就使用之前的极值点的结果作为最优解。
#(4)网络配置
# 设置动态学习率指数衰减
exponential_decay = tf.keras.optimizers.schedules.ExponentialDecay(
initial_learning_rate=0.001, #初始学习率
decay_steps=2, # 衰减步长
decay_rate=0.96) # 衰减率
# 编译
network.compile(optimizer=optimizers.Adam(learning_rate=exponential_decay),
loss=tf.losses.CategoricalCrossentropy(from_logits=True), # 交叉熵损失
metrics=['accuracy']) # 准确率指标
# 早停策略
early_stopping = keras.callbacks.EarlyStopping(
monitor = 'val_acc', # 验证集的准确率作为指标
patience = 10, # 最多忍受多少个次循环没有改进
restore_best_weights = True) # 发生早停时,自动寻找最优的monitor参数
#(5)网络训练
# 指定训练集、验证集、迭代次数
# 训练目标和验证目标需要时one_hot编码后的
model = network.fit(train_ds, # 训练集
validation_data=val_ds, # 验证集
epochs=10, # 迭代多少次
callbacks= early_stopping, # 回调函数,在训练过程中的适当时机被调用
shuffle = True, # 每轮迭代之前洗牌
verbose = 1 # 0为不在标准输出流输出日志信息,1:显示进度条,2:每个epoch输出一行记录
)
由于时间关系,这里就简单循环10次,效果如下
Epoch 1/10
33/33 [==============================] - ETA: 0s - loss: 1.1605 - accuracy: 0.4527 WARNING:tensorflow:Early stopping conditioned on metric `val_acc` which is not available. Available metrics are: loss,accuracy,val_loss,val_accuracy
33/33 [==============================] - 75s 2s/step - loss: 1.1605 - accuracy: 0.4527 - val_loss: 0.8374 - val_accuracy: 0.5796
#.................#
#.................#
Epoch 10/10
33/33 [==============================] - ETA: 0s - loss: 0.6362 - accuracy: 0.6985 WARNING:tensorflow:Early stopping conditioned on metric `val_acc` which is not available. Available metrics are: loss,accuracy,val_loss,val_accuracy
33/33 [==============================] - 96s 3s/step - loss: 0.6362 - accuracy: 0.6985 - val_loss: 0.6821 - val_accuracy: 0.6415
比较网络训练集和验证集上的准确率和损失,绘图比较
#(6)模型评估
# ==1== 计算准确率
train_acc = model.history['accuracy']
val_acc = model.history['val_accuracy']
# ==2== 损失
train_loss = model.history['loss']
val_loss = model.history['val_loss']
# ==3== 曲线图
epochs_range = range(len(train_acc)) # 横坐标,网络循环了几次
# 准确率曲线
plt.figure(figsize=(10,5))
plt.subplot(1,2,1)
plt.plot(epochs_range, train_acc, label='Training_acc')
plt.plot(epochs_range, val_acc, label='validation_acc')
plt.legend()
plt.title('Accuracy')
# 损失曲线
plt.subplot(1,2,2)
plt.plot(epochs_range, train_loss, label='Training_loss')
plt.plot(epochs_range, val_loss, label='validation_loss')
plt.legend()
plt.title('Loss')
如图所示训练集和验证集的损失都逐渐下降,随着迭代次数的增加,效果会更好,但要防止过拟合的现象出现。
采用测试集中的图像数据对网络进行预测,network.predict()得到输入图像分别属于三个分类的数值,返回numpy类型,使用np.argmax()找到最大值的索引,该索引对应的class_names标签就是预测得到的该图像所属的分类。
#(7)预测
test_pred = []
test_target = []
for images, targets in test_ds: #取一个batch的测试集生成混淆矩阵
for image, label in zip(images, targets): # 每次从batch中取出一组
# 需要给图片增加一个维度
img_array = tf.expand_dims(image, axis=0)
# 使用模型预测图片中的动物
prediction = network.predict(img_array)
# 预测结果是预测值最大值索引对应的位置
test_pred.append(class_names[np.argmax(prediction)])
# 保存真实值的标签
test_target.append(class_names[label]) # label没有做onehot编码
print('测试结果:',test_pred[:10])
print('真实结果:',test_target[:10])
#(8)混淆矩阵
from sklearn.metrics import confusion_matrix
import seaborn as sns
import pandas as pd
plt.rcParams['font.sans-serif'] = ['SimSun'] #宋体
plt.rcParams['font.size'] = 15 #设置字体大小
# 生成混淆矩阵
conf_numpy = confusion_matrix(test_target, test_pred)
# 将矩阵转化为 DataFrame
conf_df = pd.DataFrame(conf_numpy, index=class_names ,columns=class_names)
plt.figure(figsize=(8,7))
sns.heatmap(conf_df, annot=True, fmt="d", cmap="BuPu")
plt.title('混淆矩阵')
plt.ylabel('真实值')
plt.xlabel('预测值')
输出的前10张图片的预测结果如下:
测试结果: ['panda', 'dogs', 'cats', 'cats', 'panda', 'panda', 'panda', 'panda', 'dogs', 'panda']
真实结果: ['panda', 'dogs', 'dogs', 'cats', 'panda', 'panda', 'panda', 'panda', 'cats', 'panda']
为了更加清晰的展示预测值和真实值的关系,构建混淆矩阵,如图。可见一个6层的卷积网络经过10次循环后,对熊猫的预测精度最高,对狗的预测精度较低,需要增加网络层数和循环次数。