池化运算:对信号进行“收集”并“总结”,类似水池收集水资源,因为得名池化层。
收集:多变少;
功能:对二维信号(图像)进行最大值池化;
nn.MaxPool2d(kernel_size, stride=None,
padding=0, dilation=1,
return_indices=False,
ceil_mode=False)
( 最 大 值 反 池 化 示 意 图 ) (最大值反池化示意图) (最大值反池化示意图)】
早期的自编码器以及图像分割任务中都会涉及一个上采样的操作,会采用一个最大值反池化上采样。上图中的左边是一个正常的最大值池化下采样,将一个44的图像下采样到22,经过一系列的网络层之后来到一个上采样解码器的过程,就是将尺寸较小的图片上采样到尺寸较大的图片,这时就会涉及一个将像素值放到什么位置的问题。这时候就可以使用到在最大值池化中使用到的索引。
下面通过代码查看nn.MaxPool2d的具体使用方法:
import os
import torch
import random
import numpy as np
import torchvision
import torch.nn as nn
from torchvision import transforms
from matplotlib import pyplot as plt
from PIL import Image
from common_tools import transform_invert, set_seed
set_seed(1) # 设置随机种子
path_img = os.path.join(os.path.dirname(os.path.abspath(__file__)), "lena.png")
img = Image.open(path_img).convert('RGB') # 0~255
# convert to tensor
img_transform = transforms.Compose([transforms.ToTensor()])
img_tensor = img_transform(img)
img_tensor.unsqueeze_(dim=0) # C*H*W to B*C*H*W
maxpool_layer = nn.MaxPool2d((2, 2), stride=(2, 2)) # input:(i, o, size) weights:(o, i , h, w)
img_pool = maxpool_layer(img_tensor)
print("池化前尺寸:{}\n池化后尺寸:{}".format(img_tensor.shape, img_pool.shape))
img_pool = transform_invert(img_pool[0, 0:3, ...], img_transform)
img_raw = transform_invert(img_tensor.squeeze(), img_transform)
plt.subplot(122).imshow(img_pool)
plt.subplot(121).imshow(img_raw)
plt.show()
图片输出如下图所示:
通过图片可以发现,虽然图片的尺寸减小了,但是图片的质量还是可以的。池化操作可以实现一个冗余信息的剔除以及减小后面的计算量。
除了最大池化,还有另外一个常用的池化方法是平均池化。
功能:对二维信号(图像)进行平均值池化;
nn.AvgPool2d(kernel_size,
stride=None,
padding=0,
ceil_mode=False,
count_include_pad=True,
divisor_override=None)
主要参数:
下面通过代码学习nn.AvgPool2d:
import os
import torch
import random
import numpy as np
import torchvision
import torch.nn as nn
from torchvision import transforms
from matplotlib import pyplot as plt
from PIL import Image
from common_tools import transform_invert, set_seed
set_seed(1) # 设置随机种子
path_img = os.path.join(os.path.dirname(os.path.abspath(__file__)), "lena.png")
img = Image.open(path_img).convert('RGB') # 0~255
# convert to tensor
img_transform = transforms.Compose([transforms.ToTensor()])
img_tensor = img_transform(img)
img_tensor.unsqueeze_(dim=0) # C*H*W to B*C*H*W
avgpoollayer = nn.AvgPool2d((2, 2), stride=(2, 2)) # input:(i, o, size) weights:(o, i , h, w)
img_pool = avgpoollayer(img_tensor)
print("池化前尺寸:{}\n池化后尺寸:{}".format(img_tensor.shape, img_pool.shape))
img_pool = transform_invert(img_pool[0, 0:3, ...], img_transform)
img_raw = transform_invert(img_tensor.squeeze(), img_transform)
plt.subplot(122).imshow(img_pool)
plt.subplot(121).imshow(img_raw)
plt.show()
得到的输出图片如下所示,对比于最大池化,平均池化的图片亮度比最大池化的稍微暗一点:
功能:对二维信号(图像)进行最大值池化上采样,将小尺寸图片池化为大尺寸图片;
上图中的反池化过程将一个22的图片反池化为一个44的图片,这里涉及像素值应该放到哪一个位置的问题,放到哪一个位置由最大值池化中记录的最大值像素所在的位置,所在的索引。把最大值池化层中的最大值像素所在的位置传入到反池化层中,会根据从最大值池化中得到的索引将像素值放到对应的位置。所以最大值反池化层和池化层是类似的。唯一不同就是在forward()函数中药传入一个indices,也就是反池化需要的索引值。
nn.MaxUnpool2d(kernel_size,
stride=None,
padding=0)
forward(self, input, indices, output_size=None)
主要参数:
下面通过代码学习最大值反池化:
# pooling
img_tensor = torch.randint(high=5, size=(1, 1, 4, 4), dtype=torch.float) # randint()随机初始化一个4*4的图像,
maxpool_layer = nn.MaxPool2d((2, 2), stride=(2, 2), return_indices=True) # 进行最大值池化
img_pool, indices = maxpool_layer(img_tensor) # 记录最大值所在的indices
# unpooling
img_reconstruct = torch.randn_like(img_pool, dtype=torch.float) # 创建一个反池化的输入,这个输入的尺寸和池化之后的尺寸是一样的
maxunpool_layer = nn.MaxUnpool2d((2, 2), stride=(2, 2)) # 构建反池化层
img_unpool = maxunpool_layer(img_reconstruct, indices) # 将输入和indices索引传入反池化层
print("raw_img:\n{}\nimg_pool:\n{}".format(img_tensor, img_pool))
print("img_reconstruct:\n{}\nimg_unpool:\n{}".format(img_reconstruct, img_unpool))
其输出结果如下所示:
raw_img:
tensor([[[[0., 4., 4., 3.],
[3., 3., 1., 1.],
[4., 2., 3., 4.],
[1., 3., 3., 0.]]]])
img_pool:
tensor([[[[4., 4.],
[4., 4.]]]])
img_reconstruct:
tensor([[[[-1.0276, -0.5631],
[-0.8923, -0.0583]]]])
img_unpool:
tensor([[[[ 0.0000, -1.0276, -0.5631, 0.0000],
[ 0.0000, 0.0000, 0.0000, 0.0000],
[-0.8923, 0.0000, 0.0000, -0.0583],
[ 0.0000, 0.0000, 0.0000, 0.0000]]]])
线性层又称为全连接层,其每个神经元与上一层所有神经元相连,实现对前一层的线性组合。
功能: 对一维信号(向量)进行线性组合;
nn.Linear(in_features, out_features, bias=True)
主要参数:
计算公式: y = x W T + b i a s y=xW^T+bias y=xWT+bias
下面通过代码查看怎么使用nn.Linear:
inputs = torch.tensor([[1., 2, 3]]) # 构建一个输入
linear_layer = nn.Linear(3, 4) # 构建一个线性层
linear_layer.weight.data = torch.tensor([[1., 1., 1.], # 对权值矩阵进行初始化
[2., 2., 2.],
[3., 3., 3.],
[4., 4., 4.]])
linear_layer.bias.data.fill_(0.5) # 偏置
output = linear_layer(inputs)
print(inputs, inputs.shape)
print(linear_layer.weight.data, linear_layer.weight.data.shape)
print(output, output.shape)
其对应输出为:
tensor([[1., 2., 3.]]) torch.Size([1, 3])
tensor([[1., 1., 1.],
[2., 2., 2.],
[3., 3., 3.],
[4., 4., 4.]]) torch.Size([4, 3])
tensor([[ 6.5000, 12.5000, 18.5000, 24.5000]], grad_fn=<AddmmBackward>) torch.Size([1, 4])
激活函数对特征进行非线性变换,赋予多层神经网络具有深度的意义。
计算公式: y = 1 1 + e − x y=\frac{1}{1+e^{-x}} y=1+e−x1
梯度公式: y ′ = y ∗ ( 1 − y ) y'=y * (1-y) y′=y∗(1−y)
特性:
计算公式: y = s i n x c o s x = e x − e − x e x + e − x = 2 1 + e − 2 x + 1 y=\frac{sinx}{cosx}=\frac{e^x-e^{-x}}{e^x+e^{-x}}=\frac{2}{1+e{-2x}}+1 y=cosxsinx=ex+e−xex−e−x=1+e−2x2+1
梯度公式: y ′ = 1 − y 2 y'=1-y^2 y′=1−y2
特性:
计算公式: y = m a x ( 0 , x ) y=max(0,x) y=max(0,x)
梯度公式: y ′ = { 1 , x > 0 u n d e f i n e d , x = 0 0 , x < 0 y'=\left\{\begin{matrix} 1,x>0\\ undefined, x=0\\ 0, x<0 \end{matrix}\right. y′=⎩⎨⎧1,x>0undefined,x=00,x<0
特性:
nn.LeakyReLU
nn.PReLU
nn.PReLU