pytorch学习(一)——LeNet网络搭建

这篇博客介绍了如何使用PyTorch实现LeNet模型,对CIFAR10数据集进行训练和预测。首先定义了LeNet模型的结构,包括卷积层、池化层和全连接层。然后,通过DataLoader加载预处理后的训练和验证数据,使用Adam优化器和交叉熵损失函数进行训练。在训练过程中,计算并输出每500个批次的训练损失和验证准确率。最后,展示了如何对单张图片进行预测。
摘要由CSDN通过智能技术生成

本篇博客是学习B站霹雳吧啦Wz教学视频的总结

本节所用到的程序和教学视频链接:

程序
教学视频


程序结构

  • 模型搭建
  • 训练,train
  • 预测,predict

1 模型搭建:

	import torch.nn as nn
    import torch.nn.functional as F
    
    class LeNet(nn.Module):
        #初始化函数,网络层结构
        def __init__(self):
            super(LeNet, self).__init__()#
            self.conv1 = nn.Conv2d(3, 16, 5)
            self.pool1 = nn.MaxPool2d(2, 2)
            self.conv2 = nn.Conv2d(16, 32, 5)
            self.pool2 = nn.MaxPool2d(2, 2)
            self.fc1 = nn.Linear(32*5*5, 120)
            self.fc2 = nn.Linear(120, 84)
            self.fc3 = nn.Linear(84, 10)
        #正向传播
        def forward(self, x):
            x = F.relu(self.conv1(x))    # input(3, 32, 32) output(16, 28, 28)
            x = self.pool1(x)            # output(16, 14, 14)
            x = F.relu(self.conv2(x))    # output(32, 10, 10)
            x = self.pool2(x)            # output(32, 5, 5)
            x = x.view(-1, 32*5*5)       # output(32*5*5)
            x = F.relu(self.fc1(x))      # output(120)
            x = F.relu(self.fc2(x))      # output(84)
            x = self.fc3(x)              # output(10)
            return x
  • super:类函数继承
  • Conv2d: 卷积函数
  • MaxPool2d: 池化层
  • Linear:全连接层
  • x:输入图片集;按照[batch,channel,height,weight]的顺序排列
  • view:重构张量的维度, -1代表自动推理
  • 输出层没有使用softmax的原因是:在计算交叉熵函数时内部已经有softmax的计算

2 训练

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', 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', 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 = val_data_iter.next()
    
    # 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]#搜索网络输出结果的最大值,dim=1表示在channel上进行搜索,[1]表示只需要获取索引
                    accuracy = torch.eq(predict_y, val_label).sum().item() / val_label.size(0)# .sum()后获取的是一个tensor并不是数值,使用item()来获取数值

                    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()
  • train_set = torchvision.datasets.CIFAR10(root=’./data’, train=True,download=False, transform=transform)
    • 下载训练集
    • root:下载的数据集存放在哪里 ./data 当前目录的data文件夹下
    • train: 如果为true就会导入训练集样本,False就会导入测试训练集
    • download :为True会自动下载数据集,如果数据集已经下载好了,改为False
    • transform:对数据集进行预处理,通过ToTensor讲数据集样本的图片变为[通道数,高度,宽度]的排列顺序,并且将[0-255]的数据范围归一化到0~1;Normalize:使用均值和标准差标准化tensor
  • train_loader = torch.utils.data.DataLoader(train_set, batch_size=36,
    shuffle=True, num_workers=0)
    • 将上句下载的数据集分成批次(batch_size=36)导入到程序中来
    • shuffle:True是否随机提取数据
    • num_workers:载入数据的线程数,在Linux系统下可以自定义,在win下只能设置为0
  • val_data_iter = iter(val_loader)
    • 将val_loader转化为可迭代的迭代器
    • 使用 val_image, val_label = val_data_iter.next() 来获取一批迭代器,它包含了图片和对应的标签值
  • classes 是一个元组类型,它的值是不能改变的,内容就是标签值
  • nn.CrossEntropyLoss():交叉熵损失函数
  • optm.Adam(net.parameters(),lr=0.001):优化器,net.parameters()我们所需要的训练参数,这里所有的可训练参数都会进行训练,lr是学习率
  • optimizer.zero_grad() 将历史损失梯度清零:详细解释
  • loss.backward() 误差反向传播
  • optimizer.step() 参数更新
  • running_loss+= loss.item() 将计算的损失函数累加到running_loss当中
  • with torch.no_grad() 节省内存资源不会计算误差梯度
  • net.state_dict() 网络训练的权重结果

3. 预测

import torch
import torchvision.transforms as transforms
from PIL import Image

from model import LeNet


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

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

    net = LeNet()
    net.load_state_dict(torch.load('Lenet.pth'))

    im = Image.open('1.jpg')
    im = transform(im)  # [C, H, W] 更改图片矩阵维度顺序
    im = torch.unsqueeze(im, dim=0)  # [N, C, H, W] 扩充图像维度

    with torch.no_grad():
        outputs = net(im)
        predict = torch.max(outputs, dim=1)[1].data.numpy()
    print(classes[int(predict)])


if __name__ == '__main__':
    main()


附录

1.super

https://bramblexu.com/posts/3adca41/

super()函数可以隐式地将子类(sublass)里的method,与父类(superclass)里的method进行关联。这样的好处在于我们不用在子类里显式地重新创建父类method里的属性。

上述例子中,类LeNet()继承了类Module(),Module形式如下图所示:
在这里插入图片描述
当我们用super(LeNet, self).init()后,就意味着类Module()中的所有初始化参数在LeNet()中都不用再重新定义


2.Conv2d

2.1定义:
  • 2.1.1初始化函数:

    
    def __init__(
            self,
            in_channels: int, 
            out_channels: int,
            kernel_size: _size_2_t,
            stride: _size_2_t = 1,
            padding: _size_2_t = 0,
            dilation: _size_2_t = 1,
            groups: int = 1,
            bias: bool = True,
            padding_mode: str = 'zeros'  # TODO: refine this type
        ):
    
    • in_channels: 输入通道数
    • out_channels: 使用卷积核的个数,使用X个卷积核就会生成深度为x维的特征矩阵
    • kernel_size: 对应卷积核的大小
    • stride: 步长,默认为1
    • padding: 在四周进行补全的层数
    • dilation
    • groups
    • bias: 偏执,默认使用
    • padding_mode: 补全的模式,默认为0
  • 2.1.2公式:
    在这里插入图片描述

  • 2.1.3卷积输出尺寸的计算方法:

    N = ( W − F + 2 P ) / S + 1 N=(W-F+2P)/S+1 N=(WF+2P)/S+1

    • 输入图片大小为 W × W W\times W W×W
    • Filter 大小为 F × F F\times F F×F
    • 步长为S
    • padding的像素数为P

3.MaxPool2d

  • 3.1初始化函数

    def __init__(self, kernel_size: _size_1_t, stride: Optional[_size_1_t] = None, padding: _size_1_t = 0) -> None:
            super(MaxUnpool1d, self).__init__()
            self.kernel_size = _single(kernel_size)
            self.stride = _single(stride if (stride is not None) else kernel_size)
            self.padding = _single(padding)
    
    • kernel_size: 池化层大小
    • stride: 步长,如果不指定步长,就会按照kenel_size的大小执行

4.torch.no_grad()

在使用pytorch时,并不是所有的操作都需要进行计算图1的生成(计算过程的构建,以便梯度反向传播等操作)。而对于tensor的计算操作,默认是要进行计算图的构建的,在这种情况下,可以使用 with torch.no_grad():,强制之后的内容不进行计算图构建2


pytorch学习(二)——AlexNet网络搭建


  1. https://blog.csdn.net/xinming_365/article/details/107690498 ↩︎

  2. https://blog.csdn.net/weixin_44134757/article/details/105775027 ↩︎

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值