作者:twinklezai750 | 来源:互联网 | 2023-09-24 15:17
目录
一、层和块的定义和初始化
1、pytorch 中定义Module的方法——nn.Sequential()
2、层和快的初始化方法与栗子
二、在ISTA-Net中层和块的定义与应用
一、层和块的定义和初始化
1、pytorch 中定义Module的方法——nn.Sequential()
我们通过实例化nn.Sequential
来构建我们的模型, 层的执行顺序是作为参数传递的。 简而言之,nn.Sequential
定义了一种特殊的Module
, 即在PyTorch中表示一个块的类, 它维护了一个由Module
组成的有序列表。 注意,两个全连接层都是Linear
类的实例, Linear
类本身就是Module
的子类。
任何的层和模型都是Module的子类
import torch
from torch import nn
from torch.nn import functional as F
net = nn.Sequential(nn.Linear(20, 256), nn.ReLU(), nn.Linear(256, 10))
X = torch.rand(2, 20)
net(X)
2、层和快的初始化方法与栗子
在实现我们自定义块之前,我们简要总结一下每个块必须提供的基本功能:
将输入数据作为其前向传播函数的参数。
通过前向传播函数来生成输出。请注意,输出的形状可能与输入的形状不同。例如,我们下面模型中的第一个全连接的层接收一个20维的输入,但是返回一个维度为256的输出。
计算其输出关于输入的梯度,可通过其反向传播函数进行访问。通常这是自动发生的。
存储和访问前向传播计算所需的参数。
根据需要初始化模型参数。
所有的Module都有两个比较重要的函数:
(1)Init_ 我们在这里定义我们需要哪些类,需要哪些参数
class MLP(nn.Module):
# 用模型参数声明层。这里,我们声明两个全连接的层
def __init__(self):
# 调用MLP的父类Module的构造函数来执行必要的初始化。
# 这样,在类实例化时也可以指定其他函数参数,例如模型参数params(稍后将介绍)
super().__init__()
self.hidden = nn.Linear(20, 256) # 隐藏层
self.out = nn.Linear(256, 10) # 输出层
# 定义模型的前向传播,即如何根据输入X返回所需的模型输出
def forward(self, X):
# 注意,这里我们使用ReLU的函数版本,其在nn.functional模块中定义。
return self.out(F.relu(self.hidden(X)))
首先调用父类super()._init_()把内部的参数全部设好,之后初始化weight或者其他的就能初始化成功了
self.***定义层, 放在类的成员变量中
因此在这个函数中,我们一般进行初始化过程,即定义一些东西
在ISTA-Net中,同样使用这种定义方式(上图)。
(2)前向函数forward(self,x)
x是输入,self是python编程中指向自己。
使用的时候可以直接用MLP(或BasicBlock),不需要MLP.forward(), 因为构造函数里面调用了·_call_,可以通过类名直接调用函数
二、在ISTA-Net中层和块的定义与应用
块(block)可以描述单个层、由多个层组成的组件或整个模型本身。 使用块进行抽象的一个好处是可以将一些块组合成更大的组件, 这一过程通常是递归的,如图所示
例如在ISTA-Net中,程序定义了9个Phases作为一个网络结构,每次正向传播运算都要经过这一过程,进而预测值。
从编程的角度来看,块由类(class)表示。 它的任何子类都必须定义一个将其输入转换为输出的前向传播函数, 并且必须存储任何必需的参数。 注意,有些块不需要任何参数。 最后,为了计算梯度,块必须具有反向传播函数。 在定义我们自己的块时,由于自动微分(在 2.5节 中引入) 提供了一些后端实现,我们只需要考虑前向传播函数和必需的参数。
# Define ISTA-Net Block
class BasicBlock(torch.nn.Module):
def __init__(self):
super(BasicBlock, self).__init__()
self.lambda_step = nn.Parameter(torch.Tensor([0.5])) #把这个函数理解为类型转换函数,将一个不可训练的类型Tensor转换成可以训练的类型parameter,并将这个parameter绑定到这个module里面(net.parameter()中就有这个绑定的parameter,所以在参数优化的时候可以进行优化的),所以经过类型转换这个self.v变成了模型的一部分,成为了模型中根据训练可以改动的参数了
#目的是为了让这些参数最优化
self.soft_thr = nn.Parameter(torch.Tensor([0.01])) #软阈值
self.conv1_forward = nn.Parameter(init.xavier_normal_(torch.Tensor(32, 1, 3, 3)))
self.conv2_forward = nn.Parameter(init.xavier_normal_(torch.Tensor(32, 32, 3, 3))) #卷积核,对卷积核里面的参数进行学习
self.conv1_backward = nn.Parameter(init.xavier_normal_(torch.Tensor(32, 32, 3, 3)))
self.conv2_backward = nn.Parameter(init.xavier_normal_(torch.Tensor(1, 32, 3, 3)))
#init.xavier_normal_()在每一层网络保证输入和输出的方差相同
def forward(self, x, y):
x = x - self.lambda_step * x
x = x + self.lambda_step * y
x_input = x.view(-1, 1, 33, 33) #通道数=1,图像的shape是33乘33的
x = F.conv2d(x_input, self.conv1_forward, padding=1) #输入样本与卷积核进行2D卷积,外加1圈padding
x = F.relu(x) #校正单元
x_forward = F.conv2d(x, self.conv2_forward, padding=1)
x = torch.mul(torch.sign(x_forward), F.relu(torch.abs(x_forward) - self.soft_thr)) #张量中每个元素相乘
x = F.conv2d(x, self.conv1_backward, padding=1)
x = F.relu(x)
x_backward = F.conv2d(x, self.conv2_backward, padding=1)
x_pred = x_backward.view(-1, 1089)
x = F.conv2d(x_forward, self.conv1_backward, padding=1)
x = F.relu(x)
x_est = F.conv2d(x, self.conv2_backward, padding=1)
symloss = x_est - x_input
return [x_pred, symloss]