torch.nn.Sequential是一个Sequential容器,模块将按照构造函数中传递的顺序添加到模块中。
使用方式:
官方给出案例(上面所提的方式1 和 方式2的列子):
# Sequential使用实例
model = nn.Sequential(
nn.Conv2d(1,20,5),
nn.ReLU(),
nn.Conv2d(20,64,5),
nn.ReLU()
)
# Sequential with OrderedDict使用实例
model = nn.Sequential(OrderedDict([
('conv1', nn.Conv2d(1,20,5)),
('relu1', nn.ReLU()),
('conv2', nn.Conv2d(20,64,5)),
('relu2', nn.ReLU())
]))
以上两种形式为一致。一种自动命名,一种指定名字。
验证:
需要在pytorch环境下运行。
先添加:
import torch.nn as nn
from collections import OrderedDict
打印模型输出:
print(model)
---------------------------------------------------------
Sequential(
(0): Conv2d(1, 20, kernel_size=(5, 5), stride=(1, 1))
(1): ReLU()
(2): Conv2d(20, 64, kernel_size=(5, 5), stride=(1, 1))
(3): ReLU()
)
使用torch.nn.Sequential可以快速的搭建一个神经网络
为比较,先用普通方法搭建一个神经网络来对比
class Net(torch.nn.Module):
def __init__(self, n_feature, n_hidden, n_output):
super(Net, self).__init__()
self.hidden = torch.nn.Linear(n_feature, n_hidden)
self.predict = torch.nn.Linear(n_hidden, n_output)
def forward(self, x):
x = F.relu(self.hidden(x))
x = self.predict(x)
return x
net1 = Net(1, 10, 1)
上面class继承了一个torch中的神经网络结构, 然后对其进行了修改;
接下来我们来使用torch.nn.Sequential来快速搭建一个神经网络。
net2 = torch.nn.Sequential(
torch.nn.Linear(1, 10),
torch.nn.ReLU(),
torch.nn.Linear(10, 1)
)
打印输出上面2个神经网络数据,查看区别:
print(net1)
---------------------------------------------------------
Net (
(hidden): Linear (1 -> 10)
(predict): Linear (10 -> 1)
)
---------------------------------------------------------
print(net2)
---------------------------------------------------------
Sequential (
(0): Linear (1 -> 10)
(1): ReLU ()
(2): Linear (10 -> 1)
)
---------------------------------------------------------
我们可以发现,使用torch.nn.Sequential会自动加入激励函数, 但是 net1 中, 激励函数实际上是在 forward() 功能中才被调用的.
torch.nn.Sequential与torch.nn.Module区别与选择
源自:https://pytorch.org/docs/1.2.0/nn.html#conv2d
nn.Conv2d是二维卷积方法,相对应的还有一维卷积方法nn.Conv1d,常用于文本数据的处理,而nn.Conv2d一般用于二维图像。
接口定义:
class torch.nn.Conv2d(in_channels, out_channels, kernel_size, stride=1, padding=0, dilation=1, groups=1, bias=True,padding_mode='zeros')
参数解释:
channel:通道数。
一般的RGB图片,channels 数量是 3 (红、绿、蓝);而单色/灰度图片,channels 数量是 1。
channels 一般分为三种:
dilation:空洞卷积,控制 kernel 点之间的空间距离,实际在上采样上有利用(在FCN、U-net等网络结构中),看下图有助于理解
groups:分组卷积
Convolution 层的参数中有一个group参数,其意思是将对应的输入通道与输出通道数进行分组, 默认值为1, 也就是说默认输出输入的所有通道各为一组。
比如:输入数据大小为90x100x100x32,通道数32,要经过一个3x3x48的卷积,group默认是1,就是全连接的卷积层。
如果group是2,那么对应要将输入的32个通道分成2个16的通道,将输出的48个通道分成2个24的通道。对输出的2个24的通道,第一个24通道与输入的第一个16通道进行全卷积,第二个24通道与输入的第二个16通道进行全卷积。
极端情况下,输入输出通道数相同,比如为24,group大小也为24,那么每个输出卷积核,只与输入的对应的通道进行卷积。
源自:https://pytorch.org/docs/1.2.0/nn.html#batchnorm2d
BatchNorm(批规范化)主要是为了加速神经网络的收敛过程以及提高训练过程中的稳定性。通常用于解决多层神经网络中间层的协方差偏移(Internal Covariate Shift)问题,类似于网络输入进行零均值化和方差归一化的操作,不过是在中间层的输入中操作而已。使一批(Batch)feature map满足均值为0,方差为1的分布规律。这样不仅数据分布一致,而且避免发生梯度消失。
接口定义:
torch.nn.BatchNorm2d(num_features, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
参数解释:
公式:
y = γ × ( x − E [ x ] V a r [ x ] + ε ) + β \ y=γ×({x-E[x] \over \sqrt{Var[x]+ε} \quad })+β\, y=γ×(Var[x]+εx−E[x])+β
( γ \ γ\, γ 和 β \ β\, β在反向传播过程中训练得到,是对像素在BN的基础上进行的调整,具体数值是模型学习出来的,初始化 γ = 1 \ γ=1\, γ=1用来调整方差, β = 0 \ β=0\, β=0用来调整均值。
用于抵消部分标准化带来的影响,因为有时候可能标准化了不好,这个时候这两个变量就有足够的弹性来学习修整这种标准化了。)
在训练时,该层计算每次输入的均值和方差,进行移动平均。移动平均momentum 默认的动量值为0.1
在验证时,训练求得的均值和方差将用于标准化验证数据。
参数详解:
在BN操作中,最重要的无非是这四个式子:
更新过程:
注意到这里的最后一步也称之为仿射(affine),引入这一步的目的主要是设计一个通道,使得输出output至少能够回到输入input的状态(当 γ = 1 \ γ=1\, γ=1, β = 0 \ β=0\, β=0时)使得BN的引入至少不至于降低模型的表现,这是深度网络设计的一个套路。
一般来说pytorch中的模型都是继承nn.Module类的,都有一个属性trainning指定是否是训练状态,训练状态与否将会影响到某些层的参数是否是固定的,比如BN层或者Dropout层。通常用model.train()指定当前模型model为训练状态,model.eval()指定当前模型为测试/验证状态。
同时,BN的API中有几个参数需要比较关心的,一个是affine指定是否需要仿射,还有个是track_running_stats指定是否跟踪当前batch的统计特性。容易出现问题也正好是这三个参数:trainning,affine,track_running_stats。
神经网络中有各种归一化算法:Batch Normalization (BN)、Layer Normalization (LN)、Instance Normalization (IN)、Group Normalization (GN)。
从公式看它们都差不多:无非是减去均值,除以标准差,再施以线性映射。
y = γ ( x − μ ( x ) σ ( x ) ) + β \ y=γ({x-μ(x) \overσ (x)})+β\, y=γ(σ(x)x−μ(x))+β这些归一化算法的主要区别在于操作的 feature map 维度不同。
Batch Normalization (BN) 是最早出现的,也通常是效果最好的归一化方式。feature map: x ∈ R N × C × H × W . \ x∈R^{N×C×H×W} \,. x∈RN×C×H×W.包含 N 个样本,每个样本通道数为 C,高为 H,宽为 W。对其求均值和方差时,将在 N、H、W上操作,而保留通道 C 的维度。
具体来说,就是把第1个样本的第1个通道,加上第2个样本第1个通道 … 加上第 N 个样本第1个通道,求平均,得到通道 1 的均值(注意是除以 N×H×W 而不是单纯除以 N,最后得到的是一个代表这个 batch 第1个通道平均值的数字,而不是一个 H×W 的矩阵)。求通道 1 的方差也是同理。对所有通道都施加一遍这个操作,就得到了所有通道的均值和方差。具体公式为:
这里有个特别好的比喻,便于理解:
如果把 x ∈ R N × C × H × W . \ x∈R^{N×C×H×W} \,. x∈RN×C×H×W.类比为一摞书,这摞书总共有 N 本,每本有 C 页,每页有 H 行,每行 W 个字符。BN 求均值时,相当于把这些书按页码一一对应地加起来(例如第1本书第36页,第2本书第36页…),再除以每个页码下的字符总数: N × H × W . \ N×H×W \,. N×H×W.,因此可以把 BN 看成求“平均书”的操作(注意这个“平均书”每页只有一个字),求标准差时也是同理。
详细区别和参数细节建议参考:知乎,CSDN。
源自:https://pytorch.org/docs/1.2.0/nn.html#relu
nn.ReLU() 是封装好的类,继承nn.Module
接口定义:
class torch.nn.ReLU(inplace: bool = False)
参数定义:
inplace: can optionally do the operation in=plase. Default: False
inplace:默认为false(选择是否进行覆盖运算,意思是:是否将得到的值计算得到的值覆盖之前的值)
inplace=True,会改变输入数据,inplace=False,不会改变输入数据,只会产生新的输出
注:产生的计算结果不会有影响。利用in-place计算可以节省内(显)存,同时还可以省去反复申请和释放内存的时间。但是会对原变量覆盖,只要不带来错误就用。
对于ReLU非线性激励函数,其公式为:
R e L U ( x ) = m a x ( 0 , x ) \ ReLU(x)=max(0, x)\, ReLU(x)=max(0,x)
nn.ReLU() 与 F.relu()的区别
nn.ReLU() :
import torch.nn as nn
'''
nn.RuLU()
F.relu():
import torch.nn.functional as F
'''
out = F.relu(input)
其实这两种方法都是使用relu激活,只是使用的场景不一样,F.relu()是函数调用,一般使用在foreward函数里。而nn.ReLU()是模块调用,一般在定义网络层的时候使用,作为一个层结构,必须添加到nn.Module容器中才能使用。
1.为什么引入非线性激励函数?
如果不适用激励函数,那么在这种情况下每一层的输出都是上层输入的线性函数,很容易验证,无论你神经网络有多少层,输出都是输入的线性组合,与没有隐藏层效果相当,这种情况就是最原始的感知机(perceptron)了。正因为上面的原因,我们决定引入非线性函数作为激励函数,这样深层神经网络就有意义了,不再是输入的线性组合,可以逼近任意函数,最早的想法是用sigmoid函数或者tanh函数,输出有界,很容易充当下一层的输入
2.为什么引入Relu?
源自:https://pytorch.org/docs/1.2.0/nn.html#torch.nn.MaxPool2d
接口定义:
class torch.nn.MaxPool2d(kernel_size, stride=None, padding=0, dilation=1, return_indices=False, ceil_mode=False)
参数定义:
如果padding 不是0,会在输入的每一边添加相应数目0,如padding=1,则在每一边分别补0,其实最后的结果补出来是bias