首页人工智能Pytorch【深度学习(PyTorch...

【深度学习(PyTorch篇)】39.网络搭建方式

11本系列文章配套代码获取有以下两种途径:

  • 通过百度网盘获取:
链接:https://pan.baidu.com/s/1XuxKa9_G00NznvSK0cr5qw?pwd=mnsj 提取码:mnsj
  • 前往GitHub获取
https://github.com/returu/PyTorch





Pytorch中搭建神经网络有几种不同的方式,每种方式都有其特点和适用场景。选择哪种方式取决于具体的项目需求、个人习惯以及团队规范。在实际开发中,应当根据情况灵活选择合适的网络搭建方法,以达到效率和可维护性的平衡。

下面使用不同的方式搭建具有相同网络结构的卷积神经网络。

01

继承nn.Module类


这是最常见也是最灵活的搭建方式。通过继承torch.nn.Module类,重写其__init__forward方法来构造网络层并实现前向传播逻辑。这种方式允许你自由地控制网络的每一层,包括自定义层、损失函数以及网络的动态结构。不过,这种方式重复编写复杂的forward函数比较麻烦,代码量较大。
class CompleteConvNet(nn.Module):  
    def __init__(self,num_classes=10):  
        super().__init__()  

        # 第一个卷积层  
        self.conv1 = nn.Conv2d(332, kernel_size=3)  
        self.bn1 = nn.BatchNorm2d(32)  
        self.pool1 = nn.MaxPool2d(kernel_size=2, stride=2)  

        # 第二个卷积层  
        self.conv2 = nn.Conv2d(3264, kernel_size=3)  
        self.bn2 = nn.BatchNorm2d(64)  
        self.pool2 = nn.MaxPool2d(kernel_size=2, stride=2)  

        # 第三个卷积层  
        self.conv3 = nn.Conv2d(64128, kernel_size=3)  
        self.bn3 = nn.BatchNorm2d(128)  

        # 全连接层
        self.fc1 = nn.Linear(128 * 4 * 4512)
        self.fc2 = nn.Linear(512,num_classes)

    def forward(self, x):  
        # 第一个卷积层(卷积 -> 标准化 -> 激活 -> 池化)
        x = F.relu(self.bn1(self.conv1(x)))  
        x = self.pool1(x)  

        # 第二个卷积层(卷积 -> 标准化 -> 激活 -> 池化)
        x = F.relu(self.bn2(self.conv2(x)))  
        x = self.pool2(x)  

        # 第三个卷积层(卷积 -> 标准化 -> 激活)
        x = F.relu(self.bn3(self.conv3(x)))  

        # 将特征图展平  
        x = x.view(x.size(0), -1

        # 全连接层  
        x = F.relu(self.fc1(x))  
        x = self.fc2(x)  

        return x

02

使用nn.Sequential


如果网络结构是线性的,即每一层的输出直接作为下一层的输入,那么可以使用nn.Sequential容器来快速搭建网络。nn.Sequential是一个有序的容器,神经网络模块会按照在传入构造器的顺序被添加到计算图中执行。nn.Sequential可以非常方便地帮助用户搭建顺序执行的神经网络层,而无需编写前向传播的代码。具体可分为以下三种方式:
  • 1、直接使用nn.Sequential():
nn.Sequential是一种序列容器,它可以将网络层按照定义好的序列自动传递下去。这种方式简单直接,适用于层次清晰、顺序执行的网络结构。代码简洁,易于理解和维护。不过,每一层默认采用数字命名,不易区分,并且不支持通过名称直接获取层。
# 假设有10个类别
num_classes = 10

net_2 = nn.Sequential(
    # 第一个卷积层(卷积 -> 标准化 -> 激活 -> 池化)
    nn.Conv2d(3, 32, kernel_size=3),
    nn.BatchNorm2d(32),
    nn.ReLU(),
    nn.MaxPool2d(kernel_size=2, stride=2),
    # 第二个卷积层(卷积 -> 标准化 -> 激活 -> 池化)
    nn.Conv2d(32, 64, kernel_size=3),
    nn.BatchNorm2d(64),
    nn.ReLU(),
    nn.MaxPool2d(kernel_size=2, stride=2),
    # 第三个卷积层(卷积 -> 标准化 -> 激活)
    nn.Conv2d(64, 128, kernel_size=3) ,
    nn.BatchNorm2d(128),
    nn.ReLU(),
    # 展平层,将多维的特征图展平为一维张量  
    nn.Flatten(),
    # 全连接层
    nn.Linear(128 * 4 * 4, 512),
    nn.ReLU(),
    nn.Linear(512,num_classes))
该方式只能通过索引序号来获取子module
# 获取子module
net_2[0]
  • 2、nn.Sequential()搭配add_module()

这种方式首先构建一个空的Sequential容器,然后利用add_module()函数一一添加相应的层,并且可以为每一层指定一个名字,使得网络结构更加清晰,易于理解和后续修改。
# 假设有10个类别
num_classes = 10

net_3 = nn.Sequential()
# 第一个卷积层(卷积 -> 标准化 -> 激活 -> 池化)
net_3.add_module("Conv_1" , nn.Conv2d(3, 32, kernel_size=3))
net_3.add_module("BatchNorm_1" , nn.BatchNorm2d(32))
net_3.add_module("ReLU_1" , nn.ReLU())
net_3.add_module("MaxPool_1" , nn.MaxPool2d(kernel_size=2, stride=2))
# 第二个卷积层(卷积 -> 标准化 -> 激活 -> 池化)
net_3.add_module("Conv_2" , nn.Conv2d(32, 64, kernel_size=3))
net_3.add_module("BatchNorm_2" , nn.BatchNorm2d(64))
net_3.add_module("ReLU_2" , nn.ReLU())
net_3.add_module("MaxPool_2" , nn.MaxPool2d(kernel_size=2, stride=2))
# 第三个卷积层(卷积 -> 标准化 -> 激活)
net_3.add_module("Conv_3" , nn.Conv2d(64, 128, kernel_size=3) )
net_3.add_module("BatchNorm_3" , nn.BatchNorm2d(128))
net_3.add_module("ReLU_3" , nn.ReLU())
# 展平层,将多维的特征图展平为一维张量
net_3.add_module("Flatten_layer" ,  nn.Flatten())
# 全连接层
net_3.add_module("Linear_1" , nn.Linear(128 * 4 * 4, 512))
net_3.add_module("ReLU_4" , nn.ReLU())
net_3.add_module("Linear_2" , nn.Linear(512,num_classes))
因为为每一层指定了名字,因此该方式可以根据名字或者索引序号来获取子module
# 获取子module
net_3[0] , net_3.Conv_1
  • 3、nn.Sequential()搭配collections.OrderedDict()
使用OrderedDict可以为nn.Sequential中的每一层指定一个名字,这样可以使模型更加清晰,并且在之后可以方便地通过名字来引用特定的层。
from collections import OrderedDict

# 假设有10个类别
num_classes = 10

net_4 = nn.Sequential(OrderedDict([
    # 第一个卷积层(卷积 -> 标准化 -> 激活 -> 池化)
    ("Conv_1" , nn.Conv2d(3, 32, kernel_size=3)),
    ("BatchNorm_1" , nn.BatchNorm2d(32)),
    ("ReLU_1" , nn.ReLU()),
    ("MaxPool_1" , nn.MaxPool2d(kernel_size=2, stride=2)),
    # 第二个卷积层(卷积 -> 标准化 -> 激活 -> 池化)
    ("Conv_2" , nn.Conv2d(32, 64, kernel_size=3)),
    ("BatchNorm_2" , nn.BatchNorm2d(64)),
    ("ReLU_2" , nn.ReLU()),
    ("MaxPool_2" , nn.MaxPool2d(kernel_size=2, stride=2)),
    # 第三个卷积层(卷积 -> 标准化 -> 激活)
    ("Conv_3" , nn.Conv2d(64, 128, kernel_size=3) ),
    ("BatchNorm_3" , nn.BatchNorm2d(128)),
    ("ReLU_3" , nn.ReLU()),
    # 展平层,将多维的特征图展平为一维张量
    ("Flatten_layer" ,  nn.Flatten()),
    # 全连接层
    ("Linear_1" , nn.Linear(128 * 4 * 4, 512)),
    ("ReLU_4" , nn.ReLU()),
    ("Linear_2" , nn.Linear(512,num_classes))
]))
因为为每一层指定了名字,因此该方式可以根据名字或者索引序号来获取子module
# 获取子module
net_4[0] , net_4.Conv_1
  • 4、nn.Sequential()嵌套nn.Sequential()
为了使模型更加清晰,可以将每个功能块(例如,卷积、标准化、激活和池化)组合成一个内部的nn.Sequential(),然后将这些内部序列块再次放入一个外部的nn.Sequential()中。
# 假设有10个类别
num_classes = 10

# 定义各个卷积块  
conv_block1 = nn.Sequential(  
    nn.Conv2d(3, 32, kernel_size=3),  
    nn.BatchNorm2d(32),  
    nn.ReLU(),  
    nn.MaxPool2d(kernel_size=2, stride=2)  
)  

conv_block2 = nn.Sequential(  
    nn.Conv2d(32, 64, kernel_size=3),  
    nn.BatchNorm2d(64),  
    nn.ReLU(),  
    nn.MaxPool2d(kernel_size=2, stride=2)  
)  

conv_block3 = nn.Sequential(  
    nn.Conv2d(64, 128, kernel_size=3),  
    nn.BatchNorm2d(128),  
    nn.ReLU()  
)

# 定义展平层部分  
flatten_layer = nn.Flatten()  

# 定义全连接层部分  
fc_layers = nn.Sequential(  
    nn.Linear(128 * 4 * 4, 512),  # 根据实际情况,可能需要修改为128 * 4 * 4  
    nn.ReLU(),  
    nn.Linear(512, num_classes)  



# 使用nn.Sequential嵌套构建整个网络
net_5 = nn.Sequential(
    conv_block1,
    conv_block2,
    conv_block3,
    flatten_layer,
    fc_layers)

03

使用nn.ModuleList


nn.ModuleListPyTorch中的容器类,继承自nn.Module是一个模块的容器,用于存储一系列的nn.Module实例,并在forward方法中按需调用它们。因为nn.ModuleListnn.Module的子类,因此当在主module中使用它时,ModuleList能够自动被主module识别为子module
nn.Sequential相比,nn.ModuleList提供了更高的灵活性。它可以在运行时动态地添加或删除子模块,这使得网络结构可以更加灵活地进行调整。
class ConvNetList(nn.Module):  
    def __init__(self,num_classes=10):  
        super().__init__()
        # 创建一个ModuleList,用于存储网络中的各个层
        # 这些层将按照它们在列表中的顺序在前向传播中被依次应用
        self.module_list = nn.ModuleList([
            # 第一个卷积层(卷积 -> 标准化 -> 激活 -> 池化)
            nn.Conv2d(332, kernel_size=3),
            nn.BatchNorm2d(32),
            nn.ReLU(),
            nn.MaxPool2d(kernel_size=2, stride=2),
            # 第二个卷积层(卷积 -> 标准化 -> 激活 -> 池化)
            nn.Conv2d(3264, kernel_size=3),
            nn.BatchNorm2d(64),
            nn.ReLU(),
            nn.MaxPool2d(kernel_size=2, stride=2),
            # 第三个卷积层(卷积 -> 标准化 -> 激活)
            nn.Conv2d(64128, kernel_size=3) ,
            nn.BatchNorm2d(128),
            nn.ReLU(),
            # 展平层,将多维的特征图展平为一维张量  
            nn.Flatten(),
            # 全连接层
            nn.Linear(128 * 4 * 4512),
            nn.ReLU(),
            nn.Linear(512,num_classes)
        ])

    def forward(self, x):  
        # 遍历self.module_list中的每一个模块(层),并将输入数据x依次传递给这些模块
        # 每个模块接的输出结果,将被作为下一个模块的输入
        for module in self.module_list:
            x = module(x)

        return x


更多内容可以前往官网查看

https://pytorch.org/


本篇文章来源于微信公众号: 码农设计师

RELATED ARTICLES

欢迎留下您的宝贵建议

Please enter your comment!
Please enter your name here

- Advertisment -

Most Popular

Recent Comments