Pytorch学习笔记(4):模型创建(Module)、模型容器(Containers)、AlexNet构建

简介: Pytorch学习笔记(4):模型创建(Module)、模型容器(Containers)、AlexNet构建


前期回顾:

Pytorch学习笔记(1):基本概念、安装、张量操作、逻辑回归

Pytorch学习笔记(2):数据读取机制(DataLoader与Dataset)

Pytorch学习笔记(3):图像的预处理(transforms)


一、网络模型的创建步骤

网络创建流程:


模型构建的两个要素:

  • 构建子模块:在自己建立的模型(继承nn.Module)的_init_()方法
  • 拼接子模块:是在模型的forward()方法中

以LeNet模型为例:

init函数中构建子模块,构建网络需要的卷积层、池化层、激活函数等

    def __init__(self, classes):
        super(LeNet, self).__init__()
        self.conv1 = nn.Conv2d(3, 6, 5)
        self.conv2 = nn.Conv2d(6, 16, 5)
        self.fc1 = nn.Linear(16*5*5, 120)
        self.fc2 = nn.Linear(120, 84)
        self.fc3 = nn.Linear(84, classes)

forward函数中拼接子模块 ,就是模型的实现

 def forward(self, x):
        out = F.relu(self.conv1(x))
        out = F.max_pool2d(out, 2)
        out = F.relu(self.conv2(out))
        out = F.max_pool2d(out, 2)
        out = out.view(out.size(0), -1)
        out = F.relu(self.fc1(out))
        out = F.relu(self.fc2(out))
        out = self.fc3(out)
        return out

二、nn.Mudule的属性

在模型的概念当中,有一个非常重要的概念叫做nn.Module, 我们所有的模型,所有的网络层都是继承于这个类的。

•  torch.nn: 这是Pytorch的神经网络模块, 这里的Module就是它的子模块之一,另外还有几个与Module并列的子模块, 这些子模块协同工作,各司其职。

nn.Module中,有8个重要的属性, 用于管理整个模型,他们都是以有序字典的形式存在着:

•  _parameters: 存储管理属于nn.Parameter类的属性,例如权值,偏置这些参数

•  _modules: 存储管理nn.Module类, 比如LeNet中,会构建子模块,卷积层,池化层,就会存储在_modules中

•  _buffers:存储管理缓冲属性, 如BN层中的running_mean, std等都会存在这里面

•  ***_hook:存储管理钩子函数(5个与hooks有关的字典,这个先不用管)

nn.Module属性构建:

在nn.Module类中进行属性赋值时,被setattr函数拦截,在该函数中,判断即将要赋值的这个数据类型是否是nn.Parameter类,是则存储到parameters这个字典中;如果是nn.Module类,则存储在module这个字典中进行管理

nn.module总结:

  • 一个module可以包含多个子module(LeNet包含卷积层,池化层,全连接层)
  • 一个module相当于一个运算, 必须实现forward函数(从计算图的角度去理解)
  • 每个module都有8个字典管理它的属性(最常用的就是_parameters_modules

具体代码段如下:

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))

三、模型容器Containers

3.1 nn.Sequential

nn.Sequential nn.module的容器,用于按顺序包装一组网络层

以LeNet为例,将卷积池化放到一个sequential中进行特征提取,将全连接层放到一个sequential中进行分类,然后将这两个sequential拼接起来,就是LeNet网络

(1)输入数据类型非字典

用sequential容器构建包装子模型:

'''------------Sequential---------------'''
class LeNetSequential(nn.Module):
    def __init__(self, classes):
        super(LeNetSequential, self).__init__()
        self.features = nn.Sequential(
            nn.Conv2d(3, 6, 5),
            nn.ReLU(),
            nn.MaxPool2d(kernel_size=2, stride=2),
            nn.Conv2d(6, 16, 5),
            nn.ReLU(),
            nn.MaxPool2d(kernel_size=2, stride=2),)
        self.classifier = nn.Sequential(
            nn.Linear(16*5*5, 120),
            nn.ReLU(),
            nn.Linear(120, 84),
            nn.ReLU(),
            nn.Linear(84, classes),)
    def forward(self, x):
        x = self.features(x)
        x = x.view(x.size()[0], -1)
        x = self.classifier(x)
        return x

