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)

相关文章
|
22天前
|
机器学习/深度学习 搜索推荐 PyTorch
基于昇腾用PyTorch实现传统CTR模型WideDeep网络
本文介绍了如何在昇腾平台上使用PyTorch实现经典的WideDeep网络模型,以处理推荐系统中的点击率(CTR)预测问题。
189 66
|
1月前
|
供应链 监控 安全
对话|企业如何构建更完善的容器供应链安全防护体系
本期节目围绕软件供应链安全、容器安全的主要挑战以及阿里云如何帮助用户等维度展开了深入的讨论。
|
1月前
|
供应链 监控 安全
对话|企业如何构建更完善的容器供应链安全防护体系
随着云计算和DevOps的兴起,容器技术和自动化在软件开发中扮演着愈发重要的角色,但也带来了新的安全挑战。阿里云针对这些挑战,组织了一场关于云上安全的深度访谈,邀请了内部专家穆寰、匡大虎和黄竹刚,深入探讨了容器安全与软件供应链安全的关系,分析了当前的安全隐患及应对策略,并介绍了阿里云提供的安全解决方案,包括容器镜像服务ACR、容器服务ACK、网格服务ASM等,旨在帮助企业构建涵盖整个软件开发生命周期的安全防护体系。通过加强基础设施安全性、技术创新以及倡导协同安全理念,阿里云致力于与客户共同建设更加安全可靠的软件供应链环境。
150315 32
|
2月前
|
供应链 安全 Cloud Native
阿里云容器服务助力企业构建云原生软件供应链安全
本文基于2024云栖大会演讲,探讨了软件供应链攻击的快速增长趋势及对企业安全的挑战。文中介绍了如何利用阿里云容器服务ACK、ACR和ASM构建云原生软件供应链安全,涵盖容器镜像的可信生产、管理和分发,以及服务网格ASM实现应用无感的零信任安全,确保企业在软件开发和部署过程中的安全性。
|
2月前
|
机器学习/深度学习 人工智能 PyTorch
Transformer模型变长序列优化:解析PyTorch上的FlashAttention2与xFormers
本文探讨了Transformer模型中变长输入序列的优化策略,旨在解决深度学习中常见的计算效率问题。文章首先介绍了批处理变长输入的技术挑战,特别是填充方法导致的资源浪费。随后,提出了多种优化技术,包括动态填充、PyTorch NestedTensors、FlashAttention2和XFormers的memory_efficient_attention。这些技术通过减少冗余计算、优化内存管理和改进计算模式,显著提升了模型的性能。实验结果显示,使用FlashAttention2和无填充策略的组合可以将步骤时间减少至323毫秒,相比未优化版本提升了约2.5倍。
85 3
Transformer模型变长序列优化:解析PyTorch上的FlashAttention2与xFormers
|
3月前
|
机器学习/深度学习 数据采集 Docker
Docker容器化实战:构建并部署一个简单的Web应用
Docker容器化实战:构建并部署一个简单的Web应用
|
3月前
|
机器学习/深度学习 人工智能 PyTorch
使用Pytorch构建视觉语言模型(VLM)
视觉语言模型(Vision Language Model,VLM)正在改变计算机对视觉和文本信息的理解与交互方式。本文将介绍 VLM 的核心组件和实现细节,可以让你全面掌握这项前沿技术。我们的目标是理解并实现能够通过指令微调来执行有用任务的视觉语言模型。
75 2
|
3月前
|
并行计算 监控 搜索推荐
使用 PyTorch-BigGraph 构建和部署大规模图嵌入的完整教程
当处理大规模图数据时,复杂性难以避免。PyTorch-BigGraph (PBG) 是一款专为此设计的工具,能够高效处理数十亿节点和边的图数据。PBG通过多GPU或节点无缝扩展,利用高效的分区技术,生成准确的嵌入表示,适用于社交网络、推荐系统和知识图谱等领域。本文详细介绍PBG的设置、训练和优化方法,涵盖环境配置、数据准备、模型训练、性能优化和实际应用案例,帮助读者高效处理大规模图数据。
82 5
|
4月前
|
Kubernetes 监控 数据中心
容器化与微服务:构建高效开发环境的双剑合璧
【10月更文挑战第20天】本文探讨了容器化技术(如Docker和Kubernetes)与微服务架构的结合,如何共同构建高效、灵活的开发环境。容器化解决了环境一致性、快速部署和资源隔离的问题,而微服务架构则提升了系统的可维护性和可扩展性。通过容器编排工具、CI/CD流程和服务网格,两者的结合进一步优化了开发和运维效率。文章还分享了实施这两项技术的最佳实践和职业心得。
|
6天前
|
Ubuntu API 网络虚拟化
ubuntu22 编译安装docker,和docker容器方式安装 deepseek
本脚本适用于Ubuntu 22.04,主要功能包括编译安装Docker和安装DeepSeek模型。首先通过Apt源配置安装Docker,确保网络稳定(建议使用VPN)。接着下载并配置Docker二进制文件,创建Docker用户组并设置守护进程。随后拉取Debian 12镜像,安装系统必备工具,配置Ollama模型管理器,并最终部署和运行DeepSeek模型,提供API接口进行交互测试。
131 15

推荐镜像

更多