一、引言

手写数字识别是计算机视觉领域的一个重要课题,广泛应用于邮政编码识别、银行支票处理等领域。近年来,随着深度学习技术的快速发展,神经网络在图像识别任务中取得了显著的成果。本文将介绍如何使用PaddlePaddle框架构建一个全连接神经网络,实现手写数字识别。

二、PaddlePaddle简介

PaddlePaddle是百度开源的一款深度学习框架,具有易用、高效、可扩展等特点。它提供了丰富的API,支持多种深度学习模型,可以轻松实现全连接神经网络、卷积神经网络、循环神经网络等。

三、数据准备

本文使用的手写数字数据集为MNIST,它包含了0到9的手写数字图片,共有60000个训练样本和10000个测试样本。每个样本都是一个28x28的灰度图像。
首先,我们需要加载数据集并进行预处理:

import paddle
from paddle.vision.transforms import ToTensor
# 加载MNIST数据集
train_dataset = paddle.vision.datasets.MNIST(mode='train', transform=ToTensor())
test_dataset = paddle.vision.datasets.MNIST(mode='test', transform=ToTensor())
train_dataloader = paddle.io.DataLoader(train_dataset, batch_size=64, shuffle=True)
test_dataloader = paddle.io.DataLoader(test_dataset, batch_size=64, shuffle=True)

四、构建全连接神经网络

全连接神经网络(Fully Connected Neural Network,FCNN)是一种最简单的神经网络结构,每个神经元都与上一层的所有神经元相连。下面我们使用PaddlePaddle构建一个全连接神经网络:

import paddle.nn as nn
# 定义全连接神经网络
class FCNN(nn.Layer):
    def __init__(self):
        super(FCNN, self).__init__()
        self.fc1 = nn.Linear(in_features=28*28, out_features=500)
        self.fc2 = nn.Linear(in_features=500, out_features=10)
    def forward(self, x):
        x = paddle.flatten(x, 1)  # 将输入展平
        x = nn.functional.relu(self.fc1(x))
        x = self.fc2(x)
        return x
model = FCNN()

五、模型训练

接下来,我们对模型进行训练。首先定义损失函数和优化器:

# 定义损失函数
loss_fn = nn.CrossEntropyLoss()
# 定义优化器
optimizer = paddle.optimizer.Adam(parameters=model.parameters(), learning_rate=0.001)

然后进行训练:

# 训练模型
epochs = 5
for epoch in range(epochs):
    model.train()
    for batch_id, data in enumerate(train_dataloader):
        x_data, y_data = data
        y_pred = model(x_data)

        loss = loss_fn(y_pred, y_data)
        loss.backward()

        optimizer.step()
        optimizer.clear_grad()

        if batch_id % 100 == 0:
            print(f"Epoch [{epoch + 1}/{epochs}], Batch [{batch_id}], Loss: {loss.numpy()}")

六、模型评估

训练完成后,我们使用测试数据集对模型进行评估:

# 评估模型
model.eval()
total_correct = 0
total_samples = 0
for data in test_dataloader:
    x_data, y_data = data
    y_pred = model(x_data)
    correct = paddle.sum(paddle.cast(paddle.argmax(y_pred, axis=1) == y_data.squeeze(1), dtype='int32'))
    total_correct += correct.numpy()
    total_samples += x_data.shape[0]
print(f"模型准确率:{total_correct / total_samples}")

七、总体代码

import paddle
from paddle.vision.transforms import ToTensor
import paddle.nn as nn


# 定义全连接神经网络
class FCNN(nn.Layer):
    def __init__(self):
        super(FCNN, self).__init__()
        self.fc1 = nn.Linear(in_features=28 * 28, out_features=500)
        self.fc2 = nn.Linear(in_features=500, out_features=10)

    def forward(self, x):
        x = paddle.flatten(x, 1)  # 将输入展平
        x = nn.functional.relu(self.fc1(x))
        x = self.fc2(x)
        return x
if __name__ == '__main__':
    # 加载MNIST数据集
    train_dataset = paddle.vision.datasets.MNIST(mode='train', transform=ToTensor())
    test_dataset = paddle.vision.datasets.MNIST(mode='test', transform=ToTensor())

    train_dataloader = paddle.io.DataLoader(train_dataset, batch_size=64, shuffle=True)
    test_dataloader = paddle.io.DataLoader(test_dataset, batch_size=64, shuffle=True)

    model = FCNN()
    # 定义损失函数
    loss_fn = nn.CrossEntropyLoss()

    # 定义优化器
    optimizer = paddle.optimizer.Adam(parameters=model.parameters(), learning_rate=0.001)
    # 训练模型
    epochs = 5
    for epoch in range(epochs):
        model.train()
        for batch_id, data in enumerate(train_dataloader):
            x_data, y_data = data
            y_pred = model(x_data)

            loss = loss_fn(y_pred, y_data)
            loss.backward()

            optimizer.step()
            optimizer.clear_grad()

            if batch_id % 100 == 0:
                print(f"Epoch [{epoch + 1}/{epochs}], Batch [{batch_id}], Loss: {loss.numpy()}")
    # 评估模型
    model.eval()
    total_correct = 0
    total_samples = 0

    for data in test_dataloader:
        x_data, y_data = data
        y_pred = model(x_data)
        correct = paddle.sum(paddle.cast(paddle.argmax(y_pred, axis=1) == y_data.squeeze(1), dtype='int32'), axis=0)
        total_correct += correct.numpy()
        total_samples += x_data.shape[0]

    print(f"模型准确率:{total_correct / total_samples}")

    # 保存模型
    paddle.save(model.state_dict(), 'mnist_model.pdparams')