可以看到,LeNet在这里分成了两大部分:

  • 第一部分是features模块,用于特征提取
  • 第二部分是classifier部分,用于分类

每一部分都是各种网络的堆叠,然后用sequential包装起来。 然后它的forward函数也比较简单, 只需要features处理输出,然后形状变换,然后classifier就完成了。

进入sequential类的init函数,调用init函数构建相应的属性,得到八个有序字典。

首先判断输入的参数的数据类型是否为有序字典,如果为有序字典,则可以对网络层进行命名操作,如果输入不为字典,则执行else操作

在for循环中将网络层添加到sequential中

 def __init__(self, *args: Any):
        super(Sequential, self).__init__()
        if len(args) == 1 and isinstance(args[0], OrderedDict):
            for key, module in args[0].items():
                self.add_module(key, module)
        else:
            for idx, module in enumerate(args):
                self.add_module(str(idx), module)

调试中可以看出,每个网络层都被加入到module中,但此时网络层的对应的索引都为序号。

打印网络层,我们可以看到:

从中我们可以看出,每个网络层都是用序号来索引的,如果网络层过多,则很难通过序号去索引网络层,故可以对网络层进行命名。


(2)输入数据类型为字典

对sequential输入一个有序的字典来对网络层进行命名:

class LeNetSequentialOrderDict(nn.Module):
    def __init__(self, classes):
        super(LeNetSequentialOrderDict, self).__init__()
        self.features = nn.Sequential(OrderedDict({
            'conv1': nn.Conv2d(3, 6, 5),
            'relu1': nn.ReLU(inplace=True),
            'pool1': nn.MaxPool2d(kernel_size=2, stride=2),
            'conv2': nn.Conv2d(6, 16, 5),
            'relu2': nn.ReLU(inplace=True),
            'pool2': nn.MaxPool2d(kernel_size=2, stride=2),
        }))
        self.classifier = nn.Sequential(OrderedDict({
            'fc1': nn.Linear(16*5*5, 120),
            'relu3': nn.ReLU(),
            'fc2': nn.Linear(120, 84),
            'relu4': nn.ReLU(inplace=True),
            'fc3': nn.Linear(84, classes),
        }))
    def forward(self, x):
        x = self.features(x)
        x = x.view(x.size()[0], -1)
        x = self.classifier(x)
        return x

这里面Sequential包装的就是一个有序的字典, 字典中是网络名:网络层的形式。通过这个就可以对每一层网络进行命名

 def __init__(self, *args: Any):
        super(Sequential, self).__init__()
        if len(args) == 1 and isinstance(args[0], OrderedDict):
            for key, module in args[0].items():
                self.add_module(key, module)
        else:
            for idx, module in enumerate(args):
                self.add_module(str(idx), module)

调试结果中可以看出,网络层被写入sequential,注意,每个网络层前面都有单独的命名。不同于前面的序号索引:

打印网络层,我们可以看到,不同于之前,每个网络层都有自己的名字,可以更方便的通过网络名来索引网络:

总结——sequential

nn.sequential是nn.module的容器,用于按顺序包装一组网络层

  • 顺序性:各网络层之间严格按照顺序构建
  • 自带forward():自带的forward里,通过for循环依次执行前向传播运算

3.2 nn.ModuleList

nn.ModuleList是nn.module的容器,用于包装一组网络层,以迭代方式调用网络层

主要方法:

  • append():在ModuleList后面添加网络层
  • extend():拼接两个ModuleList
  • insert():指定在ModuleList中位置插入网络层

例:我们使用ModuleList来循环迭代的实现一个20个全连接层的网络的构建。

'''-------- ModuleList----------'''
class ModuleList(nn.Module):
    def __init__(self):
        super(ModuleList, self).__init__()
        self.linears = nn.ModuleList([nn.Linear(10, 10) for i in range(20)])
    def forward(self, x):
        for i, linear in enumerate(self.linears):
            x = linear(x)
        return x
net = ModuleList()
print(net)
fake_data = torch.ones((10, 10))
output = net(fake_data)
print(output)

进入container的modulelist类

将网络层加入到module中

    def __init__(self, modules: Optional[Iterable[Module]] = None) -> None:
        super(ModuleList, self).__init__()
        if modules is not None:
            self += modules

打印网络层,我们可以看到:


3.3 nn.ModuleDict

