当前位置: 首页 > article >正文

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

1.卷积神经网络

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

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

核心组件:

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

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

特点:

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

 

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 结果与结果分析

44fed5a22e554bd9ada5ecc5a7395444.png

(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 结果与结果分析

77e0b9e3ef3a496ab40b78f9173ecf21.png

(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 结果与结果分析

cf8f71ba507241a3b87550913f22777c.png

(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 结果与分析

b97b5ace633143bbaebceee46f766322.png

(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 结果(此结果在下一段代码打印)与分析:

910732015f9242e1bb88de4eb3ef8e8f.png

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.预测分析:

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

8e45a5dbadea4546a42e4f7874fd9796.png

  • 对于输入图片 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}")
    

     

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

 


http://www.kler.cn/a/371809.html

相关文章:

  • 如何实现PHP的安全最大化
  • EXPLAIN 针对性优化 SQL 查询
  • C语言笔记(指针题目)例题+图解
  • 关于InternVL2的模型训练二(如何训练目标定位模型)
  • echarts5.2.2实现 水球图表
  • MobileNetv2网络详解
  • 基于java的山区环境监督管理系统(源码+定制+开发)环境数据可视化、环境数据监测、 环境保护管理 、污染防治监测系统 大数据分析
  • 【C++】string 类深度解析:探秘字符串操作的核心
  • python如何完成金融领域的数据分析,思路以及常见的做法是什么?
  • 【Django】创建项目、启动及app过程及遇到的问题和解决方案
  • Firefox和Chrome谁的插件生态系统更完善
  • 8年经验之谈 —— 如何使用自动化工具编写测试用例?
  • Java基础(4)——构建字符串(干货)
  • 结合Intel RealSense深度相机和OpenCV来实现语义SLAM系统
  • 开源AI助力医疗革新:OCR系统与知识图谱构建
  • 大厂物联网(IoT)高频面试题及参考答案
  • HTML入门教程7:HTML样式
  • Go 读取xls文件 (shakinm/xlsReader/xls)
  • 消息队列-Rabbitmq(消息发送,消息接收)
  • 爬虫设计思路
  • ‘cmd‘ 不是内部或外部命令,也不是可运行的程序或批处理文件。
  • 【SQLite】改善默认输出格式不直观难以阅读问题:通过修改输出设置提升数据可读性
  • 【http作业】
  • Chrome异步编程
  • 深度学习数学基础之链式法则
  • 星巴克们需要找回节奏