首页 > 其他分享 >【机器学习】任务九:卷积神经网络(基于 Cifar-10 数据集的彩色图像识别分类、基于 CNN 的手写数字识别的实验)

【机器学习】任务九:卷积神经网络(基于 Cifar-10 数据集的彩色图像识别分类、基于 CNN 的手写数字识别的实验)

时间:2024-10-27 11:46:53浏览次数:7  
标签:10 基于 训练 卷积 模型 train test 识别

1.卷积神经网络

        卷积神经网络(Convolutional Neural Network, CNN)是一种专门用于处理数据网格结构(如图像、视频等)的深度学习模型,在计算机视觉任务中被广泛应用,如图像分类、目标检测、图像分割等。以下是卷积神经网络的详细介绍:

1.1 卷积神经网络 (CNN) 结构及原理

核心组件:

  • 卷积层: 使用卷积核对输入数据(如图像)进行滑动,生成特征图(feature map)。
  • 池化层: 通过下采样减少特征图的尺寸,降低计算复杂度和内存消耗。常见方法包括最大池化 (max pooling)平均池化 (average pooling)
  • 全连接层: 将卷积和池化提取到的特征映射为输出类别,实现分类或回归任务。
  • Dropout层: 防止过拟合,通过随机丢弃部分神经元。

激活函数: 常用ReLU函数引入非线性,帮助模型学习复杂模式。

特点:

  • 局部连接: 每一层只与相邻节点连接,减少参数数量。
  • 权重共享: 同一卷积核在不同位置重复使用,进一步降低计算复杂度。
CNN 结构:
  • 典型的卷积神经网络由卷积层、池化层和全连接层 3 部分组成。在图像分类中表现良好的深度卷积神经网络,往往由多个“卷积层+池化层”的组合堆叠而成,通常多达十几层甚至上百层
  • 卷积层用于提取图像中的局部特征;池化层用于对提取出的局部特征 进行降维处理,防止过拟合;全连接层用于接收池化层的输出,为后续分类任务 做准备。

1.2 经典卷积神经网络结构

LeNet-5: 主要用于手写数字识别,包括多个卷积层和池化层组合。

  • 网络结构:输入层 → 卷积层 → 池化层 → 卷积层 → 池化层 → 全连接层 → 输出层

VGGNet: 探索卷积网络深度与性能的关系,VGG16和VGG19为其常见变体。

  • 特点:多组3×3卷积核,每组卷积后接2×2最大池化层。

ResNet: 引入残差模块,允许数据直接跳过某些层,提高网络性能。

2.基于 Cifar-10 数据集的彩色图像识别分类

2.1 导入所需的模块和包并进行数据预处理

2.1.1 实现目的:

导入TensorFlow等所需模块,用于加载 CIFAR-10 数据集,并完成数据类型转换和标准化处理,确保模型可以正常使用这些数据进行训练和测试。

2.1. 代码片段与结果

'''步骤一,导入本项目所需要的模块和包,从 Keras 中导入 Cifar-10 数据集,将训练集的特征值和标签值分别存储在
x_train 和 y_train 中,将测试集的特征值和标签值分别存储在 x_test 和 y_test 中。将特征值 x_train 和 x_test 
的数据类型转换为 tf.float32,并进行标准化处理,使其取值范围为 0~1;将标签值 y_train 和 y_test的数据类型转换为 tf.int32。'''

# 导入所需要的模块与包
import tensorflow as tf
import numpy as np
import matplotlib.pyplot as plt
import os

# 导入Cifar-10数据集,将训练集和测试集存储在相应变量中
cifar10 = tf.keras.datasets.cifar10
(x_train, y_train), (x_test, y_test) = cifar10.load_data()

# 转换特征值的数据类型并进行标准化处理
x_train, x_test = tf.cast(x_train, dtype=tf.float32) / 255.0, tf.cast(x_test, dtype=tf.float32) / 255.0

# 转换标签值的数据类型
y_train, y_test = tf.cast(y_train, dtype=tf.int32), tf.cast(y_test, dtype=tf.int32)

# 显示数据集的特征值和标签值的 shape 属性值
print("x_train.shape =", x_train.shape)
print("y_train.shape =", y_train.shape)
print("x_test.shape =", x_test.shape)
print("y_test.shape =", y_test.shape)

2.1.3 代码解释:

1.导入 TensorFlow、NumPy、Matplotlib 和 OS 模块:

  • TensorFlow:用于深度学习模型构建和数据处理。
  • NumPy:用于数值计算和数据操作。
  • Matplotlib:用于数据的可视化。
  • OS:用于文件和目录操作。

2.加载 CIFAR-10 数据集:

  • tf.keras.datasets.cifar10 提供了 CIFAR-10 数据集。该数据集包含 10 类图片,适用于图像分类任务。
  • load_data() 将数据集拆分为训练集和测试集,并存储在 (x_train, y_train)(x_test, y_test) 中。

3.数据类型转换与标准化:

  • 使用 tf.cast() 将特征值转换为 tf.float32 数据类型。
  • 将像素值标准化到 [0, 1] 范围内,方便模型训练。
  • 将标签值转换为 tf.int32,用于模型的分类任务。

4.显示数据形状:

  • 通过 print() 输出训练集和测试集的特征值及标签值的形状,以验证数据加载是否正确。

2.1.4 结果与结果分析

x_train.shape = (50000, 32, 32, 3)

x_train.shape = (50000, 32, 32, 3)

y_train.shape = (50000, 1)

y_test.shape = (10000, 1)

1. 训练集数据 (x_train, y_train)

x_train.shape = (50000, 32, 32, 3)

  • 50000:训练集包含 50,000 张图像。
  • 32 × 32:每张图像的大小是 32 × 32 像素。
  • 3:代表图像的 3 个颜色通道(RGB),即每个像素点包含红、绿、蓝三种颜色值。

y_train.shape = (50000, 1)

  • 每张图像有 1 个标签,表示其所属类别。CIFAR-10 有 10 个类别,用标签 0~9 表示。

2. 测试集数据 (x_test, y_test)

