1.背景

随着人工智能技术的不断发展,各种以前需要通过人工来进行识别判断的问题都能够让人工智能来解决。手写的中文数字识别也是其中一个。本次课程设计将使用给定的中文手写数据集来训练模型。原始数据为jpg格式的图片,已将其序列化。总共15000张中文手写数字汉字灰度图片,以numpy.ndarray数据类型保存每张图片尺寸为64×64。包括“零一二三四五六七八九十百千万亿”这些汉字。

2.解决思路和具体方案

采用AlexNet神经网络模型对数据集进行训练。首先需要将自己的数据集导入程序,按一定比例划分训练集和测试集,在定义好网络之后将训练集输入网络中,通过前向传播对数据进行卷积输出。通过SGD梯度下降算法和后向传播,对模型进行训练。将训练完的模型配置保存为文件。当测试的时候,读取训练好的网络配置文件加载进网络,对测试集进行测试,将准确率输出。

3.实验步骤

实现步骤为调用main函数,具体为定义是否使用GPU训练、创建网络、将训练硬件加载进神经网络、加载数据集,并转化为能够直接使用的dataloader形式、进行训练,将训练参数传入、进行测试,并打印结果。

完整代码位于github仓库:代码

更多关于AlexNet的介绍可见 AlexNet介绍

4.程序介绍

程序结构:

image-20240222205753842

流程:

if __name__ == '__main__':
    # 定义是否使用GPU训练
    device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
    # 创建网络
    net = AlexNet()
    # 将训练硬件加载进神经网络
    net.to(device)
    # 加载数据集,并转化为能够直接使用的dataloader形式
    train_dataloader, test_dataloader = load_data(BATCH_SIZE, TEST_SIZE)
    # 进行训练,将训练参数传入
    train(net, device, LR, MOMENTUM, EPOCH, train_dataloader)
    # 进行测试,并打印结果
    test(device, test_dataloader)

AlexNet 配置

image-20240222210836934

self.fc1 = nn.Sequential(
    nn.Dropout(),  # 默认0.5的概率归零,防止过拟合
    nn.Linear(256 * 7 * 7, 1024),  # 全连接1
    nn.ReLU(inplace=True)
)
self.fc2 = nn.Sequential(
    nn.Dropout(),  # 默认0.5的概率归零,防止过拟合
    nn.Linear(1024, 512),  # 全连接2
    nn.ReLU(inplace=True)
)
self.fc3 = nn.Sequential(
    nn.Linear(512, 15)  # 全连接3
)

难点:

数据的读入困扰了我们团队很久,包括维度和类型转化。我们重写了Dataset类,让其能够正确读入我们的数据,生成Dataloader。

class MyDataset(Dataset):
    """
    将传入的数据集,转成Dataset类,方面后续转入Dataloader类
    注意定义时传入的images,targets必须为numpy数组
    """

    # 使用__init__()初始化一些需要传入的参数及数据集的调用
    def __init__(self, images, targets):
        self.len = len(images)
        self.image = torch.FloatTensor(images)  # 转换成tensor类型
        self.target = torch.FloatTensor(targets)  # 转换成tensor类型

    def __getitem__(self, index):
        return self.image[index], self.target[index]

    def __len__(self):
        return self.len

经过尝试发现不能直接训练,因此针对数字进行转化

def load_data(batch_size, test_size):
    with open("chn_mnist", "rb") as f:
        data = pickle.load(f)
    images = data["images"]
    images = np.stack((images,) * 1, axis=1)  # 加一维
    target = data["targets"].astype(np.int32)  # 转换类型
    targets = []
    for i in target:  # 直接训练会造成数组越界的报错,因此进行转换
        if i <= 10:
            i = i
        elif i == 100:
            i = 11
        elif i == 1000:
            i = 12
        elif i == 10000:
            i = 13
        elif i == 100000000:
            i = 14
        targets.append(i)
    targets = np.array(targets)

# 省略部分代码

超参

# main.py

# 超参数设置
EPOCH = 10  # 遍历数据集次数
BATCH_SIZE = 128  # 批处理尺寸
LR = 0.001  # 学习率
MOMENTUM = 0.9  # 动量
TEST_SIZE = 0.3  # 测试集在总数据集中的比例
# train.py

# 损失函数:这里用交叉熵
criterion = torch.nn.CrossEntropyLoss()
# 优化器 这里用SGD
optimizer = optim.SGD(net.parameters(), lr=lr, momentum=momentum)

image-20240222210628159

image-20240222210644408

原数据集为ImageNet,120万张图片,训练了90轮。

本次数据集为15000张图片。因此学习率调整为0.01,训练10轮。

5.结果与分析

image-20240222204728653

image-20240222204741749

从两张图可以看出,该模型用gpu训练十轮后损失函数值维持在较低的水平,同时对模型进行测试,准确率达到96.7%。准确率较高。

6.总结

通过这次的实验,让我对于整个深度学习神经网络有了更深刻的理解,之前都是学习书本上的知识,或者在平台上完成一部分代码,但是如此完整的完成一个人工智能项目还是第一次。在编写代码的过程中遇到了很多很多的报错、疑惑,通过与组员一起讨论、debug,最后成功跑通代码的喜悦是这段时间的辛苦无法超越的。通过对细节的不断钻研,我之前所遗漏的许多知识点也终于弄懂,让我对人工智能这个学科更有热情。

最后修改:2024 年 02 月 22 日
如果觉得我的文章对你有用,请随意赞赏