import torch
import torchvision
import torch.nn as nn
from model import LeNet
import torch.optim as optim
import torchvision.transforms as transforms


def main():
    transform = transforms.Compose(
        [transforms.ToTensor(),
         transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))])

    # 50000张训练图片
    # 第一次使用时要将download设置为True才会自动去下载数据集
    train_set = torchvision.datasets.CIFAR10(root='./data/CIFAR10', train=True,
                                             download=False, transform=transform)
    train_loader = torch.utils.data.DataLoader(train_set, batch_size=36,
                                               shuffle=True, num_workers=0)

    # 10000张验证图片
    # 第一次使用时要将download设置为True才会自动去下载数据集
    val_set = torchvision.datasets.CIFAR10(root='./data/CIFAR10', train=False,
                                           download=False, transform=transform)
    val_loader = torch.utils.data.DataLoader(val_set, batch_size=5000,
                                             shuffle=False, num_workers=0)
    val_data_iter = iter(val_loader)
    val_image, val_label = next(val_data_iter)

    # classes = ('plane', 'car', 'bird', 'cat',
    #            'deer', 'dog', 'frog', 'horse', 'ship', 'truck')

    net = LeNet()
    loss_function = nn.CrossEntropyLoss()
    optimizer = optim.Adam(net.parameters(), lr=0.001)

    for epoch in range(5):  # loop over the dataset multiple times

        running_loss = 0.0
        for step, data in enumerate(train_loader, start=0):
            # get the inputs; data is a list of [inputs, labels]
            inputs, labels = data

            # zero the parameter gradients
            optimizer.zero_grad()
            # forward + backward + optimize
            outputs = net(inputs)
            loss = loss_function(outputs, labels)
            loss.backward()
            optimizer.step()

            # print statistics
            running_loss += loss.item()
            if step % 500 == 499:  # print every 500 mini-batches
                with torch.no_grad():
                    outputs = net(val_image)  # [batch, 10]
                    predict_y = torch.max(outputs, dim=1)[1]
                    accuracy = torch.eq(predict_y, val_label).sum().item() / val_label.size(0)

                    print('[%d, %5d] train_loss: %.3f  test_accuracy: %.3f' %
                          (epoch + 1, step + 1, running_loss / 500, accuracy))
                    running_loss = 0.0

    print('Finished Training')

    save_path = './Lenet.pth'
    torch.save(net.state_dict(), save_path)


if __name__ == '__main__':
    main()

逐行进行解释:

导入模块

import torch
import torchvision
import torch.nn as nn
from model import LeNet
import torch.optim as optim
import torchvision.transforms as transforms

这段代码是使用PyTorch库来进行深度学习的导入语句。进行逐行解释:

import torch:
导入PyTorch库。PyTorch是一个开源的深度学习框架,允许你进行快速实验和开发。

import torchvision:
导入torchvision库,它是一个为计算机视觉任务提供数据集、模型和转换的库。

import torch.nn as nn:
导入PyTorch的神经网络模块,并为其指定一个别名nn。这使得后续使用神经网络相关的功能时可以更简洁地引用。

from model import LeNet:
从model模块中导入LeNet的自定义神经网络模型,其中定义了LeNet类。

import torch.optim as optim:
导入PyTorch的优化器模块,并为其指定一个别名optim。优化器用于更新模型的权重。

import torchvision.transforms as transforms:
导入torchvision中的图像变换功能,并为其指定一个别名transforms。在深度学习中,数据预处理是非常重要的步骤,包括图像大小调整、归一化等,torchvision.transforms提供了这些功能。
这些导入为后续的深度学习任务提供了必要的库和工具。

