《动手学深度学习》笔记2.1——神经网络从基础→进阶 (层和块 - 自定义块)

目录

0. 前言

原书正文(第五章)

第五章 - 第一节 - 层和块 - 自定义块

1. Sequential() PyTorch高级API

2. MLP() 无传入参数

3. MySequential() 传入任意层(块)

4. FixedHiddenMLP() 无传入参数-固定隐藏层

5. NestMLP() 传入嵌套块-多次嵌套

6. 效率问题

7. 小结


0. 前言

  • 课程全部代码(pytorch版)已上传到附件
  • 本章节为原书第5章,共分为5节,本篇是第1节:层和块(自定义块)
  • 本章的代码位置:chapter_deep-learning-computation/index.ipynb
  • 本章的视频链接:
    • 16 PyTorch 神经网络基础【动手学深度学习v2】_哔哩哔哩_bilibili
    • 17 使用和购买 GPU【动手学深度学习v2】_哔哩哔哩_bilibili
  • 本章核心内容:深入探索深度学习计算的关键组件, 即模型构建、参数访问与初始化、设计自定义层和块、将模型读写到磁盘, 以及利用GPU实现显著的加速
  • 从基础到进阶:本章知识将使读者从深度学习“基础用户”变为“高级用户”
  • 与后面高级模型的关系:虽然本章不介绍任何新的模型或数据集, 但后面的高级模型章节在很大程度上依赖于本章的知识
  • 同学反馈:本章的学习解开了前后章节的很多困惑,对打牢基础非常有帮助
  • 想完全读懂本章节,需要有线性回归、softmax回归、多层感知机等相关基础,主要的前置知识可参考
    • https://blog.csdn.net/weixin_57972634/category_12776752.html
    • 还非常需要python基础,得学到面向对象:https://download.csdn.net/download/weixin_57972634/89738887?spm=1001.2014.3001.5503

原书正文(第五章)

除了庞大的数据集和强大的硬件, 优秀的软件工具在深度学习的快速发展中发挥了不可或缺的作用。 从2007年发布的开创性的Theano库开始, 灵活的开源工具使研究人员能够快速开发模型原型, 避免了我们使用标准组件时的重复工作, 同时仍然保持了我们进行底层修改的能力。 随着时间的推移,深度学习库已经演变成提供越来越粗糙的抽象。 就像半导体设计师从指定晶体管到逻辑电路再到编写代码一样, 神经网络研究人员已经从考虑单个人工神经元的行为转变为从层的角度构思网络, 通常在设计架构时考虑的是更粗糙的块(block)。

之前我们已经介绍了一些基本的机器学习概念, 并慢慢介绍了功能齐全的深度学习模型。 在上一章中,我们从零开始实现了多层感知机的每个组件, 然后展示了如何利用高级API轻松地实现相同的模型。 为了易于学习,我们调用了深度学习库,但是跳过了它们工作的细节。 在本章中,我们将深入探索深度学习计算的关键组件, 即模型构建、参数访问与初始化、设计自定义层和块、将模型读写到磁盘, 以及利用GPU实现显著的加速。 这些知识将使读者从深度学习“基础用户”变为“高级用户”。 虽然本章不介绍任何新的模型或数据集, 但后面的高级模型章节在很大程度上依赖于本章的知识。

第五章 - 第一节 - 层和块 - 自定义块

之前首次介绍神经网络时,我们关注的是具有单一输出的线性模型。 在这里,整个模型只有一个输出。

注意,单个神经网络:

(1)接受一些输入;

(2)生成相应的标量输出;

(3)具有一组相关 参数(parameters),更新这些参数可以优化某目标函数。

然后,当考虑具有多个输出的网络时, 我们利用矢量化算法来描述整层神经元。

像单个神经元一样,层:

(1)接受一组输入,

(2)生成相应的输出,

(3)由一组可调整参数描述。

