Pytorch深度学习入门(三)

知识点总结

参考https://blog.youkuaiyun.com/qq_43629945/article/details/122767670?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522738323ebc12d22782dfd4269e32cfce1%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&request_id=738323ebc12d22782dfd4269e32cfce1&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~top_positive~default-1-122767670-null-null.142^v100^pc_search_result_base2&utm_term=%E6%88%91%E6%98%AF%E5%9C%9F%E5%A0%86&spm=1018.2226.3001.4187

  • 知识点1:神经网络的基本骨架nn.Module的使用
    • img

    • import torch.nn as nn
      import torch.nn.functional as F
       
      class Model(nn.Module):   #搭建的神经网络 Model继承了 Module类(父类)
          def __init__(self):   #初始化函数
              super(Model, self).__init__()   #必须要这一步,调用父类的初始化函数
              self.conv1 = nn.Conv2d(1, 20, 5)
              self.conv2 = nn.Conv2d(20, 20, 5)
       
          def forward(self, x):   #前向传播(为输入和输出中间的处理过程),x为输入
              x = F.relu(self.conv1(x))   #conv为卷积,relu为非线性处理
              return F.relu(self.conv2(x))
    • img

  • 知识点2:卷积操作
    • img

    • img

    • stride(步进)

      可以是单个数,或元组(sH,sW) — 控制横向步进和纵向步进

    • #reshape函数
      # 尺寸只有高和宽,不符合要求
      print(input.shape)  #5×5
      print(kernel.shape)  #3×3
       
      # 尺寸变换为四个数字
      input = torch.reshape(input,(1,1,5,5))  #通道数为1,batch大小为1
      kernel = torch.reshape(kernel,(1,1,3,3))
    • input:尺寸要求是batch,几个通道,高,宽(4个参数)

    • weight:尺寸要求是输出,in_channels(groups一般为1),高,宽(4个参数)

    • padding(填充)

      在输入图像左右两边进行填充,决定填充有多大。可以为一个数或一个元组(分别指定高和宽,即纵向和横向每次填充的大小)。默认情况下不进行填充

      padding=1:将输入图像左右上下两边都拓展一个像素,空的地方默认为0

    • #二维卷积
      CLASS torch.nn.Conv2d(in_channels, out_channels, kernel_size, stride=1, padding=0, dilation=1, groups=1, bias=True, padding_mode='zeros', device=None, dtype=None)
       
      # in_channels 输入通道数
      # out_channels 输出通道数
      # kernel_size 卷积核大小 
      #以上参数需要设置
       
      #以下参数提供了默认值
      # stride=1 卷积过程中的步进大小
      # padding=0 卷积过程中对原始图像进行padding的选项
      # dilation=1 每一个卷积核对应位的距离
      # groups=1 一般设置为1,很少改动,改动的话为分组卷积
      # bias=True 通常为True,对卷积后的结果是否加减一个常数的偏置
      # padding_mode='zeros' 选择padding填充的模式
    • img

    • kernel_size

      定义了一个卷积核的大小,若为3则生成一个3×3的卷积核

      • 卷积核的参数是从一些分布中进行采样得到的

      • 实际训练过程中,卷积核中的值会不断进行调整

    • in_channels & out_channels in_channels:输入图片的channel数(彩色图像 in_channels 值为3) out_channels:输出图片的channel数 in_channels 和 out_channels 都为 1 时,拿一个卷积核在输入图像中进行卷积

      out_channels 为 2 时,卷积层会生成两个卷积核(不一定一样),得到两个输出,叠加后作为最后输出

    • img

  • 知识点3: 神经网络 - 最大池化的使用
    • MaxPool:最大池化(下采样)

    • MaxUnpool:上采样

    • AvgPool:平均池化

    • AdaptiveMaxPool2d:自适应最大池化

    • CLASS torch.nn.MaxPool2d(kernel_size, stride=None, padding=0, dilation=1, return_indices=False, ceil_mode=False)
      #注意,卷积中stride默认为1,而池化中stride默认为kernel_size 
      #Ceil_mode 默认情况下为 False,对于最大池化一般只需设置 kernel_size 即可 
    • img

    • img

    • import torch
      from torch import nn
      from torch.nn import MaxPool2d
       
      input = torch.tensor([[1,2,0,3,1],
                            [0,1,2,3,1],
                            [1,2,1,0,0],
                            [5,2,3,1,1],
                            [2,1,0,1,1]],dtype=torch.float32)  #最大池化无法对long数据类型进行实现,将input变成浮点数的tensor数据类型
      input = torch.reshape(input,(-1,1,5,5)) 
      #-1表示torch计算batch_size
      #要求的 input 必须是四维的,参数依次是:batch_size、channel、高、宽
      print(input.shape)
       
      # 搭建神经网络
      class Tudui(nn.Module):
          def __init__(self):
              super(Tudui, self).__init__()
              self.maxpool1 = MaxPool2d(kernel_size=3,ceil_mode=True)
          def forward(self,input):
              output = self.maxpool1(input)
              return output
       
      # 创建神经网络
      tudui = Tudui()
      output = tudui(input)
      print(output)
    • 为什么要进行最大池化?最大池化的作用是什么? 最大池化的目的是保留输入的特征,同时把数据量减小(数据维度变小),对于整个网络来说,进行计算的参数变少,会训练地更快

      如上面案例中输入是5x5的,但输出是3x3的,甚至可以是1x1的 类比:1080p的视频为输入图像,经过池化可以得到720p,也能满足绝大多数需求,传达视频内容的同时,文件尺寸会大大缩小 池化一般跟在卷积后,卷积层是用来提取特征的,一般有相应特征的位置是比较大的数字,最大池化可以提取出这一部分有相应特征的信息

      池化不影响通道数

      池化后一般再进行非线性激活

  • 知识点4:非线性激活
    • 非线性激活:给神经网络引入一些非线性的特征

      非线性越多,才能训练出符合各种曲线或特征的模型(提高泛化能力)

    • 最常见:RELU

      • img

      • import torch
        from torch import nn
        from torch.nn import ReLU
         
        input = torch.tensor([[1,-0.5],
                              [-1,3]])
        input = torch.reshape(input,(-1,1,2,2))  #input必须要指定batch_size,-1表示batch_size自己算,1表示是1维的
        print(input.shape)   #torch.Size([1, 1, 2, 2])
         
        # 搭建神经网络
        class Tudui(nn.Module):
            def __init__(self):
                super(Tudui, self).__init__()
                self.relu1 = ReLU()  #inplace默认为False
            def forward(self,input):
                output = self.relu1(input)
                return output
         
        # 创建网络
        tudui = Tudui()
        output = tudui(input)
        print(output)
    • Sigmoid

      • img

      • class Tudui(nn.Module):
            def __init__(self):
                super(Tudui, self).__init__()
                self.sigmoid1 = Sigmoid()  #inplace默认为False,input处理完后仍为原值
            def forward(self,input):
                output = self.sigmoid1(input)
                return output
  • 知识点5:神经网络 - 线性层及其他层介绍
    • Linear Layers

      • img

      • img

      • #flatten摊平,变成一行
        # Example
        >>> t = torch.tensor([[[1, 2],
                           [3, 4]],
                          [[5, 6],
                           [7, 8]]])   #3个中括号,所以是3维的
        >>> torch.flatten(t)  #摊平
        tensor([1, 2, 3, 4, 5, 6, 7, 8])
        >>> torch.flatten(t, start_dim=1)  #变为1行
        tensor([[1, 2, 3, 4],
                [5, 6, 7, 8]])
  • 知识点6:损失函数

    • torch.nn 里的 loss function 衡量误差,在使用过程中根据需求使用,注意输入形状和输出形状即可

      loss 衡量实际神经网络输出 output 与真实想要结果 target 的差距,越小越好

      作用:

      计算实际输出和目标之间的差距 为我们更新输出提供一定的依据(反向传播):给每一个卷积核中的参数提供了梯度 grad,采用反向传播时,每一个要更新的参数都会计算出对应的梯度,优化过程中根据梯度对参数进行优化,最终达到整个 loss 进行降低的目的

    • L1LOSS

      • img

      • #input:(N,*),N是batch_size,即有多少个数据;*可以是任意维度 
        CLASS torch.nn.L1Loss(size_average=None, reduce=None, reduction='mean')
      • loss = L1Loss()
        #求和
        #loss = L1Loss(reduction='sum')
        result = loss(inputs,targets)
    • MSELOSS(均方误差)

      • img

      • #nput:(N,*)N是batch_size,即有多少个数据;*可以是任意维度 
        CLASS torch.nn.MSELoss(size_average=None, reduce=None, reduction='mean')
      • loss_mse = nn.MSELoss()
        result_mse = loss_mse(inputs,targets)
    • CROSSENTROPYLOSS(交叉熵):适用于训练分类问题,有C个类别

      • img

      • x = torch.tensor([0.1,0.2,0.3])
        y = torch.tensor([1])
        x = torch.reshape(x,(1,3))
        loss_cross = nn.CrossEntropyLoss()
        result_cross = loss_cross(x,y)
        print(result_cross)
  • 知识点7:backward 反向传播

    • 计算出每一个节点参数的梯度

      在上述代码后加一行:

      result_loss.backward()  # backward反向传播,是对result_loss,而不是对loss
  • 知识点8:优化器

    • 当使用损失函数时,可以调用损失函数的 backward,得到反向传播,反向传播可以求出每个需要调节的参数对应的梯度,有了梯度就可以利用优化器,优化器根据梯度对参数进行调整,以达到整体误差降低的目的

    • # Example:
      # SGD为构造优化器的算法,Stochastic Gradient Descent 随机梯度下降
      #构造
      optimizer = optim.SGD(model.parameters(), lr=0.01, momentum=0.9)  #模型参数、学习速率、特定优化器算法中需要设定的参数
      optimizer = optim.Adam([var1, var2], lr=0.0001)
      #调用优化器step方法
      for input, target in dataset:
          optimizer.zero_grad() #把上一步训练的每个参数的梯度清零
          output = model(input)
          loss = loss_fn(output, target)  # 输出跟真实的target计算loss
          loss.backward() #调用反向传播得到每个要更新参数的梯度
          optimizer.step() #每个参数根据上一步得到的梯度进行优化
    • 算法

      如Adadelta、Adagrad、Adam、RMSProp、SGD等等,不同算法前两个参数:params、lr 都是一致的,后面的参数不同

      params为模型的参数、lr为学习速率(learning rate)