准备训练集

 transform = transforms.Compose(
        [transforms.ToTensor(),
         transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))])

    # 50000张训练图片
    # 第一次使用时要将download设置为True才会自动去下载数据集
    train_set = torchvision.datasets.CIFAR10(root='./data/CIFAR10', train=True,
                                             download=False, transform=transform)
    train_loader = torch.utils.data.DataLoader(train_set, batch_size=16,
                                               shuffle=True, num_workers=0)

    # 10000张验证图片
    # 第一次使用时要将download设置为True才会自动去下载数据集
    val_set = torchvision.datasets.CIFAR10(root='./data/CIFAR10', train=False,
                                           download=False, transform=transform)
    val_loader = torch.utils.data.DataLoader(val_set, batch_size=5000,
                                             shuffle=False, num_workers=0)
    val_data_iter = iter(val_loader)
    val_image, val_label = next(val_data_iter)

    # classes = ('plane', 'car', 'bird', 'cat',
    #            'deer', 'dog', 'frog', 'horse', 'ship', 'truck')

    net = LeNet()
    loss_function = nn.CrossEntropyLoss()
    optimizer = optim.Adam(net.parameters(), lr=0.001)

transform = transforms.Compose([transforms.ToTensor(), transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))]):

transform.Compose是创建一个数据预处理管道。
transforms.ToTensor()将PIL图像或NumPy ndarray转换为torch.Tensor,并且会自动缩放图像的像素值到[0,1]之间。
transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5)): 对图像进行标准化,其中(0.5, 0.5, 0.5)是均值,(0.5, 0.5, 0.5)是标准差。标准化是将图像的像素值减去均值,然后除以标准差。

train_set = torchvision.datasets.CIFAR10(root=‘./data/CIFAR10’, train=True, download=True, transform=transform):
torchvision.datasets.CIFAR10使用torchvision库加载CIFAR-10数据集。
root=‘./data/CIFAR10’: 数据集的存储路径。
train=True: 表示加载训练集。
download=False: 如果设置为True,数据集会自动从互联网上下载。设置为False意味着数据集已经下载过了或者代码运行者希望自己手动下载数据集。
transform=transform: 应用之前定义的预处理转换。

train_loader = torch.utils.data.DataLoader(train_set, batch_size=36, shuffle=True, num_workers=0):

torch.utils.data.DataLoader
使用DataLoader来创建一个数据加载器,它可以从train_set中按批次加载数据。

batch_size=36: 每批次加载36张图像。

shuffle=True: 在每个训练周期开始时,数据会被随机打乱。这有助于提高模型的泛化能力。

num_workers=0: 指定加载数据时使用的进程数。这里设置为0表示使用主进程来加载数据。

准备验证集

训练集准备好了后现在是准备验证集

val_set =torchvision.datasets.CIFAR10是创建了一个CIFAR-10验证数据集的实例,其中
root='./data/CIFAR10’是指定数据集的存储路径
train = False是指我们想要的是验证数据集而不是训练数据集
download =False是因为我们之前已经从互联网上下载过了,如果是第一次使用应该设置为True
transform = transform 传递一个预处理或者转换的参数给验证数据集,前面已经介绍了这个操作的目的是实现转换为Tensor数据格式和实现归一化

val_loader = torch.utils.data.Dataloader(val_set,batch_size=500,shuffle=False,num_workers=0)

通过torch.utils.data.Dataloader实现从我们准备的验证数据集中批量的加载数据,batch_size是每次加载的数量
shuffle = False是指不随机打乱数据

val_data_iter= iter(val_loader)是创建了一个迭代器,可以从上面的验证数据加载器中逐个批次的获取数据

val_image,val_laber = next(val_data_iter)从迭代器中获取下一个数据批次,并将其解包为val_image图像数据和val_label相应的标签

实例化LeNet网络模型并设置优化器、损失函数

 net = LeNet()
 loss_function = nn.CrossEntropyLoss()
 optimizer = optim.Adam(net.parameters(), lr=0.001)

