【神经网络】(3) 卷积神经网络(CNN),案例:动物三分类,附python完整代码

各位同学好,今天和大家分享一下TensorFlow2.0深度学习中卷积神经网络的案例。现在有猫、狗、熊猫图片一千张,构建卷积神经网络实现图像的分类预测。


1. 数据加载

将训练测试数据划分好后放在同一个文件目录下,使用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']

2. 数据预处理

使用.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()

预处理后的图像展示结果如下:


3. 网络构建

这里构造一个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
_________________________________________________________________

4. 网络配置

采用学习率数衰减的方法方法,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. 模型训练

#(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. 模型评估

比较网络训练集和验证集上的准确率和损失,绘图比较

#(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')

如图所示训练集和验证集的损失都逐渐下降,随着迭代次数的增加,效果会更好,但要防止过拟合的现象出现。


7. 预测

采用测试集中的图像数据对网络进行预测,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次循环后,对熊猫的预测精度最高,对狗的预测精度较低,需要增加网络层数和循环次数。

  • 20
    点赞
  • 229
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 28
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 28
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

立Sir

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值