层
对于多层感知机而言,整个模型及其组成层都是这种架构。整个模型接受原始输入(特征),生成输出(预测), 并包含一些参数(所有组成层的参数集合)。 同样,每个单独的层接收输入(由前一层提供), 生成输出(到下一层的输入),并且具有一组可调参数, 这些参数根据从下一层反向传播的信号进行更新。
自定义层
建议首先阅读本文的自定义块部分,再查看本章节。实际上,自定义块解决的是网络连接问题;而自定义层规定了层内参数如何传递的问题。因此自定义块强调多个层的连接方式,自定义层则给定一个新的运算方式。某种意义上,在定义块的时候,层也可以在块中进行定义。
1 | class Mylinear(nn.Module): |
块
块可以描述单个层、由多个层组成的组件或整个模型本身。
自定义块
1 | class MLP(nn.Module): |
深度学习中PyTorch块的概念和Python编程中类的概念是一致的。在上述代码中,我们定义了MLP
这个类,该类继承于nn.Module
父类。完成块的定义只需要定义一个包含构造函数(init)和前向传播函数(forward)的类。
MLP
类包含了构造函数和前向传播函数。对于构造函数,我们没有传入新的参数,首先super().__init__()
方法即调用了父类nn.Module
的__init__
方法;接着,我们定义对象的属性hidden
和out
。根据构造函数可知,对象构造时不需要传入参数,我们定义方法直接采用了实例化的线性回归模型。对于前向传播函数,返回一个前向的计算公式。
值得注意的是,我们定义了维度,但在大多数块中,我们不提前定义维度,而是采用延迟初始化的技术,当数据第一次通过网络进行传播时,才开始定义维度。
我们接着为MLP
创造一个实例net
,并调用实例net
的forward
方法。
1 | net = MLP() |
在PyTorch框架中,nn.Module
是所有神经网络模块的基类,它包含了一些基本的方法和属性,用来构建和训练神经网络。其中一个重要的特性是它的__call__
方法,这个方法使得任何继承自nn.Module
的类的实例都可以像调用普通函数那样被调用。
当你创建了一个继承自nn.Module
的类的实例(如你的MLP
类),并尝试使用这个实例调用时(比如net(X)
),实际上发生的是以下几步:
__call__
方法被自动触发:当你使用net(X)
这样的语法时,Python会寻找net
的__call__
方法,并将X
作为参数传递给它。转发到
forward
方法:nn.Module
的__call__
方法内部实现了对forward
方法的调用。这意味着,当你写net(X)
时,它实际上是执行了net.forward(X)
。执行
forward
方法:在forward
方法中,定义了输入数据X
如何通过网络流动,包括经过哪些层、激活函数等,并最终返回输出结果。
因此,net(X)
的调用看似简单,但背后是通过nn.Module
的__call__
方法,自动将其转化为对forward
方法的调用。这样的设计使得模型的使用更加直观和方便,同时也保持了代码的清晰和组织性。
此外,不可以直接使用MLP(X)
这种方式调用。在PyTorch中,必须先创建类的实例才能使用它的方法,包括forward
方法。直接使用类名调用方法通常是面向静态方法或类方法的,而forward
方法并不是静态或类方法,它依赖于类实例的状态(即类实例的属性,如你的例子中的self.hidden
和self.out
层)。
顺序块
1 | #定义顺序块,用以存储神经网络 |
这段代码定义了一个名为MySequential
的Python类,它是nn.Module
的子类,用于在PyTorch框架中创建一个神经网络的顺序容器。这个容器可以按照定义的顺序执行多个网络层或其他模块:
类定义和初始化
class MySequential(nn.Module)
: 这行代码定义了一个名为MySequential
的类,它继承自nn.Module
。nn.Module
是所有神经网络模块的基类,提供了很多基本功能,如参数管理、模型保存和加载等。def __init__(self, *args)
: 这是类的构造函数,用于初始化新的MySequential
实例。*args
允许在创建实例时传递一个动态数量的参数,这些参数都应该是nn.Module
的实例,比如各种网络层。super().__init__()
: 这行代码调用父类(nn.Module
)的构造函数,是Python中常见的做法,用于正确初始化继承自父类的部分。
添加模块到容器
for idx, module in enumerate(args)
: 这个循环遍历传给构造函数的所有模块。enumerate
函数同时返回每个模块的索引(idx
)和模块本身(module
)。self._modules[str(idx)] = module
: 这行代码将每个传入的模块添加到self._modules
字典中。_modules
是nn.Module
中用于存储子模块的内置容器,其类型是OrderedDict
。使用OrderedDict
确保模块会按照添加的顺序被存储和调用。
定义前向传播
def forward(self, X)
: 这是nn.Module
必须实现的方法,定义了模型的前向传播逻辑,即如何处理输入数据X
。for block in self._modules.values()
: 这个循环遍历self._modules
中存储的所有模块。由于self._modules
是一个有序字典,这保证了模块按照它们被添加的顺序执行。X = block(X)
: 在循环中,输入X
被依次传递给每个模块(block
),模块处理后的输出成为下一模块的输入。这样可以顺序地应用所有定义的网络层或函数到输入数据上。return X
: 最后,经过所有模块处理后的结果X
被返回。这是该顺序容器处理输入后的最终输出。
块的嵌套
块是nn.Module
方法的组合,事实上,在PyTorch中,块本身也可以和nn.Module进行顺序块的定义。如:
1 | nn.Sequential(MLP, nn.Relu, nn.Relu(*arg)) |