层和块

单个神经元:

  1. 接受一些输入
  2. 生成相应的标量输出
  3. 具有一组相关参数
    • 参数可以更新以优化某些感兴趣的目标函数

层:

  1. 接受一组输入
  2. 生成相应的输出
  3. 由一组可调整参数描述

使用softmax回归时,一个单层本身就是模型

块:

  1. 块可以描述单个层、由多个层组成的组件或整个模型本身。
  2. 使用块进行抽象的一个好处是可以将一些块组合成更大的组件,这一过程通常是递归的

块由类表示

  • 它的任何子类都必须定义一个将其输入转换为输出的正向传播函数,并且必须存储任何必需的参数。
  • 注意,有些块不需要任何参数。
  • 最后,为了计算梯度,块必须具有反向传播函数。
  • 自动微分有一些后端实现,只需要考虑正向传播函数和必需的参数。

多层感知机

1
2
3
4
5
6
7
8
import torch
from torch import nn
from torch.nn import functional as F

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

X = torch.rand(2, 20)
net(X)
  • nn.Sequential()定义了一个特殊的Module,即在PyTorch中表示一个块的类。
  • 两个全连接层都是Linear类的实例,Linear类本身就是Module的子类。
  • 正向传播(forward)函数也非常简单:它将列表中的每个块连接在一起,将每个块的输出作为下一个块的输入。

自定义块

块的基本功能:

  1. 将输入数据作为其正向传播函数的参数。
  2. 通过正向传播函数来生成输出。请注意,输出的形状可能与输入的形状不同。例如,我们上面模型中的第一个全连接的层接收任意维的输入,但是返回一个维度256的输出。
  3. 计算其输出关于输入的梯度,可通过其反向传播函数进行访问。通常这是自动发生的。
  4. 存储和访问正向传播计算所需的参数。
  5. 根据需要初始化模型参数。
1
2
3
4
5
6
7
8
9
10
11
12
13
class MLP(nn.Module):
# 用模型参数声明层。这里,我们声明两个全连接的层
def __init__(self):
# 调用`MLP`的父类`Block`的构造函数来执行必要的初始化。
# 这样,在类实例化时也可以指定其他函数参数,例如模型参数`params`(稍后将介绍)
super().__init__()
self.hidden = nn.Linear(20, 256) # 隐藏层
self.out = nn.Linear(256, 10) # 输出层

# 定义模型的正向传播,即如何根据输入`X`返回所需的模型输出
def forward(self, X):
# 注意,这里我们使用ReLU的函数版本,其在nn.functional模块中定义。
return self.out(F.relu(self.hidden(X)))

顺序块--Sequential

两个关键函数:

  1. 一种将块逐个追加到列表中的函数
  2. 一种正向传播函数,用于将输入按追加块的顺序传递给块组成的“链条”。
1
2
3
4
5
6
7
8
9
10
11
12
13
class MySequential(nn.Module):
def __init__(self, *args):
super().__init__()
for block in args:
# 这里,`block`是`Module`子类的一个实例。我们把它保存在'Module'类的成员变量
# `_modules` 中。`block`的类型是OrderedDict。
self._modules[block] = block

def forward(self, X):
# OrderedDict保证了按照成员添加的顺序遍历它们
for block in self._modules.values():
X = block(X)
return X

__init__方法中,我们将每个块逐个添加到有序字典_modules中。