当我们使用softmax回归时,一个单层本身就是模型。 然而,即使我们随后引入了多层感知机,我们仍然可以认为该模型保留了上面所说的基本架构。

对于多层感知机而言,整个模型及其组成层都是这种架构。 整个模型接受原始输入(特征),生成输出(预测), 并包含一些参数(所有组成层的参数集合)。 同样,每个单独的层接收输入(由前一层提供), 生成输出(到下一层的输入),并且具有一组可调参数, 这些参数根据从下一层反向传播的信号进行更新。

事实证明,研究讨论 “比单个层大” 但 “比整个模型小” 的组件更有价值。 例如,在计算机视觉中广泛流行的ResNet-152架构就有数百层, 这些层是由层组(groups of layers)的重复模式组成。 这个ResNet架构赢得了2015年ImageNet和COCO计算机视觉比赛 的识别和检测任务 :cite:He.Zhang.Ren.ea.2016。 目前ResNet架构仍然是许多视觉任务的首选架构。 在其他的领域,如自然语言处理和语音, 层组以各种重复模式排列的类似架构现在也是普遍存在。

为了实现这些复杂的网络,我们引入了神经网络的概念。 (block)可以描述单个层、由多个层组成的组件或整个模型本身。 使用块进行抽象的一个好处是可以将一些块组合成更大的组件, 这一过程通常是递归的,如 :numref:fig_blocks所示。 通过定义代码来按需生成任意复杂度的块, 我们可以通过简洁的代码实现复杂的神经网络。

从编程的角度来看,块由(class)表示。 它的任何子类都必须定义一个将其输入转换为输出的前向传播函数, 并且必须存储任何必需的参数。 注意,有些块不需要任何参数。 最后,为了计算梯度,块必须具有反向传播函数。 在定义我们自己的块时,由于自动微分(在 《动手学深度学习》笔记1.3——矩阵求导_方向导数与梯度的关系-CSDN博客 中引入自动求导) 提供了一些后端实现,我们只需要考虑前向传播函数和必需的参数。

在构造自定义块之前,(我们先回顾一下多层感知机) (《动手学深度学习》笔记1.6——多层感知机→代码实现_两层感知机代码函数实现-CSDN博客)的代码。 下面的代码生成一个网络,其中包含一个具有256个单元和ReLU激活函数的全连接隐藏层, 然后是一个具有10个隐藏单元且不带激活函数的全连接输出层。

1. Sequential() PyTorch高级API

import torch
from torch import nn
from torch.nn import functional as F  # functional Module包括了一些没有参数的函数,下面会看到具体用法net = nn.Sequential(nn.Linear(20, 256), nn.ReLU(), nn.Linear(256, 10))  # 单隐藏层(256个神经元), 输入20→输出10X = torch.rand(2, 20)  # 权重X,初始化成2行20列的、均值为0方差为1的随机矩阵
# 2是批量大小,20是输入的维度,10是输出的大小
net(X)

在这个例子中,我们通过实例化nn.Sequential来构建我们的模型, 层的执行顺序是作为参数传递的。 简而言之,(nn.Sequential定义了一种特殊的 Module), 即在PyTorch中表示一个块的类, 它维护了一个由Module组成的有序列表。 注意,两个全连接层都是Linear类的实例, Linear类本身就是Module的子类。 另外,到目前为止,我们一直在通过net(X)调用我们的模型来获得模型的输出。 这实际上是net.__call__(X)的简写。 这个前向传播函数非常简单: 它将列表中的每个块连接在一起,将每个块的输出作为下一个块的输入

2. MLP() 无传入参数

要想直观地了解块是如何工作的,最简单的方法就是自己实现一个块 ,进而从零定义和执行块。 在实现我们自定义块之前,我们简要总结一下每个块必须提供的基本功能。

  1. 将输入数据作为其前向传播函数的参数。
  2. 通过前向传播函数来生成输出。请注意,输出的形状可能与输入的形状不同。例如,我们上面模型中的第一个全连接的层接收一个20维的输入,但是返回一个维度为256的输出。
  3. 计算其输出关于输入的梯度,可通过其反向传播函数进行访问。通常这是自动发生的。
  4. 存储和访问前向传播计算所需的参数。
  5. 根据需要初始化模型参数。

