【深度学习】pytorch——神经网络工具箱nn

news2024/11/26 15:00:44

笔记为自我总结整理的学习笔记,若有错误欢迎指出哟~

深度学习专栏链接:
http://t.csdnimg.cn/dscW7

pytorch——神经网络工具箱nn

  • 简介
  • nn.Module
    • nn.Module实现全连接层
    • nn.Module实现多层感知机
  • 常用神经网络层
    • 图像相关层
      • 卷积层(Conv)实现锐化过滤器
    • 常见激活函数
    • ModuleList和Sequential
    • 循环神经网络层(RNN)
  • 优化器
  • nn.functional VS nn.Module
  • nn.init实现参数初始化
  • nn.Module深入分析
    • nn.Module基类的构造函数
    • 访问模型中的子模块

简介

PyTorch神经网络工具箱nn是一个用于构建深度学习模型的核心模块。它提供了一组简单而灵活的API,可以轻松地定义、训练和评估各种类型的神经网络。

nn模块中包含了许多预定义的模块和方法,如线性层、卷积层、循环神经网络、损失函数等,可以直接调用这些模块来构建深度学习模型。以下是nn模块的主要功能:

  1. 定义神经网络模型:可以使用nn模块定义各种类型的神经网络。通过继承nn.Module类,可以创建一个自己的神经网络类,并在其中定义各个层的结构和参数。

  2. 易于使用的层:nn模块提供了各种类型的层,如线性层、卷积层、池化层、归一化层等。这些层已经默认实现了前向传播和反向传播的过程,可以直接使用。

  3. 非线性激活函数:nn模块提供了常见的激活函数,如ReLU、Sigmoid、Tanh等,可以在层之间添加非线性变换。

  4. 自动求导:nn模块基于PyTorch的自动求导机制,可以自动计算梯度并进行反向传播。这使得用户可以更加专注于模型的设计和实现,而不必手动计算梯度。

  5. 训练和优化:可以使用nn模块中的优化器(如SGD、Adam等)来训练模型,并使用预定义的损失函数(如交叉熵、均方误差等)来评估模型的性能。

  6. 序列化和保存:可以使用nn模块中的API将模型序列化为文件,并在需要时恢复模型。

PyTorch神经网络工具箱nn提供了一组简单而灵活的API,可以方便地定义、训练和评估各种类型的神经网络。同时也支持用户自定义的模型和层,使得用户可以根据自己的需求和应用场景创建更加复杂的深度学习模型。

nn.Module

nn.Module是PyTorch中所有神经网络模型的基类。所有用户自定义的神经网络模型都应该继承自nn.Module类,并实现其中的方法。

nn.Module提供了一些重要的属性和方法,使得用户可以方便地定义神经网络的结构和参数,并进行正向传播和反向传播的计算。

以下是nn.Module类的一些重要属性和方法:

  1. __init__(self):构造函数,用于初始化模型的结构和参数。在这个方法中,用户可以定义模型中的各个层,并指定它们的参数。

  2. forward(self, input):前向传播方法。在这个方法中,用户定义了模型的正向传播过程。通过调用各个层的前向传播方法,并对它们的输出进行组合和变换,最终生成模型的输出。

  3. parameters(self):返回模型中所有可学习的参数。这个方法返回一个迭代器,可以用于遍历模型中的所有参数,并进行优化和更新。

  4. to(self, device):将模型移动到指定的设备上,如CPU或GPU。通过这个方法,可以方便地将模型加载到合适的设备上进行计算。

  5. state_dict(self)load_state_dict(self, state_dict):这两个方法用于序列化和加载模型的状态。state_dict()方法返回一个包含模型所有状态的字典,可以将其保存到文件中。而load_state_dict()方法则从给定的字典中加载模型的状态。

  6. train()eval():这两个方法用于设置模型的训练模式和评估模式。在训练模式下,模型会保留一些特定的操作,如Dropout层,在评估模式下会关闭这些操作。

通过继承nn.Module类,并重写其中的方法,用户可以方便地定义自己的神经网络模型。同时,nn.Module还提供了一些实用的方法和功能,如参数管理、设备移动和状态保存等,使得模型的训练和部署变得更加简单和高效。

nn.Module实现全连接层

用nn.Module实现全连接层。全连接层,又名仿射层,输出 y \textbf{y} y和输入 x \textbf{x} x满足 y=Wx+b \textbf{y=Wx+b} y=Wx+b W \textbf{W} W b \textbf{b} b是可学习的参数。

import torch as t
from torch import nn

class Linear(nn.Module): # 继承nn.Module
    def __init__(self, in_features, out_features):
        super(Linear, self).__init__() # 等价于nn.Module.__init__(self)
        self.w = nn.Parameter(t.randn(in_features, out_features))
        self.b = nn.Parameter(t.randn(out_features))
    
    def forward(self, x):
        x = x.mm(self.w) # x.@(self.w)
        return x + self.b.expand_as(x)
    
    
layer = Linear(4,3)
input = t.randn(2,4)
output = layer(input)
print(output)

for name, parameter in layer.named_parameters():
    print(name, parameter) # w and b 

代码解释:
这段代码定义了一个自定义的线性层Linear,它继承自nn.Module类,并重写了其中的__init__forward方法。该层的输入维度为in_features,输出维度为out_features

class Linear(nn.Module): 
    def __init__(self, in_features, out_features):
        super(Linear, self).__init__() 
        self.w = nn.Parameter(t.randn(in_features, out_features))
        self.b = nn.Parameter(t.randn(out_features))
    
    def forward(self, x):
        x = x.mm(self.w) 
        return x + self.b.expand_as(x)

