自学内容网 自学内容网

基于前馈神经网络模型和卷积神经网络的MINIST数据集训练

目录

前馈神经网络FNN模型

卷积神经网络CNN模型


前馈神经网络FNN模型

'''
@author: lxy
@function:   model--mnist
@date : 2024/10/25
'''

# 导入必要的库
import torch
import torch.nn as nn
import torchvision.datasets as dsets
import torchvision.transforms as transforms
from torch.nn.init import normal_,constant_
from torch.autograd import Variable
# 设置超参数
input_size = 784  # 输入层大小,因为MNIST数据集的图片是28x28大小的
hidden_size1 = 128
hidden_size2 = 64
num_class = 10  # 类别数,MNIST数据集有10个数字类别
num_epochs = 10  # 训练的轮数
batch_size = 50  # 每批次的样本数量
learning_rate = 0.01  # 学习率

# 准备数据集
# MNIST数据集的加载,包括训练集和测试集
train_dataset = dsets.MNIST(root='./data', train=True,
                            transform=transforms.ToTensor(),
                            download=True)
test_dataset = dsets.MNIST(root='./data', train=False,
                           transform=transforms.ToTensor())

# 将数据集封装成DataLoader,方便批量加载和打乱数据
train_loader = torch.utils.data.DataLoader(dataset=train_dataset,
                                           batch_size=batch_size,
                                           shuffle=True)
test_loader = torch.utils.data.DataLoader(dataset=test_dataset,
                                          batch_size=batch_size,
                                          shuffle=False)

class FNN(nn.Module):
    def __init__(self, input_size, hidden_size1,hidden_size2,num_classes):
        super(FNN, self).__init__()
        self.fc1 = nn.Linear(input_size, hidden_size1)  # 隐藏层
        normal_(self.fc1.weight, mean=0, std=0.01)
        constant_(self.fc1.bias, val=1.0)
        self.fc2 = nn.Linear(hidden_size1, hidden_size2)  # 隐藏层
        normal_(self.fc2.weight, mean=0, std=0.01)
        constant_(self.fc2.bias, val=1.0)
        self.fc3 = nn.Linear(hidden_size2, num_classes)  # 输出层
        normal_(self.fc3.weight, mean=0, std=0.01)
        constant_(self.fc3.bias, val=1.0)

    def forward(self, x):
        x = torch.relu(self.fc1(x))  # 使用 ReLU 激活函数
        x = torch.relu(self.fc2(x))
        out = self.fc3(x)
        return out

# 实例化模型
model = FNN(input_size, hidden_size1,hidden_size2,num_class)
# 定义损失函数和优化器
criterion = nn.CrossEntropyLoss()
optimizer = torch.optim.SGD(model.parameters(), lr=learning_rate)

# 训练模型
for epoch in range(num_epochs):  # 遍历所有的epoch
    for i, (images, labels) in enumerate(train_loader):  # 遍历每个批次的数据
        images = Variable(images.view(-1, 28 * 28))  # 将图片展平
        labels = Variable(labels)
        optimizer.zero_grad()  # 梯度清零
        outputs = model(images)  # 前向传播
        loss = criterion(outputs, labels)  # 计算损失
        loss.backward()  # 反向传播
        optimizer.step()  # 更新权重
        if (i + 1) % 100 == 0:
            print('Epoch: [%d/%d], Step: [%d/%d], Loss: %.4f'
                  % (epoch + 1, num_epochs, i + 1, len(train_loader), loss.item()))

print('模型训练完成')

# 保存模型权重
torch.save(model.state_dict(), 'model.pkl')
print('模型保存完成')

# 测试模型
correct = 0
total = 0
for images, labels in test_loader:
    images = Variable(images.view(-1, 28 * 28))
    outputs = model(images)
    _, predicted = torch.max(outputs.data, 1)
    total += labels.size(0)
    correct += (predicted == labels).sum().item()

# 打印测试精度
print(f'模型的精度为:{100 * correct / total:.2f}%')
print('测试完成')

运行结果:


Epoch: [1/10], Step: [100/1200], Loss: 2.3350
Epoch: [1/10], Step: [200/1200], Loss: 2.3100
Epoch: [1/10], Step: [300/1200], Loss: 2.2857
Epoch: [1/10], Step: [400/1200], Loss: 2.2872
Epoch: [1/10], Step: [500/1200], Loss: 2.3032
Epoch: [1/10], Step: [600/1200], Loss: 2.3065
Epoch: [1/10], Step: [700/1200], Loss: 2.2811
Epoch: [1/10], Step: [800/1200], Loss: 2.3087
Epoch: [1/10], Step: [900/1200], Loss: 2.2884
Epoch: [1/10], Step: [1000/1200], Loss: 2.3137
Epoch: [1/10], Step: [1100/1200], Loss: 2.2961
Epoch: [1/10], Step: [1200/1200], Loss: 2.3011
Epoch: [2/10], Step: [100/1200], Loss: 2.2949
Epoch: [2/10], Step: [200/1200], Loss: 2.3026
Epoch: [2/10], Step: [300/1200], Loss: 2.2837
Epoch: [2/10], Step: [400/1200], Loss: 2.2555
Epoch: [2/10], Step: [500/1200], Loss: 2.2819
Epoch: [2/10], Step: [600/1200], Loss: 2.2609
Epoch: [2/10], Step: [700/1200], Loss: 2.2413
Epoch: [2/10], Step: [800/1200], Loss: 2.1767
Epoch: [2/10], Step: [900/1200], Loss: 1.9502
Epoch: [2/10], Step: [1000/1200], Loss: 1.9029
Epoch: [2/10], Step: [1100/1200], Loss: 1.5652
Epoch: [2/10], Step: [1200/1200], Loss: 1.4752
Epoch: [3/10], Step: [100/1200], Loss: 1.3914
Epoch: [3/10], Step: [200/1200], Loss: 1.2248
Epoch: [3/10], Step: [300/1200], Loss: 1.2547
Epoch: [3/10], Step: [400/1200], Loss: 1.0720
Epoch: [3/10], Step: [500/1200], Loss: 0.8522
Epoch: [3/10], Step: [600/1200], Loss: 1.0576
Epoch: [3/10], Step: [700/1200], Loss: 0.8513
Epoch: [3/10], Step: [800/1200], Loss: 0.7558
Epoch: [3/10], Step: [900/1200], Loss: 0.8906
Epoch: [3/10], Step: [1000/1200], Loss: 0.6811
Epoch: [3/10], Step: [1100/1200], Loss: 0.7370
Epoch: [3/10], Step: [1200/1200], Loss: 0.7120
Epoch: [4/10], Step: [100/1200], Loss: 0.7183
Epoch: [4/10], Step: [200/1200], Loss: 0.6526
Epoch: [4/10], Step: [300/1200], Loss: 1.0557
Epoch: [4/10], Step: [400/1200], Loss: 0.9142
Epoch: [4/10], Step: [500/1200], Loss: 0.6779
Epoch: [4/10], Step: [600/1200], Loss: 0.4618
Epoch: [4/10], Step: [700/1200], Loss: 0.9941
Epoch: [4/10], Step: [800/1200], Loss: 0.6586
Epoch: [4/10], Step: [900/1200], Loss: 0.8161
Epoch: [4/10], Step: [1000/1200], Loss: 0.4814
Epoch: [4/10], Step: [1100/1200], Loss: 0.7023
Epoch: [4/10], Step: [1200/1200], Loss: 0.5938
Epoch: [5/10], Step: [100/1200], Loss: 0.5277
Epoch: [5/10], Step: [200/1200], Loss: 0.6421
Epoch: [5/10], Step: [300/1200], Loss: 0.6591
Epoch: [5/10], Step: [400/1200], Loss: 0.7547
Epoch: [5/10], Step: [500/1200], Loss: 0.5321
Epoch: [5/10], Step: [600/1200], Loss: 0.7591
Epoch: [5/10], Step: [700/1200], Loss: 0.8456
Epoch: [5/10], Step: [800/1200], Loss: 0.4955
Epoch: [5/10], Step: [900/1200], Loss: 0.6119
Epoch: [5/10], Step: [1000/1200], Loss: 0.4185
Epoch: [5/10], Step: [1100/1200], Loss: 0.7572
Epoch: [5/10], Step: [1200/1200], Loss: 0.3567
Epoch: [6/10], Step: [100/1200], Loss: 0.4471
Epoch: [6/10], Step: [200/1200], Loss: 0.4590
Epoch: [6/10], Step: [300/1200], Loss: 0.5883
Epoch: [6/10], Step: [400/1200], Loss: 0.7611
Epoch: [6/10], Step: [500/1200], Loss: 0.3657
Epoch: [6/10], Step: [600/1200], Loss: 0.4927
Epoch: [6/10], Step: [700/1200], Loss: 0.3680
Epoch: [6/10], Step: [800/1200], Loss: 0.5498
Epoch: [6/10], Step: [900/1200], Loss: 0.2330
Epoch: [6/10], Step: [1000/1200], Loss: 0.4561
Epoch: [6/10], Step: [1100/1200], Loss: 0.4381
Epoch: [6/10], Step: [1200/1200], Loss: 0.5882
Epoch: [7/10], Step: [100/1200], Loss: 0.2238
Epoch: [7/10], Step: [200/1200], Loss: 0.1837
Epoch: [7/10], Step: [300/1200], Loss: 0.3769
Epoch: [7/10], Step: [400/1200], Loss: 0.2923
Epoch: [7/10], Step: [500/1200], Loss: 0.3122
Epoch: [7/10], Step: [600/1200], Loss: 0.3876
Epoch: [7/10], Step: [700/1200], Loss: 0.4610
Epoch: [7/10], Step: [800/1200], Loss: 0.2549
Epoch: [7/10], Step: [900/1200], Loss: 0.3639
Epoch: [7/10], Step: [1000/1200], Loss: 0.5007
Epoch: [7/10], Step: [1100/1200], Loss: 0.4893
Epoch: [7/10], Step: [1200/1200], Loss: 0.3306
Epoch: [8/10], Step: [100/1200], Loss: 0.3167
Epoch: [8/10], Step: [200/1200], Loss: 0.5069
Epoch: [8/10], Step: [300/1200], Loss: 0.2262
Epoch: [8/10], Step: [400/1200], Loss: 0.3192
Epoch: [8/10], Step: [500/1200], Loss: 0.3022
Epoch: [8/10], Step: [600/1200], Loss: 0.3831
Epoch: [8/10], Step: [700/1200], Loss: 0.3850
Epoch: [8/10], Step: [800/1200], Loss: 0.2427
Epoch: [8/10], Step: [900/1200], Loss: 0.2228
Epoch: [8/10], Step: [1000/1200], Loss: 0.5374
Epoch: [8/10], Step: [1100/1200], Loss: 0.2917
Epoch: [8/10], Step: [1200/1200], Loss: 0.2410
Epoch: [9/10], Step: [100/1200], Loss: 0.2362
Epoch: [9/10], Step: [200/1200], Loss: 0.6535
Epoch: [9/10], Step: [300/1200], Loss: 0.4043
Epoch: [9/10], Step: [400/1200], Loss: 0.1589
Epoch: [9/10], Step: [500/1200], Loss: 0.2606
Epoch: [9/10], Step: [600/1200], Loss: 0.3407
Epoch: [9/10], Step: [700/1200], Loss: 0.4839
Epoch: [9/10], Step: [800/1200], Loss: 0.3456
Epoch: [9/10], Step: [900/1200], Loss: 0.2724
Epoch: [9/10], Step: [1000/1200], Loss: 0.3831
Epoch: [9/10], Step: [1100/1200], Loss: 0.2052
Epoch: [9/10], Step: [1200/1200], Loss: 0.4371
Epoch: [10/10], Step: [100/1200], Loss: 0.3577
Epoch: [10/10], Step: [200/1200], Loss: 0.5289
Epoch: [10/10], Step: [300/1200], Loss: 0.3724
Epoch: [10/10], Step: [400/1200], Loss: 0.6010
Epoch: [10/10], Step: [500/1200], Loss: 0.4006
Epoch: [10/10], Step: [600/1200], Loss: 0.2830
Epoch: [10/10], Step: [700/1200], Loss: 0.4382
Epoch: [10/10], Step: [800/1200], Loss: 0.2223
Epoch: [10/10], Step: [900/1200], Loss: 0.4305
Epoch: [10/10], Step: [1000/1200], Loss: 0.3229
Epoch: [10/10], Step: [1100/1200], Loss: 0.2160
Epoch: [10/10], Step: [1200/1200], Loss: 0.2330
模型训练完成
模型保存完成
模型的精度为:91.00%
测试完成