2. 搭建小实战和 Sequential 的使用

  • Sequential 的使用

    • # Using Sequential to create a small model. When `model` is run,
      # input will first be passed to `Conv2d(1,20,5)`. The output of
      # `Conv2d(1,20,5)` will be used as the input to the first
      # `ReLU`; the output of the first `ReLU` will become the input
      # for `Conv2d(20,64,5)`. Finally, the output of
      # `Conv2d(20,64,5)` will be used as input to the second `ReLU`
      model = nn.Sequential(
                nn.Conv2d(1,20,5),
                nn.ReLU(),
                nn.Conv2d(20,64,5),
                nn.ReLU()
              )
       
      # Using Sequential with OrderedDict. This is functionally the
      # same as the above code
      model = nn.Sequential(OrderedDict([
                ('conv1', nn.Conv2d(1,20,5)),
                ('relu1', nn.ReLU()),
                ('conv2', nn.Conv2d(20,64,5)),
                ('relu2', nn.ReLU())
              ]))
  • 对 CIFAR10 进行分类的简单神经网络

    • img

    • img

    • from torch import nn
      from torch.nn import Conv2d, MaxPool2d, Flatten, Linear
       
       
      class Tudui(nn.Module):
          def __init__(self):
              super(Tudui, self).__init__()
              self.conv1 = Conv2d(in_channels=3, out_channels=32, kernel_size=5, padding=2)  #第一个卷积
              self.maxpool1 = MaxPool2d(kernel_size=2)   #池化
              self.conv2 = Conv2d(32,32,5,padding=2)  #维持尺寸不变,所以padding仍为2
              self.maxpool2 = MaxPool2d(2)
              self.conv3 = Conv2d(32,64,5,padding=2)
              self.maxpool3 = MaxPool2d(2)
              self.flatten = Flatten()  #展平为64x4x4=1024个数据
              # 经过两个线性层:第一个线性层(1024为in_features,64为out_features)、第二个线性层(64为in_features,10为out_features)
              self.linear1 = Linear(1024,64)
              self.linear2 = Linear(64,10)  #10为10个类别,若预测的是概率,则取最大概率对应的类别,为该图片网络预测到的类别
          def forward(self,x):   #x为input
              x = self.conv1(x)
              x = self.maxpool1(x)
              x = self.conv2(x)
              x = self.maxpool2(x)
              x = self.conv3(x)
              x = self.maxpool3(x)
              x = self.flatten(x)
              x = self.linear1(x)
              x = self.linear2(x)
              return x
       
      tudui = Tudui()
      print(tudui)

3. 网络模型的保存与读取

  • 两种方式保存模型

    • import torch
      import torchvision.models
       
      vgg16 = torchvision.models.vgg16(pretrained=False)  # 网络中模型的参数是没有经过训练的、初始化的参数
      #方式1:不仅保存了网络模型的结构,也保存了网络模型的参数
      torch.save(vgg16,"vgg16_method1.pth")
      #方式2:网络模型的参数保存为字典,不保存网络模型的结构(官方推荐的保存方式,用的空间小)
      torch.save(vgg16.state_dict(),"vgg16_method2.pth") 
      ​
  • 两种方式加载模型

    • #方式1:对应保存方式1,打印出的是网络模型的结构
      model = torch.load("vgg16_method1.pth",)
      print(model)  # 打印出的只是模型的结构,其实它的参数也被保存下来了
      #方式2:对应保存方式2,打印出的是参数的字典形式
      model = torch.load("vgg16_method2.pth")
      print(model)
  • 用方式1保存的话,加载时要让程序能够访问到其定义模型的一种方式

    • 需要将 model_save.py 中的网络结构复制到 model_load.py 中,即下列代码需要复制到 model_load.py 中(为了确保加载的网络模型是想要的网络模型)

    • 但是不需要创建了,即在 model_load.py 中不需要写:

      tudui = Tudui()
  • imgimg