__init__方法中,首先调用父类的构造函数来初始化模型,然后定义了两个参数self.wself.b,它们分别对应于该层的权重矩阵和偏置向量,并将它们标记为可学习的参数。

forward方法中,首先将输入张量x与权重矩阵self.w相乘,然后加上偏置向量self.b,最终得到该层的输出。

接下来,代码创建了一个Linear对象layer,并传入了输入的维度4和输出的维度3。然后定义了一个随机输入张量input,其形状为(2, 4)

layer = Linear(4,3)
input = t.randn(2,4)

最后,将输入张量传入layer对象的forward方法中,得到输出张量output

output = layer(input)

通过print(output)语句打印输出张量output的值,可以查看模型对给定输入的计算结果。

print(output)

此外,代码还使用named_parameters方法遍历了layer对象中的所有参数,并打印了它们的名称和值。可以获取到wb两个参数。

for name, parameter in layer.named_parameters():
    print(name, parameter)

nn.Module实现多层感知机

在这里插入图片描述

import torch as t
from torch import nn

class Linear(nn.Module): # 继承nn.Module
    def __init__(self, in_features, out_features):
        super(Linear, self).__init__() # 等价于nn.Module.__init__(self)
        self.w = nn.Parameter(t.randn(in_features, out_features))
        self.b = nn.Parameter(t.randn(out_features))
    
    def forward(self, x):
        x = x.mm(self.w) # x.@(self.w)
        return x + self.b.expand_as(x)

class Perceptron(nn.Module):
    def __init__(self, in_features, hidden_features, out_features):
        nn.Module.__init__(self)
        self.layer1 = Linear(in_features, hidden_features) # 此处的Linear是自定义的全连接层
        self.layer2 = Linear(hidden_features, out_features)
    def forward(self,x):
        x = self.layer1(x)
        x = t.sigmoid(x)
        return self.layer2(x)
    
perceptron = Perceptron(3,4,1)
for name, param in perceptron.named_parameters():
    print(name, param.size())

代码解释:

首先,定义了一个名为Linear的类,它继承自nn.Module。这个类代表了一个线性层,包含权重参数w和偏置参数b

class Linear(nn.Module):
    def __init__(self, in_features, out_features):
        super(Linear, self).__init__()
        self.w = nn.Parameter(t.randn(in_features, out_features))
        self.b = nn.Parameter(t.randn(out_features))
    
    def forward(self, x):
        x = x.mm(self.w)
        return x + self.b.expand_as(x)

然后,定义了一个名为Perceptron的类,它也继承自nn.Module。这个类代表了一个多层感知机模型,由两个线性层组成。

class Perceptron(nn.Module):
    def __init__(self, in_features, hidden_features, out_features):
        nn.Module.__init__(self)
        self.layer1 = Linear(in_features, hidden_features)
        self.layer2 = Linear(hidden_features, out_features)
    
    def forward(self, x):
        x = self.layer1(x)
        x = t.sigmoid(x)
        return self.layer2(x)

在初始化方法中,创建了两个Linear对象self.layer1self.layer2作为感知机的两个层。在前向传播方法中,输入数据首先通过self.layer1进行计算,然后经过sigmoid激活函数,最终通过self.layer2得到输出。

最后,创建了一个Perceptron对象perceptron,并打印出其中的参数名称和大小。

perceptron = Perceptron(3, 4, 1)
for name, param in perceptron.named_parameters():
    print(name, param.size())

这段代码展示了如何使用自定义的线性层构建一个多层感知机模型,并打印出其中的参数信息。通过这个例子,我们可以理解如何使用PyTorch构建神经网络模型,并对其进行调试和优化。

module中parameter的命名规范:

  • 对于类似self.param_name = nn.Parameter(t.randn(3, 4)),命名为param_name
  • 对于子Module中的parameter,会其名字之前加上当前Module的名字。如对于self.sub_module = SubModel(),SubModel中有个parameter的名字叫做param_name,那么二者拼接而成的parameter name 就是sub_module.param_name

常用神经网络层

在神经网络中,有许多常用的层用于构建各种不同类型的模型。以下是几个常见的神经网络层及其用途:

  1. 全连接层(Fully Connected Layer):全连接层是最基本的层之一,也称为线性层或密集层。它将输入的每个元素与权重相乘,并通过添加偏差项得到输出。全连接层通常用于提取特征和进行分类。

  2. 卷积层(Convolutional Layer):卷积层是专门用于处理图像和空间数据的层。它使用卷积运算对输入数据进行滤波,以提取局部的空间特征。卷积层通常用于图像识别、目标检测和语音处理等任务。

  3. 池化层(Pooling Layer):池化层主要用于减少特征图的空间尺寸,并保留最重要的特征。常见的池化操作包括最大池化和平均池化,它们分别选择每个区域中的最大值或平均值作为输出。

  4. 递归层(Recurrent Layer):递归层用于处理序列数据,如自然语言处理和时间序列预测。它们具有记忆机制,可以在每个时间步骤上传递信息。常见的递归层包括循环神经网络(RNN)和长短期记忆网络(LSTM)。

  5. 嵌入层(Embedding Layer):嵌入层用于将离散型的符号或类别数据编码为连续型的低维向量表示。它在自然语言处理中广泛应用,将单词或字符映射到连续的向量空间中。

  6. 规范化层(Normalization Layer):规范化层用于在神经网络中对输入数据进行标准化处理,以提高模型的稳定性和收敛速度。常见的规范化操作包括批量归一化(Batch Normalization)和层归一化(Layer Normalization)。

  7. 激活函数层(Activation Layer):激活函数层对神经网络的输出应用非线性变换,以引入非线性能力。常见的激活函数包括ReLU(Rectified Linear Unit)、Sigmoid和Tanh等。