Process finished with exit code 0

数据集处理部分:

1、数据存储路径(root='./data')。如果本地没有数据集文件,download=True 将从网上下载 MNIST 数据集,并保存到指定路径。

2、

  • train=True 和 train=False 参数分别加载训练集和测试集。
  • 这将 MNIST 数据集自动划分成两个独立的数据集:一个用于模型训练(训练集),一个用于模型评估(测试集)

3、transform=transforms.ToTensor() 将图像数据从 PIL 图像或 NumPy 数组格式转换为 PyTorch 张量,还会将图像的像素值从 [0, 255] 归一化为 [0, 1] 的范围

transforms.ToTensor()和归一化函数transforms.Normalize()的使用

Pytorch的torch.utils.data中Dataset以及DataLoader等详解

模型训练部分:

梯度清零optimizer.zero_grad()-->前向传播outputs = model(images) --->计算损失loss = criterion(outputs, labels) --->反向传播误差loss.backward()---->更新权重optimizer.step()

通过 enumerate() 可以同时获取批次的索引 i 和数据 images, labels

enumerate函数深度剖析

_, predicted = torch.max(outputs.data, 1)中需要注意:

1、max括号内的第二个参数1是指定了要沿着哪个维度寻找最大值。在这里,表示沿着每个样本的类别输出维度,最后函数返回两个值:预测类别输出的概率最大值和对应的索引。

