接下来,我们将正式进入神经网络的搭建环节。首先介绍的是roech中用于神经网络搭建的函数。
nn.Module的使用
官方网址:Module — PyTorch 2.4 documentation
上示例:
import torch.nn as nn
import torch.nn.functional as F
'''我们的模型类必须继承nn.Module'''
class Model(nn.Module):
'''
我们可以自行修改其中的部分,来创建我们自己的神经网络
一般来说,我们定义的模型类主要就是重写以下的两个函数
'''
def __init__(self):
super().__init__()
self.conv1 = nn.Conv2d(1, 20, 5)
self.conv2 = nn.Conv2d(20, 20, 5)
def forward(self, x):
x = F.relu(self.conv1(x))
return F.relu(self.conv2(x))
我们来仔细讲解一下上述代码中的forward()这个函数。这部分不懂的建议先去学习以下深度学习的理论知识。
在__init__()函数中,我们定义了两个参数,分别是self.conv1和self.conv2,这两个实际上是定义了两个卷积层,它们被用在forward()函数中,F.relu()其实是激活函数,如果看过深度学习的相关书籍,那么这个你应该认识,它的函数图像是这个样子的:
相当于,它对我们的卷积结果进行了一次非线性运算。所以实际上这个forward()函数所做的事情如下:
上面的例子是官网上的,接下来我自己定义一个简单的神经网络看一下:
from torch import nn
import torch
class mynn(nn.Module):
def __init__(self) :
super().__init__()
#这个网络简单的实现将数据+1的操作
def forward(self,input):
return input+1
mynn1=mynn()
x=torch.tensor(1.0)
output=mynn1(x)
print(output)
输出结果:
tensor(2.)
卷积层(Convolution)
上面的小例子我们简单地使用了卷积。这里将再详细讲一下卷积层。
强烈建议直接去看视频:土堆说卷积操作(可选看)_哔哩哔哩_bilibili
这里只记录一下关键点,用作备忘录
网址:torch.nn.functional.conv2d — PyTorch 2.4 documentation
打开网址可以看到:
其实差别就是1维数据、2维数据、3维数据,用法大差不差,卷积层多用于处理图像,因此我们以二维来讲解。
它的一些参数:
- input:输入,输入包括四个参数,batch_size,channels(通道数),H高,W宽。详细来说:batch_size:表示一次传入网络的样本数目,即批量大小(batch size)。它决定了在每次训练迭代中,神经网络同时处理的样本数量。in_channels: 输入数据的通道数,例如 RGB 图像的通道数为 3。
- weigt:卷积核或权重
- bias:偏置项
- stride:步长,经过每一步计算后,卷积核移动多少距离
- padding:填充输入边界,默认没有填充
小例子:
from torch import nn
import torch
import torch.nn.functional as F
class mynn(nn.Module):
def __init__(self) :
super().__init__()
def forward(self,input):
return input+1
input=torch.tensor([[1,2,0,3,1],
[0,1,2,3,1],
[1,2,1,0,0],
[5,2,3,1,1],
[2,1,0,1,1]])
kernel=torch.tensor([[1,2,1],
[0,1,0],
[2,1,0]])
#以上定义的数据不符合卷积层输入的要求,使用reshape调整尺寸
input=torch.reshape(input,(1,1,5,5))
kernel=torch.reshape(kernel,(1,1,3,3))
result=F.conv2d(input=input,weight=kernel,stride=1)
print(result)
result=F.conv2d(input=input,weight=kernel,stride=2)
print(result)
'''
输出结果:
tensor([[[[10, 12, 12],
[18, 16, 16],
[13, 9, 3]]]])
tensor([[[[10, 12],
[13, 3]]]])
'''
池化层(Pooling)
与卷积层一样,这一部分也涉及基础理论知识,建议看视频学。放一些学习笔记在这里。
官网:torch.nn — PyTorch 2.4 documentation
最重要的是上图中画线的方法,也是我们常说的下采样(最大池化)。
没有默认值的参数只有kernel_size(池化核)。
这里单独提一下参数dilation,它用于实现空洞卷积。
池化层的作用是减少特征数量,从而防止过拟合。来看一个最大池化的例子,理解一下什么是池化层。
假设这里是下采样,池化核为3*3大小的窗口(kernel_size=3,而如果不设置stride参数,那么默认stride=kernel_size),池化核就像卷积核一样在输入数据上不停移动,每次筛出池化核圈住的最大值(下采样,最大池化)。如果被圈住的数据不足,那么就看ceil_mode参数设置的是什么了,如果为True,则保留,即取不足的数中的最大值;反之,则舍弃。
从这个过程应该不难看出,下采样的池化核是没有值的。
接下来是代码部分:
首先,老师的视频应该是几年前做的,现在对于池化层,输入有两种方式,这点要注意:
from torch import nn
import torch
#建立神经网络
class mynn(nn.Module):
def __init__(self) :
super().__init__()
self.maxPool1=nn.MaxPool2d(kernel_size=3,ceil_mode=True)
def forward(self,input):
return self.maxPool1(input)
#池化层的输入必须是float32,因此可以在定义tensor时指定其存储类型
input=torch.tensor([[1,2,0,3,1],
[0,1,2,3,1],
[1,2,1,0,0],
[5,2,3,1,1],
[2,1,0,1,1]],dtype=torch.float32)
#以上定义的数据不符合池化层输入的要求,使用reshape调整尺寸
input=torch.reshape(input,(1,1,5,5))
MyNn=mynn()
output=MyNn(input)
print(output)
运行结果:
tensor([[[[2., 3.],
[5., 1.]]]])
如果把参数ceil_mode设置为False,那么结果就不同了:
tensor([[[[2.]]]])
我们也可以拿之前使用的图像数据集来查看一下下采样效果:
from torch import nn
import torch
import torchvision
from torch.utils.data import DataLoader
from torch.utils.tensorboard import SummaryWriter
dataset=torchvision.datasets.CIFAR10("../dataset",train=False,download=True,transform=torchvision.transforms.ToTensor())
dataloader=DataLoader(dataset,batch_size=64)
#建立神经网络
class mynn(nn.Module):
def __init__(self) :
super().__init__()
self.maxPool1=nn.MaxPool2d(kernel_size=3,ceil_mode=False)
def forward(self,input):
return self.maxPool1(input)
writer=SummaryWriter('Logs')
step=0
mynn1=mynn()
for data in dataloader:
imgs,targets=data
writer.add_images('input',imgs,step)
output=mynn1(imgs)
writer.add_images('output',output,step)
step=step+1
writer.close()
使用tensorboard打开:
填充层(Padding)
这个比较少用。
Padding Layers(填充层)主要用于在图像处理或序列处理中调整输入的尺寸,以便与期望的输入尺寸或模型结构匹配。
-
保持特征图尺寸:
- 当应用卷积操作时,特征图的尺寸通常会缩小(根据卷积核的大小和步幅)。如果需要在不改变特征图深度的情况下保持特征图的尺寸,就可以使用Padding Layer在特征图的边缘填充额外的值(通常是0),以扩展特征图的尺寸。
-
处理边界效应:
- 在卷积神经网络中,卷积操作会在输入的边缘上产生边界效应,这可能导致输出特征图尺寸减小。通过在输入的边缘填充,可以减少或消除这种边界效应,确保输出特征图的尺寸与预期的一致性。
-
处理变长序列:
- 在处理文本等变长序列时,可以使用填充层将序列扩展到统一的长度,以便进行批处理操作。这种填充通常使用特殊的值(如0)填充序列的末尾,这样可以告知模型忽略填充部分。
非线性激活
网址:torch.nn — PyTorch 2.4 documentation
之前稍微提到过的ReLU函数就属于这里。
另外一个比较常用的是Sigmoid函数:Sigmoid — PyTorch 2.4 documentation
from torch import nn
import torch
import torchvision
from torch.utils.data import DataLoader
from torch.utils.tensorboard import SummaryWriter
dataset=torchvision.datasets.CIFAR10("../dataset",train=False,download=True,transform=torchvision.transforms.ToTensor())
dataloader=DataLoader(dataset,batch_size=64)
#建立神经网络
class mynn(nn.Module):
def __init__(self) :
super().__init__()
self.relu1=nn.ReLU()
self.sigmoid1=nn.Sigmoid()
def forward(self,input):
output=self.sigmoid1(input)
return output
writer=SummaryWriter('Logs')
step=0
mynn1=mynn()
for data in dataloader:
imgs,targets=data
writer.add_images('input',imgs,step)
output=mynn1(imgs)
writer.add_images('output',output,step)
step=step+1
writer.close()
同样,在tensorboard中打开看: