5. 池化层、非线性激活和并搭建一个简单的神经网络

    本次,我们接着学习神经网络的知识,这里介绍池化层,和经典的非线性激活函数,并简要的介绍一下 pytorch 中的其它层次,最后我们使用搭建神经网络(并使用以下 torch.nn.Sequential)。

Pooling Layers

  本小节,我们介绍一下池化层的相关知识。

     池化层的作用

  除了卷积层,卷积网络也经常使用池化层来缩减模型的大小,提高计算速度,同时提高所提取特征的鲁棒性。也就是池化层赋予了我们网络更快的运算速度和更高的鲁棒性。
  而且池化层是我们卷积神经网络中经常用到的层,基本每个网络都会有池化层,池化层也很简单。

常见的池化层有 max-pooling 和 average-pooling,该两种操作一个是取卷积核中的 最大值,一个是取平均值

Max Pooling是对某个Filter抽取到若干特征值,只取得其中最大的那个Pooling层作为保留值,其他特征值全部抛弃,值最大代表只保留这些特征中最强的,抛弃其他弱的此类特征。
有以下几个优点:
1、保证特征的位置与旋转不变性。对于图像处理这种特性是很好的。至于如何理解图像进行池化(pooling)后的平移不变性?
手边没有图,我就抽象一点回答了。假如三个元素(1,5,3)取max就取到5,如果三个元素向右平移一下变成(0,1,5),那取max之后还是5,具备了平移不变性。大概就是这么个理。
2、减少模型参数数量,缓解过拟合问题
3、可以把变长的输入x整理成固定长度的输入。CNN往往最后连接全连接层,在训练过程中,神经元个数需要固定好,但是cnn输入x长度不确定,通过pooling操作,每个filter固定取一个值。有多少个Filter,Pooling就有多少个神经元,这样就可以把全连接层神经元固定住。

     pytorch 池化层帮助文档查看


这里,我们详细查看以下 torch.nn.MaxPool2d 为例




     代码实例测试

  请注意看,他的帮助文档标注的是可以使 NCHW,也可以是 CHW 两种模式。

请注意,我们这里的 stride 默认值是 kernel_size,这样也就达到了我们池化、采样的目的。
import torch
import torchvision
from torch.utils.tensorboard import SummaryWriter


my_data = torch.randn((1, 1, 7, 7))

ceil_max_pool = torch.nn.MaxPool2d(kernel_size=3, ceil_mode=True)
floor_max_pool = torch.nn.MaxPool2d(kernel_size=3, ceil_mode=False) # stride 默认值是kernel_size

ceil_result = ceil_max_pool(my_data)
floor_result = floor_max_pool(my_data)

print(my_data)
print(ceil_result)
print(floor_result)

运行结果:


下面我们考虑采样一下CIFAR10 这个数据集的图片,并使用 average_pooling 和 max_pooling 试一下。

import torch
import torchvision
from torch.utils.data import DataLoader
from torch.utils.data import Dataset
from torch.utils.tensorboard import SummaryWriter


data_path = "../../data_cifar10"
data_test = torchvision.datasets.CIFAR10(data_path, train=False, transform=torchvision.transforms.ToTensor(),
                                         download=True)
data_loader = DataLoader(data_test, batch_size=64)


class MyModel(torch.nn.Module):
    def __init__(self):
        super(MyModel, self).__init__()
        self.max_pool = torch.nn.MaxPool2d(kernel_size=3, ceil_mode=True)
        self.ave_pool = torch.nn.AvgPool2d(kernel_size=3, ceil_mode=True)

    def forward(self, img):
        return self.max_pool(img), self.ave_pool(img)


writer = SummaryWriter(log_dir="../../logs")
my_model = MyModel()
step = 0
for imgs, targets in data_loader:
    writer.add_images("images-original", imgs, step)
    imgs_max_pooling, imgs_ave_pooling = my_model(imgs)
    writer.add_images("images-max-pooling", imgs_max_pooling, step)
    writer.add_images("images-ave-pooling", imgs_ave_pooling, step)
writer.close()

Non-linear Activations

     非线性激活的作用

  总体来讲,激活函数是用来加入非线性因素的,解决线性模型所不能解决的问题。具体的一些详细的解释,请参考下述链接:
参考资料知乎:神经网络激活函数的作用和原理?有没有形象解释?

     Non-linear Activations Doc

  介绍完非线性激活的作用之后,我们查看 pytorch 关于非线性激活的 API 和他们的帮助文档。这里,我们主要介绍的是 ReLU 和 Sigmoid 两个常见的激活函数。




     代码示范

下面我们写一个代码查看一下 ReLU 和 Sigmoid 的效果

import torch.nn as nn
import torch
import torchvision

data = torch.randn((3, 3))
print(data)

my_relu = nn.ReLU()
my_sigmoid = nn.Sigmoid()

print(my_relu(data))
print(my_sigmoid(data))

运行结果如下所示:


神经网络的其它层次介绍

  首先我们查阅 torch.nn 的官方帮助文档:


  可以看到,他有很多层次 layers 和 功能函数 function,但是我们已经将最最最经常使用的层给介绍过了。下面我建议各位小伙伴们选择几个喜欢的层次,练一练自己的官方文档阅读能力。
  我选择我比较喜欢的两个部分,一个是 Normalization Layers 和 Dropout Layers

     Normalization Layers



     Dropout Layers