2、_是一个惯用的占位符,用于忽略函数返回的第一个值(即最大值本身),只保留了预测的类别索引。

卷积神经网络CNN模型

'''
@author: lxy
@function:  minist recognition based in CNN model
@date : 2024/10/29
'''
import torch
import torch.nn as nn
import torch.optim as optim
from torchvision import datasets, transforms
from torch.utils.data import DataLoader


class CNN_minist(nn.Module):
    def __init__(self):
        super(CNN_minist, self).__init__()
        # 第一个卷积模块
        self.conv1 = nn.Sequential(
            # 卷积层
            nn.Conv2d(
                in_channels=1,  # 输入通道数(灰度图像为1)
                out_channels=16,  # 输出通道数(卷积核数量)
                kernel_size=5,  # 卷积核的大小
                stride=1,  # 卷积步长
                padding=2  # 填充,使得输出与输入的宽高相同
            ),
            nn.ReLU(),  # 激活函数,增加非线性
            nn.MaxPool2d(kernel_size=2)  # 最大池化层,减小特征图尺寸
        )
        # 第二个卷积模块
        self.conv2 = nn.Sequential(
            nn.Conv2d(16, 32, 5, 1, 2),  # 输入通道数为16,输出通道数为32
            nn.ReLU(),  # 激活函数
            nn.MaxPool2d(2)  # 最大池化层
        )
        '''
        原始图像像素:28*28*1
        经过第一个卷积层: 28*28*16(填充Padding为2)
        经过第一个池化层:14*14*16
        经过第二个卷积层:14*14*32
        经过第二个池化层:7*7*32
        '''
        # 全连接层,输入为7*7*32,输出为10(分类数)
        self.out = nn.Linear(7 * 7 * 32, 10)

    def forward(self, x: torch.Tensor):
        # 前向传播过程
        x = self.conv1(x)  # 经过第一个卷积模块
        x = self.conv2(x)  # 经过第二个卷积模块
        x = x.view(x.size(0), -1)  # 将特征图展平为一维
        output = self.out(x)  # 通过全连接层得到输出
        return output

def start():
    input_size = 28  # 输入图像的尺寸为28x28
    num_classes = 10  # 标签类别数量(数字0-9)
    num_epochs = 3  # 训练的总轮数
    batch_size = 64  # 每个批次的样本数量

    # 下载和加载训练集
    train_dataset = datasets.MNIST(root='./data',
                                   train=True,  # 使用训练集
                                   transform=transforms.ToTensor(),  # 转换为Tensor格式
                                   download=True)  # 下载数据集

    # 下载和加载测试集
    test_dataset = datasets.MNIST(root='./data',
                                  train=False,  # 使用测试集
                                  transform=transforms.ToTensor(),  # 转换为Tensor格式
                                  download=True)  # 下载数据集

    # 构建训练和测试的批量数据加载器
    train_loader = DataLoader(dataset=train_dataset,
                              batch_size=batch_size,
                              shuffle=True)  # 打乱训练数据
    test_loader = DataLoader(dataset=test_dataset, batch_size=batch_size,
                             shuffle=False)  # 测试数据不打乱

    myModel = CNN_minist()  # 实例化模型
    criterion = nn.CrossEntropyLoss()  # 选择交叉熵损失作为损失函数
    optimizer = optim.Adam(myModel.parameters(), lr=0.001)  # 选择Adam优化器

    # 开始训练
    for epoch in range(num_epochs):
        for idx, data in enumerate(train_loader):
            inputs, labels = data  # 获取输入数据和标签
            myModel.train()  # 设置模型为训练模式
            optimizer.zero_grad()  # 梯度归零,防止累加
            outputs = myModel(inputs)  # 前向传播,得到模型输出
            loss = criterion(outputs, labels)  # 计算损失
            loss.backward()  # 反向传播
            optimizer.step()  # 更新模型参数

            # 每100步输出一次损失
            if (idx + 1) % 100 == 0:
                print('Epoch: [%d/%d], Step: [%d/%d], Loss: %.4f'
                      % (epoch + 1, num_epochs, idx + 1, len(train_loader), loss.item()))

    print('模型训练完成')

    # 测试模型
    correct = 0  # 正确预测的数量
    total = 0    # 总预测数量
    with torch.no_grad():  # 关闭梯度计算
        for inputs, labels in test_loader:
            outputs = myModel(inputs)  # 前向传播得到输出
            _, predicted = torch.max(outputs.data, 1)  # 预测结果
            total += labels.size(0)  # 更新总样本数
            correct += (predicted == labels).sum().item()  # 统计正确预测的数量

    # 打印模型在测试集上的精度
    print(f'模型的精度为:{100 * correct / total:.2f}%')
    print('测试完成')

