模式识别编程实践3:基于神经网络的手写字符识别

时间:2024-10-02 11:48:48

????欢迎莅临我的个人主页????????这里是我专注于深度学习领域、用心分享知识精粹与智慧火花的独特角落!????

????如果大家喜欢文章,欢迎:关注????+点赞????????+评论✍????+收藏????,如有错误敬请指正!????

????“请不要相信胜利就像山坡上的蒲公英一样唾手可得,但是请相信生活中总有美好值得我们全力以赴,哪怕粉身碎骨!”????

前言

????模式识别导论课程编程实践作业????

目录

任务说明

数据文件

​编辑程序实现思路

数据处理

自定义神经网络

1️⃣整体网络架构

2️⃣特征提取部分

3️⃣全局平均池化

4️⃣分类器部分

5️⃣参数量

数据划分及打包

模型训练

模型测试 

性能评估结果分析


任务说明

使用MNIST手写数据集,设计神经网络结构以实现字符识别,网络结构可采用多层感知器或卷积神经网络

数据文件

????下载地址:Minst数据集

????文件内容


程序实现思路

该项目的完整资源我已经上传GitHub,有需要的可自取

GitHub地址:https://github.com/Rostiute-W/Minst_recogntion-Pytorch/tree/main

????项目目录

数据处理

由于Minst数据集是以二进制的形式存储的,为了便于后续处理,我将其由二进制转为了PNG图片形式,并分为traintest文件夹(train含60000张图像,test含1000张图像

# convert2img.py

import os
import idx2numpy
from PIL import Image
from tqdm import tqdm

# --------------------------------------------------------------#
#                      将IDX文件转换为图片
# --------------------------------------------------------------#

# -------------------------设置IDX文件的路径---------------------#
train_images_path = 'test3/dataset/MNIST/train-images.idx3-ubyte'
train_labels_path = 'test3/dataset/MNIST/train-labels.idx1-ubyte'
test_images_path = 'test3/dataset/MNIST/t10k-images.idx3-ubyte'
test_labels_path = 'test3/dataset/MNIST/t10k-labels.idx1-ubyte'
# -------------------------保存图片根目录------------------------#
output_dir = 'test3/dataset'
# -------------------------检查文件是否存在----------------------#
for path in [train_images_path, train_labels_path, test_images_path, test_labels_path]:
    if not os.path.exists(path):
        raise FileNotFoundError(f"文件未找到: {path}")
os.makedirs(output_dir, exist_ok=True)
# ---------------------------保存为PNG格式----------------------#
def save_images(images, labels, dataset_type='train'):
    for label in range(10):
        label_dir = os.path.join(output_dir, dataset_type, str(label))
        os.makedirs(label_dir, exist_ok=True)

    num_samples = images.shape[0]
    print(f"\033[31m保存 {dataset_type} 集中的 {num_samples} 张图片\033[0m")
    for idx in tqdm(range(num_samples)):
        image = images[idx]
        label = labels[idx]
        # -----------------转换为PIL图像-------------------#
        img = Image.fromarray(image).convert('L')
        img_name = f"{idx}.png"
        img_path = os.path.join(output_dir, dataset_type, str(label), img_name)
        img.save(img_path)


if __name__ == '__main__':
    # ---------------------------加载训练集图像和标签----------------------------#
    train_images = idx2numpy.convert_from_file(train_images_path)
    train_labels = idx2numpy.convert_from_file(train_labels_path)
    # ---------------------------加载测试集图像和标签----------------------------#
    test_images = idx2numpy.convert_from_file(test_images_path)
    test_labels = idx2numpy.convert_from_file(test_labels_path)
    # -------------------------------保存训练集图像------------------------------#
    save_images(train_images, train_labels, 'train')
    # -------------------------------保存测试集图像------------------------------#
    save_images(test_images, test_labels, 'test')

    print("\033[31mMNIST数据集已成功转换为PNG格式图片\033[0m")

????目录结构

自定义神经网络

注:我使用的深度学习框架是Pytorch

# -------------------------------------------------------------#
#                      自定义网络结构
# -------------------------------------------------------------#
class MinstNet(nn.Module):
    def __init__(self, num_classes=10):
        super(MinstNet, self).__init__()
        self.features = nn.Sequential(
            nn.Conv2d(3, 32, kernel_size=3, stride=1, padding=1),
            nn.BatchNorm2d(32),
            nn.ReLU(inplace=True),
            nn.MaxPool2d(kernel_size=2, stride=2),
            nn.Conv2d(32, 64, kernel_size=3, stride=1, padding=1),
            nn.BatchNorm2d(64),
            nn.ReLU(inplace=True),
            nn.MaxPool2d(kernel_size=2, stride=2),
            nn.Conv2d(64, 128, kernel_size=3, stride=1, padding=1),
            nn.BatchNorm2d(128),
            nn.ReLU(inplace=True),
        )
        self.global_pool = nn.AdaptiveAvgPool2d((1, 1))
        self.classifier = nn.Sequential(
            nn.Linear(128, 128),
            nn.ReLU(inplace=True),
            nn.Dropout(0.5),
            nn.Linear(128, num_classes),
        )

    def forward(self, x):
        x = self.features(x)
        x = self.global_pool(x)
        x = torch.flatten(x, start_dim=1)
        x = self.classifier(x)
        return x

1️⃣整体网络架构

        这个神经网络是一个用于图像分类任务的卷积神经网络(CNN),主要由特征提取部分全局平均池化层分类器部分组成。

2️⃣特征提取部分

由一系列卷积层、批量归一化层和激活函数层组成。

  1. 首先,使用三个卷积模块进行特征提取。每个卷积模块包含一个卷积层、一个批量归一化层和一个 ReLU 激活函数。这种组合有助于加速网络的训练收敛速度,并提高模型的泛化能力。
  2. 卷积层的参数设置如下:
    1. 第一个卷积层将输入的三通道图像转换为 32 通道的特征图,卷积核大小为 3x3,步长为 1,填充为 1,这可以保持输入图像的尺寸不变。
    2. 第二个卷积层将 32 通道的特征图进一步转换为 64 通道,参数设置与第一个卷积层类似。
    3. 第三个卷积层将 64 通道的特征图转换为 128 通道。
  3. 每个卷积模块之后跟着一个最大池化层池化核大小为 2x2,步长为 2,用于降低特征图的空间尺寸,减少计算量和参数数量,同时提取更抽象的特征

3️⃣全局平均池化

        使用自适应平均池化层将特征图的空间尺寸压缩为 1x1,即每个通道的特征被压缩为一个值。这一步的作用是不同大小的输入图像转换为固定长度的特征向量,方便后续的分类操作。

4️⃣分类器部分

两个全连接层、一个 Dropout 层和最后的输出层组成。

  • 第一个全连接层将全局平均池化后的 128 维特征向量映射到 128 维的隐藏层。
  • 接着使用 ReLU 激活函数增加模型的非线性表达能力
  • Dropout 层以 0.5 的概率随机丢弃神经元的输出,有助于防止过拟合
  • 最后一个全连接层将 128 维的隐藏层映射到指定数量的类别(由参数num_classes决定,默认为 10),输出每个类别的预测概率。

5️⃣参数量

可以看到哈,这个网络其实是非常简单的,参数量也相对少很多(只有11万左右),但是对于Minst数据集这种分类任务来说还是绰绰有余的

数据划分及打包

        这个部分我使用的是B站博主霹雳吧啦Wz的代码,并对其进行了部分修改以适应本次的分类任务。最终可以得到类别标签的txt、测试集的txt文件最终测试的文本数据集),我的训练集又划分为了训练集和验证集,二者的比例是8:2,也就是最终进行训练的图像有48004张验证的图像有11996张测试的图像有10000张。