在下面的代码片段中,我们从零开始编写一个块。 它包含一个多层感知机,其具有256个隐藏单元的隐藏层和一个10维输出层。 注意,下面的MLP类继承了表示块的类。 我们的实现只需要提供我们自己的构造函数(Python中的__init__函数)和前向传播函数。

class MLP(nn.Module):  # 定义MLP是nn.Module的一个子类(涉及到python面向对象编程的相关知识)# 用模型参数声明层。这里,我们声明两个全连接的层def __init__(self):  # 创建对象(实例)时,__init__会自动执行# 调用MLP的父类Module的构造函数来执行必要的初始化。# 这样,在类实例化时也可以指定其他函数参数,例如模型参数params(稍后将介绍)super().__init__()  # super() 调用父类nn.Module的构造函数__init__(),初始化模型(必要)的参数和子模块self.hidden = nn.Linear(20, 256)  # 隐藏层:存在类的成员变量hidden里面self.out = nn.Linear(256, 10)  # 输出层:存在类的成员变量hidden里面# 定义模型的前向传播,即如何根据输入X返回所需的模型输出def forward(self, X):  # 前向传播:逐层运算;更新参数在反向传播过程中# 注意,这里我们使用ReLU的函数版本,其在nn.functional模块中定义。return self.out(F.relu(self.hidden(X))) # 先把输入X存到hidden层里面,得到隐藏层的输出# 用ReLU激活后放入out层里

我们首先看一下前向传播函数,它以X作为输入, 计算带有激活函数的隐藏表示,并输出其未规范化的输出值。 在这个MLP实现中,两个层都是实例变量。 要了解这为什么是合理的,可以想象实例化两个多层感知机(net1net2), 并根据不同的数据对它们进行训练。 当然,我们希望它们学到两种不同的模型。

实例化多层感知机的层,然后在每次调用前向传播函数时调用这些层

注意一些关键细节: 首先,我们定制的__init__函数通过super().__init__() 调用父类的__init__函数, 省去了重复编写模版代码的痛苦。 然后,我们实例化两个全连接层, 分别为self.hiddenself.out。 注意,除非我们用到一个新的运算符, 否则我们不必担心反向传播基础的函数或参数初始化, 系统将调用父类nn.Module的__init__()函数自动初始化。

我们来试一下这个MLP()函数:

net = MLP()
net(X)

的一个主要优点是它的多功能性。 我们可以子类化块以创建层(如全连接层的类)、 整个模型(如上面的MLP类)或具有中等复杂度的各种组件。 我们在接下来的章节中充分利用了这种多功能性, 比如在处理卷积神经网络时。

3. MySequential() 传入任意层(块)

现在我们可以更仔细地看看Sequential类是如何工作的, 回想一下Sequential的设计是为了把其他模块串起来。 为了构建我们自己的简化的MySequential, 我们只需要定义两个关键函数:

  1. 一种将块逐个追加到列表中的函数;
  2. 一种前向传播函数,用于将输入按追加块的顺序传递给块组成的“链条”。

下面的MySequential类提供了与默认Sequential类相同的功能。

