其他分享
首页 > 其他分享> > 代码分析--模型的创建

代码分析--模型的创建

作者:互联网

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