4.使用GPU训练的方式

  • img

  • img

  • img

  • device = torch.device("cuda" if torch.cuda.is_available() else "cpu")

4.复现CIFAR10模型

import torch
import torchvision.datasets
from torch.utils.tensorboard import SummaryWriter
from torch import nn
from torch.utils.data import DataLoader
​
# 准备数据集,CIFAR10 数据集是PIL Image,要转换为tensor数据类型
train_data = torchvision.datasets.CIFAR10(root="dataset", train=True, transform=torchvision.transforms.ToTensor(),
                                          download=True)
test_data = torchvision.datasets.CIFAR10(root="dataset", train=False, transform=torchvision.transforms.ToTensor(),
                                         download=True)
​
# 看一下训练数据集和测试数据集都有多少张(如何获得数据集的长度)
train_data_size = len(train_data)  # length 长度
test_data_size = len(test_data)
# 如果train_data_size=10,那么打印出的字符串为:训练数据集的长度为:10
print("训练数据集的长度为:{}".format(train_data_size))  # 字符串格式化,把format中的变量替换{}
print("测试数据集的长度为:{}".format(test_data_size))
​
# 利用 DataLoader 来加载数据集
train_dataloader = DataLoader(train_data, batch_size=64)
test_dataloader = DataLoader(test_data, batch_size=64)
​
​
# 创建网络模型
​
class Tudui(nn.Module):
    def __init__(self):
        super(Tudui, self).__init__()#继承父类
        self.model = nn.Sequential(
            nn.Conv2d(3, 32, 5, 1, 2),
            nn.MaxPool2d(2),
            nn.Conv2d(32, 32, 5, 1, 2),
            nn.MaxPool2d(2),
            nn.Conv2d(32, 64, 5, 1, 2),
            nn.MaxPool2d(2),
            nn.Flatten(),
            nn.Linear(64 * 4 * 4, 64),
            nn.Linear(64, 10)
​
        )
​
    def forward(self, x):
        x = self.model(x)
        return x
​
​
tudui = Tudui()
if torch.cuda.is_available():
    tudui = tudui.cuda()
​
# 创建损失函数
loss_fn = nn.CrossEntropyLoss()  # 分类问题可以用交叉熵
if torch.cuda.is_available():
    loss_fn = loss_fn.cuda()
# 定义优化器
learning_rate = 0.01  # 另一写法:1e-2,即1x 10^(-2)=0.01
optimizer = torch.optim.SGD(tudui.parameters(), lr=learning_rate)  # SGD 随机梯度下降
​
# 设置训练网络的一些参数
total_train_step = 0  # 记录训练次数
total_test_step = 0  # 记录测试次数
epoch = 100  # 训练轮数
​
# 添加tensorboard
writer = SummaryWriter("logs_train_common")
for i in range(epoch):
    print("----------第{}轮训练开始-----------".format(i + 1))  # i从0-9
    # 训练步骤开始
    for data in train_dataloader:
        imgs, targets = data
        if torch.cuda.is_available():
            imgs = imgs.cuda()
            targets = targets.cuda()
        outputs = tudui(imgs)
        loss = loss_fn(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.item()))
            writer.add_scalar("train_loss_100", loss.item(), total_train_step)
​
    # 测试步骤开始
​
    tudui.eval()
    total_test_loss = 0
    total_accuracy = 0
    with torch.no_grad():  # 无梯度,不进行调优
        for data in test_dataloader:
            imgs, targets = data
            if torch.cuda.is_available():
                imgs = imgs.cuda()
                targets = targets.cuda()
            outputs = tudui(imgs)
            loss = loss_fn(outputs, targets)  # 该loss为部分数据在网络模型上的损失,为tensor数据类型
            # 求整体测试数据集上的误差或正确率
            total_test_loss = total_test_loss + loss.item()  # loss为tensor数据类型,而total_test_loss为普通数字
            # 求整体测试数据集上的误差或正确率
            accuracy = (outputs.argmax(1) == targets).sum()  # 1:横向比较,==:True或False,sum:计算True或False个数
            total_accuracy = total_accuracy + accuracy
    print("整体测试集上的Loss:{}".format(total_test_loss))
    print("整体测试集上的正确率:{}".format(total_accuracy / test_data_size))
    writer.add_scalar("test_loss_100", total_test_loss, total_test_step)
    writer.add_scalar("test_accuracy_100", total_accuracy / test_data_size, total_test_step)
    total_test_step += 1
​
    torch.save(tudui, "tudui_{}.pth".format(i))  # 每一轮保存一个结果
    print("模型已保存")
​
writer.close()
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值