从零开始搭建一个PyTorch模型

从零开始搭建一个PyTorch模型

为了更好的理解PyTorch模型的架构,本文使用手写数字识别的例子,从零开始一步步搭建一个标准的前馈神经网络。

目标:训练一个3层的神经网络,以手写数字图像作为输入,经过神经网络的计算,识别出图像中的数字。

QQ_1721733273880

1.神经网络的搭建思路?

为了设计一个处理图像数据的神经网络,首先需要明确输入的图像数据的大小和格式。我们要处理的图像数据是28*28像素的灰色图像。

QQ_1721733727463

这样的灰色图像包括了 28 ∗ 28 = 784 28*28=784 2828=784个数据点,我们要先将它展平为 1 ∗ 784 1*784 1784大小的向量,然后将这个向量输入到神经网络中。

QQ_1721734337080

我们将使用一个三层的神经网络来处理图片对应的向量x,输入层需要接收784维的图片向量x,x中的每个维度的数据都有一个神经元来接收,故输入层要包含784个神经元。

QQ_1721734526157

隐藏层用于特征提取,将输入的特征向量处理为更高级的特征向量。因为手写数字识别并不复杂,故将隐藏层的神经元个数设置为256。这样输入层与隐藏层之间就会有一个 784 ∗ 256 784*256 784256大小的线性层。它可以将输入为784维的输入向量转换为256维的输出向量。该输出向量继续向前传播到达输出层。

QQ_1721734784406

由于最终的输出要对应0~9这10个数字,所以输出层要定义10个神经元来对应这10个数字。

QQ_1721734970892

这样,256维的向量经过隐藏层和输出层的线性层计算后,就得到了10维的输出结果。这个10维的向量就代表了10个数字的预测得分。

QQ_1721735143902

为了继续得到10个数字的预测概率,将输出层的输出输入到Softmax层,Softmax层会将10维向量转换为10个概率值 p 0 − p 9 p_0-p_9 p0p9。每个概率值都对应一个数字,也就是输入图片是某一个数字的可能性。

QQ_1721735385146

2.模型搭建流程

2.1 加载数据集

"""
第一步:加载数据集-构建训练数据和测试数据各自的Dataloader。
"""
# 对图片进行预处理,将图片数据转换为张量,并进行归一化
transform = transforms.Compose([transforms.ToTensor(), transforms.Normalize((0.5,), (0.5,), )])

# 下载数据集并预处理
train_dataset = dataset.MNIST(root="./MINST", train=True, download=True, transform=transform)
test_dataset = dataset.MNIST(root="./MINST", train=False, download=True, transform=transform)

# 构建dataloader,实现小批量的数据读取
train_loader = torch.utils.data.DataLoader(train_dataset, batch_size=batch_size, shuffle=True)
test_loader = torch.utils.data.DataLoader(test_dataset, batch_size=batch_size, shuffle=False)

2.2 定义模型(搭建网络结构)

"""
第二步:定义模型,包括模型结构的初始化`__init__(self)`和前向传播`forward(self, x)`。
* __init__(self)函数:用于定义前向传播需要用到的网络结构。
* forward(self, x)函数:用于实现前向传播的逻辑。
"""


class Model(nn.Module):
    def __init__(self, input_size, hideen_size, output_size):
        super().__init__()
        # 线性层1,输入层和隐藏层之间的线性层
        self.layer1 = nn.Linear(input_size, hideen_size)
        # 激活函数
        self.relu = nn.ReLU()
        # 线性层2,隐藏层和输出层之间的线性层
        self.layer2 = nn.Linear(hideen_size, output_size)

    def forward(self, x):
        # 将输入的图片数据x展平成一维数据
        x = x.view(-1, input_size)
        # 前向传播
        x = self.layer1(x)
        x = self.relu(x)
        x = self.layer2(x)
        return x

2.3 创建三个对象(模型本身、优化器、损失函数)

"""
第三步:创建三个对象(模型本身、优化器、损失函数)
"""
# 实例化模型
model = Model(input_size, hidden_size, output_size)
# 优化器
optimizer = optim.Adam(model.parameters(), lr=learning_rate)
# 损失函数
criterion = nn.CrossEntropyLoss()

2.4 模型训练

