神经网络构建

突发奇想想要写一篇神经网络的构建,虽然现在人为一步步构建神经网络已经有些过时,但是从头开始构建神经网络还是能有助于我们理解网络架构的。

先上代码:

import torch
from torch import nn
import torch.nn.functional as F
class MyAlexNet(nn.Module):
    def __init__(self):
        super(MyAlexNet, self).__init__()
        self.c1=nn.Conv2d(in_channels=3,out_channels=48,kernel_size=11,stride=4,padding=2)
        self.ReLU=nn.ReLU()
        self.c2=nn.Conv2d(in_channels=48,out_channels=128,kernel_size=5,stride=1,padding=2)
        self.s2=nn.MaxPool2d(2)
        self.c3=nn.Conv2d(in_channels=128,out_channels=192,kernel_size=3,stride=1,padding=1)
        self.s3=nn.MaxPool2d(2)
        self.c4=nn.Conv2d(in_channels=192,out_channels=192,kernel_size=3,stride=1,padding=1)
        self.c5=nn.Conv2d(in_channels=192,out_channels=128,kernel_size=3,stride=1,padding=1)
        self.s5=nn.MaxPool2d(kernel_size=3,stride=2)
        self.flatten=nn.Flatten()
        self.f6=nn.Linear(128*6*6,2048)
        self.f7=nn.Linear(2048,2048)
        self.f8=nn.Linear(2048,1000)
        self.f9=nn.Linear(1000,2)
    def forward(self,x):
        x=self.ReLU(self.c1(x))
        x=self.ReLU(self.c2(x))
        x=self.s2(x)
        x=self.ReLU(self.c3(x))
        x=self.s3(x)
        x=self.ReLU(self.c4(x))
        x=self.ReLU(self.c5(x))
        x=self.s5(x)
        x=self.flatten(x)
        x=self.f6(x)
        x=F.dropout(x,p=0.5)
        x=self.f7(x)
        x=F.dropout(x,p=0.5)
        x=self.f8(x)
        x=F.dropout(x,p=0.5)
        x=self.f9(x)
        return x
if __name__ == '__main__':
    x=torch.rand([1,3,224,224])
    model=MyAlexNet()
    y=model(x)
    print(y[0][0].item())
    print(y.shape)

我们定义MyAlexNet网络,继承自父类。

我们先看第一个函数:torch.nn.Conv2d是用于实现二维卷积操作,是构建CNN(循环神经网络的关键部件)。我们可以选择输入和输出的维度,卷积核大小,步长,以及填充层数。

例如我们的输入是一个1*3*224*224的图像数据,这里1指的是批次,我们的网络能接受的张量的形状是批次*通道数*W*H。为了与我们的模型相匹配,这里的通道数量强制性要求为3。

对于数据1*3*224*224二维卷积操作不改变其批次数,我们输出的维度是1*48*n*m,这个n和m如何计算呢?
我们的卷积核形状是11*11,步长为4,填充层为2,首先考虑填充层,填充完成后我们的数据形状是1*3*228*228,是因为填充层是在上下左右都进行填充,在长和宽的维度上都增加了4层。

接着我们考虑卷积核,228*228在经过卷积核处理之后形状是218*218,再经过隔4取1的操作,所得到的形状是int(218/4+1)=55。

所以最后得到的形状是1*48*55*55。

我们来验证一下:

显然,我们的计算无误。

格外需要注意的是,我们使用的Conv2d网络是进行二维卷积操作,接受的张量一定是四维的,第一个维度表示批次,第二个维度表示通道数,也就是channel,channel的改变是由out——channels这个参数决定的,剩下的两个维度表示的是要处理的二维面,在代码中就是224*224。决定处理后二维面形状的是卷积核形状,步数,填充层大小。

在定义完成第一层卷积之后,我们定义激活函数,这里我们使用的是ReLU激活函数。

然后我们接着搭建第二层卷积神经网络,现在我们的张量形状是1*48*55*55,这一层我们要将输出通道数转化为128,即有128个卷积核。对于输出维度的计算,我们还是先看padding,c2层的padding是2,经过padding处理之后,我们张量形状变成了1*128*59*59,然后经过卷积处理,形状变成1*128*55*55。由于步长为1,所以步长不改变输出张量的形状。

我们看一下真实结果:

以上说明我们的计算没有发生错误。

接着定义最大池化层,参数表示滑动窗口的形状,显然,滑动窗口的形状是2*2,滑动窗口的工作原理类似于卷积核,但是滑动窗口不进行卷积操作,仅有筛选的作用,提取在窗口内的最大值。

我们的输入形状是1*128*55*55,显然,使用2*2的滑动窗口并不能把我们的权重全部覆盖,这里的处理是将未覆盖部分舍去,所以对于每一个二维面的覆盖部分是54*54,经过池化处理之后,得到的二维面的形状是27*27。

验证结果如下:

接下来继续进入卷积层,后文中我们不再考虑通道数,只计算二维平面的形状。目前形状为27*27,经过padding=1的处理之后,形状是29*29,卷积核形状是3*3,经过卷积核处理之后形状是27*27,由于步长还是1,结合输出通道数,所以最终得到的形状是1*192*27*27。

同理,我们继续断点验证:

接着我们继续使用最大池化层来处理,同理,得到的形状是1*192*13*13。

看下一层卷积,卷积核大小是3*3,padding是1,形状先被修改位15*15,经过卷积处理之后变成了13*13,所以形状是1*192*13*13。

看下一层卷积,仍不改变二维平面的形状,所以得到的是1*128*13*13。

之后还是一个最大池化层,这里的最大池化层传入了两个参数,卷积核,也就是滑动窗口大小是3*3,步长为2。如果不定义步长,默认步长与卷积核,也就是滑动窗口的大小相同,就是说,如果这一步不定义stride为2,那么默认stride=kernel_size=3。所以先考虑池化处理步长为1,得到的二维平面形状是11,再隔2取1,得到的任意方向上大小是int(11/2+1)=6。

所以处理之后得到的张量形状是1*128*6*6,验证如下:

接下来是平展层,平展层只会对图像进行平展,不会涉及批次,所以得到的形状是[1,128*6*6]。

然后我们使用全连接层使其一步步降维。这里就不再赘述。

最终使其形状转化为1*2,也就是批次*2,2表示处理二分类问题。得到的每个数值表示每个分类对应的概率。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

丘小羽

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值