containers

graph LR
A["Containers"] --> B["nn.Sequetial"]
B["nn.Sequetial"] --> C["wrap multiple network layers in sequence"]
A["Containers"] --> D["nn.ModuleList"]
D["nn.ModuleList"] --> E["wrap multiple network layers in the way like the list of python"]
A["Containers"] --> F["nn.ModuleDict"]
F["nn.ModuleDict"] --> G["wrap multiple network layers in the way like the dict of python"]

the sequential of the container

The nn.Sequential is the container of the nn.module,wrapping the network layers in sequence.

在传统的机器学习中,有一步叫做特征工程。需要人为的设计特征,将特征送去分类器中分类。在深度学习的阶段,就已经弱化了特征工程的概念,尤其是卷积神经网络,对图像的特征我们完全不需要去设计,都可以交给卷积神经网络自动去学习特征,最后会加上几个全连接层,用于输出分类结果。而在早期的神经网络中,用于分类的分类器正是全连接构成的,所以在神经网络阶段,也有习惯以全连接层为界限,将网络模型分为特征提取模块和分类模块,对一个大的模型进行划分,对模型进行管理。

e.g

LeNet:

Conv1 --> pool1 --> Conv2 --> pool2 --> fc1 --> fc2 --> fc3

from the [Conv1 to pool2] is features(特征提取器)

from the [fc1 to fc3] is classifier(分类器)

**nn.Sequentail is the container of the nn.module,wrapping the network layers in sequence.

  • sequential:the construction of the layers is strictly in order
  • 自带forward():自带forward,通过for循环一次执行前向传播运算。**
    def __init__(self, classes):
super(LeNetSequential, self).__init__()
self.features = nn.Sequential(
nn.Conv2d(3, 6, 5),
nn.ReLU(),
nn.MaxPool2d(kernel_size=2, stride=2),
nn.Conv2d(6, 16, 5),
nn.ReLU(),
nn.MaxPool2d(kernel_size=2, stride=2),) self.classifier = nn.Sequential(
nn.Linear(16*5*5, 120),
nn.ReLU(),
nn.Linear(120, 84),
nn.ReLU(),
nn.Linear(84, classes),)

以上就完成了模型构建的构建子模块,我们可以看到子模块就两个概念,feature和classifier。紧接着定义它的前向传播forward。

def forward(self, x):
x = self.features(x)
x = x.view(x.size()[0], -1)
x = self.classifier(x)
return x

只需要把输入x输入到features,features输出再经过一个形状的变换,再输入到classifier中得到一个分类的结果。

在代码中的

net = LeNetSequential(classes=2)

中设置断点进行debug,进行观察如何构建一个nn.sequential

nn.sequential给它输入一系列的网络层就能构成一个容器。我们在它的结构体内进行debug,在最后一个maxpool2d层进行step into。此时进入到container.py的sequential类,class Sequential(Module)表明了sequential也是继承的module类。既然是继承module类的,那么也会有8个参数来管理属性。

if len(args) == 1 and isinstance(args[0], OrderedDict):
for key, module in args[0].items():
self.add_module(key, module)
else:
for idx, module in enumerate(args):
self.add_module(str(idx), module)

先判断传进来的参数判断,数据类型是不是一个有序质点。在这里应该不是一个有序质点,因此进入else,对循环传入进来参数进行取出每一个网络层,然后采用Module类方法,将网络层添加到sequential中。不断的添加进来。完成了一个sequential构成。然后回到self.features = nn.Sequential。发现此时的modules还是空的,是因为我们没有进行赋值“=”的运算,只是进行了等号右边的sequential的构建。再进行。

目前进入到含有sequential的modules不为空,由于本身sequential也是一个modules,因此自身也可以进行探查。在sequential的Module里就有网络层了,是按顺序构成的。

接下来我们进入

output = net(fake_img)

进行step into,观察前向传播是如何实现的。进入内部后,我们通过_call_impl的函数体内部进行观察,能够看出在

result = self.forward(*input, **kwargs)

设置step into。此时来到了原始的代码中class LeNetSequential(nn.Module):的def forward(self, x)。我们发现,只需要把x赋值给self.features(x),那么我们就能够自动执行这六层的网络传播。比之前的方法简单很多,我们进入self.features看看为什么那么简洁,我们进入feature后,因为self.feature是一个sequential,sequential又继承与module,因此我们又会进入module.py的call函数中。我们直接进入result = self.forward(*input,**kwargs),这下我们来到了container.py中的class Sequential(Module):函数中的def foward,代码如下