下面,我们简单写一个代码来进行测试一下看看,对CIFAR10 数据集首先进行 sigmod 操作,然后直接 Normalization 正则一下。

import torch
import torch.nn as nn
import torchvision
import torch.utils.data
from torch.utils.tensorboard import SummaryWriter


image_path = "../../data_cifar10"
log_path = "../../logs"
writer = SummaryWriter(log_dir=log_path)
dataset = torchvision.datasets.CIFAR10(root=image_path, train=False, transform=torchvision.transforms.ToTensor(), download=True)
dataloader = torch.utils.data.DataLoader(dataset, batch_size=64, drop_last=False)


class MyModel(nn.Module):
    def __init__(self):
        super(MyModel, self).__init__()
        self.model = nn.Sequential(
            nn.Sigmoid(),
            nn.BatchNorm2d(num_features=3)
        )

    def forward(self, x):
        return self.model(x)


my_model = MyModel()
step = 0
for images, targets in dataloader:
    model_images = my_model(images)
    writer.add_images("original-images", images, step)
    writer.add_images("sigmoid-norm-images", model_images, step)
    step += 1
writer.close()

运行 tensorboard 查看结果



     Dropout Layer

打开 dropout 官方文档,pick了 nn.Dropout2d 进行查阅。



下面,我写一个二维的卷积,续接这个 Dropout Layer 实例一下子:

import torch
import torch.nn as nn
import torchvision
import torch.utils.data
from torch.utils.tensorboard import SummaryWriter


image_path = "../../data_cifar10"
log_path = "../../logs"
writer = SummaryWriter(log_dir=log_path)
dataset = torchvision.datasets.CIFAR10(root=image_path, train=False, transform=torchvision.transforms.ToTensor(), download=True)
dataloader = torch.utils.data.DataLoader(dataset, batch_size=64, drop_last=False)


class MyModel(nn.Module):
    def __init__(self):
        super(MyModel, self).__init__()
        self.model = nn.Sequential(
            nn.Conv2d(in_channels=3, out_channels=3, kernel_size=3, stride=1, padding=1),
            nn.Dropout(p=0.2, inplace=False)
        )

    def forward(self, x):
        return self.model(x)


my_model = MyModel()
step = 0
for images, targets in dataloader:
    model_images = my_model(images)
    writer.add_images("read-original-images", images, step)
    writer.add_images("Con2d-Dropout-images", model_images, step)
    step += 1
writer.close()

tensorboard 查看图片



搭建CIFAR 10 model

  这里,我们将神经网络的基本结果说的差不过了,下面我们尝试建立一下一个简单的神经网络
在搭建一个神经网络之前,我们先介绍一下之前没有用过的两个函数。

  1. nn.Flatten()
  2. nn.linear()

     nn.Flatten() 函数

  该函数理解较为简单,帮助文档写的也是非常的简洁。


     nn.linear()函数

  挺简单的一个线性变换函数:


     CIFAR 10 Model 网络的构建

首先查看网络的样子:


然后根据网络之前的形态变换,将其转换成代码即可,主要看他们的 inchannel, outchannel,kernel 等等的信息。

写出来的网络代码:

import torch
import torchvision
import torch.nn as nn
from torch.utils.data import DataLoader
from torch.utils.tensorboard import SummaryWriter


images_path = "../../data_cifar10"
logs_path = "../../logs"
dataset = torchvision.datasets.CIFAR10(root=images_path, train=False, transform=torchvision.transforms.ToTensor(),
                                       download=True)
dataloader = DataLoader(dataset=dataset, batch_size=64, drop_last=False)


class MyModel(torch.nn.Module):
    def __init__(self):
        super(MyModel, self).__init__()
        self.model = torch.nn.Sequential(
            nn.Conv2d(in_channels=3, out_channels=32, kernel_size=5, stride=1, padding=2),
            nn.MaxPool2d(kernel_size=2, padding=0),
            nn.Conv2d(in_channels=32, out_channels=32, kernel_size=5, stride=1, padding=2),
            nn.MaxPool2d(kernel_size=2, padding=0),
            nn.Conv2d(in_channels=32, out_channels=64, kernel_size=5, stride=1, padding=2),
            nn.MaxPool2d(kernel_size=2, padding=0),
            nn.Flatten(start_dim=1, end_dim=-1), # 将它展平之后, dimension0 是 batch,后面的特征需要改变
            nn.Linear(in_features=1024, out_features=64),
            nn.Linear(in_features=64, out_features=10)
        )

    def forward(self, x):
        return self.model(x)


step = 0
my_model = MyModel()
tmp = torch.randn((1, 3, 32, 32))
# tmp2 = my_model(tmp)
writer = SummaryWriter(log_dir=logs_path)
# for images, targets in dataloader:
#     images_proceed = my_model(images)
#     writer.add_images("original", images, step)
#     writer.add_images("proceed", images_proceed, step) # 没办法的当图片写进入,应为规格不符要求
#     step += 1
writer.add_graph(my_model, input_to_model=tmp)
writer.close()

下面是看起来很有意思的运算图 graph


posted @ 2021-11-12 15:09  lucky_light  阅读(468)  评论(0编辑  收藏  举报