"""
第四步:模型训练(两种情况)- 手写数字识别案例的数据集划分为训练集-测试集
* 1.数据集划分为训练集-验证集-测试集:在训练集上训练出模型,将该模型在验证集上进行验证,以保存效果最好的模型。
* 2.数据集划分为训练集-测试集:在训练集上训练出模型(往往保存后面的epoch所对应的模型,根据自己情况所定)。
"""
for epoch in range(total_epochs):  # 外层循环,代表了整个训练数据集的遍历次数,每次迭代进行一次训练与验证

    for batch_index, (data, labels) in enumerate(train_loader):  # 内部循环包括训练与验证,每次加载一个batch
        # 设置模型为训练模式
        model.train()

        """
        训练步骤包括五步:
        * 1. 模型前向传播
        * 2. 计算损失
        * 3. 利用optimizer.zero_grad()将梯度清零
        * 4. 利用loss.backward()计算梯度
        * 5. 利用optimizer.step更新模型参数
        """
        output = model(data)  # 模型前向传播
        loss = criterion(output, labels)  # 计算损失
        optimizer.zero_grad()  # 利用optimizer.zero_grad()将梯度清零
        loss.backward()  # 利用loss.backward()计算梯度
        optimizer.step()  # 利用optimizer.step更新模型参数

        # 每迭代100个batch打印一次训练信息
        if (batch_index + 1) % 100 == 0:
            print("Epoch [{}/{}], Step [{}/{}], Train Loss:{:.4f}".format(epoch + 1, total_epochs, batch_index + 1,
                                                                          len(train_loader),
                                                                          loss.item()))
    if epoch >= total_epochs - 1:
        # 保存模型
        torch.save(model.state_dict(), "model.pth")

    """
    验证步骤包括两步:
    * 验证不需要计算梯度,所以用`with torch.no_grad():`包裹代码
    * 1. 模型前向传播
    * 2. 计算损失
    """
    with torch.no_grad():
        # 设置模型为评估模式
        model.eval()

        test_loss = 0.0  # 初始化验证数据集的总损失为 0
        count = 0  # 初始化正确预测的样本数为 0
        for data, labels in test_loader:
            outputs = model(data)  # 模型前向传播
            loss = criterion(outputs, labels)  # 计算损失
            count += torch.sum(outputs.argmax(1) == labels).item()  # 计算正确预测的样本数
            # 为了计算整个验证数据集的总损失,将当前批次的损失乘以该批次的样本数,然后累加到 test_loss。
            test_loss += loss.item() * data.size(0)

        # 将累加的总损失除以测试数据集的总样本数,获得平均损失。
        test_loss /= len(test_loader.dataset)
        # 计算精确度
        acc = count / len(test_loader.dataset)
        if acc > check:  # 当精确度有提升时,保存模型
            torch.save(model.state_dict(), "model.pth")

    # 每次迭代结束,打印模型在验证集上的平均损失。
    print("Epoch [{}/{}], Valid Loss:{:.4f}".format(epoch + 1, total_epochs, test_loss))

2.5 模型测试

"""
第五步:模型测试
* 1. 初始化模型
* 2. 加载训练好的模型
* 3. 验证模型性能
"""
model = Model(input_size, hidden_size, output_size)  # 初始化模型
model.load_state_dict(torch.load("model.pth"))  # 加载模型
print("load successfully!")
count = 0  # 初始化正确预测的样本数为 0
# 验证模型
for data, labels in test_loader:
    outputs = model(data)
    count += torch.sum(outputs.argmax(1) == labels).item()  # 计算正确预测的样本数
acc = count / len(test_loader.dataset)
print("Test Accuracy:{:.4f}".format(acc))

3.手写数字识别可执行代码

"""
coding:utf-8
* @Author:FHTT-Tian
* @name:tutorial.py
* @Time:2024/7/23 星期二 19:57
* @Description: 手写数字识别案例:从零开始构建一个模型,用于手写数字识别
"""
import torch
import torchvision.datasets as dataset
import torchvision.transforms as transforms
from torch import nn, optim
from torch.utils.data import DataLoader

# 超参数设置
input_size = 28 * 28
hidden_size = 256
output_size = 10
batch_size = 128
learning_rate = 0.0001
total_epochs = 10
check = 0  # 保存效果最好的模型的评估指标

