[神经网络]--手写数字识别

1、神经网络结构

class NetWork(nn.Module):
    def __init__(self):
        super(NetWork, self).__init__()
        # 第一层卷积
        self.conv1 = nn.Conv2d(1, 20, 5, 1)  # 输入通道1,输出通道20,卷积核大小5,步长1
        self.conv2 = nn.Conv2d(20, 50, 5, 1)  # 输入通道20,输出通道50,卷积核大小5,步长1
        # 池化层
        self.pool = nn.MaxPool2d(2, 2)  # 池化核大小2,步长2
        self.fc1 = nn.Linear(4 * 4 * 50, 500)  # 假设图像被压缩到4x4,每个通道50个特征图
        self.fc2 = nn.Linear(500, 10)  # 输出层,10个类别

    def forward(self, x):
        x = self.pool(F.relu(self.conv1(x)))
        x = self.pool(F.relu(self.conv2(x)))
        x = x.view(-1, 4 * 4 * 50)  # 展平
        x = F.relu(self.fc1(x))
        x = self.fc2(x)
        return x

1.1 卷积层 (Conv2d)

  • 第一层卷积 (self.conv1):
    • 输入: 图像数据,假设是灰度图,因此输入通道数为1。
    • 输出: 20个特征图(或称为卷积后的图像),每个特征图是通过一个5x5的卷积核在输入图像上滑动并计算得到的。这一步骤旨在提取图像中的低级特征,如边缘、角点等。
    • 参数: 卷积核大小为5x5,步长为1,意味着卷积核在输入图像上每次移动1个像素。
  • 第二层卷积 (self.conv2):
    • 输入: 第一层卷积输出的20个特征图。
    • 输出: 50个特征图,每个特征图通过另一个5x5的卷积核在第一层输出的特征图上滑动并计算得到。这一步进一步提取更高级别的特征。
    • 参数: 同样使用5x5的卷积核,步长为1。

1.2. 池化层 (MaxPool2d)

  • 作用: 池化层(在这里是最大池化)用于减少特征图的空间尺寸(即高度和宽度),从而减少计算量和参数数量,同时保留重要特征。
  • 实现: 通过一个2x2的窗口在特征图上滑动,每次取窗口内的最大值作为输出。这有助于减少特征图的维度,同时保留最重要的特征。
  • 在网络中的应用: 在两层卷积之后各应用了一个池化层,以减小特征图的尺寸。

1.3. 全连接层 (Linear)

  • 第一层全连接 (self.fc1):
    • 输入: 池化层输出的特征图被展平成一维向量。假设经过两次卷积和池化后,特征图的尺寸为4x4,且有50个通道,因此输入向量的长度为4450。
    • 输出: 500个神经元的输出,这一层通常用于学习特征之间的非线性组合。
    • 作用: 将学到的“分布式特征表示”映射到样本的标记空间。
  • 第二层全连接 (self.fc2):
    • 输入: 第一层全连接的输出,即500个神经元的输出。
    • 输出: 10个神经元的输出,对应于10个类别的得分或概率(在分类任务中)。
    • 作用: 最终的分类层,输出每个类别的预测得分或概率。

代码上可以用nn.Sequential进行优化

2、数据加载以及数据预处理