x_test.shape = (10000, 32, 32, 3)

  • 10000:测试集包含 10,000 张图像。
  • 32 × 32:每张图像的尺寸为 32 × 32 像素。
  • 3:表示每张图像有 3 个颜色通道(RGB)。

y_test.shape = (10000, 1)

  • 每张测试图像也有 1 个标签,对应其类别。

2.2 构建 CNN 模型

2.2.1 实现目的:

构建一个用于 CIFAR-10 图像分类的卷积神经网络(CNN)模型。通过多个卷积层、池化层和全连接层的组合来提取图像特征,并最终完成分类任务。

2.2.2 代码片段:

'''步骤二,构建 CNN 模型,图像识别分类网络模型采用卷积神经网络,包括两
个卷积层、两个池化层、4 个 Dropout 层、一个拉伸层、两个全连接层和一个输
出层'''
# 构建 CNN 模型
model = tf.keras.models.Sequential([
    
    # 使用 Input 层明确指定输入形状
    tf.keras.Input(shape=x_train.shape[1:]),  
    
    # 创建卷积层
    tf.keras.layers.Conv2D(32, kernel_size=(3, 3), padding='same', activation=tf.nn.relu),
    
    # 创建最大池化层
    tf.keras.layers.MaxPool2D(pool_size=(2, 2), strides=(1, 1), padding='same'),  
    
    # 创建 Dropout 层
    tf.keras.layers.Dropout(0.2),  
    
    # 创建第二个卷积层
    tf.keras.layers.Conv2D(64, kernel_size=(3, 3), padding='same', activation=tf.nn.relu),
    
    # 创建最大池化层
    tf.keras.layers.MaxPool2D(pool_size=(2, 2), strides=(1, 1), padding='same'),  
    
    # 创建 Dropout 层
    tf.keras.layers.Dropout(0.2),  
    
    # 创建拉伸层(Flatten)
    tf.keras.layers.Flatten(),  
    
    # 创建全连接层
    tf.keras.layers.Dense(512, activation='relu'),  
    
    # 创建 Dropout 层
    tf.keras.layers.Dropout(0.2),  
    
    # 创建另一个全连接层
    tf.keras.layers.Dense(256, activation='relu'),  
    
    # 创建 Dropout 层
    tf.keras.layers.Dropout(0.5),  
    
    # 创建全连接层作为输出层
    tf.keras.layers.Dense(10, activation='softmax')  
])

# 打印模型结构
model.summary()

2.2.3 代码解释:

1.Sequential 模型:

  • 使用 tf.keras.models.Sequential() 按顺序定义模型结构。

2.输入层:

  • tf.keras.Input() 定义输入层,输入数据的形状与训练数据集的形状一致。

3.卷积层:

  • 第一层卷积:Conv2D(32, (3, 3)) 使用 32 个大小为 3x3 的卷积核,提取低层次特征。
  • 第二层卷积:Conv2D(64, (3, 3)) 使用 64 个卷积核,提取更复杂的特征。

4.池化层:

  • 使用 MaxPool2D(pool_size=(2, 2), strides=(1, 1)) 进行最大池化,减少特征图的尺寸,保留主要信息。

5.Dropout 层:

  • 在多处使用 Dropout() 随机丢弃神经元,防止过拟合。丢弃概率分别为 0.2 和 0.5。

6.拉伸层(Flatten):

  • 使用 Flatten() 将二维特征图展开为一维向量,方便传入全连接层。

7.全连接层:

  • 第一层全连接层:512 个神经元,激活函数为 ReLU。
  • 第二层全连接层:256 个神经元,激活函数为 ReLU。

8.输出层:

  • 使用 Dense(10, activation='softmax') 作为输出层,适用于 CIFAR-10 的 10 分类任务。

9.打印模型结构:

  • 使用 model.summary() 打印模型的结构、参数数量和层次关系。

2.2.4 结果与结果分析

(1)模型层结构:

1.Conv2D (卷积层)

  • 输出形状(None, 32, 32, 32)
    说明:32 个 3×3 的卷积核,输出 32 个特征图。
  • 参数数量:896
    计算:$(3 \times 3 \times 3 + 1) \times 32 = 896$(包含偏置项)。

2.MaxPooling2D (最大池化层)

  • 输出形状(None, 32, 32, 32)
    说明:池化窗口为 2×2,没有改变输出形状。

3.Dropout (丢弃层)

  • 丢弃部分神经元,防止过拟合。

4.第二个 Conv2D (卷积层)

  • 输出形状(None, 32, 32, 64)
    说明:64 个 3×3 的卷积核。
  • 参数数量:18,496
    计算:$(3 \times 3 \times 32 + 1) \times 64 = 18,496$。

5.Flatten (拉伸层)

  • 输出形状(None, 65536)
    说明:将 32×32×64 的特征图展平为一维向量。

6.Dense (全连接层)

  • 输出形状(None, 512)
    参数数量:33,554,944
    计算:$65536 \times 512 + 512 = 33,554,944$。

7.Dense (全连接层)

  • 输出形状(None, 256)
    参数数量:131,328
    计算:$512 \times 256 + 256 = 131,328$。

8.输出层 (Dense)

  • 输出形状(None, 10)
    说明:10 个神经元,表示 10 个分类类别。
  • 参数数量:2,570
    计算:$256 \times 10 + 10 = 2,570$。
(2)模型总参数:
  • 总参数数量:33,708,234
  • 可训练参数数量:33,708,234
  • 非可训练参数:0

2.3 编译、训练和评估 CNN 模型

2.3.1 实现目的:

  1. 编译模型:配置优化器、损失函数和评估指标,为训练过程做好准备。
  2. 训练模型:用训练集训练模型,并在训练过程中监控验证集性能。
  3. 评估模型:在测试集上评估模型性能,确保模型的泛化能力。

2.3.3 代码片段:

'''步骤三,编译、训练和评估 CNN 模型'''
# 编译网络模型
model.compile(optimizer='adam', 
              loss=tf.keras.losses.SparseCategoricalCrossentropy(), 
              metrics=['sparse_categorical_accuracy'])