八、训练完成后的使用

# 导入paddle库,这是百度提供的深度学习框架
import paddle
# 导入将图像转换为张量的方法
from paddle.vision.transforms import ToTensor
# 导入神经网络模块,用于定义神经网络层
import paddle.nn as nn

# 从main模块导入我们定义的全连接神经网络类FCNN 也可以自己在本文文件中再定义一个class FCNN 要和之前训练的模型类一模一样。
from main import FCNN

# 实例化我们的模型FCNN
model = FCNN()  
# 设置模型进入评估模式,这意味着如Dropout或BatchNorm层会使用不同的行为
model.eval()
# 从磁盘加载模型的参数
model_state_dict = paddle.load('mnist_model.pdparams')
# 将加载的参数设置到模型中
model.set_state_dict(model_state_dict)

# 定义一个函数来处理单张图片并返回其预测结果
def predict(img_path):
    # 加载图片,这里的实现可能依赖于具体的实现细节
    img = paddle.vision.image_load(img_path) 
    # 将图片转换为张量类型,并进行归一化处理
    img = ToTensor()(img)
    # 增加一个维度,模拟batch_size=1的情况,因为模型训练时可能是批量输入
    img = paddle.unsqueeze(img, axis=0)
    # 使用模型进行预测
    pred = model(img)
    # 获取预测结果中的最大值索引,即为预测类别
    predicted_class = pred.argmax(axis=1).numpy()
    # 返回预测类别
    return predicted_class

# 调用predict函数来预测指定路径下的图片
ans = predict('dataset/test/0/0.jpg')
# 打印预测结果
print(ans)

自定义数据集的训练

当前代码下请保证你的数据集目录结构为:

dataset/
    train/
        0/
            0_0.jpg
            0_1.jpg
            ...
        1/
            1_0.jpg
            1_1.jpg
            ...
        ...
import os
from PIL import Image
import paddle
from paddle import nn
from paddle.vision.transforms import Compose, ToTensor

# 定义模型
class FCNN(nn.Layer):
    def __init__(self):
        super(FCNN, self).__init__()
        self.fc1 = nn.Linear(in_features=28 * 28, out_features=500)
        self.fc2 = nn.Linear(in_features=500, out_features=10)

    def forward(self, x):
        x = paddle.flatten(x, 1)  # 将输入展平
        x = nn.functional.relu(self.fc1(x))
        x = self.fc2(x)
        return x
# 自定义数据集
class CustomDataset(paddle.io.Dataset):
    def __init__(self, root_dir, transform=None):
        """
        初始化方法
        :param root_dir: 数据集根目录
        :param transform: 可选的变换函数
        """
        super().__init__()
        self.root_dir = root_dir
        self.transform = transform
        self.images, self.labels = self._load_data()

    def _load_data(self):
        images = []
        labels = []
        for label in os.listdir(self.root_dir):
            class_dir = os.path.join(self.root_dir, label)
            for image_name in os.listdir(class_dir):
                img_path = os.path.join(class_dir, image_name)
                images.append(img_path)
                labels.append(int(label))
        return images, labels

    def __getitem__(self, index):
        """
        获取一个样本
        :param index: 样本索引
        :return: (image, label) 元组
        """
        img_path, label = self.images[index], self.labels[index]
        image = Image.open(img_path).convert('L')  # 假设是灰度图
        if self.transform is not None:
            image = self.transform(image)
        return image, label

    def __len__(self):
        """
        返回数据集中样本的数量
        :return: 样本数量
        """
        return len(self.images)

if __name__ == '__main__':
    # 使用自定义数据集
    train_dataset = CustomDataset(root_dir='dataset/train', transform=Compose([ToTensor()]))

    train_dataloader = paddle.io.DataLoader(train_dataset, batch_size=64, shuffle=True)

    # 定义模型、损失函数、优化器等
    model = FCNN()
    loss_fn = nn.CrossEntropyLoss()
    optimizer = paddle.optimizer.Adam(parameters=model.parameters(), learning_rate=0.001)

    # 训练模型
    epochs = 5
    for epoch in range(epochs):
        model.train()
        for batch_id, (x_data, y_data) in enumerate(train_dataloader):
            y_pred = model(x_data)
            loss = loss_fn(y_pred, y_data)
            loss.backward()
            optimizer.step()
            optimizer.clear_grad()
            if batch_id % 100 == 0:
                print(f"Epoch [{epoch + 1}/{epochs}], Batch [{batch_id}], Loss: {loss.numpy()}")

    # 保存模型
    paddle.save(model.state_dict(), 'mnist_model.pdparams')
Logo

魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。

更多推荐