代码我就不放了,具体可以看utils.pydataloader.py

模型训练

训练流程没啥好说的,就是正常的训练即可。不懂的看这儿!

参数需要注意的是,本次训练采用了AMP(自动混合精度)优化器是Adam损失函数是交叉熵,我没有设置按照轮数的模型保存,直接存储的就是性能最佳的模型,同时训练过程中的损失和准确率曲线也进行了绘制(含训练和验证),这些均存储在logs文件夹

具体可以看train.pycallbacks.py

模型测试 

        这个部分参考了B站博主Bubbliiiing的代码,然后我自己重新写了一遍,现在可以通过输入文本数据的txt文件来测试模型在整个数据集上的性能,并且添加了一些其它的可视化效果,如混淆矩阵、Recall和Precision条形图

具体可以看infer.pyeval.py

性能评估结果分析

这个网络我只训练了50轮,但是效果还是挺不错的

????损失曲线和准确率曲线

可以明显看到,模型在20轮左右的时候就开始收敛了,并且根据得到的文本数据来看,整个过程验证损失最低达到了0.1073,准确率达到了96.61%,数据已经相当可观了

????混淆矩阵

大部分的数据还是能够正确分类的,少部分数据出现误识别,其中5的识别错误率最高

????测试集指标

可以看到哈,无论是准确率、精确率、召回率还是F1分数,指标均达到了97%以上,并且对于单张图片的预测(手动测试)准确率非常高,可以说该模型在Minst数据分类任务上表现得十分出色

????总结

        总的来说,此次分类任务完成的效果还是比较不错的,对于这样一个简单的项目十分推荐用于上手深度学习,我在GitHub上传的每一个代码均写了非常详尽的注释,即便是小白也能轻松上手噢!至此,综合编程实践项目算是全部完成了,非常感谢您能够看到这里,有任何疑问可以随时私信或评论留言噢!????