这只是一小部分常用的神经网络层,实际应用中还有许多其他类型的层,如注意力层、残差连接等。根据任务和问题的不同,选择合适的层组合可以有效地构建出强大的神经网络模型。

图像相关层

图像相关层主要包括卷积层(Conv)、池化层(Pool)等,这些层在实际使用中可分为一维(1D)、二维(2D)、三维(3D),池化方式又分为平均池化(AvgPool)、最大值池化(MaxPool)、自适应池化(AdaptiveAvgPool)等。而卷积层除了常用的前向卷积之外,还有逆卷积(TransposeConv)。

卷积层(Conv)实现锐化过滤器

# 导入所需的库
from PIL import Image
from torchvision.transforms import ToTensor, ToPILImage
import torch as t
import torch.nn as nn

# 创建将图像转换为张量的转换器
to_tensor = ToTensor()

# 创建将张量转换为图像的转换器
to_pil = ToPILImage()

# 打开图像文件
lena = Image.open('C:/图片路径/1.png')

# 将彩色图像转换为灰度图像,并将其通道数从3变为1。并将图像转换为张量,并添加一维作为batch_size
input = to_tensor(lena.convert('L')).unsqueeze(0) 

# 定义锐化卷积核
kernel = t.ones(3, 3)/-9.
kernel[1][1] = 1
conv = nn.Conv2d(1, 1, (3, 3), 1, bias=False)
conv.weight.data = kernel.view(1, 1, 3, 3)

# 对输入进行卷积操作
out = conv(input)

# 将输出张量转换为图像,并显示出来
to_pil(out.data.squeeze(0))

在这里插入图片描述
在这里插入图片描述

这段代码的功能是实现一个锐化过滤器,对输入图像进行锐化处理。具体步骤如下:

  1. 创建将图像转换为张量的转换器to_tensor = ToTensor()和将张量转换为图像的转换器to_pil = ToPILImage()

  2. 打开图像文件lena = Image.open('图片路径')

  3. input = to_tensor(lena.convert('L')).unsqueeze(0),将彩色图像转换为灰度图像,并将其通道数从3变为1。并将图像转换为张量,并添加一维作为batch_size,即将它转换为输入大小为(1, channels, height, width)的模型输入。

  4. kernel = t.ones(3, 3)/-9.; kernel[1][1] = 1
    定义锐化卷积核:这个卷积核是一个3x3的矩阵,中心点为1,其它位置为-1/9,即偏移量为-1/9。

  5. conv = nn.Conv2d(1, 1, (3, 3), 1, bias=False),创建一个卷积层conv,该卷积层的输入通道数为1,输出通道数为1,卷积核大小为(3, 3),步长为1,偏置为False。

  6. conv.weight.data = kernel.view(1, 1, 3, 3),将之前创建的卷积核kernel转换为相同形状的张量,并将其赋给卷积层的权重。

  7. out = conv(input):对输入进行卷积操作,使用创建好的卷积核对输入进行卷积操作,得到一个输出大小为(1, 1, height, width)的张量。

  8. to_pil(out.data.squeeze(0)),将输出张量转换为图像,并显示出来。由于输入时添加了一维batch_size,因此需要先通过squeeze()去除这一维。

常见激活函数

函数图像
sigmoid在这里插入图片描述
tanh在这里插入图片描述
ReLU在这里插入图片描述
leaky ReLU在这里插入图片描述

下面是一个PyTorch实现常见激活函数的例子:

import torch
import torch.nn as nn

# 定义输入张量x
x = torch.randn(1, 10)

# Sigmoid激活函数
sigmoid = nn.Sigmoid()
activated_x = sigmoid(x)
print("Sigmoid激活后的输出:", activated_x)

# Tanh(双曲正切)激活函数
tanh = nn.Tanh()
activated_x = tanh(x)
print("Tanh激活后的输出:", activated_x)

# ReLU激活函数
relu = nn.ReLU()
activated_x = relu(x)
print("ReLU激活后的输出:", activated_x)

# LeakyReLU激活函数
leaky_relu = nn.LeakyReLU(negative_slope=0.01)
activated_x = leaky_relu(x)
print("LeakyReLU激活后的输出:", activated_x)

# Softmax激活函数
softmax = nn.Softmax(dim=1)
activated_x = softmax(x)
print("Softmax激活后的输出:", activated_x)

在这个例子中,我们首先定义了一个大小为(1, 10)的输入张量x,然后分别使用PyTorch中的ReLU、Sigmoid、Tanh、LeakyReLU和Softmax激活函数对其进行处理,并输出处理后的结果。

请注意,每个激活函数都是通过创建一个相应的PyTorch模型来实现的。例如,ReLU激活函数是通过创建一个nn.ReLU的实例来实现的。在使用时,我们只需要将输入张量传递给模型即可。

输出结果如下:

Sigmoid激活后的输出: tensor([[0.6914, 0.3946, 0.2316, 0.3845, 0.6496, 0.7061, 0.3284, 0.4206, 0.8200, 0.6755]])
Tanh激活后的输出: tensor([[ 0.6678, -0.4035, -0.8334, -0.4386,  0.5492,  0.7046, -0.6141, -0.3099, 0.9080,  0.6250]])
ReLU激活后的输出: tensor([[0.8068, 0.0000, 0.0000, 0.0000, 0.6172, 0.8765, 0.0000, 0.0000, 1.5163,  0.7332]])
LeakyReLU激活后的输出: tensor([[ 0.8068, -0.0043, -0.0120, -0.0047,  0.6172,  0.8765, -0.0072, -0.0032, 1.5163,  0.7332]])
Softmax激活后的输出: tensor([[0.1407, 0.0409, 0.0189, 0.0392, 0.1164, 0.1508, 0.0307, 0.0456, 0.2860,  0.1307]])