"""
第一步:加载数据集-构建训练数据和测试数据各自的Dataloader。
"""
# 对图片进行预处理,将图片数据转换为张量,并进行归一化
transform = transforms.Compose([transforms.ToTensor(), transforms.Normalize((0.5,), (0.5,), )])

# 下载数据集并预处理
train_dataset = dataset.MNIST(root="./MINST", train=True, download=True, transform=transform)
test_dataset = dataset.MNIST(root="./MINST", train=False, download=True, transform=transform)

# 构建dataloader,实现小批量的数据读取
train_loader = torch.utils.data.DataLoader(train_dataset, batch_size=batch_size, shuffle=True)
test_loader = torch.utils.data.DataLoader(test_dataset, batch_size=batch_size, shuffle=False)

"""
第二步:定义模型,包括模型结构的初始化`__init__(self)`和前向传播`forward(self, x)`。
* __init__(self)函数:用于定义前向传播需要用到的网络结构。
* forward(self, x)函数:用于实现前向传播的逻辑。
"""


class Model(nn.Module):
    def __init__(self, input_size, hideen_size, output_size):
        super().__init__()
        # 线性层1,输入层和隐藏层之间的线性层
        self.layer1 = nn.Linear(input_size, hideen_size)
        # 激活函数
        self.relu = nn.ReLU()
        # 线性层2,隐藏层和输出层之间的线性层
        self.layer2 = nn.Linear(hideen_size, output_size)

    def forward(self, x):
        # 将输入的图片数据x展平成一维数据
        x = x.view(-1, input_size)
        # 前向传播
        x = self.layer1(x)
        x = self.relu(x)
        x = self.layer2(x)
        return x


"""
第三步:创建三个对象(模型本身、优化器、损失函数)
"""
# 实例化模型
model = Model(input_size, hidden_size, output_size)
# 优化器
optimizer = optim.Adam(model.parameters(), lr=learning_rate)
# 损失函数
criterion = nn.CrossEntropyLoss()

"""
第四步:训练模型(两种情况)- 手写数字识别案例的数据集划分为训练集-测试集
* 1.数据集划分为训练集-验证集-测试集:在训练集上训练出模型,将该模型在验证集上进行验证,以保存效果最好的模型。
* 2.数据集划分为训练集-测试集:在训练集上训练出模型(往往保存后面的epoch所对应的模型,根据自己情况所定)。
"""
for epoch in range(total_epochs):  # 外层循环,代表了整个训练数据集的遍历次数,每次迭代进行一次训练与验证

    for batch_index, (data, labels) in enumerate(train_loader):  # 内部循环包括训练与验证,每次加载一个batch
        # 设置模型为训练模式
        model.train()

        """
        训练步骤包括五步:
        * 1. 模型前向传播
        * 2. 计算损失
        * 3. 利用optimizer.zero_grad()将梯度清零
        * 4. 利用loss.backward()计算梯度
        * 5. 利用optimizer.step更新模型参数
        """
        output = model(data)  # 模型前向传播
        loss = criterion(output, labels)  # 计算损失
        optimizer.zero_grad()  # 利用optimizer.zero_grad()将梯度清零
        loss.backward()  # 利用loss.backward()计算梯度
        optimizer.step()  # 利用optimizer.step更新模型参数

        # 每迭代100个batch打印一次训练信息
        if (batch_index + 1) % 100 == 0:
            print("Epoch [{}/{}], Step [{}/{}], Train Loss:{:.4f}".format(epoch + 1, total_epochs, batch_index + 1,
                                                                          len(train_loader),
                                                                          loss.item()))
    if epoch >= total_epochs - 1:
        # 保存模型
        torch.save(model.state_dict(), "model.pth")

"""
第五步:测试模型
* 1. 初始化模型
* 2. 加载训练好的模型
* 3. 验证模型性能
"""
model = Model(input_size, hidden_size, output_size)  # 初始化模型
model.load_state_dict(torch.load("model.pth"))  # 加载模型
print("load successfully!")
count = 0  # 初始化正确预测的样本数为 0
# 验证模型
for data, labels in test_loader:
    outputs = model(data)
    count += torch.sum(outputs.argmax(1) == labels).item()  # 计算正确预测的样本数
acc = count / len(test_loader.dataset)
print("Test Accuracy:{:.4f}".format(acc))

QQ_1721814911607

参考

😃😃😃

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值