os.environ["CUDA_VISIBLE_DEVICES"] = "-1"
# 训练网络模型
history = model.fit(x_train, y_train, 
                    batch_size=128, 
                    epochs=10, 
                    validation_split=0.2)

# 评估网络模型
model.evaluate(x_test, y_test, 
               batch_size=64, 
               verbose=2)

2.3.3 代码解释:

1.模型编译 (model.compile):

  • optimizer='adam':Adam 优化器,兼具自适应学习率和动量优化的特点。
  • loss=tf.keras.losses.SparseCategoricalCrossentropy():用于多分类任务的损失函数。
  • metrics=['sparse_categorical_accuracy']:评估指标为稀疏分类准确率,用于处理标签是整数编码的情况。

2.训练模型 (model.fit):

  • batch_size=128:每次梯度更新时处理 128 个样本。
  • epochs=10:训练模型 10 个完整的周期(轮次)。
  • validation_split=0.2:将 20% 的训练数据用于验证模型性能。

3.评估模型 (model.evaluate):

  • 在测试集上评估模型性能,batch_size=64 指定每次处理的样本数。
  • verbose=2 控制输出日志的详细程度。

2.3.4 结果与结果分析

(1)训练过程:

1.趋势分析:

1.Epoch 1/10

  • 训练损失(loss):1.3763
  • 训练准确率(sparse_categorical_accuracy):51.22%
  • 验证损失(val_loss):1.1739
  • 验证准确率(val_sparse_categorical_accuracy):59.02%

2.Epoch 10/10

  • 训练损失:0.3544
  • 训练准确率:87.84%
  • 验证损失:1.0411
  • 验证准确率:69.57%

趋势分析:

训练损失和训练准确率逐渐优化,模型在训练数据上的表现不断提高,最终训练准确率达到 87.84%

验证集的准确率逐渐提高到 69.57%,但验证损失(val_loss)在第 7-10 轮趋于不稳定(略有增加),表明模型可能出现了一些过拟合

(2)测试集评估结果:

loss: 1.0738 sparse_categorical_accuracy: 69.16%

解释:

  • 测试损失:1.0738
  • 测试准确率:69.16%
  • 在测试集上的准确率为 69.16%,与验证集准确率(69.57%)非常接近,表明模型的泛化能力良好
  • 测试损失验证损失接近,进一步验证模型的性能稳定。

2.4 可视化训练的结果

2.4.1 实现目的:

使用 Matplotlib 将训练损失、验证损失以及训练准确率、验证准确率随轮次的变化情况绘制成折线图,帮助分析模型的训练过程和性能。

2.4.2 代码片段:

'''步骤四,可视化训练的结果'''
print(history.history)

# 确保 epochs 数组的定义与 loss 和 acc 的数据一致
epochs = range(1, len(history.history['loss']) + 1)

# 创建画布并设置画面大小
plt.figure(figsize=(10, 3))

# 在子图1中绘制损失函数的折线图
plt.subplot(121)
plt.plot(epochs, history.history['loss'], color='b', label='train')  # 蓝色线表示训练集损失
plt.plot(epochs, history.history['val_loss'], color='r', label='validate')  # 红色线表示验证集损失
plt.xlabel('Epochs')  # 添加横轴标签
plt.ylabel('Loss')  # 添加纵轴标签
plt.legend()  # 显示图例

# 在子图2中绘制准确率的折线图
plt.subplot(122)
plt.plot(epochs, history.history['sparse_categorical_accuracy'], color='b', label='train')  # 蓝色线表示训练集准确率
plt.plot(epochs, history.history['val_sparse_categorical_accuracy'], color='r', label='validate')  # 红色线表示验证集准确率
plt.xlabel('Epochs')  # 添加横轴标签
plt.ylabel('Accuracy')  # 添加纵轴标签
plt.legend()  # 显示图例

# 显示图像
plt.show()

2.4.3 代码解释:

1.打印训练历史:

  • history.history 中包含每一轮训练和验证的损失准确率

2.绘制损失函数曲线:

  • 子图 1 展示训练和验证的损失随轮次的变化:
    • 蓝线:训练集损失
    • 红线:验证集损失

3.绘制准确率曲线:

  • 子图 2 展示训练和验证的准确率随轮次的变化:
    • 蓝线:训练集准确率
    • 红线:验证集准确率

4.显示图像:

  • 使用 plt.show() 将两张图同时显示出来。

2.4.4 结果与结果分析

(1)损失曲线(左图):
  • 蓝色线(train):训练集的损失值逐渐下降,说明模型在逐步学习特征。
  • 红色线(validate):验证集的损失在前几轮下降后逐渐趋于平稳并略有上升。

分析:

  • 训练损失持续降低,而验证损失在后期上升,表明模型可能出现过拟合
  • 改进建议
    • 增加 Dropout 层的使用或增大丢弃率。
    • 使用更多正则化(如 L2 正则化)。
    • 尝试减少模型的复杂度或收集更多数据。
(2)准确率曲线(右图):
  • 蓝色线(train):训练集的准确率逐渐提升,最终接近 90%。
  • 红色线(validate):验证集的准确率在 70% 附近趋于平稳。

分析:

  • 训练准确率不断上升,而验证准确率停滞在约 70% 左右。这进一步表明模型可能在训练集上表现良好,但在验证集上出现过拟合

2.5 应用卷积神经网络模型

2.5.1 实现目的:
  • 随机选取 10 张测试集中的图片,使用训练好的 CNN 模型对其进行预测,并将标签值和预测值显示在图像上方。
  • 验证模型的效果,观察模型在测试集上的分类准确性
2.5.2 代码片段:

'''步骤五,应用卷积神经网络模型'''
plt.figure()  # 创建画布