ReLU函数有个inplace参数,如果设为True,它会把输出直接覆盖到输入中,这样可以节省内存/显存。之所以可以覆盖是因为在计算ReLU的反向传播时,只需根据输出就能够推算出反向传播的梯度。但是只有少数的autograd操作支持inplace操作(如tensor.sigmoid_()),除非你明确地知道自己在做什么,否则一般不要使用inplace操作。

# ReLU激活函数
relu = nn.ReLU(inplace=True)
activated_x = relu(x)
print("ReLU激活后的输出:", activated_x)

ModuleList和Sequential

ModuleListSequential是PyTorch中用于构建神经网络模型的两个重要类。

ModuleList是一个容器类,它可以包含多个子模块(如层、激活函数等),并将它们作为一个整体来处理。通过使用ModuleList,可以方便地管理和组织多个子模块。

下面是一个使用ModuleList构建模型的示例:

import torch
import torch.nn as nn

class MyModel(nn.Module):
    def __init__(self):
        super(MyModel, self).__init__()
        self.layers = nn.ModuleList([
            nn.Linear(10, 20),
            nn.ReLU(),
            nn.Linear(20, 30),
            nn.Tanh()
        ])
    
    def forward(self, x):
        for layer in self.layers:
            x = layer(x)
        return x

在这个示例中,MyModel类定义了一个包含四个子模块的模型。在模型的构造函数中,创建了一个ModuleList对象,并将四个子模块添加到其中。在模型的forward方法中,通过遍历ModuleList中的子模块,并将输入逐层传递给它们,以得到最终的输出。

相比于直接使用列表或元组来存储子模块,使用ModuleList的一个优点是,它会自动地注册子模块,使得子模块的参数可以被模型的其他部分识别和更新。

Sequential是另一个用于构建模型的类,它提供了一种更加简洁的方式来定义连续的层序列。使用Sequential,可以以序列的形式将多个层(如线性层、激活函数等)串联起来,形成一个完整的神经网络模型。

以下是一个使用Sequential构建模型的示例:

import torch
import torch.nn as nn

model = nn.Sequential(
    nn.Linear(10, 20),
    nn.ReLU(),
    nn.Linear(20, 30),
    nn.Tanh()
)

在这个示例中,直接使用Sequential类来构造模型,并将每个层作为参数传递给Sequential的构造函数。这样,每个层都会按照顺序依次被添加到模型中。

使用Sequential的一个优点是,它提供了一种更加简洁和直观的方式来定义模型结构。然而,由于Sequential只适用于顺序连接的模型,因此无法灵活地处理一些复杂的网络结构,如跳跃连接或多路连接等。

ModuleListSequential都是用于构建神经网络模型的重要工具。ModuleList适用于管理和组织多个子模块,而Sequential适用于简洁地定义顺序连接的模型。选择使用哪个类取决于具体的需求和模型结构。

循环神经网络层(RNN)

循环神经网络层(RNN)是一种用于处理序列数据的神经网络层。与传统的前馈神经网络不同,RNN将一个序列中的每个元素作为输入,并通过记忆状态来传递信息,从而在序列中实现信息的持续传递。

在RNN中,当前时刻的输出不仅取决于当前时刻的输入,还取决于之前所有时刻的输入和记忆状态。具体地,RNN的计算可以表示为:

h t = f ( W ∗ x t + U ∗ h t − 1 ) h_t = f(W * x_t + U * h_{t-1}) ht=f(Wxt+Uht1)
其中,h_t表示当前时刻的记忆状态,x_t表示当前时刻的输入,W和U是权重矩阵。

RNN的基本结构如下所示:
在这里插入图片描述
RNN的优点是可以处理任意长度的序列数据,并且具有较强的记忆能力,可以学习到序列中的长期依赖关系。然而,由于RNN在计算过程中需要多次重复使用相同的权重矩阵,导致梯度消失或爆炸的问题,影响了其训练效果。为了解决这个问题,后来出现了一些改进的RNN结构,如LSTM和GRU等。

以下是使用PyTorch实现一个简单的RNN模型的示例:

import torch
import torch.nn as nn

class MyRNN(nn.Module):
    def __init__(self, input_size, hidden_size, output_size):
        super(MyRNN, self).__init__()
        self.hidden_size = hidden_size
        self.rnn = nn.RNN(input_size, hidden_size, batch_first=True)
        self.fc = nn.Linear(hidden_size, output_size)
    
    def forward(self, x):
        h0 = torch.zeros(1, x.size(0), self.hidden_size) # 初始化记忆状态
        out, hn = self.rnn(x, h0)
        out = self.fc(out[:, -1, :]) # 取最后一个时刻的输出作为模型输出
        return out

在这个示例中,我们创建了一个名为MyRNN的RNN模型类,并在其中使用nn.RNN类来定义RNN层。在模型的forward方法中,我们首先通过torch.zeros函数初始化记忆状态,并将输入张量和记忆状态传递给RNN层进行计算。然后,我们通过取最后一个时刻的输出,并使用一个全连接层来将其映射到输出空间中,得到最终的模型输出。

优化器