net = LeNet()这是调用了我们之前创造的model模块中的LeNet类,并将其实例化
loss_function = nn.CorssEntropyLoss()是定义了一个损失函数,并使用交叉熵损失
optimizer = optim.Adam(net.parameters(),lr=0.001)
定义了一个优化器,获取网络中所有的参数(权重和偏置),这些参数将在训练中被优化
lr=0.001是指Adam算法中设置参数的学习率是0.001

开始训练

    for epoch in range(10):  # loop over the dataset multiple times

        running_loss = 0.0
        for step, data in enumerate(train_loader, start=0):
            # get the inputs; data is a list of [inputs, labels]
            inputs, labels = data

            # zero the parameter gradients
            optimizer.zero_grad()
            # forward + backward + optimize
            outputs = net(inputs)
            loss = loss_function(outputs, labels)
            loss.backward()
            optimizer.step()

            # print statistics
            running_loss += loss.item()
            if step % 500 == 499:  # print every 500 mini-batches
                with torch.no_grad():
                    outputs = net(val_image)  # [batch, 10]
                    predict_y = torch.max(outputs, dim=1)[1]
                    accuracy = torch.eq(predict_y, val_label).sum().item() / val_label.size(0)

                    print('[%d, %5d] train_loss: %.3f  test_accuracy: %.3f' %
                          (epoch + 1, step + 1, running_loss / 500, accuracy))
                    running_loss = 0.0

    print('Finished Training')

    save_path = './Lenet.pth'
    torch.save(net.state_dict(), save_path)


if __name__ == '__main__':
    main()

for epoch in range(10):
开始了一个训练的循环,代表训练数据集的多个周期epoch,在这个循环中,会遍历整个训练数据集10次。

running _loss = 0将整个训练的损失初始化为0,然后开始积累每个epoch产生的训练损失
for step,data in enumerate(train_loader,start=0)
使用enumerate函数遍历训练数据加载器,每个data里面包含输入和标签

inputs,labels = data 从data中解包输入和标签
optimizer.zero_grad()在每次迭代开始时,将参数的梯度清零,这个因为在反向传播开始前,我们需要确保梯度没有被积累

outputs = net(inputs)将输入数据喂给神经网络进行前向计算,获得输出
loss = loss_function(outputs,labels)通过损失函数计算损失
loss.backward()计算损失函数的梯度
optimizer.step()反向传播更新参数有
running_loss + = loss.item()这里注意一定要有item()因为loss是个Tensor包含了data和梯度两个部分,将当前的mini_batch的损失累加到总的训练损失上

if step % 500 ==499 检查运行的批次数,每500批次打印训练和测试的统计信息

with torch.no_grad():
在接下来的模型验证过程中,我们不需要像刚才的训练过程一样计算梯度,所以使用这个上下文管理器来关闭梯度的计算,
outputs = net(val_image)还是将验证的输入数据喂给模型计算输出,注意这里的模型是我们当前训练好的模型,这个过程实际上就是预测和推理,

predict = torch.max(outputs,dim=1)[1]找到输出中每行的最大值,并获取其索引作为预测的类别。
accuracy = torch.eq(predict, val_label).sum().item() / val_label.size(0)
计算预测的准确率。这里使用的是“相等”函数(torch.eq)来确定预测和真实标签是否匹配。

print(‘[%d, %5d] train_loss: %.3f test_accuracy: %.3f’ %
(epoch + 1, step + 1, running_loss / 500, accuracy))
打印训练的轮次、步骤、平均训练损失、以及测试准确率。

running_loss =0

在打印完统计信息后,将running_loss重置为0,为下一个小批次做准备
save_path = ‘./Lenet.pth’
torch.save(net.state_dict(), save_path)
保存训练好的模型权重到文件。使用.pth作为文件扩展名是PyTorch模型的标准格式。

if name == ‘main’:
main()
检查脚本是否作为主程序运行,如果是,则调用main()函数。这是常见的做法,确保代码只在直接运行时执行,而不是在导入时执行。

02-28 14:31