for i in range(10):  # 循环显示10张图片
    n = np.random.randint(1, 10000)  # 生成随机整数 n
    plt.subplot(2, 5, i + 1)  # 创建子图 (2行5列)
    plt.axis("off")  # 不显示坐标轴
    plt.rcParams['font.sans-serif'] = ['SimHei']  # 设置字体为 SimHei,支持中文显示

    # 显示测试集中随机图片,使用灰度颜色映射
    plt.imshow(x_test[n], cmap='gray')

    # 将图片 reshape 为模型输入的形状 (1, 32, 32, 3)
    demo = tf.reshape(x_test[n], (1, 32, 32, 3))

    # 使用模型预测该图片的类别
    y_pred = np.argmax(model.predict(demo))

    # 设置子图标题,显示标签值与预测值
    title = "标签值: " + str((y_test.numpy())[n, 0]) + "\n预测值: " + str(y_pred)
    plt.title(title)

# 展示图像
plt.show()
2.5.3 代码解释:

1.画布与子图:

  • plt.figure():创建一个用于显示图像的画布。
  • plt.subplot(2, 5, i + 1):将画布分成 2 行 5 列的子图,每次在新的子图中显示一张图片。

2.随机选取测试图片:

  • 使用 np.random.randint(1, 10000) 生成 1 到 9999 之间的随机数,作为图片索引。

3.显示图片:

  • 使用 plt.imshow() 在子图中显示随机图片,使用 灰度颜色映射 (cmap='gray')。

4.模型预测:

  • 将选中的图片 reshape 为模型的输入形状 (1, 32, 32, 3)
  • 使用 model.predict() 对图片进行预测,并使用 np.argmax() 获取预测的类别。

5.设置标题:

  • 子图标题中显示标签值预测值,标签值来自 y_test,预测值来自模型的预测结果。

6.展示图像:

  • 使用 plt.show() 展示 10 张图片及其标签与预测结果。

2.5.4 结果与分析

(1)展示内容:

每个子图显示了一张来自 CIFAR-10 测试集的随机图片,且每张图片的标题标注了:

  • 标签值(真实类别):来自 y_test,表示图片的真实类别标签。
  • 预测值(模型预测结果):模型根据输入图像预测的类别。
(2)分析结果:

1.正确预测的示例

  • 标签值:6预测值:6:模型正确地识别出图片中的类别。
  • 标签值:7预测值:7:另一个成功分类的示例。

2.错误预测的示例

  • 标签值:4预测值:3:模型将真实类别 4 误判为 3。
  • 标签值:3预测值:2:该图片也被模型错误分类。
(3)模型表现观察:
  • 部分图片的预测结果是正确的,说明模型对某些类别的识别效果较好。
  • 某些类别的预测出现错误,这可能是因为:
    • 类别之间的相似性导致分类困难(如狗和猫)。
    • 数据不足或模型过拟合,使得模型在某些类别上泛化能力不足。

3.基于 CNN 的手写数字识别的实验

3.1 随机选取并保存测试图片

3.1.1 实现目的:

通过随机从 MNIST 测试集中选取一些图片,并将它们以 PNG 格式保存到指定目录。这有助于我们检查数据集的样本内容,并在后续使用这些图片进行预测和验证。

3.1.2 代码片段:

import numpy as np
import matplotlib.pyplot as plt
from tensorflow.keras.datasets import mnist

# 加载 MNIST 数据集
(x_train, y_train), (x_test, y_test) = mnist.load_data()

# 创建保存路径
import os
save_path = 'test_images'
os.makedirs(save_path, exist_ok=True)

# 保存一些图片为 PNG
def save_images(images, labels, indices):
    for i in indices:
        img = images[i]
        label = labels[i]
        file_name = f"{save_path}/{label}_{i}.png"
        plt.imsave(file_name, img, cmap='gray')
        print(f"保存图片: {file_name}")

# 从测试集里保存 3 张图片 (你可以指定其他索引)
save_images(x_test, y_test, [0, 3, 9])  # 保存索引 0, 3, 9 处的图片

3.1.3 代码解释:

1.导入库

  • numpy:用于处理数组和数值操作。
  • matplotlib.pyplot:用于保存和显示图片。
  • tensorflow.keras.datasets:用于加载 MNIST 数据集。

2.加载数据集

  • 使用 mnist.load_data() 加载 MNIST 手写数字数据集,该数据集包含 60,000 张训练图片和 10,000 张测试图片。
    • x_trainx_test:保存图片的 numpy 数组,每张图片大小为 28x28 像素。
    • y_trainy_test:保存图片对应的标签(0-9)。

3.创建保存路径

  • 使用 os.makedirs() 创建 test_images 文件夹,如果文件夹不存在则会自动创建。

4.定义 save_images() 函数

  • 遍历传入的索引列表 indices,根据索引获取图片和对应标签。
  • 使用 plt.imsave() 将图片保存为 PNG 格式,并以 {标签}_{索引}.png 命名。

5.调用 save_images() 函数

  • 选取测试集中的第 0、3、9 张图片,并将其保存到 test_images 文件夹中。

3.1.4 结果与分析:

保存图片: test_images/7_0.png

保存图片: test_images/0_3.png

保存图片: test_images/9_9.png

1.图片内容检查

  • 打开 test_images 文件夹,检查是否存在 3 张 PNG 图片。
  • 图片的名称格式为 {标签}_{索引}.png,其中 标签 是图片的真实类别。

2.潜在问题与解决方案

  • 路径问题:确保有权限在当前工作目录下创建文件夹和保存文件。
  • 图片内容错误:如果保存的图片标签与预期不符,请检查代码中的索引是否正确。

3.实际应用

  • 这些保存的图片可以用于模型的预测测试,验证训练模型的准确性。
  • 在接下来的步骤中,我们将使用这些保存的图片,并通过训练好的模型进行预测。

3.2 数据的读取与预处理

3.2.1 实现目的:

本步骤的目的是加载 MNIST 数据集,并对其进行预处理。具体目标包括:

  1. 检查本地是否存在数据集,如果没有则自动从网上下载。
  2. 为每张图片增加通道维度,以适配 CNN 模型的输入需求。
  3. 将图片的像素值缩放至 [0, 1] 区间,提高模型的训练性能。
  4. 打印数据集的形状,并验证每张图片是否为单通道(灰度图)。

3.2.2 代码片段:

import os
import tensorflow as tf
from tensorflow.keras.datasets.mnist import load_data

class DataSource:
    def __init__(self):
        # 数据集路径配置(使用当前目录)
        data_path = os.path.join(os.getcwd(), 'data', 'mnist.npz')

        # 检查本地是否存在数据集
        if os.path.exists(data_path):
            (x_train, y_train), (x_test, y_test) = load_data(path=data_path)
        else:
            print("mnist.npz 文件不存在,从网上下载数据集...")
            (x_train, y_train), (x_test, y_test) = load_data()

        # 增加一个通道维度
        x_train = x_train[..., tf.newaxis]
        x_test = x_test[..., tf.newaxis]

        # 像素值缩放到 [0, 1] 之间
        x_train, x_test = x_train / 255.0, x_test / 255.0

        self.train_images, self.train_labels = x_train, y_train
        self.test_images, self.test_labels = x_test, y_test

# 创建 DataSource 实例,加载数据
data = DataSource()

# 打印训练集和测试集的形状
print(f"训练集图片形状: {data.train_images.shape}")
print(f"训练集标签形状: {data.train_labels.shape}")
print(f"测试集图片形状: {data.test_images.shape}")
print(f"测试集标签形状: {data.test_labels.shape}")

# 验证是否为单通道
print(f"训练集单张图片通道数: {data.train_images.shape[-1]}")
print(f"测试集单张图片通道数: {data.test_images.shape[-1]}")

3.2.3 代码解释:

1.导入库

  • os:用于文件和目录的路径操作。
  • tensorflow:用于加载 MNIST 数据集并进行张量操作。

2.定义 DataSource

  • 检查数据集是否存在于 data 目录中。如果存在则加载本地数据集,否则自动从网上下载。
  • 增加通道维度:将每张图片从 (28, 28) 形状扩展为 (28, 28, 1),以适应 CNN 模型的输入格式。
  • 像素值缩放:将图片的像素值从 [0, 255] 缩放到 [0, 1] 区间,提高模型训练的效率和收敛速度。

3.创建 DataSource 实例

  • 实例化 DataSource 类并加载数据,将数据集保存在 train_imagestrain_labelstest_imagestest_labels 中。

4.打印数据集的形状

  • 输出训练集和测试集的图片形状与标签形状。
  • 检查每张图片的通道数是否为 1,确保数据为灰度图。

3.2.4 结果与分析:

训练集图片形状: (60000, 28, 28, 1)
训练集标签形状: (60000,)
测试集图片形状: (10000, 28, 28, 1)
测试集标签形状: (10000,)
训练集单张图片通道数: 1
测试集单张图片通道数: 1

1.预期结果分析

  • 形状正确:训练集有 60,000 张图片,测试集有 10,000 张图片,每张图片大小为 (28, 28, 1)
  • 通道数:每张图片应为单通道(灰度图)。

2.潜在问题与解决方案

  • 数据集不存在:如果程序提示 "mnist.npz 文件不存在",确保程序有网络访问权限以从网上下载数据集。
  • 维度错误:如果图片形状不符合 (28, 28, 1),检查是否正确添加了新维度。

3.实际应用

  • 预处理后的数据可直接用于训练 CNN 模型,提高模型的训练效率。
  • 数据集结构清晰,确保了模型输入格式的一致性。

3.3 搭建卷积神经网络(CNN模型)

3.3.1 实现目的:

本步骤旨在构建一个卷积神经网络(CNN),用于对 MNIST 手写数字数据集进行分类。卷积神经网络是一种适用于图像数据的深度学习模型,通过卷积层提取特征,并通过全连接层进行分类。本模型设计为一个简单的多层 CNN,适用于 MNIST 数据集。

3.3.2 代码片段:

from tensorflow.keras import layers, models

class CNN:
    def __init__(self):
        # 构建 Sequential 模型,按层堆叠模型结构
        model = models.Sequential([
            # 第1层卷积层 + 最大池化层
            layers.Conv2D(32, (3, 3), activation='relu', input_shape=(28, 28, 1)),
            layers.MaxPooling2D((2, 2)),

            # 第2层卷积层 + 最大池化层
            layers.Conv2D(64, (3, 3), activation='relu'),
            layers.MaxPooling2D((2, 2)),

            # 第3层卷积层
            layers.Conv2D(64, (3, 3), activation='relu'),

            # 展平层 + 全连接层
            layers.Flatten(),
            layers.Dense(64, activation='relu'),

            # 输出层(10类,用 softmax 激活函数)
            layers.Dense(10, activation='softmax')
        ])

        # 打印模型结构
        model.summary()
        self.model = model  # 将模型存储为类的属性

3.3.3 代码解释:

1.导入所需模块

  • layersmodels:来自 TensorFlow 的 Keras API,分别用于创建网络层和模型。

2.模型构建

  • Sequential:顺序模型,按层堆叠每一层。
  • 卷积层:提取图像的局部特征,通过 ReLU 激活函数引入非线性。
    • 第1层:32 个 3x3 卷积核,输入形状为 (28, 28, 1)
    • 第2层:64 个 3x3 卷积核。
    • 第3层:64 个 3x3 卷积核。

3.池化层

  • 最大池化层:每次将 2x2 区域的最大值作为特征,减少特征维度。

4.展平层

  • 将卷积层输出的多维张量展平为一维向量,便于输入到全连接层。

5.全连接层

  • Dense:全连接层,包含 64 个神经元,并使用 ReLU 激活函数。

6.输出层

  • Dense:输出层包含 10 个神经元,对应 10 个类别,使用 softmax 激活函数进行多分类。

7.模型摘要

  • model.summary():打印模型结构,包括每层的输出形状和参数数量。

3.3.4 结果(此结果在下一段代码打印)与分析:

1.Conv2D (卷积层)

  • 功能:提取图像特征,如边缘、纹理等。
  • 输出形状(None, 26, 26, 32),表示卷积后的输出尺寸为 26×26,每个像素点有 32 个通道。
  • 参数数量 (Param #):320。这由卷积核的数量和大小决定。

2.MaxPooling2D (最大池化层)

  • 功能:缩减图像尺寸,保留重要特征,减少参数。
  • 输出形状(None, 13, 13, 32),池化层将图像尺寸缩小了一半。

3.Flatten (展平层)

  • 功能:将多维的特征图展平为一维,供全连接层使用。
  • 输出形状(None, 576),将图像的特征展平为 576 维向量。

4.Dense (全连接层)

  • 功能:用于分类或回归任务,将展平的特征映射到输出类别上。
  • 输出形状:第一层为 (None, 64),第二层为 (None, 10),表示 10 个输出类别。

5.总参数数量:

  • Total params (总参数数量): 93,322
  • Trainable params (可训练参数): 93,322
  • Non-trainable params (不可训练参数): 0

3.4 训练模型、保存结果与可视化

3.4.1 实现目的:

本部分的目的是对构建的卷积神经网络(CNN)进行训练,并在训练过程中记录日志、保存模型检查点,以便可视化和模型的复用。训练结束后,打印测试结果以评估模型性能。

3.4.2 代码片段:

import numpy as np
from datetime import datetime
from tensorflow.keras.callbacks import TensorBoard, ModelCheckpoint
# 忽略警告
import warnings
warnings.filterwarnings('ignore', category=UserWarning)


class Train:
    def __init__(self):
        self.network = CNN()  # 创建CNN实例
        self.data = DataSource()  # 加载数据集

    def train(self):
        # 设置 TensorBoard 日志路径
        logdir = "./logs/scalars/" + datetime.now().strftime("%Y%m%d-%H%M%S")
        tensorboard_cb = TensorBoard(log_dir=logdir)

        # # 设置模型检查点路径
        # check_path = './ckpt/cp-{epoch:04d}.ckpt'
        # save_model_cb = ModelCheckpoint(check_path, save_weights_only=True, verbose=1)
        # 设置模型检查点路径(每5个epoch保存一次)
        check_path = './ckpt/cp-{epoch:04d}.weights.h5'

        save_model_cb = tf.keras.callbacks.ModelCheckpoint(
            filepath=check_path,  # 修改文件后缀为 .weights.h5
            save_weights_only=True,
            verbose=1
        )


        # 编译模型
        self.network.model.compile(optimizer='adam',
                                   loss='sparse_categorical_crossentropy',
                                   metrics=['accuracy'])

        # 训练模型并保存日志
        training_history = self.network.model.fit(
            self.data.train_images,
            self.data.train_labels,
            epochs=10,
            validation_data=(self.data.test_images, self.data.test_labels),
            callbacks=[tensorboard_cb, save_model_cb]
        )

        # 保存模型为 .keras 格式
        os.makedirs('./keras', exist_ok=True)
        self.network.model.save('./keras/model.keras')
        print("模型已保存为 './keras/model.keras'")

        # 打印最终测试结果
        test_loss, test_acc = self.network.model.evaluate(self.data.test_images, 
                                                          self.data.test_labels)
        print(f"准确率:{test_acc * 100:.2f}%,共测试了 {len(self.data.test_labels)} 张图片")
        print("平均误差:", np.average(training_history.history['loss']))

if __name__ == "__main__":
    mnist_train = Train()
    mnist_train.train()

3.4.3 代码解释:

1.引入必要的库:

  • TensorBoardModelCheckpoint 用于训练过程的监控和模型权重保存。
  • warnings 模块用于忽略不必要的警告信息,保持输出清晰。

2.初始化训练实例:

  • Train 类的 __init__() 方法创建了 CNN 模型实例,并加载了数据源。

3.训练配置:

  • TensorBoard 可视化: 配置 TensorBoard 的日志路径,每次运行生成新的日志目录,使用当前时间戳命名。
  • 模型检查点: 使用 ModelCheckpoint 在每个 epoch 结束后保存模型权重。文件名以 cp-{epoch:04d}.weights.h5 形式保存,确保每个 epoch 都有独立的权重文件。

4.模型编译:

  • 使用 Adam 优化器和 sparse_categorical_crossentropy 损失函数。
  • 指定 accuracy 为评估指标,用于监控训练和验证过程的分类准确率。

5.模型训练:

  • 训练数据和标签通过 fit() 方法传入,训练 10 个 epoch。
  • 每个 epoch 结束时,验证集上的损失和准确率会被计算并打印。
  • 使用回调函数记录训练过程的 TensorBoard 日志,并在每 5 个 epoch 保存一次模型权重。

6.模型保存:

  • 训练完成后,将模型保存为 .keras 格式,方便后续加载和预测。

7.模型评估:

  • 使用 evaluate() 方法在测试集上评估模型性能,并打印准确率和平均损失。

3.4.4 结果与分析:

Epoch 1/10
1871/1875 ━━━━━━━━━━━━━━━━━━━━ 0s 5ms/step - accuracy: 0.8945 - loss: 0.3399
Epoch 1: saving model to ./ckpt/cp-0001.weights.h5
1875/1875 ━━━━━━━━━━━━━━━━━━━━ 14s 6ms/step - accuracy: 0.8947 - loss: 0.3394 - val_accuracy: 0.9853 - val_loss: 0.0450
Epoch 2/10
1867/1875 ━━━━━━━━━━━━━━━━━━━━ 0s 5ms/step - accuracy: 0.9863 - loss: 0.0462
Epoch 2: saving model to ./ckpt/cp-0002.weights.h5
1875/1875 ━━━━━━━━━━━━━━━━━━━━ 11s 6ms/step - accuracy: 0.9863 - loss: 0.0462 - val_accuracy: 0.9860 - val_loss: 0.0424
Epoch 3/10
1871/1875 ━━━━━━━━━━━━━━━━━━━━ 0s 5ms/step - accuracy: 0.9899 - loss: 0.0324
Epoch 3: saving model to ./ckpt/cp-0003.weights.h5
1875/1875 ━━━━━━━━━━━━━━━━━━━━ 11s 6ms/step - accuracy: 0.9899 - loss: 0.0324 - val_accuracy: 0.9875 - val_loss: 0.0379
Epoch 4/10
1871/1875 ━━━━━━━━━━━━━━━━━━━━ 0s 5ms/step - accuracy: 0.9918 - loss: 0.0257
Epoch 4: saving model to ./ckpt/cp-0004.weights.h5
1875/1875 ━━━━━━━━━━━━━━━━━━━━ 11s 6ms/step - accuracy: 0.9918 - loss: 0.0257 - val_accuracy: 0.9916 - val_loss: 0.0264
Epoch 5/10
1870/1875 ━━━━━━━━━━━━━━━━━━━━ 0s 5ms/step - accuracy: 0.9948 - loss: 0.0178
Epoch 5: saving model to ./ckpt/cp-0005.weights.h5
1875/1875 ━━━━━━━━━━━━━━━━━━━━ 11s 6ms/step - accuracy: 0.9948 - loss: 0.0178 - val_accuracy: 0.9906 - val_loss: 0.0293
Epoch 6/10
1875/1875 ━━━━━━━━━━━━━━━━━━━━ 0s 5ms/step - accuracy: 0.9952 - loss: 0.0138
Epoch 6: saving model to ./ckpt/cp-0006.weights.h5
1875/1875 ━━━━━━━━━━━━━━━━━━━━ 11s 6ms/step - accuracy: 0.9952 - loss: 0.0138 - val_accuracy: 0.9919 - val_loss: 0.0270
Epoch 7/10
1868/1875 ━━━━━━━━━━━━━━━━━━━━ 0s 5ms/step - accuracy: 0.9962 - loss: 0.0119
Epoch 7: saving model to ./ckpt/cp-0007.weights.h5
1875/1875 ━━━━━━━━━━━━━━━━━━━━ 11s 6ms/step - accuracy: 0.9962 - loss: 0.0119 - val_accuracy: 0.9925 - val_loss: 0.0293
Epoch 8/10
1874/1875 ━━━━━━━━━━━━━━━━━━━━ 0s 5ms/step - accuracy: 0.9977 - loss: 0.0079
Epoch 8: saving model to ./ckpt/cp-0008.weights.h5
1875/1875 ━━━━━━━━━━━━━━━━━━━━ 11s 6ms/step - accuracy: 0.9977 - loss: 0.0079 - val_accuracy: 0.9894 - val_loss: 0.0419
Epoch 9/10
1870/1875 ━━━━━━━━━━━━━━━━━━━━ 0s 5ms/step - accuracy: 0.9972 - loss: 0.0080
Epoch 9: saving model to ./ckpt/cp-0009.weights.h5
1875/1875 ━━━━━━━━━━━━━━━━━━━━ 11s 6ms/step - accuracy: 0.9972 - loss: 0.0080 - val_accuracy: 0.9921 - val_loss: 0.0317
Epoch 10/10
1870/1875 ━━━━━━━━━━━━━━━━━━━━ 0s 5ms/step - accuracy: 0.9977 - loss: 0.0072
Epoch 10: saving model to ./ckpt/cp-0010.weights.h5
1875/1875 ━━━━━━━━━━━━━━━━━━━━ 11s 6ms/step - accuracy: 0.9977 - loss: 0.0072 - val_accuracy: 0.9921 - val_loss: 0.0382
模型已保存为 './keras/model.keras'
313/313 ━━━━━━━━━━━━━━━━━━━━ 1s 2ms/step - accuracy: 0.9886 - loss: 0.0526
准确率:99.21%,共测试了 10000 张图片
平均误差: 0.03288608025759458

1.结果分析:

  • 训练表现:随着 epoch 数的增加,模型的训练准确率逐渐提高,损失函数逐渐降低。
  • 测试集表现:在测试集上的准确率达到了 99.21%,表明模型在 MNIST 数据集上有非常好的表现。
  • 平均误差:训练的平均误差较低,表明模型已成功拟合训练数据。

2.优化建议:

  • 正则化:可以引入 Dropout 或 L2 正则化来防止过拟合。
  • 数据增强:通过随机裁剪或旋转图片,提高模型的泛化能力。

3.5 小标题:加载模型并进行预测

3.5.1 实现目的:

本步骤的目标是使用训练好的卷积神经网络(CNN)模型,对手写数字图片进行预测,验证模型的分类能力。通过加载保存的模型,读取测试图片,并显示模型的预测结果,判断模型是否能够准确识别输入图片中的数字。

3.5.2 代码片段:

# 步骤4:加载模型并进行预测
from PIL import Image

class Predict:
    def __init__(self):
        # 加载已训练的模型
        self.model = tf.keras.models.load_model('./keras/model.keras')

    def predict(self, image_path):
        # 读取并预处理图片
        img = Image.open(image_path).convert('L')
        img = img.resize((28, 28))  # 确保图片大小为 28x28
        img_array = np.array(img).reshape(1, 28, 28, 1) / 255.0  # 归一化

        # 进行预测
        prediction = self.model.predict(img_array)
        predicted_label = np.argmax(prediction[0])
        print(f"{image_path} -> 预测数字为:{predicted_label}")

# 测试预测
if __name__ == "__main__":
    app = Predict()
    app.predict('./test_images/7_0.png')
    app.predict('./test_images/0_3.png')
    app.predict('./test_images/9_9.png')

3.5.3 代码解释:

1.加载模型:

  • 使用 tf.keras.models.load_model() 加载保存为 model.keras 格式的训练模型。该模型已经通过训练,并保存在目录 ./keras 下。

2.读取和预处理图片:

  • 使用 PIL 库中的 Image.open() 读取指定路径的图片,并将其转换为灰度模式(L)。
  • 使用 img.resize((28, 28)) 将图片大小调整为 28x28,以符合模型的输入要求。
  • 使用 np.array() 将图片转换为 NumPy 数组,并 reshape 为 (1, 28, 28, 1),以便输入到模型中进行预测。最后对像素值归一化到 [0, 1] 范围内。

3.模型预测:

  • 使用 self.model.predict() 对图片数据进行预测,得到一个概率数组。
  • 使用 np.argmax() 获取预测结果中概率最高的类别标签,即模型认为的数字。

4.测试代码:

  • 代码通过调用 app.predict(),对三张测试图片进行预测,并打印结果到控制台。

3.5.4 结果与分析:

1/1 ━━━━━━━━━━━━━━━━━━━━ 0s 95ms/step
./test_images/7_0.png -> 预测数字为:7
1/1 ━━━━━━━━━━━━━━━━━━━━ 0s 20ms/step
./test_images/0_3.png -> 预测数字为:0
1/1 ━━━━━━━━━━━━━━━━━━━━ 0s 19ms/step
./test_images/9_9.png -> 预测数字为:9

1.预测分析:

这些结果表明模型对这几张测试图片的识别非常准确。

  • 对于输入图片 7_0.png,模型成功预测为数字 7。
  • 对于输入图片 0_3.png,模型成功预测为数字 0。
  • 对于输入图片 9_9.png,模型成功预测为数字 9。

2.模型表现:

  • 模型在这些测试图片上的预测结果全部正确,说明模型在训练过程中已经学习到了有效的特征。
  • 预测速度较快,每次预测耗时在 20ms-95ms 之间,表明模型推理效率较高。

3.优化建议:

  • 异常处理: 在加载图片时增加异常捕获,以避免文件缺失或路径错误导致程序崩溃:
    try:
        img = Image.open(image_path).convert('L')
    except FileNotFoundError:
        print(f"文件未找到: {image_path}")
    

  • 批量预测: 如果需要对多张图片进行预测,可以编写循环批量加载和预测,提升效率。

标签:10,基于,训练,卷积,模型,train,test,识别
From: https://blog.csdn.net/FHY26828/article/details/143244934

相关文章

  • 基于SSM平面设计课程在线学习系统的设计
    管理员账户功能包括:系统首页,个人中心,学生管理,教师管理,课程类型管理,课程学习管理,试题讲解管理,作业信息管理前台账号功能包括:系统首页,个人中心,课程学习,在线讨论,系统公告,后台管理开发系统:Windows架构模式:SSMJDK版本:JavaJDK1.8开发工具:IDEA(推荐)数据库版本:mysql5.7数据库......
  • 基于SSM轻型卡车零部件销售系统的设计
    管理员账户功能包括:系统首页,个人中心,用户管理,配件类型管理,配件信息管理,订单信息管理,检修休息管理,系统管理用户账号功能包括:系统首页,个人中心,订单信息管理,检修信息管理,售后信息管理开发系统:Windows架构模式:SSMJDK版本:JavaJDK1.8开发工具:IDEA(推荐)数据库版本:mysql5.7数......
  • 20241016 模拟赛(最终测试,空间跳跃,快速访问,门童)
    看题目戳这里总结时间分配:早自习20min。听歌60min,游走60min。100min考试。t1看了40min没看出来转t2,t2打了一半发现负数没想出来,最后二三十分钟打t3暴力,结果神奇般地0pts,因为根节点深度设为1。当然t4没看一眼。唉。下次打模拟赛的时候把耳机摘了。结果:30+0+0+0总结:wssb解析......
  • 1024程序员节快乐——华为静态路由实现路由负载分担实验案例
    目录静态路由实现路由负载分担静态路由简介配置注意事项组网需求配置思路操作步骤配置文件静态路由实现路由负载分担实验组网图形图1 配置静态路由实现路由负载分担组网图 静态路由简介配置注意事项组网需求配置思路操作步骤配置文件静态路由简介静态路......
  • 基于RBF神经网络的双参数自适应光储VSG构网逆变器MATLAB仿真模型
    “电气仔推送”获得资料(专享优惠)模型简介此模型源侧部分采用光伏发电系统与混合储能系统(蓄电池+超级电容),并网逆变器采用虚拟同步发电机(VSG)控制,为系统提供惯量阻尼支撑。同时对VSG控制部分进行了改进,采用RBF径向基神经网络对虚拟惯量及虚拟阻尼进行自适应控制,自适应方法完全复......
  • 【毕业设计】基于Springboot + Vue的城市垃圾分类管理系统
    随着城市化进程的加快,城市垃圾的处理与分类成为了社会关注的重点。传统的垃圾管理方式通常依赖于人工分拣和纸质记录,这种方式不仅效率低下,还容易产生信息记录错误和数据丢失的情况。因此,开发一个智能化的城市垃圾分类管理系统显得尤为重要。本文将介绍基于SpringBoot开发的城市......
  • 【毕业设计】基于SpringBoot + Vue的扶贫助农系统
    在当今数字化时代,信息技术的快速发展为各个领域带来了新的机遇和挑战。特别是在扶贫助农领域,传统的管理方法往往面临效率低、错误率高、检索困难等问题。为了提高扶贫助农信息的管理效率,本文介绍了一个基于SpringBoot框架开发的扶贫助农系统。1.为什么需要一个扶贫助农系统?......
  • 智能提醒助手——基于HarmonyOS Next的多场景后台任务实现
    本文旨在深入探讨华为鸿蒙HarmonyOSNext系统(截止目前API12)的技术细节,基于实际开发实践进行总结。主要作为技术分享与交流载体,难免错漏,欢迎各位同仁提出宝贵意见和问题,以便共同进步。本文为原创内容,任何形式的转载必须注明出处及原作者。一、项目背景与需求分析智能提醒助手......
  • Java基于String类的题
    题目:字符串中的第一个唯一字符链接:387.字符串中的第一个唯一字符-力扣(LeetCode)给定一个字符串 s ,找到 它的第一个不重复的字符,并返回它的索引 。如果不存在,则返回 -1 。示例1:输入:s="leetcode"输出:0示例2:输入:s="loveleetcode"输出:2示例3:......
  • 基于JAVASE的题
    字符集合描述:每组数据输入一个字符串,字符串最大长度为100,且只包含字母,不可能为空串,区分大小写。每组数据一行,按字符串原有的字符顺序,输出字符集合,记重复出现并靠后的字母不输出。例如:输入:ABABc输出:ABc思路:每组输入,所以要进行while循环输入,写一个fun方法进行描写,首先实......