# 启动训练和测试过程
start()
Epoch: [1/3], Step: [100/938], Loss: 0.1791
Epoch: [1/3], Step: [200/938], Loss: 0.1613
Epoch: [1/3], Step: [300/938], Loss: 0.1663
Epoch: [1/3], Step: [400/938], Loss: 0.0369
Epoch: [1/3], Step: [500/938], Loss: 0.1555
Epoch: [1/3], Step: [600/938], Loss: 0.2144
Epoch: [1/3], Step: [700/938], Loss: 0.1035
Epoch: [1/3], Step: [800/938], Loss: 0.0371
Epoch: [1/3], Step: [900/938], Loss: 0.0677
Epoch: [2/3], Step: [100/938], Loss: 0.0502
Epoch: [2/3], Step: [200/938], Loss: 0.1408
Epoch: [2/3], Step: [300/938], Loss: 0.0790
Epoch: [2/3], Step: [400/938], Loss: 0.1037
Epoch: [2/3], Step: [500/938], Loss: 0.0250
Epoch: [2/3], Step: [600/938], Loss: 0.0199
Epoch: [2/3], Step: [700/938], Loss: 0.0180
Epoch: [2/3], Step: [800/938], Loss: 0.0766
Epoch: [2/3], Step: [900/938], Loss: 0.0188
Epoch: [3/3], Step: [100/938], Loss: 0.0366
Epoch: [3/3], Step: [200/938], Loss: 0.0248
Epoch: [3/3], Step: [300/938], Loss: 0.0155
Epoch: [3/3], Step: [400/938], Loss: 0.0330
Epoch: [3/3], Step: [500/938], Loss: 0.0067
Epoch: [3/3], Step: [600/938], Loss: 0.0412
Epoch: [3/3], Step: [700/938], Loss: 0.0165
Epoch: [3/3], Step: [800/938], Loss: 0.0459
Epoch: [3/3], Step: [900/938], Loss: 0.1818
模型训练完成
模型的精度为:98.97%
测试完成

模型构建部分

在 PyTorch 中,Sequential 是一个方便的容器,用于将多个层(layer)按顺序组合在一起,形成一个简单的神经网络模型。使用 Sequential 可以让我们以更简洁的方式定义模型结构,而不需要显式地编写 forward 方法。

EG:

import torch
import torch.nn as nn
import torch.optim as optim
import torchvision.transforms as transforms
from torchvision import datasets

# 定义一个简单的卷积神经网络
class SimpleCNN(nn.Module):
    def __init__(self):
        super(SimpleCNN, self).__init__()
        self.model = nn.Sequential(
            nn.Conv2d(1, 16, kernel_size=3, padding=1),  # 输入1个通道,输出16个通道
            nn.ReLU(),
            nn.MaxPool2d(kernel_size=2),  # 最大池化
            nn.Conv2d(16, 32, kernel_size=3, padding=1),  # 输入16个通道,输出32个通道
            nn.ReLU(),
            nn.MaxPool2d(kernel_size=2),
            nn.Flatten(),  # 展平
            nn.Linear(32 * 7 * 7, 10)  # 输入特征为32*7*7,输出为10个类别
        )

    def forward(self, x):
        return self.model(x)

# 实例化模型
model = SimpleCNN()

# 打印模型结构
print(model)

Sequential的使用和搭建实战可见输出与sequential内容一致


SimpleCNN(
  (model): Sequential(
    (0): Conv2d(1, 16, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
    (1): ReLU()
    (2): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
    (3): Conv2d(16, 32, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
    (4): ReLU()
    (5): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
    (6): Flatten(start_dim=1, end_dim=-1)
    (7): Linear(in_features=1568, out_features=10, bias=True)
  )
)

Process finished with exit code 0

Sequential的使用和搭建实战


原文地址:https://blog.csdn.net/qq_73704268/article/details/143342531

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!