在PyTorch中,可以使用torch.optim模块来实现优化器的基本使用方法、对模型的不同部分设置不同的学习率以及调整学习率。下面我们分别介绍这些内容。

  1. 优化器的基本使用方法

首先,需要创建一个优化器对象以便于优化模型的参数。假设我们已经定义好了一个模型对象model,可以按照以下方式创建一个优化器对象:

import torch.optim as optim

optimizer = optim.Adam(model.parameters(), lr=0.01)

在这个示例中,我们使用Adam优化算法来更新模型参数。我们将模型对象model的所有可训练参数(即权重和偏置)传递给优化器的构造函数,并设置初始学习率为0.01

接下来,在训练循环中,可以按照以下方式进行参数更新:

optimizer.zero_grad() # 清零梯度
loss.backward() # 反向传播,计算梯度
optimizer.step() # 更新参数

在这个示例中,我们首先调用zero_grad()函数清零所有参数的梯度,然后调用backward()函数计算损失函数关于模型参数的梯度,最后调用step()函数更新参数。

  1. 对模型的不同部分设置不同的学习率

对模型的不同部分设置不同的学习率,以便更好地调整模型参数。在PyTorch中,可以通过以下方式实现:

optimizer = optim.Adam([
                {'params': model.conv1.parameters()},
                {'params': model.conv2.parameters(), 'lr': 0.01},
                {'params': model.fc.parameters(), 'lr': 0.001}
            ], lr=0.0001)

在这个示例中,将模型的三个部分(conv1、conv2和fc)分别放在不同的字典中,并为每个字典设置不同的学习率。最后,我们将所有字典传递给优化器的构造函数,并设置初始学习率为0.0001

  1. 调整学习率

有时候,在训练过程中需要调整学习率,以便更好地优化模型参数。在PyTorch中,可以按照以下方式调整学习率:

# 减小学习率到原来的1/10
for param_group in optimizer.param_groups:
    param_group['lr'] /= 10.0

在这个示例中,我们首先遍历所有参数组,然后将每个参数组的学习率除以10.0,从而减小学习率到原来的1/10。注意,optimizer.param_groups是一个列表,其中每个元素都是一个字典,包含了与该参数组相关的所有信息,例如学习率、权重衰减系数等。

nn.functional VS nn.Module

nn.functional和nn.Module都是PyTorch深度学习库中非常重要的模块。它们都有助于定义神经网络模型,但它们的使用方式和目的略有不同。

  1. nn.functional

nn.functional是PyTorch中一个包含各种非线性函数、池化函数、卷积函数等的模块。这些函数都被实现为纯函数(pure function),即它们的输出只取决于输入,而不依赖于任何外部状态。因此,它们通常用于构建没有参数的简单层或者复杂的自定义损失函数。

下面是一个使用nn.functional实现的简单的全连接层:

import torch.nn as nn
import torch.nn.functional as F

class MyNet(nn.Module):
    def __init__(self):
        super(MyNet, self).__init__()
        self.fc1 = nn.Linear(784, 256)
        self.fc2 = nn.Linear(256, 10)

    def forward(self, x):
        x = F.relu(self.fc1(x))
        x = self.fc2(x)
        return x

在这个示例中,使用了nn.Linear来创建两个全连接层,然后在forward方法中使用了F.relu函数作为激活函数。

  1. nn.Module

nn.Module是PyTorch中的一个基类,所有的神经网络模型都应该继承它。nn.Module提供了一些有用的方法,例如parameters()named_parameters()modules()等,可以方便地访问模型中的参数和子模块。在继承nn.Module类后,我们需要实现__init__forward方法来定义模型结构和前向传播过程。

下面是一个使用nn.Module实现的简单的全连接层:

import torch.nn as nn

class MyNet(nn.Module):
    def __init__(self):
        super(MyNet, self).__init__()
        self.fc1 = nn.Linear(784, 256)
        self.fc2 = nn.Linear(256, 10)

    def forward(self, x):
        x = nn.functional.relu(self.fc1(x))
        x = self.fc2(x)
        return x

在这个示例中,使用了nn.Linear来创建两个全连接层,并在__init__方法中将它们定义为模型的属性。然后在forward方法中使用了nn.functional.relu函数作为激活函数。

总的来说,nn.Module用于构建更复杂、带有参数的神经网络模型,而nn.functional则用于构建简单的不含参数的层或者自定义损失函数。当构建神经网络时,nn.Module往往是首选,因为它提供了更多的灵活性和可扩展性。

nn.init实现参数初始化

nn.init是PyTorch深度学习库中一个用于初始化神经网络权重的模块。在训练神经网络时,通常需要对权重进行初始化以提高模型的收敛速度和泛化能力。

nn.init提供了多种初始化方法,包括常见的正态分布初始化、均匀分布初始化和Xavier初始化等。下面以Xavier初始化为例进行讲解。

  1. Xavier初始化

Xavier初始化是一种常用的权重初始化方法,旨在使输入信号和输出信号的方差相等。这可以帮助加速模型的收敛,并提高模型的性能。Xavier初始化的具体公式如下:

W ∼ U [ − 6 n i n + n o u t , 6 n i n + n o u t ] W \sim U[-\frac{\sqrt{6}}{\sqrt{n_{in}+n_{out}}}, \frac{\sqrt{6}}{\sqrt{n_{in}+n_{out}}}] WU[nin+nout 6 ,nin+nout 6 ]

其中, U [ a , b ] U[a, b] U[a,b]表示从区间 [ a , b ] [a, b] [a,b]的均匀分布中随机采样, n i n n_{in} nin n o u t n_{out} nout分别表示权重的输入和输出维度。

  1. 使用nn.init进行Xavier初始化

