Pytorch 5.1 深度学习计算:层和块
层
此前,我们学习了多层感知机(Multi Layer Perceptron ,MLP) 。神经网络的核心组件是层\((layer)\),它是一种数据处理模块,我们可以将其看作数据过滤器。输入一些数据经过处理后,输出的数据变得更加有用。
块
块\((block)\)可以描述单个层、由多个层组成的组件或整个模型本身。比如下例图中的所有都是块。
理解
从编程的角度上来说,每一个块都是一个类\((class)\),或者说可以使用一个类来表示。 它的任何子类都必须定义一个将其输入转换为输出的前向传播函数, 并且必须存储任何必需的参数。
回顾 MLP 框架的实现:
import torch
from torch.nn import functional as F
from torch import nn
# Reviewing MLP
net = nn.Sequential(nn.Linear(20,256),nn.ReLU(),nn.Linear(256,10))
X = torch.rand(2,20)
net(X)
[Out:]
tensor([[-0.0345, 0.0057, 0.0227, -0.1984, -0.2237, -0.0464, -0.0189, -0.2631,
-0.2752, -0.1415],
[-0.1851, 0.0243, -0.0286, -0.1372, -0.1288, -0.1205, 0.1659, -0.3036,
-0.2170, -0.0545]], grad_fn=<AddmmBackward>)
我们通过实例化 \(nn.Sequential\) 来构建我们的模型, 层的执行顺序是作为参数传递的。 我们通过 \(net(x)\) 调用我们的模型来获得模型的输出 , 这个其实是 \(net.\_\_call\_\_(X)\) 的简写。这个是python 类自带的一些函数。
自定义块实现
class MLP(nn.Module):
# 用模型参数声明层。这里,我们声明两个全连接的层
def __init__(self):
super().__init__() # 调用父类nn.Module进行初始化(相当于Cpp中的构造函数)
self.hidden = nn.Linear(20,256) # Hidden Layer
self.out = nn.Linear(256,10) # Output Layer
# 定义模型的前向传播,即如何根据输入X返回所需的模型输出
def forward(self, X):
# 注意,这里我们使用ReLU的函数版本,其在nn.functional模块中定义。
return self.out(F.relu(self.hidden(X)))
net = MLP()
net(X)
[Out:]
tensor([[ 0.0214, -0.1030, 0.0920, 0.0377, 0.0155, 0.1304, -0.0631, -0.2136,
-0.0108, -0.0536],
[-0.0288, 0.0612, 0.0023, 0.0298, -0.0258, 0.0822, -0.0131, -0.2086,
0.0226, -0.0265]], grad_fn=<AddmmBackward0>)
这里就要解释下为何要如此定义这个类了, 从块的功能入手: (或者直接看官网实例:CLICK HERE)
(1)将输入数据作为其前向传播函数的参数。
(2)过前向传播函数来生成输出。
(3)计算其输出关于输入的梯度,可通过其反向传播函数进行访问。
(4)存储和访问前向传播计算所需的参数。
(5)根据需要初始化模型参数。
代码中的super 函数是python类里面的继承的函数 , 可以理解\(super().\_\_init\_\_()\) 为 \(nn.Module().\_\_init\_\_()\) 。可以将其看成Cpp中的构造函数,作用是直接初始化,不必要写重复的代码,具体可以查看我的python基础的另外一篇文章。 CLICK HERE
而我们导入的 \(torch.nn.functional\) 这个库是Pytorch 的一个函数的一个库,其中有Convolution functions : conv1d 、 conv2d 、 conv3d ;Pooling functions: avg_pool1d、avg_pool2d、avg_pool3d 等等等等,可以自行查阅官方文档:CLICK HERE
顺序块
为了更加直观查看 \(torch.nn.Sequential()\) 是如何工作的,我们会尝试写一个自己的 \(Sequential\) 。 我们知道\(Sequential\) 的功能是:
(1) 一种将块逐个追加到列表中的函数。
(2)一种前向传播函数,用于将输入按追加块的顺序传递给块组成的“链条”。
class MySequential(nn.Module):
def __init__(self,*args):
super().__init__()
for idx,module in enumerate(args): # 返回索引值和使用什么操作
self._modules[str(idx)] = module # 用字典存起来
def forward(self,X):
for block in self._modules.values():
X = block(X)
return X
net = MySequential(nn.Linear(20,256),nn.ReLU(),nn.Linear(256,10))
net(X)
[Out:]
tensor([[ 0.1353, 0.1192, -0.0364, -0.0749, -0.2969, -0.2655, -0.0070, 0.0905,
0.0866, 0.2755],
[ 0.1561, 0.2128, -0.0533, -0.1302, -0.3092, -0.2072, 0.0382, -0.0209,
-0.0411, 0.2099]], grad_fn=<AddmmBackward0>)
\(self.\_modules\) 我们称之为有序词典 , 是我们自己创建的。 通过 \(enumerate()\) 这个函数得到索引值和我们传入的 "操作" , 比如说 \(nn.Linear(in\_features,out\_features)\) 。 而索引值 \(idx\) 就是我们的顺序,我们通过字典的形式存入 \(self.\_modules\) 中,通过遍历这个字典中的 \(values\) 我们可以依次拿到 "操作" 。
当然你也可以嵌套使用自己定义的 "块" 和 \(torch.nn.Sequential()\) ,因为它们都是 \(nn.Module()\) 的子类。 更多用法可以看沐神的d2l: CLICK HERE
posted on 2022-01-20 22:54 YangShusen' 阅读(259) 评论(0) 编辑 收藏 举报