class MySequential(nn.Module):def __init__(self, *args):  # *args是可变参数列表, 在实例化 MySequential 类时传入任意数量的参数# *args 是一个元组(tuple),可以传入多个 nn.Module 子类实例(层,块),它们会被打包成一个元组super().__init__()  # 和上面MLP()一样,调用父类nn.Module中的__init__()来初始化基本的模型和参数for idx, module in enumerate(args):  # enumerate()是内置函数,可同时获取可迭代对象的索引和对应的元素# 可以这样理解: 下方MySequential()传入的3个成员nn.Linear(20, 256),nn.ReLU(),nn.Linear(256, 10)# 放到了*args元组里,为每个成员(层,块)生成一个顺序idx,按顺序放进成员变量_module里self._modules[str(idx)] = module  # 成员变量_module的类型是OrderedDict(顺序字典)def forward(self, X):# OrderedDict保证了按照成员(层,块)添加的顺序遍历(调用)它们for block in self._modules.values():  # 按顺序每次用values()拿一个成员(层,块),放到block里,传入XX = block(X)  # 这里的X可看作中间变量,不断按顺序在成员(层,块)间塑性非线性变换return X  # 返回最后输出的X

__init__函数将每个模块逐个添加到有序字典_modules中。 读者可能会好奇为什么每个Module都有一个_modules属性? 以及为什么我们使用它而不是自己定义一个Python列表? 简而言之,_modules的主要优点是: 在模块的参数初始化过程中, 系统知道在_modules字典中查找需要初始化参数的子块。

  • MySequential的前向传播函数被调用时, 每个添加的块都按照它们被添加的顺序执行。

现在可以使用我们的 MySequential类 重新实现多层感知机

net = MySequential(nn.Linear(20, 256), nn.ReLU(), nn.Linear(256, 10))
net(X)

请注意,MySequential的用法与之前为Sequential类编写的代码相同(咱的MySequential算是基础版的Sequential

拓展:可参考/回顾 《动手学深度学习》笔记1.6——多层感知机→代码实现_两层感知机代码函数实现-CSDN博客 )

4. FixedHiddenMLP() 无传入参数-固定隐藏层

Sequential类使模型构造变得简单, 允许我们组合新的架构,而不必定义自己的类。 然而,并不是所有的架构都是简单的顺序架构。 当需要更强的灵活性时,我们需要定义自己的块。 例如,我们可能希望在前向传播函数中执行Python的控制流。 此外,我们可能希望执行任意的数学运算, 而不是简单地依赖预定义的神经网络层。

class FixedHiddenMLP(nn.Module):def __init__(self):super().__init__()# 不计算梯度的随机权重参数。因此其在训练期间保持不变self.rand_weight = torch.rand((20, 20), requires_grad=False)self.linear = nn.Linear(20, 20)def forward(self, X):X = self.linear(X)# 使用创建的常量参数以及relu和mm函数X = F.relu(torch.mm(X, self.rand_weight) + 1)# 复用全连接层。这相当于两个全连接层共享参数X = self.linear(X)# 控制流while X.abs().sum() > 1:X /= 2return X.sum()

在这个FixedHiddenMLP模型中,我们实现了一个隐藏层, 其权重(self.rand_weight)在实例化时被随机初始化,之后为常量。 这个权重不是一个模型参数,因此它永远不会被反向传播更新。 然后,神经网络将这个固定层的输出通过一个全连接层。

注意,在返回输出之前,模型做了一些不寻常的事情: 它运行了一个while循环,在L1范数大于1的条件下, 将输出向量除以2,直到它满足条件为止。 最后,模型返回了 X 中所有项的和。 注意,此操作可能不会常用于在任何实际任务中, 我们只展示如何将任意代码集成到神经网络计算的流程中。

net = FixedHiddenMLP()
net(X)

输出:

5. NestMLP() 传入嵌套块-多次嵌套

我们可以[混合搭配各种组合块的方法]。 在下面的例子中,我们以一些想到的方法嵌套块,嵌套了用 nn.Sequential() 合并好的块。