使用nn.init进行Xavier初始化非常简单,只需要将需要初始化的参数传递给相应的初始化函数即可。例如,以下代码演示了如何使用nn.init进行Xavier初始化:

import torch.nn as nn
import torch.nn.init as init

class MyNet(nn.Module):
    def __init__(self):
        super(MyNet, self).__init__()
        self.fc1 = nn.Linear(784, 256)
        self.fc2 = nn.Linear(256, 10)

        # 对权重进行Xavier初始化
        init.xavier_uniform_(self.fc1.weight)
        init.xavier_uniform_(self.fc2.weight)

    def forward(self, x):
        x = nn.functional.relu(self.fc1(x))
        x = self.fc2(x)
        return x

在这个示例中,使用nn.Linear创建了两个全连接层,并在__init__方法中将它们定义为模型的属性。然后使用init.xavier_uniform_函数对权重进行Xavier初始化。

总的来说,使用nn.init进行权重初始化可以帮助模型更快地收敛并提高模型的性能。但需要注意的是,权重初始化并不是万能的,有时候还需要调整学习率、正则化等超参数来优化模型。

nn.Module深入分析

nn.Module基类的构造函数

def __init__(self):
    self._parameters = OrderedDict()
    self._modules = OrderedDict()
    self._buffers = OrderedDict()
    self._backward_hooks = OrderedDict()
    self._forward_hooks = OrderedDict()
    self.training = True

nn.Module基类的构造函数__init__是所有PyTorch神经网络模型的基础构建块之一。其主要作用是初始化模型的各个属性,并创建一个空的有序字典来存储模型的参数、子模块、缓存、正向/反向传播钩子等。

下面对__init__函数中定义的各个属性进行解释:

  1. self._parameters:有序字典,用于存储模型的可学习参数(例如权重和偏置)。在模型中定义的nn.Parameter类型的变量会自动添加到该字典中。

  2. self._modules:有序字典,用于存储模型的子模块。在模型中定义的nn.Module类型的变量会自动添加到该字典中。

  3. self._buffers:有序字典,用于存储模型的缓存。例如,一些中间结果、滑动平均值等可以保存在这里。

  4. self._backward_hooks:有序字典,用于存储模型的反向传播钩子,即在反向传播过程中执行的函数。

  5. self._forward_hooks:有序字典,用于存储模型的正向传播钩子,即在正向传播过程中执行的函数。

  6. self.training:布尔变量,用于指示模型的训练状态。在训练过程中,该变量为True,在测试过程中,该变量为False。

总的来说,nn.Module基类的构造函数初始化了神经网络模型中的各个属性,并创建了一个有序字典来存储模型的参数、子模块、缓存、正向/反向传播钩子等。这些属性和有序字典提供了方便的机制来访问和管理模型的组件,并可以方便地进行序列化和反序列化。

示例代码:

import torch.nn as nn

class MyNet(nn.Module):
    def __init__(self):
        super(MyNet, self).__init__()
        self.fc1 = nn.Linear(784, 256)
        self.fc2 = nn.Linear(256, 10)

    def forward(self, x):
        x = nn.functional.relu(self.fc1(x))
        x = self.fc2(x)
        return x

# 创建模型实例
model = MyNet()

# 打印 Module 属性
print("Parameters:")
for name, param in model.named_parameters():
    print(name, param.size())

print("\nModules:")
for name, module in model.named_modules():
    print(name, module)

print("\nBuffers:")
for name, buffer in model.named_buffers():
    print(name, buffer.size())

print("\nForward hooks:")
for hook in model._forward_hooks.values():
    print(hook)

print("\nBackward hooks:")
for hook in model._backward_hooks.values():
    print(hook)

在这个示例中,我们创建了一个继承自nn.Module基类的神经网络模型,并在其中定义了两个全连接层。然后,我们通过访问实例对象的属性和有序字典,打印了模型的ParameterModuleBuffer以及正向/反向传播钩子等属性。输出结果如下:

Parameters:
fc1.weight torch.Size([256, 784])
fc1.bias torch.Size([256])
fc2.weight torch.Size([10, 256])
fc2.bias torch.Size([10])

Modules:
 MyNet(
  (fc1): Linear(in_features=784, out_features=256, bias=True)
  (fc2): Linear(in_features=256, out_features=10, bias=True)
)
fc1 Linear(in_features=784, out_features=256, bias=True)
fc2 Linear(in_features=256, out_features=10, bias=True)

Buffers:

Forward hooks:

Backward hooks:

从输出结果中可以看出:

  • Parameters属性打印了模型的可学习参数,即两个全连接层的权重和偏置。
  • Modules属性打印了模型的子模块,包括整个模型本身和其两个全连接层。
  • Buffers属性为空,因为在模型中没有定义任何缓存。
  • Forward hooks属性为空,因为在模型中没有定义任何正向传播钩子。
  • Backward hooks属性为空,因为在模型中没有定义任何反向传播钩子。

访问模型中的子模块

import torch
import torch.nn as nn

class SubNet(nn.Module):
    def __init__(self):
        super(SubNet, self).__init__()
        self.conv = nn.Conv2d(3, 16, kernel_size=3, padding=1)
        self.pool = nn.MaxPool2d(kernel_size=2, stride=2)
        
    def forward(self, x):
        x = self.conv(x)
        x = self.pool(x)
        return x

class MyNet(nn.Module):
    def __init__(self):
        super(MyNet, self).__init__()
        self.subnet1 = SubNet()
        self.subnet2 = SubNet()
        self.fc = nn.Linear(16 * 8 * 8, 10)
        
    def forward(self, x):
        x1 = self.subnet1(x)
        x2 = self.subnet2(x)
        x = torch.cat((x1, x2), dim=1)
        x = x.view(-1, 16 * 8 * 8)
        x = self.fc(x)
        return x

