© 2023 yanghn. All rights reserved. Powered by Obsidian
5.1 层与块
要点
- 块(Module)可以理解为神经网络的一个基本组件,可以由许多层组成,也可以由许多块组成。
- 一个块必须要实现
forward
方法,这个方法可以随意搭配其他块、随意自定义计算规则完成向前传播
1. 自定义块(Module)
一个模块(Module)是一个包含网络操作和可以持有状态(例如,权重和偏置)的高级组件。
以PyTorch为例,所有神经网络模块都应从基类 nn.Module
继扭,并需要实现 forward
方法来定义前向传播过程。例如,定义一个简单的多层感知器(MLP)可能如下所示:
import torch
from torch import nn
from torch.nn import functional as F
class MLP(nn.Module):
# 用模型参数声明层。这里,我们声明两个全连接的层
def __init__(self):
# 调用MLP的父类Module的构造函数来执行必要的初始化。
# 这样,在类实例化时也可以指定其他函数参数,例如模型参数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)))
`super().__init__()`
创建子类实例时将自动调用父类的 __init__()
方法,但如果重写 __init__()
方法,就不会自动调用,需要加上 super().__init__()
调用父类的构造方法
net = MLP()
net(X)
为什么 `net(x)` 会自动调用 `forward` 方法?
因为会自动调用 net.__call__(X)
方法,这个方法自动计算 forward
函数
2. 顺序块
回忆 nn.Sequential
也是一种特殊的 Module,也是 Module 的子类,是顺序将各层网络串联起来:
net = nn.Sequential(nn.Linear(20, 256), nn.ReLU(), nn.Linear(256, 10))
X = torch.rand(2, 20)
net(X)
我们可以自己实现这种顺序块:
class MySequential(nn.Module):
def __init__(self, *args):
super().__init__()
for idx, module in enumerate(args):
# 这里,module是Module子类的一个实例。我们把它保存在'Module'类的成员
# 变量_modules中。_module的类型是OrderedDict
self._modules[str(idx)] = module
def forward(self, X):
# OrderedDict保证了按照成员添加的顺序遍历它们
for block in self._modules.values():
X = block(X)
return X
*args
见 [[python中的*与**用法]]
`_modules`
self._modules
是 OrderedDict
类型,表示字典的 key 是有顺序的,从 Python 3.7 开始,标准的 dict
已经变成了有序的(可以简单理解为 for
循环输出顺序与字典定义顺序一致,例如上面代码第 11 行)
调用自定义顺序块:
net = MySequential(nn.Linear(20, 256), nn.ReLU(), nn.Linear(256, 10))
net(X)