def forward(self, input):
for module in self:
input = module(input)
return input

此时我们能看出这段代码非常简洁,其主要思想就是利用for循环对网络进行循环的forward。我们进行调试,从modules中取出module进行forwad,将我们的input迭代输入到卷积、relu、maxpool等

这里发现网络层在上一节中是有name的,比如conv1,conv2,但是在这里是没有的,通过序号来索引的。因此在复杂的网络中,很难通过序号去索引,因此采用dict去命名索引。

LeNetSequentialOrderDict

核心代码如下:


class LeNetSequentialOrderDict(nn.Module):
def __init__(self, classes):
super(LeNetSequentialOrderDict, self).__init__() self.features = nn.Sequential(OrderedDict({
'conv1': nn.Conv2d(3, 6, 5),
'relu1': nn.ReLU(inplace=True),
'pool1': nn.MaxPool2d(kernel_size=2, stride=2), 'conv2': nn.Conv2d(6, 16, 5),
'relu2': nn.ReLU(inplace=True),
'pool2': nn.MaxPool2d(kernel_size=2, stride=2),
})) self.classifier = nn.Sequential(OrderedDict({
'fc1': nn.Linear(16*5*5, 120),
'relu3': nn.ReLU(), 'fc2': nn.Linear(120, 84),
'relu4': nn.ReLU(inplace=True), 'fc3': nn.Linear(84, classes),
})) def forward(self, x):
x = self.features(x)
x = x.view(x.size()[0], -1)
x = self.classifier(x)
return x

通过单步调试去理解sequential是如何通过dict给网络命名的。

容器之ModuleList

nn.ModuleList是 nn.module的容器,用于包装一组网络层,以迭代方式调用网络层

主要方法:

  • append():在ModuleList后面添加网络层
  • extend():拼接两个ModuleList
  • insert():指定在ModuleList中位置插入网络层

    modulelist就可以采用列表生成式,通过for循环去生成网络层。例子生成一个20个全连接层,每个全连接层有10个神经元的网络

    其主要的核心算法分别为:
class ModuleList(nn.Module):
def __init__(self):
super(ModuleList, self).__init__()
self.linears = nn.ModuleList([nn.Linear(10, 10) for i in range(20)]) def forward(self, x):
for i, linear in enumerate(self.linears):
x = linear(x)
return x

以及核心的modulelist代码:

def __init__(self, modules: Optional[Iterable[Module]] = None) -> None:
super(ModuleList, self).__init__()
if modules is not None:
self += modules

此时modules是一个List,因此通过不断的+的拼接合成一个modulelist.

容器之ModuleLDict

nn.ModuleDict是 nn.module的容器,用于包装一组网络层,以索引方式调用网络层。字典一样包装,给每一个网络层加上一个名称,可以通过名称去索引网络层。

主要方法:

  • clear():清空ModuleDict
  • items():返回可迭代的键值对(key-value pairs)
  • keys():返回字典的键(key)
  • values():返回字典的值(value)
  • pop():返回一对键值,并从字典中删除

    核心代码

class ModuleDict(nn.Module):
def __init__(self):
super(ModuleDict, self).__init__()
self.choices = nn.ModuleDict({
'conv': nn.Conv2d(10, 10, 3),
'pool': nn.MaxPool2d(3)
}) self.activations = nn.ModuleDict({
'relu': nn.ReLU(),
'prelu': nn.PReLU()
}) def forward(self, x, choice, act):
x = self.choices[choice](x)
x = self.activations[act](x)
return x

和之前的sequentaildict的不同,sequentaildict是有序的,moduledict是无序的,能够通过dict对Key来指定网络层。这里的forward会增加两个参数,choice和act来选择我们的网络层。因此在后面的output就需要进行修改,代码如下:

output = net(fake_img,'conv','prelu')

修改后的Output用来指定对应的网络和激活函数

之前的output无需修改,为

output = net(fake_img)

容器总结

  • nn.Sequential:顺序性,各网络层之间严格按顺序执行,常用于block构建
  • nn.ModuleList:迭代性,常用于大量重复网构建,通过for循环实现重复构建
  • nn.ModuleDict:索引性,常用于可选择的网络层

代码

