跳至主要內容

三、PyTorch—模型

原创Xenny约 1058 字大约 4 分钟机器学习机器学习PyTorch

三、PyTorch—模型

  • 在第一篇 一、PyTorch 基础open in new window 中的例子我们已经使用了nn.Module来创建了一个线性回归模型。本文将来介绍Module的具体属性以及行为,了解如何搭建一个复杂的机器学习模型。

Module

  • 这里我们介绍的是torch.nn子包的Module类,它是所有网络层的基类。我们将通过继承该类来实现自己的网络。

    class myNet(nn.Module):
        def __init__(self):
            pass
    
        def forward(self, x):
            pass
    

    这是一个基本的网络模型结构,对于一般的网络模型,我们只需要在__init__定义每一层的结构,在forward实现具体正向传播预测过程即可。

Parameter

  • 在构建模型时一个重点的概念便是模型的参数,本质上参数也就是一个带梯度的张量,但是实际编写代码时我们需要用nn.Parameter对张量参数进行封装,它会自动给张量加上requires_grad=True属性并且一个更重要的事情是将参数注册到该模型的参数列表中(两种写法可以参考基础篇的博文)。

    注册到模型参数列表我们便可以通过一些方法来管理这些参数。例如:

    module.parameters() # 返回一个生成器,包含全部参数
    module.get_parameter(name) # 按名称获取参数
    

Functional

  • nn.functional中包含各类功能组件的函数实现。例如

    激活函数:relu,sigmoid,tanh,softmax 模型层:linear,conv2d,dropout2d 损失函数:mse_loss,cross_entropy,binary_cross_entropy

    不过我们在使用的使用一般都是使用的他们的封装Module类,即把上述函数都看成是一个模型处理。名称是上述名称的驼峰式命名,例如交叉熵模型为nn.CrossEntropyLoss

模型容器

  • 在构建模型时,一个模型往往会包含很多其他模型,例如上述提到的各类激活函数,损失函数。同时一个模型也将包含很多层,例如卷积层、池化层、全连接层等等,此时我们可以利用torch提供的模型容器来组装这些层。按照不同的组织方式,分为三大类

    1. nn.Sequetial

      按顺序包装多个模型,一般用于包装特征提取操作。这里面各个子模型将按顺序依次调用。前者的输出作为后者的输入。

    2. nn.ModuleList

      类似于python的列表,将多个模型组装为一个迭代器,和Sequetial的不同就是调用Sequetial将自动按序调用每个模型,而ModuleList只是组装为一个迭代器,具体如何调用还需要自己写调用顺序。

    3. nn.ModuleDict

      类似于python的字典,将多个模型组装为一个带有键值的字典。

  • 对于Sequetial的自动按需调用或许还有点用,那后两者为什么不直接用python的原身对象呢?

    除了统一语义之外,原因和上面的Parameter参数包装一样,使用它们对模型进行包装还有一个作用就是会将模型自动注册为当前模型的子模块。当操作父模块(例如移动到GPU)时才能正确的自动处理这些子模块。

AlexNet

  • 最后我们还是以上一篇的猫狗二分类问题来看看Module的使用,这里我们将使用AlexNet来进行训练。

    class AlexNet(nn.Module):
        def __init__(self, num_classes=1000):
            super(AlexNet, self).__init__()
            
            self.features = nn.Sequential(
                nn.Conv2d(in_channels=3, out_channels=96, kernel_size=11, stride=4, padding=2),
                nn.ReLU(inplace=True),
                nn.MaxPool2d(kernel_size=3, stride=2),
                nn.BatchNorm2d(96),
                
                nn.Conv2d(in_channels=96, out_channels=256, kernel_size=5, stride=1, padding=2),
                nn.ReLU(inplace=True),
                nn.MaxPool2d(kernel_size=3, stride=2),
                nn.BatchNorm2d(256),
                
                nn.Conv2d(in_channels=256, out_channels=384, kernel_size=3, stride=1, padding=1),
                nn.ReLU(inplace=True),
                nn.BatchNorm2d(384),
                
                nn.Conv2d(in_channels=384, out_channels=384, kernel_size=3, stride=1, padding=1),
                nn.ReLU(inplace=True),
                nn.BatchNorm2d(384),
                
                nn.Conv2d(in_channels=384, out_channels=256, kernel_size=3, stride=1, padding=1),
                nn.ReLU(inplace=True),
                nn.MaxPool2d(kernel_size=3, stride=2),
                nn.BatchNorm2d(256)
            )
            
            self.avgpool = nn.AdaptiveAvgPool2d((6, 6))
            
            self.classifier = nn.Sequential(
                nn.Dropout(),
                nn.Linear(256 * 6 * 6, 4096),
                nn.ReLU(inplace=True),
                
                nn.Dropout(),
                nn.Linear(4096, 4096),
                nn.ReLU(inplace=True),
                
                nn.Linear(4096, num_classes)
            )
    
        def forward(self, x):
            x = self.features(x)
            x = self.avgpool(x)
            x = torch.flatten(x, 1)
            x = self.classifier(x)
            return x
    
  • 其他训练部分以及数据集请参考 二、PyTorch—DataLoader & Transforms,使用该模型的预测准确性为68%。