2.1 数据转换流程(transform

  • transforms.ToTensor():这个转换将PIL Image或者NumPy ndarray(HxWxC)转换成形状为[C, H, W]的FloatTensor,并且值会被缩放到[0.0, 1.0]的范围内。这里的C、H、W分别代表颜色通道数、高度、宽度。对于MNIST数据集(灰度图像),颜色通道数C为1。

  • transforms.Normalize((0.5,), (0.5,)):这个转换会对Tensor进行标准化处理,使其均值为0,标准差为1。这里的(0.5,)(0.5,)分别是均值和标准差。对于灰度图像(范围[0, 1]),将它们减去0.5然后除以0.5(实际上这相当于将范围从[0, 1]转换到[-1, 1]),但这种处理方式不严格符合传统的均值为0、标准差为1的标准化定义,因为这里的均值和标准差是人为设定的,而不是根据数据集的统计特性计算得出的。

2.2 加载数据集

  • 使用datasets.MNIST类来加载MNIST数据集。这个类会自动从互联网下载数据集(如果本地没有且download=True),并存储到指定的根目录(这里是'./data')。

  • 通过设置train=True来加载训练集,train=False来加载测试集。

  • transform参数用于指定数据预处理流程。

2.3 打印数据集大小

  • 使用len(train_dataset)len(test_dataset)来获取训练集和测试集的大小(即包含的图片数量),并打印出来。

2.4 输出结果

输出结果将显示训练集和测试集的大小,对于MNIST数据集,训练集通常包含60000张图片,测试集包含10000张图片。因此,输出大致如下:

train dataset size 60000  
test dataset size 10000

3、数据加载

通过DataLoader为训练集(train_dataset)和测试集(test_dataset)创建了数据加载器(train_dataloadertest_dataloader),并指定了每个批次(batch)的大小为64。

    # 利用dataloader加载数据
    train_dataloader = DataLoader(train_dataset, batch_size=64)
    test_dataloader = DataLoader(test_dataset, batch_size=64)

4、训练前置准备

    # 实例网络模型
    mnist_model = NetWork()

    # 定义损失函数
    loss_f = nn.CrossEntropyLoss()

    # 优化器
    learning_rate = 1e-2
    optimizer = torch.optim.SGD(mnist_model.parameters(), lr=learning_rate)

    # 设置训练网络参数
    # 记录训练测试
    total_train_step = 0
    total_test_step = 0
    # 训练的轮数
    echo = 10

    writer = SummaryWriter("./logs")

初始化 mnist_model 的网络模型,定义了损失函数 loss_f 为交叉熵损失(适合多分类问题),配置了优化器为随机梯度下降(SGD)并设置了学习率。此外,设置训练的一些基础参数,包括训练步数、测试步数的计数器,训练的轮数(这里通过变量 echo 表示,但通常我们使用 epochs(爱剖析龙) 或类似的命名),以及创建了一个 SummaryWriter 实例 writer 用于记录训练过程中的各种指标(如损失、准确率等),这些指标可以通过TensorBoard进行可视化。

TensorBoard的使用就是训练完或者训练中 新开终端执行命令

➜  ~ tensorboard --logdir=./logs --port=6007

5、训练步骤

设置了训练步骤的开始,包括将模型设置为训练模式(mnist_model.train()),这是非常重要的,因为某些层(如DropoutBatchNorm)在训练和评估模式下会有不同的行为。接下来,遍历训练数据加载器(train_dataloader),对每批数据进行前向传播、计算损失、反向传播和优化,并且还包含了记录训练步数和损失的逻辑。

        # 训练步骤开始
        mnist_model.train()  # 针对某些特定的层生效
        for data in train_dataloader:
            imgs, targets = data
            print(imgs.shape)
            outputs = mnist_model(imgs)
            loss = loss_f(outputs, targets)

            optimizer.zero_grad()
            loss.backward()
            optimizer.step()
            total_train_step += 1
            if total_train_step % 100 == 0:
                print("训练次数:{},loss:{}".format(total_train_step, loss))
                writer.add_scalar("train_loss", loss.item(), total_train_step)

6、评估

        # 测试步骤
        mnist_model.eval()  # 针对某些特定层生效
        total_test_loss = 0
        total_accuracy = 0
        with torch.no_grad():
            for data in test_dataloader:
                imgs, targets = data
                outputs = mnist_model(imgs)
                loss = loss_f(outputs, targets)
                total_test_loss += loss
                accuracy = (outputs.argmax(1) == targets).sum()
                total_accuracy += accuracy

        print("整体测试集上的loss:{}".format(total_test_loss))
        print("整体测试集上的正确率:{}".format(total_accuracy / test_dataset_size))
        writer.add_scalar("test_loss", total_test_loss, total_test_step)
        writer.add_scalar("test_accuracy", total_accuracy / test_dataset_size, total_test_step)
        total_test_step += 1

设置了模型为评估模式(mnist_model.eval()),在测试时,我们通常不需要计算梯度,因此使用torch.no_grad()上下文管理器来禁用梯度计算是一个好习惯,这可以帮助节省内存和计算资源。

7、模型保存

        torch.save(mnist_model, "model_{}.pth".format(i))
        print("模型已保存!")

保存模型 方便后面在c++环境加载调用  这个后面在写

8、完整代码

# https://www.bilibili.com/video/BV134421U77t/?spm_id_from=333.337.search-card.all.click&vd_source=c49c4312f98c9a2bcaf5cb1b07324412

import torch
from torch import nn
import torch.nn.functional as F
from torchvision import transforms
from torchvision import datasets
from torch.utils.data import DataLoader
from torch.utils.tensorboard import SummaryWriter


class NetWork(nn.Module):
    def __init__(self):
        super(NetWork, self).__init__()
        # 第一层卷积
        self.conv1 = nn.Conv2d(1, 20, 5, 1)  # 输入通道1,输出通道20,卷积核大小5,步长1
        self.conv2 = nn.Conv2d(20, 50, 5, 1)  # 输入通道20,输出通道50,卷积核大小5,步长1
        # 池化层
        self.pool = nn.MaxPool2d(2, 2)  # 池化核大小2,步长2
        self.fc1 = nn.Linear(4 * 4 * 50, 500)  # 假设图像被压缩到4x4,每个通道50个特征图
        self.fc2 = nn.Linear(500, 10)  # 输出层,10个类别

    def forward(self, x):
        x = self.pool(F.relu(self.conv1(x)))
        x = self.pool(F.relu(self.conv2(x)))
        x = x.view(-1, 4 * 4 * 50)  # 展平
        x = F.relu(self.fc1(x))
        x = self.fc2(x)
        return x


if __name__ == '__main__':
    transform = transforms.Compose([
        transforms.ToTensor(),
        transforms.Normalize((0.5,), (0.5,))
    ])
    # 下载并加载训练集
    train_dataset = datasets.MNIST(root='./data', train=True, download=True, transform=transform)
    # 下载并加载测试集
    test_dataset = datasets.MNIST(root='./data', train=False, download=True, transform=transform)

    train_dataset_size = len(train_dataset)
    test_dataset_size = len(test_dataset)
    print("train dataset size {}".format(train_dataset_size))
    print("test dataset size {}".format(test_dataset_size))

    # 利用dataloader加载数据
    train_dataloader = DataLoader(train_dataset, batch_size=64)
    test_dataloader = DataLoader(test_dataset, batch_size=64)

    # 实例网络模型
    mnist_model = NetWork()

    # 定义损失函数
    loss_f = nn.CrossEntropyLoss()

    # 优化器
    learning_rate = 1e-2
    optimizer = torch.optim.SGD(mnist_model.parameters(), lr=learning_rate)

    # 设置训练网络参数
    # 记录训练测试
    total_train_step = 0
    total_test_step = 0
    # 训练的轮数
    echo = 10

    writer = SummaryWriter("./logs")

    for i in range(echo):
        print("-------- 第 {} 轮训练开始 --------".format(i + 1))

        # 训练步骤开始
        mnist_model.train()  # 针对某些特定的层生效
        for data in train_dataloader:
            imgs, targets = data
            print(imgs.shape)
            outputs = mnist_model(imgs)
            loss = loss_f(outputs, targets)

            optimizer.zero_grad()
            loss.backward()
            optimizer.step()
            total_train_step += 1
            if total_train_step % 100 == 0:
                print("训练次数:{},loss:{}".format(total_train_step, loss))
                writer.add_scalar("train_loss", loss.item(), total_train_step)
        # 测试步骤
        mnist_model.eval()  # 针对某些特定层生效
        total_test_loss = 0
        total_accuracy = 0
        with torch.no_grad():
            for data in test_dataloader:
                imgs, targets = data
                outputs = mnist_model(imgs)
                loss = loss_f(outputs, targets)
                total_test_loss += loss
                accuracy = (outputs.argmax(1) == targets).sum()
                total_accuracy += accuracy

        print("整体测试集上的loss:{}".format(total_test_loss))
        print("整体测试集上的正确率:{}".format(total_accuracy / test_dataset_size))
        writer.add_scalar("test_loss", total_test_loss, total_test_step)
        writer.add_scalar("test_accuracy", total_accuracy / test_dataset_size, total_test_step)
        total_test_step += 1

        torch.save(mnist_model, "model_{}.pth".format(i))
        print("模型已保存!")
    writer.close()

卷积神经网络(Convolutional Neural Networks,简称CNN)是一种在图像处理中非常重要的神经网络结构。它可以用于手写体数字识别等任务。下面是一个使用CNN进行手写体数字识别的示例: ```python import tensorflow as tf from tensorflow.keras.datasets import mnist # 加载MNIST数据集 (x_train, y_train), (x_test, y_test) = mnist.load_data() # 数据预处理 x_train = x_train.reshape(-1, 28, 28, 1) / 255.0 x_test = x_test.reshape(-1, 28, 28, 1) / 255.0 y_train = tf.keras.utils.to_categorical(y_train, num_classes=10) y_test = tf.keras.utils.to_categorical(y_test, num_classes=10) # 构建卷积神经网络模型 model = tf.keras.models.Sequential([ tf.keras.layers.Conv2D(32, (3, 3), activation='relu', input_shape=(28, 28, 1)), tf.keras.layers.MaxPooling2D((2, 2)), tf.keras.layers.Flatten(), tf.keras.layers.Dense(128, activation='relu'), tf.keras.layers.Dense(10, activation='softmax') ]) # 编译模型 model.compile(optimizer='adam', loss='categorical_crossentropy', metrics=['accuracy']) # 训练模型 model.fit(x_train, y_train, epochs=5, batch_size=32, validation_data=(x_test, y_test)) # 评估模型 test_loss, test_acc = model.evaluate(x_test, y_test) print('Test accuracy:', test_acc) ``` 这个示例使用了TensorFlow和Keras库来构建和训练一个卷积神经网络模型,用于手写体数字识别任务。模型首先加载了MNIST数据集,并进行了数据预处理。然后,通过添加卷积层、池化层、全连接层和输出层来构建了一个简单的卷积神经网络模型。最后,使用训练集对模型进行训练,并使用测试集评估模型的准确性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值