nn.ModuleDict是nn.module的容器,用于包装一组网络层,以索引方式调用网络层。

主要方法:

  • clear():清空ModuleDict
  • items():返回可迭代的键值对(key-value pairs)
  • keys():返回字典的键(key)
  • values():返回字典的值(values)
  • pop():返回一对键值,并从字典中删除

具体代码如下:

'''-----------ModuleDict---------------'''
class ModuleDict(nn.Module):
    def __init__(self):
        super(ModuleDict, self).__init__()
        self.choices = nn.ModuleDict({
            'conv': nn.Conv2d(10, 10, 3),
            'pool': nn.MaxPool2d(3)
        })
        self.activations = nn.ModuleDict({
            'relu': nn.ReLU(),
            'prelu': nn.PReLU()
        })
    def forward(self, x, choice, act):
        x = self.choices[choice](x)
        x = self.activations[act](x)
        return x
net = ModuleDict()
fake_img = torch.randn((4, 10, 32, 32))
output = net(fake_img, 'conv', 'relu')    # 在这里可以选择我们的层进行组合
print(output)

上面通过self.choices这个ModuleDict可以选择卷积或者池化

下面通过self.activations这个ModuleDict可以选取是用哪个激活函数

这在选择网络层的时候挺实用,比如要做时间序列预测的时候,我们往往会用到GRU或者LSTM, 我们就可以通过这种方式来对比哪种网络的效果好。 而具体选择哪一层是前向传播那完成,会看到多了两个参数。

打印网络层,我们可以看到:


容器总结

nn.sequential:顺序性,各网络层之间严格按照顺序执行,常用于block构建

nn.ModuleList:迭代性,常用于大量重复网络构建,通过for循环实现重复构建

nn.ModuleDict:索引性,常用于可选择的网络层


四、 AlexNet构建

关于AlexNet的网络结构详解可参考我这篇文章:经典神经网络论文超详细解读(一)——AlexNet学习笔记(翻译+精读)

下面看看AlexNet的源代码:

class AlexNet(nn.Module):
    def __init__(self, num_classes=1000):
        super(AlexNet, self).__init__()
        self.features = nn.Sequential(
            nn.Conv2d(3, 64, kernel_size=11, stride=4, padding=2),
            nn.ReLU(inplace=True),
            nn.MaxPool2d(kernel_size=3, stride=2),
            nn.Conv2d(64, 192, kernel_size=5, padding=2),
            nn.ReLU(inplace=True),
            nn.MaxPool2d(kernel_size=3, stride=2),
            nn.Conv2d(192, 384, kernel_size=3, padding=1),
            nn.ReLU(inplace=True),
            nn.Conv2d(384, 256, kernel_size=3, padding=1),
            nn.ReLU(inplace=True),
            nn.Conv2d(256, 256, kernel_size=3, padding=1),
            nn.ReLU(inplace=True),
            nn.MaxPool2d(kernel_size=3, stride=2),
        )
        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 学习笔记] 3.1 模型创建步骤与 nn.Module - 知乎 (zhihu.com)

Pytorch基础学习(第三章-Pytorch模型搭建)_nn.maxpool2d(2)

