pytorch基础学习(4)

  1. 在不同的文献中卷积核的叫法可能不同,Kernel、Filter、Weight指的都是卷积核。
  2. 2D卷积
    1. Kernel Channels
      1. Kernel Channels是两个值组成的一组值,表征了这个卷积核将输入图像从多少个通道映射到多少个通道上(也可以反过来写):(和Input Channels相等的通道数,用了多少种卷积核):
        1. 种:实际用的卷积核数量是Input Channels乘以后面的这个"卷积核种数“,即”对于每种卷积核都按照输入通道数分成了不同通道上的卷积核,对于不同通道上的同种卷积核:它们不共享权值,但共享一个偏置。
        2. 理解实际卷积核的数量是输入通道数乘以卷积核种类数,可以运行:
          import torch
          from torch import nn
          layer = nn.Conv2d(2, 3, kernel_size=3, stride=1, padding=0)
          layer.weight
          可以看到输出的有三组(因为有三种卷积核),每组有两个3乘3的卷积核(因为输入有两个通道)。
    2. Kernel Size指卷积核的长宽,如为3则卷积核是3乘3的。
    3. 例子:
      """2维的卷积层,用于图片的卷积"""
      # 输入图像的通道数=1(灰度图像),卷积核的种类数=3
      # 卷积核的shape是3乘3的,扫描步长为1,不加padding
      layer = nn.Conv2d(1, 3, kernel_size=3, stride=1, padding=0)
      
      """使用上面定义的卷积层layer和输入x,完成一次卷积的前向运算"""
      out = layer.forward(x)
      特别注意,在使用时应该直接使用layer(x)而不是layer.forward(x),因为前者实际是调用了__call__(),而PyTorch在这个函数中定义了一些hooks,如果要使用这些钩子的功能就只能用前者了!它会先运行hooks再运行.forward()函数。
    4. 查看卷积层信息:
      1. 在前面定义的卷积层的基础上,查看一下卷积层的权重(即卷积核)信息和偏置信息:
        print(layer.weight)
        print(layer.bias)
      2. 查看一下shape:
        print(layer.weight.shape)
        print(layer.bias.shape)

    5. F.conv2d(函数式接口):
      1. PyTorch里一般小写的都是函数式的接口,相应的大写的是类式接口。函数式的更加low-level一些,如果不需要做特别复杂的配置只要用类式接口就够了。
      2. 可以这样理解:nn.Conv2d是[2D卷积层],而F.conv2d是[2D卷积操作]。
        """手动定义卷积核(weight)和偏置"""
        w = torch.rand(16, 3, 5, 5)  # 16种3通道的5乘5卷积核
        b = torch.rand(16)  # 和卷积核种类数保持一致(不同通道共用一个bias)
        """2D卷积得到输出"""
        out = F.conv2d(x, w, b, stride=1, padding=1)  # 步长为1,外加1圈padding
    6. 在CNN中,基本的单元是一个Conv2d,后面配上[Batch Norm, pooling, ReLU],后面三个的顺序不一定。
  3. pooling池化
    1. 下采样(down-sample)的一种手段,让feature map减小;而up-sample则是上采样,实际上做了放大图像的操作。
    2. 池化核的移动位置一般是没有重叠的,然后在池化核扫过的地方进行一个函数运算,将其映射到一个新值上,也就完成了down-sample。
    3. 函数和类接口:
      # 池化层(池化核为2*2,步长为2),最大池化
      layer = nn.MaxPool2d(2, stride=2)
      使用函数式接口:# 池化运算(池化核为2*2,步长为2),平均池化
      out = F.avg_pool2d(x, 2, stride=2)
    4. up-sample:最近插值 :只要简单复制最近的元素指定的次数就可以了
       # 向上采样,放大2倍,最近插值
      out = F.upsample(x, scale_factor=2, mode='nearest') # 旧的接口
      print(out.shape)
      
      out = F.interpolate(x, scale_factor=2, mode='nearest') # 新的接口
  4. ReLU
    1. 把负的地方映射到0,相当于把feature map中的响应低的部分去掉,并保持正的响应不变。
    2. 类式接口:
      # ReLU激活,inplace=True表示直接覆盖掉ReLU目标的内存空间
      layer = nn.ReLU(inplace=True)
      out = layer(x)
    3. 函数式接口:
      # ReLU激活
      out = F.relu(x)
  5. normalization:
    1. 标准的Batch Normalization一个Batch的图像数据shape为[样本数N, 通道数C, 高度H, 宽度W],将其最后两个维度flatten,得到的是[N, C, H*W],标准的Batch Normalization就是在通道Channel这个维度上进行移动,对所有样本的所有值求均值和方差,所以有几个通道,得到的就是几个均值和方差。
    2. Layer Normalization:Layer Normalization是在实例即样本N的维度上滑动,对每个样本的所有通道的所有值求均值和方差,所以一个Batch有几个样本实例,得到的就是几个均值和方差。
    3. Instance Normalization:在样本N和通道C两个维度上滑动,对Batch中的N个样本里的每个样本n,和C个通道里的每个样本c,其组合[n, c]求对应的所有值的均值和方差,所以得到的是N⋅C个均值和方差
  6. Batch Normalization
    1. 为解决sigmoid梯度消失或爆炸提出,当将输出送入Sigmoid这样的激活函数之前,进行一个Normalize的操作,例如将其变换到N(0,σ^2),即在0的附近,主要在一个小范围内变动。
    2. 直观解释:输入的x范围相差较大时会导致相应权值w1和w2的变化引起的变化幅度不同,还会使得搜索空间看起来有点"扭曲",如果正则化后:从哪个位置开始搜索应该都能更好的找到全局最优,是一个**特征缩放(feature scaling)**的操作。
    3. 输入数据是6张3通道784个像素点的数据,将其分到三个通道上,在每个通道上也就是[6, 784]的数据,然后分别得到和通道数一样多的统计数据均值μ和方差σ ,将每个像素值减去μ除以σ也就变换到了接近N(0,1)的分布,后面又使用参数β 和γ 将其变换到接近N(β,γ)的分布。
    4. 注意:μ和σ只是样本中的统计数据,是没有梯度信息的,不过会保存在运行时参数里。而γ 和β 属于要训练的参数,他们是有梯度信息的。
    5. 例子:
      # Batch Normalization层,因为输入是将高度H和宽度W合成了一个维度,所以这里用1d
      layer = nn.BatchNorm1d(16)  # 传入通道数
      out = layer(x)
      
      # 全局的均值mu
      print(layer.running_mean)
      # 全局的方差sigma^2
      print(layer.running_var)
      1. 注意layer.running_mean和layer.running_var得到的是全局的均值和方差,不是当前Batch上的,只不过这里只跑了一个Batch而已所以它就是这个Batch上的。现在还没有办法直接查看某个Batch上的这两个统计量的值。
      2. 如果在定义层时使用了参数affine=False,那么就是固定γ = 1和β = 0 不自动学习,这时参数layer.weight和layer.bias将是None。
    6. 类似于Dropout,Batch Normalization在训练和测试时的行为不同。
      1. 在定义layer后,看到参数train=True表示现在在训练模式(默认),train=False表示在测试模式。
      2. 要切换为Test模式,需要在定义layer后使用layer.eval()将其转换!而不是直接写参数train=False。因为总要在训练了之后再做测试吧!如果这种参数放到层的定义时去指定,那就没法切换了(因为定义只有一次)。
    7. 使用了Batch Normalization让Converge(收敛)的速度加快了,可以直观理解,使用了靠近0的部分的Sigmoid激活,其梯度信息更大了。并且能够得到一个更好的解。

    8. 提升了Robust(鲁棒性),这使得网络更加稳定,理解:如果参数有大有小,解空间像左边一样,那么稍微调整学习率可能就发生抖动,或者训练速度太慢,这让超参数的调整没有那么敏感。

  7. 查看网络中一个层上的所有参数,用vars(layer)查看。

猜你喜欢

转载自blog.csdn.net/weixin_45647721/article/details/128163372
今日推荐