# 创建模型实例
model = MyNet()

# 查看直接子模块
print("Children:")
for name, module in model.named_children():
    print(name, module)

# 查看所有子模块(包括当前模块)
print("\nModules:")
for name, module in model.named_modules():
    print(name, module)

# 查看命名直接子模块
print("\nNamed Children:")
for name, module in model.named_children():
    print(name, module)

# 查看命名所有子模块(包括当前模块)
print("\nNamed Modules:")
for name, module in model.named_modules():
    print(name, module)

在这个示例中,我们定义了一个SubNet子模块和一个MyNet模型,其中MyNet包含了两个SubNet子模块和一个全连接层。我们使用named_childrennamed_modules函数查看了直接子模块、所有子模块以及它们的命名版本。

输出结果如下:

Children:
subnet1 SubNet(
  (conv): Conv2d(3, 16, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
  (pool): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
)
subnet2 SubNet(
  (conv): Conv2d(3, 16, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
  (pool): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
)

Modules:
 MyNet(
  (subnet1): SubNet(
    (conv): Conv2d(3, 16, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
    (pool): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
  )
  (subnet1.conv): Conv2d(3, 16, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
  (subnet1.pool): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
  (subnet2): SubNet(
    (conv): Conv2d(3, 16, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
    (pool): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
  )
  (subnet2.conv): Conv2d(3, 16, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
  (subnet2.pool): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
  (fc): Linear(in_features=1024, out_features=10, bias=True)
)
subnet1 SubNet(
  (conv): Conv2d(3, 16, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
  (pool): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
)
subnet1.conv Conv2d(3, 16, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
subnet1.pool MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
subnet2 SubNet(
  (conv): Conv2d(3, 16, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
  (pool): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
)
subnet2.conv Conv2d(3, 16, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
subnet2.pool MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
fc Linear(in_features=1024, out_features=10, bias=True)

Named Children:
subnet1 SubNet(
  (conv): Conv2d(3, 16, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
  (pool): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
)
subnet2 SubNet(
  (conv): Conv2d(3, 16, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
  (pool): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
)

Named Modules:
 MyNet MyNet(
  (subnet1): SubNet(
    (conv): Conv2d(3, 16, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
    (pool): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
  )
  (subnet1.conv): Conv2d(3, 16, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
  (subnet1.pool): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
  (subnet2): SubNet(
    (conv): Conv2d(3, 16, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
    (pool): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
  )
  (subnet2.conv): Conv2d(3, 16, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
  (subnet2.pool): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
  (fc): Linear(in_features=1024, out_features=10, bias=True)
)
subnet1 SubNet(
  (conv): Conv2d(3, 16, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
  (pool): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
)
subnet1.conv Conv2d(3, 16, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
subnet1.pool MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
subnet2 SubNet(
  (conv): Conv2d(3, 16, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
  (pool): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
)
subnet2.conv Conv2d(3, 16, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
subnet2.pool MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
fc Linear(in_features=1024, out_features=10, bias=True)

从输出结果可以看出:

  • 使用named_children函数可以返回直接子模块,并可以通过返回的元组中的第一个元素访问子模块的名称,第二个元素访问子模块本身。
  • 使用named_modules函数可以返回所有子模块(包括当前模块),同样可以通过返回的元组中的第一个元素访问模块的名称,第二个元素访问模块本身。
  • named_childrennamed_modules函数都有一个命名版本,分别为named_childennamed_modules。这两个函数返回的元组中,第一个元素是子模块的名称,第二个元素是子模块本身。

这些函数提供了一种方便的方式来管理、访问和控制复杂的神经网络模型中的子模块。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1175791.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

前端数据加解密:保护敏感信息的关键

前言 如今,数据安全和隐私保护变得至关重要。本文旨在引领大家探索前端数据加密与解密的基本概念,并介绍常用的加密算法,让大家深入了解数据加解密的世界,探究其背后的原理、最佳实践和常见应用场景。 前端主流加密方式 对称加密 …

【Nuxt】在 Nuxt3 中使用 Element Plus

安装 Element Plus 和图标库 pnpm add element-plus --save pnpm add element-plus/icons-vue安装 Nuxt Element Plus 模块 pnpm add element-plus/nuxt -D配置 Nuxt 项目 在 nuxt.config.ts 中配置 // https://nuxt.com/docs/api/configuration/nuxt-config export defaul…

聚观早报 |小米CarWith启动兼容测试;「天工」大模型开放

【聚观365】11月6日消息 小米CarWith启动兼容测试 「天工」大模型开放 邮政快递揽收超20亿件 华为Mate 60 Pro开启预约申购 VERTU持续探索前沿科技 小米CarWith启动兼容测试 小米CarWith是打造“人车家生态”中不可或缺的一环,在最新升级的2.0版本中&#xff…

VMware Ubuntu 共享文件夹

VMware Ubuntu 共享文件夹 flyfish 物理机配置 Network Adapter设置 此处设置为NAT Shared Folders设置 虚拟机配置 vmware-hgfsclient sudo vmhgfs-fuse .host:/ /mnt -o nonempty -o allow_other 或者 sudo vmhgfs-fuse .host:/ /mnt/ -o allow_other第一行命令是查看共…

算法题:203. 移除链表元素(递归法、设置虚拟头节点法等3种方法)Java实现创建链表与解析链表

1、算法思路 讲一下设置虚拟头节点的那个方法,设置一个新节点指向原来链表的头节点,这样我们就可以通过判断链表的当前节点的后继节点值是不是目标删除值,来判断是否删除这个后继节点了。如果不设置虚拟头节点,则需要将头节点和后…

网络层重要协议 --- IP协议

小王学习录 今日摘录IP数据报数据报首部IPv4的局限及解决方法 地址管理路由选择扩展:NAT和NAPT的结合使用 今日摘录 关山难越,谁悲失路之人。萍水相逢,尽是他乡之客。 网络层的职责是地址管理和路由选择,在网络层中最重要的协议…

程序员为啥要做副业(05)-业务解决方案缔造者

点击下方“JavaEdge”,选择“设为星标” 第一时间关注技术干货! 免责声明~ 任何文章不要过度深思! 万事万物都经不起审视,因为世上没有同样的成长环境,也没有同样的认知水平,更「没有适用于所有人的解决方案…

机器人控制——C++ HSM状态机基础知识

本章将向您介绍使用HSM的基本知识。为了简单起见,我们将在这里学习如何编写单层次(也称为平面)状态机,并在下一章中介绍层次状态机。 让我们从我们可以编写的最简单的状态机开始。 // simplest_state_machine.cpp#include "…

签名加密发送请求

本篇文章介绍下通过HMAC-SHA1加密验证,将加密之后得到的签名作为参数发送请求,假如咱们需要按照下列要求将参数加密之后传递: 咱们来逐步分析得到签名的步骤和最终发送请求需要携带的参数内容,本次实现是在node.js的环境下。 首先…

计算机系统

由计算机硬件系统和计算机软件系统组成的综合体 冯诺伊曼计算机 定义 以“存储程序”的概念为基础的各类计算机通称为冯诺伊曼机 特点 计算机由运算器、存储器、控制器、输入设备和输出设备五大部件组成指令和数据以同等地位存放于存储器内,并可按地址寻访指令和…

Python Faker批量生成测试数据

一、前言 在做自动化测试或压力测试时会需要大批量生成测试数据,简单的方式你可以写一个存储过程使用随机函数来生成记录,但这种生成数据看起来不够真实,其实有蛮多现成的工具可以完成这一任务。 二、Faker基本使用介绍 faker是一个生成伪…

Python零基础入门教程(非常详细)

目前python可以说是一门非常火爆的编程语言,应用范围也非常的广泛,工资也挺高,未来发展也极好。 Python究竟应该怎么学呢,我自己最初也是从零基础开始学习Python的,给大家分享Python的学习思路和方法。一味的买书看书…

从零开始搭建微服务(一)

构建项目父工程 添加公共核心模块 安装nacos 安装nacos nacos 文档地址: https://nacos.io/zh-cn/docs/what-is-nacos.html 本文使用版本2.2.2 下载地址:https://github.com/alibaba/nacos/archive/refs/tags/2.2.2.zip 使用nacos 我们下载是源代码 解…

轻松编写第一个油猴脚本

第一步:安装油猴 首先,我们需要安装油猴。油猴是一个免费的浏览器扩展,可让您在浏览器中运行脚本。 在 Chrome 浏览器中,可以通过以下步骤安装油猴: 在 Chrome 浏览器中打开 Chrome 网上应用店。搜索 "油猴&qu…

揭秘ChatGPT的智慧密码:向量数据库的神奇作用解析

2023年8月,亚马逊云科技推出了自己的向量引擎Amazon OpenSearch Serverless向量引擎,这被认为是其旗下大语言模型Amazon Titan超越ChatGPT的一个重要契机。 那么,这个Amazon OpenSearch Serverless向量引擎有何厉害之处?为什么能…

Elasticsearch:ES|QL 中的数据丰富

在之前的文章 “Elasticsearch:ES|QL 查询语言简介”,我有介绍 ES|QL 的 ENRICH 处理命令。ES|QL ENRICH 处理命令在查询时将来自一个或多个源索引的数据与 Elasticsearch 丰富索引中找到的字段值组合相结合。这个有点类似于关系数据库查询中所使用的 jo…

数据治理概念篇:(一)相关术语与名词

数据治理内容众多,其中与数据治理相关名词和术语也是多不胜数。下面记录一下常见的数据治理相关名词。 1.数据仓库 1.1.名词解释 Bill Inmon 将数据仓库定义为:“面向主题的、整合的、随时间变化的、相对稳定的支持管理决策的数据集合”。 Ralph Kimbal…

【python】爬取斗鱼直播照片保存到本地目录

一、导入必要的模块: 这篇博客将介绍如何使用Python编写一个爬虫程序,从斗鱼直播网站上获取图片信息并保存到本地。我们将使用requests模块发送HTTP请求和接收响应,以及os模块处理文件和目录操作。 如果出现模块报错 进入控制台输入&#xff…

APP开发:用途与未来前景|软件定制开发|网站小程序建设

APP开发:用途与未来前景|软件定制开发|网站小程序建设 APP开发已成为现代科技趋势的一部分,无论是日常生活还是商业领域,都有它的身影。通过开发APP,我们可以将想法、功能和内容转化为直观、易用的移动设备应用程序,满…

【算法| 差分 No.1】AcWing 797. 差分 AcWing 798. 差分矩阵

个人主页:兜里有颗棉花糖 欢迎 点赞👍 收藏✨ 留言✉ 加关注💓本文由 兜里有颗棉花糖 原创 收录于专栏【手撕算法系列专栏】 🍔本专栏旨在提高自己算法能力的同时,记录一下自己的学习过程,希望对大家有所帮…