相关文章
|
25天前
|
机器学习/深度学习 关系型数据库 MySQL
大模型中常用的注意力机制GQA详解以及Pytorch代码实现
GQA是一种结合MQA和MHA优点的注意力机制,旨在保持MQA的速度并提供MHA的精度。它将查询头分成组,每组共享键和值。通过Pytorch和einops库,可以简洁实现这一概念。GQA在保持高效性的同时接近MHA的性能,是高负载系统优化的有力工具。相关论文和非官方Pytorch实现可进一步探究。
76 4
|
10天前
|
存储 运维 监控
构建高效稳定的Docker容器监控体系
【4月更文挑战第18天】 在现代微服务架构中,Docker容器已成为部署和运行应用的标准环境。随之而来的挑战是如何有效监控这些容器的性能与健康状况,确保系统的稳定性和可靠性。本文将探讨构建一个高效稳定的Docker容器监控体系的关键技术和方法,包括日志管理、性能指标收集以及异常检测机制,旨在为运维人员提供实用的指导和建议。
|
10天前
|
机器学习/深度学习 算法 PyTorch
PyTorch模型优化与调优:正则化、批归一化等技巧
【4月更文挑战第18天】本文探讨了PyTorch中提升模型性能的优化技巧,包括正则化(L1/L2正则化、Dropout)、批归一化、学习率调整策略和模型架构优化。正则化防止过拟合,Dropout提高泛化能力;批归一化加速训练并提升性能;学习率调整策略动态优化训练效果;模型架构优化涉及网络结构和参数的调整。这些方法有助于实现更高效的深度学习模型。
|
10天前
|
机器学习/深度学习 PyTorch 算法框架/工具
PyTorch与迁移学习:利用预训练模型提升性能
【4月更文挑战第18天】PyTorch支持迁移学习,助力提升深度学习性能。预训练模型(如ResNet、VGG)在大规模数据集(如ImageNet)训练后,可在新任务中加速训练,提高准确率。通过选择模型、加载预训练权重、修改结构和微调,可适应不同任务需求。迁移学习节省资源,但也需考虑源任务与目标任务的相似度及超参数选择。实践案例显示,预训练模型能有效提升小数据集上的图像分类任务性能。未来,迁移学习将继续在深度学习领域发挥重要作用。
|
13天前
|
运维 Kubernetes Devops
构建高效自动化运维体系:DevOps与容器技术融合实践
【4月更文挑战第15天】 在当今快速发展的信息技术时代,传统的IT运维模式已难以满足业务敏捷性的需求。本文旨在探讨如何通过整合DevOps理念和容器技术来构建一个高效的自动化运维体系。文章将详细阐述DevOps的核心原则、容器技术的基础知识,以及两者结合的优势。此外,文中还将分享一系列实践经验,包括持续集成/持续部署(CI/CD)流程的搭建、微服务架构的应用,以及监控和日志管理策略的优化,以期帮助企业实现快速、可靠且安全的软件交付过程。
|
15天前
|
运维 Devops 持续交付
构建高效稳定的云基础设施:DevOps与容器化技术融合实践
【4月更文挑战第13天】 在当今快速迭代和持续部署的软件开发环境中,传统的IT运维模式已难以满足业务发展的需求。本文聚焦于如何通过融合DevOps理念与容器化技术,构建一个高效、稳定且易于管理的云基础设施。文章将探讨持续集成/持续交付(CI/CD)流程的优化、容器化技术的最佳实践、以及微服务架构下的应用管理,以期为企业提供一种改进运维效率、加速产品上市时间,同时保障系统稳定性的解决方案。
|
30天前
|
运维 Kubernetes Devops
构建高效稳定的云基础设施:DevOps与容器化技术融合实践
随着企业数字化转型的不断深入,传统的IT运维模式已经难以满足快速迭代和持续交付的需求。本文将探讨如何通过结合DevOps文化与容器化技术,构建一个既高效又稳定的云基础设施。文章首先概述了DevOps的核心理念及其在现代运维中的重要性,然后详细介绍了容器化技术,特别是Docker和Kubernetes在实现微服务架构中的应用。最后,文中通过案例分析展示了这一融合实践如何在真实环境中提升运维效率和系统稳定性。
21 7
|
1月前
|
运维 Kubernetes 监控
构建高效稳定的容器化运维环境
在现代IT基础设施中,容器技术以其轻量级、快速部署和易于管理的特性成为企业数字化转型的重要支撑。本文将深入探讨如何构建一个高效且稳定的容器化运维环境,涵盖从容器选择、集群管理到持续集成与持续部署(CI/CD)的最佳实践。文章旨在为运维工程师提供一套系统的解决方案,以应对日益复杂的业务需求和技术挑战。
|
1月前
|
运维 监控 云计算
构建高效稳定的Docker容器监控体系
随着微服务架构的普及,Docker容器作为其核心承载单元,在系统运维中扮演着日益重要的角色。本文旨在探讨如何构建一个高效且稳定的Docker容器监控体系,以确保容器运行的可靠性和系统的高可用性。文章首先分析了容器监控的必要性,接着详细介绍了监控体系的设计理念和组件选择,最后提供了实施过程中的关键步骤与最佳实践。通过本文,读者将掌握构建和维护Docker容器监控体系的有效方法。
|
4天前
|
存储 Kubernetes Docker
Kubernetes(K8S)集群管理Docker容器(概念篇)
Kubernetes(K8S)集群管理Docker容器(概念篇)