class NestMLP(nn.Module):def __init__(self):super().__init__()self.net = nn.Sequential(nn.Linear(20, 64), nn.ReLU(),  # 隐藏层1nn.Linear(64, 32), nn.ReLU())  # 隐藏层2self.linear = nn.Linear(32, 16)  # 输出层def forward(self, X):return self.linear(self.net(X))  # 一次嵌套chimera = nn.Sequential(NestMLP(), nn.Linear(16, 20), FixedHiddenMLP())  # 二次嵌套,两个块夹一层拼在一起
chimera(X)

输出:

6. 效率问题

读者可能会开始担心操作效率的问题。 毕竟,我们在一个高性能的深度学习库中进行了大量的字典查找、 代码执行和许多其他的Python代码。 Python的问题全局解释器锁 是众所周知的。 在深度学习环境中,我们担心速度极快的GPU可能要等到CPU运行Python代码后才能运行另一个作业。

7. 小结

  • 一个块可以由许多层组成;一个块可以由许多块组成。
  • 块可以包含代码。
  • 块负责大量的内部处理,包括参数初始化和反向传播。
  • 层和块的顺序连接由Sequential块处理。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.xdnf.cn/news/149672.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

【目标检测】隐翅虫数据集386张VOC+YOLO

隐翅虫数据集:图片来自网页爬虫,删除重复项后整理标注而成 数据集格式:Pascal VOC格式YOLO格式(不包含分割路径的txt文件,仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件) 图片数量(jpg文件个数):386 标注…

C++核心编程和桌面应用开发 第五天(new delete malloc 静态成员 静态成员函数 单例)

目录 1.new运算符 1.1开批数组 2.delete运算符 3.malloc和new的区别 4.万能指针接收new对象 5.静态成员 6.静态成员函数 7. 单例 7.1概念 7.2常见场景 1.new运算符 C中用new进行动态内存分配,new会在调用构造函数之前,成功进行内存分配&#x…

利用F.interpolate()函数进行插值操作

函数简介 功能: 利用插值方法,对输入的张量数组进行上\下采样操作,换句话说就是科学合理地改变数组的尺寸大小,尽量保持数据完整。 torch.nn.functional.interpolate(input, sizeNone, scale_factorNone, modenearest, align_c…

RabbitMQ是什么?RabbitMQ简介

一:技术背景 假如我们有一个支付服务,支付服务的业务逻辑是:首先支付扣减余额,更新支付单状态,更新订单状态,发短信,给这个用户增加积分。在这个场景下,如果我们使用同步调用通信&am…

vscode将c++项目打包exe进行反汇编练习

vscode将c&c项目打包成控制台exe全过程,进行c反汇编练习,反汇编只有不断的练习才能巩固、积累经验。 一、打包exe 创建新项目,选择c,Windows桌面向导 直接点击创建 直接点确定 直接点击运行即可,可以看到我的exe…

15 跨组件通信依赖注入provide和inject

Provide / Inject 通常,当我们需要从父组件向子组件传递数据时,我们使用 props。想象一下这样的结构:有一些深度嵌套的组件,而深层的子组件只需要父组件的部分内容。在这种情况下,如果仍然将 prop 沿着组件链逐级传递…

ROS2 技术及分布式介绍

PC端开发环境搭建 WSL环境搭建 https://www.guyuehome.com/46574 In Windows 11 builds that support wslg: 1. Open up powershell and enter wsl --install ROS2系统安装 方法一 • 设置编码 Bash $ sudo apt update && sudo apt install loca…

EffcientNetV2(2021):更快、更强、效率更高的EffcientNet!

EffcientNetV2: Smaller Models and Faster Training EfficientNetV2:更小的模型和更快的训练 论文地址: https://arxiv.org/abs/2104.00298 本文介绍了 EfficientNetV2,这是一个新的卷积网络系列,与以前的模型相比,它…

HDFS_API文件和文件夹

代码: Beforepublic void init() throws URISyntaxException, IOException {URI uri new URI("hdfs://master:9000");// 创建一个配置文件Configuration entries new Configuration();// 获取到了客户端对象 // entries.set("dfs.replicat…

【嵌入式linux开发】SPI设备文件读取ICM-40609D传感器

【嵌入式linux开发】SPI设备文件操作ICM-40609D传感器 前言一、数据手册浅读二、linux系统下使用SPI设备文件操作ICM-40609-D三、ros1发布imu数据3.1、创建ros1工作空间3.2、数据发布节点代码 前言 在本篇博客中,将从ICM-40609-D传感器的数据手册出发,简…

公安局软件管理平台建设方案和必要性,论文-3-———未来之窗行业应用跨平台架构

三、平台功能设计 四、技术架构 1. 前端界面 - 采用简洁、易用的设计风格,适应不同终端设备的访问。 - 基于 HTML5、CSS3 和 JavaScript 构建。 2. 后端服务 - 选择主流的 Web 开发框架,如 未来之窗跨平台架构,VUE。 - 数据库…

Github Webhook触发Jenkins自动构建

1.功能说明 Github Webhook可以触发Jenkins自动构建,通过配置Github Webhook,每次代码变更之后(例如push操作),Webhook会自动通知Jenkins服务器,Jenkins会自动执行预定义的构建任务(如Jenkins …

Redis-认识与应用(从ChatGpt的角度看Redis)

问题:您好,我的项目是在线教育平台,用springboot3搭建,我现在想学redis,请问redis能在我的项目中有什么应用场景呢 问题:就是我项目能应用上具体什么场景,请给我例子,并给我具体代码…

springboot整合openfeign

文章目录 准备一、引入必要依赖二、写一个feign client并暴露到注册中心2.1 client2.2 开启Feign客户端功能 三、别的服务引入IProductClient并调用方法3.1 建一个order-service,引入IProductClient所在模块3.2 注入IProductClient,并调用方法 四、启动服…

JAVA基本简介(期末)

1、JDK JRE JVM (1)JDK JAVA标准开发包,提供了编译、运行JAVA程序所需的各种工具和资源,包括JAVA编译器、JAVA运行时的环境,及常用的JAVA类库等 (2)JRE JAVA运行环境,用于解释执行JA…

JW01二氧化碳传感器(串行通信 STM32)

目录 一、介绍 二、传感器原理 1.工作原理介绍 2.串口数据流格式 三、程序设计 main.c文件 usart3.h文件 usart3.c文件 四、实验效果 五、资料获取 项目分享 一、介绍 JW01-CO2检测模块是一种用于检测空气中二氧化碳浓度的传感器模块。它可以广泛应用于室内空气质量…

美畅物联丨GB/T 28181系列之TCP/UDP被动模式和TCP主动模式

GB/T 28181《安全防范视频监控联网系统信息传输、交换、控制技术要求》作为我国安防领域的重要标准,为视频监控系统的建设提供了全面的技术指导和规范。该标准详细规定了视频监控系统的信息传输、交换和控制技术要求,在视频流传输方面,GB/T 2…

【Day20240924】05git 两人协作 冲突

git 两人协作 冲突 命令行解决 两个人修改同一文件时 的冲突可视化解决 两个人修改同一文件时 的冲突参考 命令行解决 两个人修改同一文件时 的冲突 假设kerwin.js是项目的路由文件。tiechui文件夹是组员铁锤的工作目录;test2008文件夹是组长的工作目录。此时&…

Redis 优化

目录 优雅的 key 删除 Bigkey 恰当的数据类型 批处理优化 Pipeline 集群下的批处理 服务端优化 持久化配置 慢查询 命令以及安全配置 内存安全和配置 内存缓冲区配置 集群最佳实践 集群带宽问题 集群还是主从 优雅的 key 删除 Bigkey Bigkey 内存占用较多&…

ubuntu 安裝 Poetry 示例

ubuntu 安裝 Poetry 示例 一、前言 poetry 是一个命令行工具,安装之后就可以使用 poetry 指令。可以将其安装全局环境或者是虚拟环境,我推荐安装在全局环境,这样在后面使用时不需要单独激活虚拟环境。 (1)安装 Poet…