采用sequential容器,改写Alexnet,给features中每一个网络层增加名字,并通过下面这行代码打印出来

print(alexnet._modules['features']._modules.keys())

改写的Alexnet如下:

import torch
import torch.nn as nn
from .utils import load_state_dict_from_url
from collections import OrderedDict __all__ = ['AlexNet', 'alexnet'] model_urls = {
'alexnet': 'https://download.pytorch.org/models/alexnet-owt-4df8aa71.pth',
} class AlexNet(nn.Module): def __init__(self, num_classes=1000):
super(AlexNet, self).__init__()
self.features = nn.Sequential(OrderedDict({
'conv1' : nn.Conv2d(3, 64, kernel_size=11, stride=4, padding=2),
'relu1': nn.ReLU(inplace=True),
'maxpool2d1': nn.MaxPool2d(kernel_size=3, stride=2),
'conv2':nn.Conv2d(64, 192, kernel_size=5, padding=2),
'relu2':nn.ReLU(inplace=True),
'MaxPool2d2': nn.MaxPool2d(kernel_size=3, stride=2),
'Conv2d3': nn.Conv2d(192, 384, kernel_size=3, padding=1),
'ReLU3': nn.ReLU(inplace=True),
'Conv2d4':nn.Conv2d(384, 256, kernel_size=3, padding=1),
'ReLU4': nn.ReLU(inplace=True),
'Conv2d5': nn.Conv2d(256, 256, kernel_size=3, padding=1),
'ReLU5': nn.ReLU(inplace=True),
'MaxPool2d3':nn.MaxPool2d(kernel_size=3, stride=2),
}))
self.avgpool = nn.AdaptiveAvgPool2d((6, 6))
self.classifier = nn.Sequential(OrderedDict({
'dropout1': nn.Dropout(),
'linear1': nn.Linear(256 * 6 * 6, 4096),
'relu1': nn.ReLU(inplace=True),
'dropout2': nn.Dropout(),
'linear2': nn.Linear(4096, 4096),
'relu2': nn.ReLU(inplace=True),
'linear3': nn.Linear(4096, num_classes),
})) def forward(self, x):
x = self.features(x)
x = self.avgpool(x)
x = torch.flatten(x, 1)
x = self.classifier(x)
return x def alexnet(pretrained=False, progress=True, **kwargs):
r"""AlexNet model architecture from the
`"One weird trick..." <https://arxiv.org/abs/1404.5997>`_ paper. Args:
pretrained (bool): If True, returns a model pre-trained on ImageNet
progress (bool): If True, displays a progress bar of the download to stderr
"""
model = AlexNet(**kwargs)
if pretrained:
state_dict = load_state_dict_from_url(model_urls['alexnet'],
progress=progress)
model.load_state_dict(state_dict)
return model

输出结果为

odict_keys(['conv1', 'relu1', 'maxpool2d1', 'conv2', 'relu2', 'MaxPool2d2', 'Conv2d3', 'ReLU3', 'Conv2d4', 'ReLU4', 'Conv2d5', 'ReLU5', 'MaxPool2d3'])

如果使用如下代码:

alexnet = torchvision.models.AlexNet()
print(alexnet._modules['classifier']._modules.keys())

得到的输出结果为:

odict_keys(['dropout1', 'linear1', 'relu1', 'dropout2', 'linear2', 'relu2', 'linear3'])

pytorch(12)ContainersAndAlexNet的更多相关文章

  1. Netruon 理解(12):使用 Linux bridge 将 Linux network namespace 连接外网

    学习 Neutron 系列文章: (1)Neutron 所实现的虚拟化网络 (2)Neutron OpenvSwitch + VLAN 虚拟网络 (3)Neutron OpenvSwitch + GR ...

  2. 基于MVC4+EasyUI的Web开发框架经验总结(12)--利用Jquery处理数据交互的几种方式

    在基于MVC4+EasyUI的Web开发框架里面,大量采用了Jquery的方法,对数据进行请求或者提交,方便页面和服务器后端进行数据的交互处理.本文主要介绍利用Jquery处理数据交互的几种方式,包括 ...

  3. Web 在线文件管理器学习笔记与总结(11)获取文件夹信息 (12)返回上一级操作

    (11)获取文件夹信息 文件夹没有修改操作. index.php: <?php require 'dir.func.php'; require 'file.func.php'; require ...

  4. Android菜鸟的成长笔记(12)——Handler、Loop、MessageQueue

    原文:[置顶] Android菜鸟的成长笔记(12)——Handler.Loop.MessageQueue 当一个程序第一次启动时,Android会启动一条主线程(Main Thread),主线程主要 ...

  5. Windows Phone开发(12):认识一下独具个性的磁贴

    原文:Windows Phone开发(12):认识一下独具个性的磁贴 对"磁贴"的理解是一点也不抽象的,为什么呢?只要你愿意启动WP系统,无论你是在模拟器中还是在真机中,是的,桌面 ...

  6. 网站静态化处理—web前端优化—中(12)

    网站静态化处理—web前端优化—中(12) Web前端很多优化原则都是从如何提升网络通讯效率的角度提出的,但是这些原则使用的时候还是有很多陷阱在里面,如果我们不能深入理解这些优化原则背后所隐藏的技术原 ...

  7. python入门(12)dict

    python入门(12)dict Python内置了字典:dict的支持,dict全称dictionary,在其他语言中也称为map,使用键-值(key-value)存储,具有极快的查找速度. 举个例 ...

  8. (1-2)line-height的各类属性值

    (1-2)line-height的各类属性值 首先来个疑问!没有问题印象不深嘛 一.line-height支持哪些属性值呢? 五只手指头就能数过来了咯. 比如normal, <number> ...

  9. Java设计模式(12)迭代模式(Iterator模式)

    上了这么多年学,我发现一个问题,好象老师都很喜欢点名,甚至点名都成了某些老师的嗜好,一日不点名,就饭吃不香,觉睡不好似的,我就觉得很奇怪,你的课要是讲的好,同学又怎么会不来听课呢,殊不知:“误人子弟, ...

随机推荐

  1. 哈希算法解决:HDU1686 && POJ2774 && POJ3261

    HDU1686 题意: 找A串在B串中的出现次数(可重叠),可用KMP做,这里只提供哈希算法做的方法 题解: 先得到A串的hash值,然后在B中枚举起点,长度为lena的子串,检验hash值是否相同就 ...

  2. 郁闷的出纳员 HYSBZ - 1503

    OIER公司是一家大型专业化软件公司,有着数以万计的员工.作为一名出纳员,我的任务之一便是统计每位员工的 工资.这本来是一份不错的工作,但是令人郁闷的是,我们的老板反复无常,经常调整员工的工资.如果他 ...

  3. F(x) HDU - 4734

    题意: 给你一个n位的数x(A(n)A(n-1)...A(1)),那么F(x)=A(n)*2^(n-1)+A(n-1)*2^(n-2)......+A(1)*2^(0) 题目输入A.B 你需要找出来在 ...

  4. Educational Codeforces Round 91 (Rated for Div. 2) A. Three Indices (模拟)

    题意:有一长度为\(n\)的序列,问是否能找到\(a_{i}<a_{j},a_{j}>a_{k},(i<j<k)\),如果满足,输出其位置. 题解:直接暴力两头找即可,最坏复杂 ...

  5. 阿里云 MaxCompute(ODPS)

    大数据产品架构 BASE - Dataworks ODPS - MaxCompute ODPS 功能组成(Open Data Process Service) ODPS 是旧称,阿里云公有云服务中现称 ...

  6. HTTP 请求过程以及报文结构

    目录 HTTP 请求流程 HTTP 请求报文 请求行 方法字段(Request Method) URL字段(Uniform Resource Locator) HTTP 协议版本字段(略) 请求/响应 ...

  7. 忘记Mysql的root用户密码处理方法(以mysql 5.5.33为例)

    1.修改mysql服务器的脚本 ~]#vi /etc/rc.d/init.d/mysqld #找到$bindir/mysqld_safe --datadir="$datadir" ...

  8. woj1018(HDU4384)KING KONG 循环群

    title: woj1018(HDU4384)KING KONG 循环群 date: 2020-03-19 09:43:00 categories: [acm] tags: [acm,woj,数学] ...

  9. 101道Numpy、Pandas练习题

    无论是数据分析还是机器学习,数据的预处理必不可少. 其中最常用.最基础的Python库非numpy和pandas莫属,很多初学者可能看了很多教程,但是很快就把用法忘光了. 光看不练假把式,今天向大家推 ...

  10. ES6 Map to Array

    ES6 Map to Array function differentSymbolsNaive(str) { // write code here. const map = new Map(); co ...