持续更新.......
PyTorch
把与深度学习模型搭建相关的全部类全部在 torch.nn
这个子模块中。根据类的功能分为以下几大部分:
- Containers:容器类,如
torch.nn.Module
;torch.nn.ModuleList
;torch.nn.Sequential()
; - Convolution Layers:卷积层,如
torch.nn.Conv2d
; - Pooling Layers:池化层,如
torch.nn.MaxPool2d
; - Non-linear activations:非线性激活层,如
torch.nn.ReLU
; - Normalization layers:归一化层,如
torch.nn.BatchNorm2d
; - Recurrent layers:循环神经层,如
torch.nn.LSTM
; - Transformer layers:transformer 层,如
torch.nn.TransformerEncoder
; - Linear layers:线性连接层,如
torch.nn.Linear
; - Dropout layers:dropout 层,如
torch.nn.Dropout
;
torch.device
torch.device 是 PyTorch 中的一个类,它指定了张量(Tensor)和模型应该运行在哪个设备上。在深度学习中,通常会使用 GPU 来加速计算,而 torch.device 允许你轻松地在 CPU 和 GPU 之间切换。
torch.device代表将torch.Tensor分配到的设备的对象。
torch.device包含一个设备类型('cpu'或'cuda'设备类型)和可选的设备的序号。如果设备序号不存在,则为当前设备;
例如,torch.Tensor用设备构建'cuda'的结果等同于'cuda:X',其中X是torch.cuda.current_device()的结果。
torch.Tensor的设备可以通过Tensor.device访问属性。
在使用torch.device之前,你需要了解当前系统上可用的设备。可以使用 torch.cuda.device_count() 来获取系统上 GPU 的数量。如果系统没有 GPU,这个函数会返回 0。
import torch
''' 获取 GPU 的数量 '''
gpu_count=torch.cuda.device_count()print(f"Number of GPUs available: {gpu_count}")
(1)通过字符串构造设备
torch.device('cpu')
torch.device('cuda') # current cuda devicedevice(type='cuda')
(2)通过字符串+序号构造设备
torch.device('cuda', 0),device(type='cuda', index=0)
torch.device('cpu', 0),device(type='cpu', index=0)
(3)可直接使用字符串构建
torch.device函数中的参数通常可以用一个字符串替代。这允许使用代码快速构建原型。
cuda1 = torch.device('cuda:1')
torch.randn((2,3), device=cuda1)
torch.randn((2,3), 'cuda:1')
两种方法等同
(4)有cuda的也可直接输入序号
出于传统原因,可以通过单个设备序号构建设备,将其视为cuda设备。这匹配Tensor.get_device(),它为cuda张量返回一个序数,并且不支持cpu张量。
torch.device(1)
device(type='cuda', index=1)
(5)创建Tensor时直接指定设备
指定设备的方法可以使用(properly formatted)字符串或(legacy)整数型设备序数,即以下示例均等效:
torch.randn((2,3), device=torch.device('cuda:1'))
torch.randn((2,3), device='cuda:1')
torch.randn((2,3), device=1) # legacy
(6)在CPU和GPU之间进行数据的互转
# 创建一个在 CPU 上的张量
tensor_on_cpu = torch.randn(2, 2)
# 将张量移动到 GPU
tensor_on_gpu = tensor_on_cpu.to(device)
# 再将张量移回 CPU
tensor_on_cpu = tensor_on_gpu.to("cpu")
torch.cuda
该包增加了对CUDA张量类型的支持,实现了与CPU张量相同的功能,但使用GPU进行计算。
在PyTorch中,从CUDA获取数据主要涉及两个步骤:将数据移至GPU,以及从GPU获取数据。
将数据移至GPU
在PyTorch中,可以使用.cuda()
方法将张量移至GPU。例如,假设我们有一个名为x
的张量,并且我们想要将其移至GPU,可以执行以下操作:
x = x.cuda()
这将把x
张量移至GPU上,使其能够利用GPU的并行计算能力。
从GPU获取数据
要从GPU获取数据,可以使用.cpu()
方法将张量从GPU移回CPU。例如,假设我们有一个名为y
的张量,它当前位于GPU上,我们想要将其移回CPU,可以执行以下操作:
y = y.cpu()
这将把y
张量从GPU移回CPU,使得我们可以在CPU上对其进行进一步处理。
利用CUDA加速PyTorch计算
在PyTorch中,通过将模型和数据移至GPU,我们可以利用CUDA加速计算。下面是一个简单的示例,展示了如何在PyTorch中使用CUDA加速计算:
import torch
import torch.nn as nn
import torch.optim as optim
# 定义一个简单的线性模型
model = nn.Linear(10, 1)
# 将模型移至GPU
model = model.cuda()
# 定义损失函数和优化器
criterion = nn.MSELoss()
optimizer = optim.SGD(model.parameters(), lr=0.01)
# 生成一些随机数据作为输入和输出
inputs = torch.randn(100, 10).cuda()
outputs = torch.randn(100, 1).cuda()
# 训练模型
for epoch in range(100):
# 前向传播
outputs_pred = model(inputs)
loss = criterion(outputs_pred, outputs)
# 反向传播和优化
optimizer.zero_grad()
loss.backward()
optimizer.step()
# 打印损失值
print('Epoch [{}/{}], Loss: {:.4f}'.format(epoch+1, 100, loss.item()))
在上述示例中,我们首先定义了一个简单的线性模型,并将其移至GPU。然后,我们生成了一些随机数据作为输入和输出,并将它们也移至GPU。在训练过程中,我们利用GPU进行前向传播、反向传播和优化,从而加速模型的训练。
torch.randn
torch.randn
函数是PyTorch中用于生成具有正态分布(均值为0,标准差为1)的随机数的函数。它可以用于创建具有指定形状的张量,并且张量中的每个元素都是独立的随机数,遵循标准正态分布(均值为0,标准差为1),该函数的构成如下:
torch.randn(*size, out=None, dtype=None, layout=torch.strided, device=None, requires_grad=False)
下面是各个参数的解释:
*size:表示生成的张量的形状。这是一个可变参数,你可以传递一个或多个整数来指定张量的形状。例如,torch.randn(2, 3)将创建一个形状为(2, 3)的张量。
out:可选参数,如果指定了这个参数,生成的随机张量将会被存储在这个参数指定的张量中。
dtype:可选参数,用于指定生成的随机张量的数据类型。默认为None,表示使用默认的数据类型(通常是float32)。
layout:可选参数,用于指定生成的随机张量的布局。默认是torch.strided,通常不需要修改。
device:可选参数,用于指定生成的随机张量所在的设备。默认是None,表示使用默认的设备(通常是CPU)。你可以指定为torch.device("cuda:0")来将随机张量放在GPU上。
requires_grad:可选参数,用于指定生成的随机张量是否需要梯度跟踪。默认为False。
这些参数可以根据你的需求进行自定义,但最常见的用法是传递一个或多个整数作为*size
参数来指定生成的随机张量的形状。
以下是torch.randn
函数的用法示例:
import torch
# 创建一个形状为(3, 4)的张量,其中的元素都是独立的随机数
random_tensor = torch.randn(3, 4)
print(random_tensor)
在这个示例中,random_tensor
是一个形状为(3, 4)的张量,其中包含了随机生成的标准正态分布的随机数。
我们也可以使用mean
和std
参数来指定不同均值和标准差的正态分布。例如:
# 创建一个形状为(2, 2)的张量,均值为2,标准差为0.5的正态分布随机数
custom_random_tensor = torch.randn(2, 2, mean=2, std=0.5)
print(custom_random_tensor)
这将创建一个形状为(2, 2)的张量,其中的元素是从均值为2,标准差为0.5的正态分布中随机抽样得到的随机数。
总之,torch.randn
函数是一个用于生成随机数的常用函数,可以用于初始化神经网络的权重,生成随机数据进行实验等多种情况。
permute
主要用于变换tensor的维度。与transpose的区别在于permute能处理任意高维矩阵的转置,而transpose仅限于2D矩阵。此外,permute操作可能使tensor在内存上不连续,需要使用contiguous()使其连续以便进行view操作。在最新版本中,torch.reshape()简化了这一流程。
permute(dims)
参数dims用矩阵的维数代入,一般默认从0开始。即第0维,第1维等等
也可以理解为,第0块,第1块等等。当然矩阵最少是两维才能使用permute
如是两维,dims分别为是0和1
可以写成permute(0,1)这里不做任何变化,维数与之前相同
如果写成permute(1,0)得到的就是矩阵的转置
如果三维是permute(0,1,2)
import torch
x = torch.randn(2, 3, 5)
print(x.size())
print(x.permute(2, 0, 1).size())
>>>torch.Size([2, 3, 5])
>>>torch.Size([5, 2, 3])
0代表共有几块维度:本例中0对应着2块矩阵
1代表每一块中有多少行:本例中1对应着每块有3行
2代表每一块中有多少列:本例中2对应着每块有5列
所以是3块2行5列的三维矩阵
这些0,1,2并没有任何实际的意义,也不是数值,只是用来标识区别。有点类似于x,y,z来区分三个坐标维度,是人为规定好的
nn.Module
PyTorch中nn.Module基类的定义
在PyTorch中,nn.Module
类是构建神经网络模型的基础类,所有自定义的层、模块或整个神经网络架构都需要继承自这个类。nn.Module
类提供了一系列属性和方法用于管理网络的结构和训练过程中的计算。尽管这里不能提供完整的源代码(因为它涉及大量内部逻辑和API细节),但我可以给出一个简化的 nn.Module
类的基本结构,并描述其关键方法:
#此处简化了nn.Module的定义,实际PyTorch源码更为复杂
import torch
class nn.Module:
def __init__(self):
super().__init__()
#存储子模块的字典
self. modules = dict()
#参数和缓冲区的集合
self._parameters =OrderedDict()
self._buffers = OrderedDict()
def __setattr(self,name, value):
#特殊处理参数和子模块的设置
if isinstance(value, nn.Parameter):
#注册参数到_parameters 字典中
self.register_parameter(name, value)
elif isinstance(value, Module) and not isinstance(value,Container):
#注册子模块到_modules字典中
self.add_module(name, value)
else:
#对于普通属性,执行标准的setattr操作
object.__setattr__(self, name, value
def add_module(self, name: str, module: 'Module') -> Nlone
r"""添加子模块到当前模块"""
#内部实现细节省略..
self._modules[name] = module
def register_parameter(self, name: str, param: nn.Parameter) -> None:
r""注册一个新的参数"""
#内部实现细节省略...
self._parameters[name]= param
def parameters(self, recurse: bool = True)->Iterator[nn.Parameter]:
r"""返回一个包含所有可学习参数的迭代器"""
#内部实现细节省略.
return iter(getattr(self, '_parameters', {}).values())
def forward(self, *input: Tensor)->Tensor:
r"""定义前向传播操作""
raise NotImplementedError
#还有许多其他的方法如:zero_grad、to、state_dict、load_state_dict等等...
#在自定义模型时,继承nn.Module并重写forward方法
class MyModel(nn.Module):
def __init__(self):
super(MyModel, self). init
self.linear = nn.Linear(20, 30)
def forward(self, x):
return self.linear(x)
这段代码定义了 PyTorch 中 nn.Module
类的基础结构。在实际的 PyTorch 源码中,nn.Module
的实现更为复杂,但这里简化后的代码片段展示了其核心部分。
-
class nn.Module:
:定义了一个名为nn.Module
的类,它是所有神经网络模块(如卷积层、全连接层、激活函数等)的基类。 -
def __init__(self):
:这是类的初始化方法,在创建一个nn.Module
或其子类实例时会被自动调用。这里的self
参数代表将来创建出的实例自身。-
super().__init__()
:调用父类的构造函数,确保基类的初始化逻辑得到执行。在这里,虽然没有显示指定父类,但因为nn.Module
是其他所有模块的基类,所以实际上它是在调用自身的构造函数来初始化内部状态。 -
self._modules = dict()
:声明并初始化一个字典_modules
,用于存储模型中的所有子模块。每个子模块是一个同样继承自nn.Module
的对象,并通过名称进行索引。这样可以方便地管理和组织复杂的层次化网络结构。 -
self._parameters = OrderedDict()
:使用有序字典(OrderedDict)类型声明和初始化一个变量_parameters
,用来保存模型的所有可学习参数(权重和偏置等)。有序字典保证参数按添加顺序存储,这对于一些依赖参数顺序的操作(如加载预训练模型的权重)是必要的。 -
self._buffers = OrderedDict()
:类似地,声明并初始化另一个有序字典_buffers
,用于存储模型中的缓冲区(Buffer)。缓冲区通常是不参与梯度计算的变量,比如在 BatchNorm 层中存储的均值和方差统计量。
-
总结来说,这段代码为构建神经网络模型提供了一个基础框架,其中包含了对子模块、参数和缓冲区的管理机制,这些基础设施对于构建、运行和优化深度学习模型至关重要。在自定义模块时,开发者通常会在此基础上添加更多的层和功能,并重写 forward
方法以定义前向传播逻辑。
nn.Module类中的关键属性
在PyTorch的nn.Module
类中,有以下几个关键属性和方法:
-
init(self, ...)
: 这是每个派生自nn.Module
的类都必须重载的方法,在该方法中定义并初始化模型的所有层和参数。 -
.parameters()
:这是一个动态生成器,用于获取模型的所有可学习参数(权重和偏置等)。这些参数都是nn.Parameter
类型的张量,在训练过程中可以自动计算梯度。
示例:
for param in model.parameters
print(param)
.buffers()
:类似于.parameters()
,但返回的是模块内定义的非可学习缓冲区变量,例如一些统计量或临时存储数据。
.named_parameters()
和 .named_buffers()
:与上面类似,但返回元组形式的迭代器,每个元素是一个包含名称和对应参数/缓冲区的元组,便于按名称访问特定参数。
.children()
和 .modules()
:这两个方法分别返回一个包含当前模块所有直接子模块的迭代器和包含所有层级子模块(包括自身)的迭代器。
.state_dict()
:该方法返回一个字典,包含了模型的所有状态信息(即参数和缓冲区),方便保存和恢复模型。
.train()
和 .eval()
:方法用于切换模型的运行模式。在训练模式下,某些层如批次归一化层会有不同的行为;而在评估模式下,通常会禁用dropout层并使用移动平均统计量(对于批归一化层)。
._parameters
和 ._buffers
:这是内部字典属性,分别储存了模型的所有参数和缓冲区,虽然不推荐直接操作,但在自定义模块时可能需要用到。
.to(device)
:将整个模型及其参数转移到指定设备上,比如从CPU到GPU。
其他内部维护的属性,如 _forward_pre_hooks
和 _forward_hooks
用于实现向前传播过程中的预处理和后处理钩子,以及 _backward_hooks
用于反向传播过程中的钩子,这些通常在高级功能开发时使用。
forward(self, input)
:定义模型如何处理输入数据并生成输出,这是构建神经网络的核心部分,每次调用模型实例都会执行 forward
函数。
add_module(name, module)
:将一个子模块添加到当前模块,并通过给定的名字引用它。
register_parameter(name, param)
:注册一个新的参数到模块中。
zero_grad()
:将模块及其所有子模块的参数梯度设置为零,通常在优化器更新前调用。
train(mode=True)
和 eval()
:切换模型的工作模式,在训练模式下会启用批次归一化层和丢弃层等依赖于训练/预测阶段的行为,在评估模式下则关闭这些行为。
state_dict()
和 load_state_dict(state_dict)
:用于保存和加载模型的状态字典,其中包括模型的权重和配置信息,便于模型持久化和迁移。
其他与模型保存和恢复相关的方法,例如 save(filename)
、load(filename)
等。
请注意,具体的属性和方法可能会随着PyTorch版本的更新而有所增减或改进。
nn.Module子类的定义和使用
在PyTorch中,nn.Module
类扮演着核心角色,它是构建任何自定义神经网络层、复杂模块或完整神经网络架构的基础构建块。通过继承 nn.Module
并在其子类中定义模型结构和前向传播逻辑(forward()
方法),开发者能够方便地搭建并训练深度学习模型。
具体来说,在自定义一个 nn.Module
子类时,通常会执行以下操作:
初始化 (init
):在类的初始化方法中定义并实例化所有需要的层、参数和其他组件。
class MyModel(nn.Module):
def __init__(self, input_size, hidden_size, output_size):
super(MyModel, self).__init__()
self.layer1 = nn.Linear(input_size, hidden_size)
self.layer2 = nn.Linear(hidden_size, output_size)
前向传播 (forward
):实现前向传播函数来描述输入数据如何通过网络产生输出结果。
class MyModel(nn.Module):
def forward(self, x):
x = torch.relu(self.layer1(x))
x = self.layer2(x)
return x
管理参数和模块:
-
使用
.parameters()
或.named_parameters()
访问模型的所有可学习参数。 -
使用
add_module()
添加子模块,并给它们命名以便于访问。 -
使用
register_buffer()
为模型注册非可学习的缓冲区变量。
训练与评估模式切换:
-
使用
model.train()
将模型设置为训练模式,这会影响某些层的行为,如批量归一化层和丢弃层。 -
使用
model.eval()
将模型设置为评估模式,此时会禁用这些依赖于训练阶段的行为。
保存和加载模型状态:
-
调用
model.state_dict()
获取模型权重和优化器状态的字典形式。 -
使用
torch.save()
和torch.load()
来保存和恢复整个模型或者仅其状态字典。 -
通过
model.load_state_dict(state_dict)
加载先前保存的状态字典到模型中。
此外,nn.Module
还提供了诸如移动模型至不同设备(CPU或GPU)、零化梯度等实用功能,这些功能在整个模型训练过程中起到重要作用。
nn.ModuleList
pytorch小记:nn.ModuleList和nn.Sequential的用法以及区别-CSDN博客
神经网络工具箱 torch.nn之Module、ModuleList、Sequential
ModuleList
就像一个普通的Python的List
,我们可以使用下标来访问它,好处是传入的ModuleList
的所有Module
都会注册到PyTorch里,这样Optimizer
就能找到其中的参数,从而用梯度下降进行更新。但是nn.ModuleList
并不是Module
(的子类),因此它没有forward
等方法,通常会被放到某个Module
里。
只用list
来定义网络中的层:
import torch
import torch.nn as nn
class MyNet(nn.Module):
def __init__(self):
super(MyNet, self).__init__()
self.combine = []
self.combine.append(nn.Linear(200,100))
self.combine.append(nn.Linear(100,50))
Net = MyNet()
print(Net)
结果:
MyNet()
可以看到结果并没有显示添加的全连接层信息,接下来采用ModuleList:
import torch
import torch.nn as nn
class MyNet(nn.Module):
def __init__(self):
super(MyNet, self).__init__()
self.combine = nn.ModuleList()
self.combine.append(nn.Linear(200,100))
self.combine.append(nn.Linear(100,50))
Net = MyNet()
print(Net)
结果:
MyNet(
(combine): ModuleList(
(0): Linear(in_features=200, out_features=100, bias=True)
(1): Linear(in_features=100, out_features=50, bias=True)
)
)
可以看到PyTorch 自动识别 nn.ModuleList
中的参数,不能识别普通的 list
。
注意:nn.ModuleList
并没有定义一个网络,而是将不同的模块储存在一起,这些模块之间并没有什么先后顺序可言:
import torch
import torch.nn as nn
class MyNet(nn.Module):
def __init__(self):
super(MyNet, self).__init__()
self.combine = nn.ModuleList()
self.combine.append(nn.Linear(10,100))
self.combine.append(nn.Linear(100,60))
self.combine.append(nn.Linear(50,10))
def forward(self, x):
x = self.combine[2](x)
x = self.combine[0](x)
x = self.combine[1](x)
return x
MyNet = MyNet()
print(MyNet)
input = torch.randn(32, 50)
print('MyNet(input).shape: ',MyNet(input).shape)
结果:
MyNet(
(combine): ModuleList(
(0): Linear(in_features=10, out_features=100, bias=True)
(1): Linear(in_features=100, out_features=60, bias=True)
(2): Linear(in_features=50, out_features=10, bias=True)
)
)
MyNet(input).shape: torch.Size([32, 60])
nn.Sequential
当模型中只是简单的前馈网络时,即上一层的输出直接作为下一层的输入,这时可以采用nn.Sequential()
模块来快速搭建模型,而不必手动在forward()
函数中一层一层地前向传播。因此,如果想快速搭建模型而不考虑中间过程的话,推荐使用nn.Sequential()
模块:
class Model(nn.Module):
def __init__(self):
super(Model, self).__init__()
self.layer = nn.Sequential(
nn.Conv2d(1, 20, 5),
nn.Conv2d(20, 20, 5)
)
def forward(self, x):
x = self.layer(x)
return x
Model = Model()
print(Model)
结果如下,采用了默认的命名方式 (按序号 0,1,2,3...)
Model(
(layer): Sequential(
(0): Conv2d(1, 20, kernel_size=(5, 5), stride=(1, 1))
(1): Conv2d(20, 20, kernel_size=(5, 5), stride=(1, 1))
)
)
Sequential 的其他写法:
第一种方式在改写的时候展示了,以下是第二种方式,指定每个 module 的名字,示例 7 如下,nn.Sequential()对象.add_module(层名,层class的实例):
class Model(nn.Module):
def __init__(self):
super(Model, self).__init__()
# 第二种
self.layer = nn.Sequential()
# 添加名字和层
self.layer.add_module('conv1', nn.Conv2d(1, 20, 5))
self.layer.add_module('conv2', nn.Conv2d(20, 20, 5))
# 第一种
# self.layer = nn.Sequential(
# nn.Conv2d(1, 20, 5),
# nn.Conv2d(20, 20, 5)
# )
def forward(self, x):
x = self.layer(x)
return x
Model = Model()
print(Model)
结果:
Model(
(layer): Sequential(
(conv1): Conv2d(1, 20, kernel_size=(5, 5), stride=(1, 1))
(conv2): Conv2d(20, 20, kernel_size=(5, 5), stride=(1, 1))
)
)
第三种写法展示如下,nn.Sequential(OrderedDict([*多个(层名,层class的实例)]))
:
from collections import OrderedDict
class Model(nn.Module):
def __init__(self):
super(Model, self).__init__()
# 第一钟
# self.layer = nn.Sequential(
# nn.Conv2d(1, 20, 5),
# nn.Conv2d(20, 20, 5)
# )
# 第二种
# self.layer = nn.Sequential()
# self.layer.add_module('conv1', nn.Conv2d(1, 20, 5))
# self.layer.add_module('conv2', nn.Conv2d(20, 20, 5))
# 第三种
self.layer = nn.Sequential(OrderedDict([
('conv1', nn.Conv2d(1, 20, 5)),
('conv2', nn.Conv2d(20, 20, 5))
]))
def forward(self, x):
x = self.layer(x)
return x
Model = Model()
print(Model)
总结:
- 可以把
Sequential
当作list
来看,允许构建序列化的模块。 nn.sequential()
是一个有序的容器,神经网络模块将按照在传入构造器的顺序依次被添加到计算图中执行
nn.Sequential ,nn.ModuleList和nn.ModuleDict 区别
继承
nn.Sequential 、 nn.ModuleList 、 nn.ModuleDict 类都继承自 Module 类
各自用法
net = nn.Sequential(nn.Linear(128, 256), nn.ReLU()) net = nn.ModuleList([nn.Linear(128, 256), nn.ReLU()]) net = nn.ModuleDict({'linear': nn.Linear(128, 256), 'act': nn.ReLU()})
区别
-
ModuleList 仅仅是一个储存各种模块的列表,这些模块之间没有联系也没有顺序(所以不用保证相邻层的输入输出维度匹配),而且没有实现 forward 功能需要自己实现
-
和 ModuleList 一样, ModuleDict 实例仅仅是存放了一些模块的字典,并没有定义 forward 函数需要自己定义
-
而 Sequential 内的模块需要按照顺序排列,要保证相邻层的输入输出大小相匹配,内部 forward 功能已经实现,所以,直接如下写模型,是可以直接调用的,不再需要写forward,sequential 内部已经有 forward
转换
-
将 nn.ModuleList 转换成 nn.Sequential
module_list = nn.ModuleList([nn.Linear(128, 256), nn.ReLU()]) net = nn.Sequential(*module_list)
-
nn.ModuleDict转换为nn.Sequential
module_dict = nn.ModuleDict({'linear': nn.Linear(128, 256), 'act': nn.ReLU()}) net = nn.Sequential(*module_dict.values())
nn.Linear
nn.Linear的原理
从名称就可以看出来,nn.Linear表示的是线性变换,原型就是初级数学里学到的线性函数:y=kx+b
不过在深度学习中,变量都是多维张量,乘法就是矩阵乘法,加法就是矩阵加法,因此nn.Linear()运行的真正的计算就是:
output = weight @ input + bias
@: 在python中代表矩阵乘法
input: 表示输入的Tensor,可以有多个维度
weights: 表示可学习的权重,shape=(output_feature,in_feature)
bias: 表示科学习的偏置,shape=(output_feature)
in_feature: nn.Linear 初始化的第一个参数,即输入Tensor最后一维的通道数
out_feature: nn.Linear 初始化的第二个参数,即返回Tensor最后一维的通道数
output: 表示输入的Tensor,可以有多个维度
nn.Linear的使用
nn.Linear()的初始化:
nn.Linear(in_feature,out_feature,bias)
in_feature
: int型, 在forward
中输入Tensor最后一维的通道数
out_feature
: int型, 在forward
中输出Tensor最后一维的通道数
bias
: bool型, Linear线性变换中是否添加bias偏置
nn.Linear()的执行:(即执行forward函数)
out=nn.Linear(input)
input
: 表示输入的Tensor,可以有多个维度 output
: 表示输入的Tensor,可以有多个维度
举例:
2维 Tensor
m = nn.Linear(20, 40)
input = torch.randn(128, 20)
output = m(input)
print(output.size()) # [(128,40])
4维 Tensor
m = nn.Linear(128, 64)
input = torch.randn(512, 3,128,128)
output = m(input)
print(output.size()) # [(512, 3,128,64))
nn.Conv2d
深入理解卷积函数torch.nn.Conv2d的各个参数以及计算公式(看完写模型就很简单了)-CSDN博客
【pytorch】卷积操作原理解析与nn.Conv2d用法详解-CSDN博客
output_size = (input_size - kernel_size + 2* padding)/stride + 1
nn.BatchNorm
BatchNorm是深度网络中经常用到的加速神经网络训练,加速收敛速度及稳定性的算法,是深度网络训练必不可少的一部分,几乎成为标配;
BatchNorm强行将数据拉回到均值为0,方差为1的正太分布上,一方面使得数据分布一致,另一方面避免梯度消失;
BatchNorm 即批规范化,是为了将每个batch的数据规范化为统一的分布,帮助网络训练, 对输入数据做规范化,称为Covariate shift;
数据经过一层层网络计算后,数据的分布也在发生着变化,因为每一次参数迭代更新后,上一层网络输出数据,经过这一层网络参数的计算,数据的分布会发生变化,这就为下一层网络的学习带来困难 -- 也就是在每一层都进行批规范化(Internal Covariate shift),方便网络训练,因为神经网络本身就是要学习数据的分布;
下面通过代码掩饰BatchNorm的作用;
首先要清楚,BatchNorm后是不改变输入的shape的;
nn.BatchNorm1d: N * d --> N * d
nn.BatchNorm2d: N * C * H * W -- > N * C * H * W
nn.BatchNorm3d: N * C * d * H * W --> N * C * d * H * W
下面讲解nn.BatchNorm1d,和nn.BatchNorm2d的情况
BatchNorm1d
首先看其参数:
torch.nn.BatchNorm1d(num_features, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True, device=None, dtype=None)
参数含义:
num_features:如果你输出的tensor是(N,C,L)维度的,那么这里定义为C;如果你输入的tensor是(N,L)维度的,则此处设定为L。这里N表示batch_size,C是数据的channel(通道),L是特征维度(数据长度)。
eps:对输入数据进行归一化时加在分母上,防止除零。
momentum :计算整个样本全局均值running_mean和方差running_var时是采用动量的模式进行,这个设定的是这个动量的大小,后文会提到。
affine:一个布尔值,当设置为True时,此模块具有可学习的仿射参数weight和bias,一般我们做正则化是使得数据服从N(0,1),但是经过仿射变换可以到到N(bias,weight^2)的正态分布。这两个参数可以学习的,初始化时weigt为1,bias为0
track_running_stats: 设为True时,BatchNorm层会统计全局均值running_mean和方差running_var。
从参数的含义我们可以知道,针对不同的tensor输出,我们提前设定的num_features时不一样的,当做BN的tensor维度是(N,C,L)时,我们定义的num_features是C,意味着我们会根据每个通道的不同样本的L长度特征进行相加再除以N*L得到均值,因此会得到C个均值。再具体点的实例就是输入为(5,3,10)的tensor,我们会取[0,0,:],[1,0,:],....[4,0,:]这些向量的数值(一共有5*10个数字)加起来除以5*10得到第一个通道的均值,并对以上数字进行正则化。当做BN的tensor维度是(N,L),我们定义的num_features是L,因此我们会计算出L个均值和方差,可以看成(N,L,1)的形式,每一个特征长度为1,只有L个通道,具体点的实例:输入维度为(4,5)的tensor,会取[0,0],[1,0],[2,0],[3,0]这4个数进行正则化,可以知道我们最终会得到L个均值和方差
momentum参数的应用是为了计算全局样本的均值和方差的,因为当训练完样本数据后,我们可以得到一个整个样本的均值和方差,但是这个均值和方差的得到不是把所有样本都计算遍历一遍计算得到的,而是在每一个betch经过BatchNorm1d的时候,内部会储存下该次batch的均值和方差,并通过以下等式来计算得到全局的均值和方差。
xnew=(1−momentum)×xcur+momentum×xbatch𝑥𝑛𝑒𝑤=(1−𝑚𝑜𝑚𝑒𝑛𝑡𝑢𝑚)×𝑥𝑐𝑢𝑟+𝑚𝑜𝑚𝑒𝑛𝑡𝑢𝑚×𝑥𝑏𝑎𝑡𝑐ℎ
如果track_running_stats=False,则在内部不会进行计算全局均值running_mean和方差running_var。
下面直接上例子来看看:
m = nn.BatchNorm1d(5, affine=False, momentum=0.1)
tensor = torch.FloatTensor([i for i in range(20)]).reshape(4,5)
print(tensor)
output = m(tensor)
print(output)
print(m.running_mean)
print(m.running_var)
结果如下:
### tensor的取值
tensor([[ 0., 1., 2., 3., 4.],
[ 5., 6., 7., 8., 9.],
[10., 11., 12., 13., 14.],
[15., 16., 17., 18., 19.]])
### BN之后的结果
tensor([[-1.3416, -1.3416, -1.3416, -1.3416, -1.3416],
[-0.4472, -0.4472, -0.4472, -0.4472, -0.4472],
[ 0.4472, 0.4472, 0.4472, 0.4472, 0.4472],
[ 1.3416, 1.3416, 1.3416, 1.3416, 1.3416]])
### 全局均值(由于momentum=0.1,running_mean初始值为0,所以这是根据公式计算过后的结果)
tensor([0.7500, 0.8500, 0.9500, 1.0500, 1.1500])
### 全局方差
tensor([5.0667, 5.0667, 5.0667, 5.0667, 5.0667])
我们再来看看,输入的tensor是三维的情况:
m = nn.BatchNorm1d(2, affine=False, momentum=0.1)
tensor = torch.FloatTensor([i for i in range(18)]).reshape(3,2,3)
print(tensor)
output = m(tensor)
print(output)
print(m.running_mean)
print(m.running_var)
结果如下:
### tensor的取值
tensor([[[ 0., 1., 2.],
[ 3., 4., 5.]],
[[ 6., 7., 8.],
[ 9., 10., 11.]],
[[12., 13., 14.],
[15., 16., 17.]]])
### BN之后的取值
tensor([[[-1.4094e+00, -1.2081e+00, -1.0067e+00],
[-1.4094e+00, -1.2081e+00, -1.0067e+00]],
[[-2.0135e-01, -2.9802e-08, 2.0135e-01],
[-2.0135e-01, 5.9605e-08, 2.0135e-01]],
[[ 1.0067e+00, 1.2081e+00, 1.4094e+00],
[ 1.0067e+00, 1.2081e+00, 1.4094e+00]]])
### 全局均值和方差
tensor([0.7000, 1.0000])
tensor([3.6750, 3.6750])
大概检验一下,根据计算公式,第一个均值应该是每个样本通道1的所有特征求和得到的均值也就是(0+1+2+6+7+8+12+13+14)/ 9 = 7 (全局均值是乘以了momentum=0.1的结果),方差应该是:[(0−7)2+.....+(14−7)2]/9=24.66[(0−7)2+.....+(14−7)2]/9=24.66(注意这里是有偏样本方差分母是N)。[0,0,0]位置这个数BN之后为:(0−7)/√24.66=−1.4094(0−7)/24.66=−1.4094,有些同学可能或说为什么全局方差不对呢?因为全局方差中计算的是无偏的样本方差(分母是N-1),并且初始值running_var=1.
BatchNorm2d
详细解读nn.BatchNorm2d——批量标准化操作-CSDN博客
对于所有的batch中样本的同一个channel的数据元素进行标准化处理,即如果有C个通道,无论batch中有多少个样本,都会在通道维度上进行标准化处理,一共进行C次。
在卷积神经网络的卷积层之后总会添加BatchNorm2d进行数据的归一化处理,这使得数据在进行Relu之前不会因为数据过大而导致网络性能的不稳定,BatchNorm2d()函数数学原理如下:
torch.nn.BatchNorm2d(num_features, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True, device=None, dtype=None)
其主要需要输入4个参数:
1)num_features:输入数据的shape一般为[batch_size, channel, height, width], num_features为其中的channel;
2)eps: 分母中添加的一个值,目的是为了计算的稳定性,默认:1e-5;
3)momentum: 一个用于运行过程中均值和方差的一个估计参数,默认值为0.1.
4)affine:当设为true时,给定可以学习的系数矩阵 γ \gamma γ和 β \beta β
5)track_running_stats:一个布尔值,当设置为True时,模型追踪running_mean和running_variance,当设置为False时,模型不跟踪统计信息,并在训练和测试时都使用测试数据的均值和方差来代替,默认为True
LayerNorm(层标准化)
torch.nn.LayerNorm(normalized_shape, eps=1e-05, elementwise_affine=True, device=None, dtype=None)
参数看起来和BatchNorm差不多,但是LayerNorm不会记录全局的均值和方差。最重要的就是前三个参数。
normalized_shape:可以设定为:int,列表,或者torch.Size([3, 4])
eps:对输入数据进行归一化时加在分母上,防止除零。
elementwise_affine:是否进行仿射变换,如果是True则此模块具有可学习的仿射参数weight和bias,使得能够使得数据不仅仅是服从N(0,1)正态分布。
nn.Dropout
一句话总结:Dropout的是为了防止过拟合而设置
详解部分:
1.Dropout是为了防止过拟合而设置的
2.Dropout顾名思义有丢掉的意思
3.nn.Dropout(p = 0.3) # 表示每个神经元有0.3的可能性不被激活
4.Dropout只能用在训练部分而不能用在测试部分
5.Dropout一般用在全连接神经网络映射层之后,如代码的nn.Linear(20, 30)之后
class Dropout(nn.Module):
def__init__(self):
super(Dropout,self).__init__()
self.linear = nn.Linear(20, 40)
self.dropout = nn.Dropout(p=0.3)# p=0.3表示下图(a)中的坤经元有p=0.3的概
def forward(self, inputs):
out = self.linear(inputs)
out = self.dropout(out)
return out
net = Dropout()
# Dropout只能用在train而不能用在test