代码分析--模型的创建
作者:互联网
https://blog.csdn.net/Dear_learner/article/details/122920181
构建模型的两大要素:
● 构建子模块(比如网络结构中的卷积层、池化层、激活层、全连接层);
● 拼接子模块(将子模块按照一定的顺序拼接起来,最终得到想到的网络结构)。
LeNet类继承了nn.Module,并且在__init__方法中实现了各个子模块的构建。
LetNet类里面的forward方法——通过调用forward方法来实现子模块的拼接操作。
在构造模型中继承了 nn.Module 类,所有的网络层都是继承于这个类的。
torch.nn: 这是 Pytorch 的神经网络模块,这里的 Module 就是它的子模块之一,另外还有几个与 Module 并列的子模块。如:
nn.init:权值初始化 nn.functional:函数的具体实现,如卷积,池化,激活等 nn.Parameter:张量子类,表示可学习的参数,如权重,偏置。
nn.Module:所有网络的基类,管理网络的属性。
在构造网络结构是需要继承 nn.Module 类,并重新构造 __init__和forward两个方法,但需要注意的有:
● 一般将网络中具有可学习参数的层(如卷积层、全连接层)放在构造函数 __init__中,当然也可以把不具有参数的层也放在里面;
● 一般把不具有可学习参数的层(如ReLU、dropout、BatchNormanation层)可放在构造函数中,也可不放在构造函数中,如果不放在构造函数__init__里面,则在forward方法里面可以使用nn.functional来代替;
● forward方法是必须要重写的,它是实现模型的功能,实现各个层之间的连接关系的核心。
例如:
1、将所有的层都放在构造函数 __init__中,然后在forward中将这些层按顺序连接起来。
2、将没有训练参数的层放到forward中,所以这些层没有出现在model中,但是在运行关系是在forward中通过torch.nn.function实现。
nn.Module的三种Sequential 实现方法:通过Sequential来包装层
即将几个层包装成一个大的层(块),然后直接调用包装成后的层。
第一种:Sequential 的输出结果的模型的每一层没有名称,默认是0,1,2,3来命名的。
1 import torch.nn as nn 2 3 model = nn.Sequential( 4 nn.Conv2d(3, 24, 3), 5 nn.ReLU(), 6 nn.Conv2d(24, 5, 3), 7 nn.ReLU()) 8 print(model) 9 10 #输出 11 Sequential( 12 (0): Conv2d(3, 24, kernel_size=(3, 3), stride=(1, 1)) 13 (1): ReLU() 14 (2): Conv2d(24, 5, kernel_size=(3, 3), stride=(1, 1)) 15 (3): ReLU() 16 )
第二种:可以给每一层添加名称,但是并不能通过名称来获取层,依然要通过索引来获取,即model[2]是正确的,而model[‘conv2’]是错误的。
import torch.nn as nn from collections import OrderedDict model = nn.Sequential(OrderedDict([ ('conv1', nn.Conv2d(1,20,5)), ('relu1', nn.ReLU()), ('conv2', nn.Conv2d(20,64,5)), ('relu2', nn.ReLU()) ])) print(model) print(model[2]) # 通过索引获取第几个层 """ 输出结果: Sequential( (conv1): Conv2d(1, 20, kernel_size=(5, 5), stride=(1, 1)) (relu1): ReLU() (conv2): Conv2d(20, 64, kernel_size=(5, 5), stride=(1, 1)) (relu2): ReLU() ) Conv2d(20, 64, kernel_size=(5, 5), stride=(1, 1)) """
第三种:add_module方法是Sequential继承了父类Module的方法。
import torch.nn as nn from collections import OrderedDict model = nn.Sequential() model.add_module("conv1",nn.Conv2d(1,20,5)) model.add_module('relu1', nn.ReLU()) model.add_module('conv2', nn.Conv2d(20,64,5)) model.add_module('relu2', nn.ReLU()) print(model) print(model[2]) # 通过索引获取第几个层 ”“” 输出结果: Sequential( (conv1): Conv2d(1, 20, kernel_size=(5, 5), stride=(1, 1)) (relu1): ReLU() (conv2): Conv2d(20, 64, kernel_size=(5, 5), stride=(1, 1)) (relu2): ReLU() ) Conv2d(20, 64, kernel_size=(5, 5), stride=(1, 1)) “”“
nn.Module中几个常见获取网络曾的方法:
def children(self): def named_children(self): def modules(self): def named_modules(self, memo=None, prefix=''): ''' 注意:这几个方法返回的都是一个Iterator迭代器,故而通过for循环访问,当然也可以通过next '''
总结:
(1)model.children()和model.named_children()方法返回的是迭代器iterator;
(2)model.children():每一次迭代返回的每一个元素实际上是 Sequential 类型,而Sequential类型又可以使用下标index索引来获取每一个Sequenrial 里面的具体层,比如conv层、dense层等;
(3)model.named_children():每一次迭代返回的每一个元素实际上是 一个元组类型,元组的第一个元素是名称,第二个元素就是对应的层或者是Sequential。
总结:
(1)model.modules()和model.named_modules()方法返回的是迭代器iterator;
(2)model的modules()方法和named_modules()方法都会将整个模型的所有构成(包括包装层、单独的层、自定义层等)由浅入深依次遍历出来,只不过modules()返回的每一个元素是直接返回的层对象本身,而named_modules()返回的每一个元素是一个元组,第一个元素是名称,第二个元素才是层对象本身。
(3)如何理解children和modules之间的这种差异性。注意pytorch里面不管是模型、层、激活函数、损失函数都可以当成是Module的拓展,所以modules和named_modules会层层迭代,由浅入深,将每一个自定义块block、然后block里面的每一个层都当成是module来迭代。而children就比较直观,就表示的是所谓的“孩子”,所以没有层层迭代深入。
标签:nn,--,创建,代码,modules,ReLU,Sequential,model,Conv2d 来源: https://www.cnblogs.com/ethan-tao/p/15983662.html