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

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

深度学习专栏链接:
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 nnclass Linear(nn.Module): # 继承nn.Moduledef __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 nnclass Linear(nn.Module): # 继承nn.Moduledef __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 nnclass 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 nnmodel = 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 nnclass MyRNN(nn.Module):def __init__(self, input_size, hidden_size, output_size):super(MyRNN, self).__init__()self.hidden_size = hidden_sizeself.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 optimoptimizer = 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 Fclass 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 nnclass 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 initclass 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 nnclass 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 nnclass 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 xclass 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.mzph.cn/news/133032.shtml

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

相关文章

Tensor.scatter_add_函数解释:

Tensor.scatter_add_(dim, index, src) → Tensor out.scatter_add_(dim, index, src) 1.参数: dim (int) – 哪一dim进行操作 index (LongTensor) – 要在的out的哪一index进行操作 src (Tensor) – 待操作的源数字 2.官方的解释的操作如下: 3.例…

【JAVA学习笔记】63 -坦克大战1.3-敌方发射子弹,击中坦克消失并爆炸,敌人坦克随机移动,规定范围限制移动

项目代码 https://github.com/yinhai1114/Java_Learning_Code/tree/main/IDEA_Chapter18/src/com/yinhai/tankgame1_3 〇、要求 增加功能 1.让敌人的坦克也能够发射子弹(可以有多颗子弹) 2.当我方坦克击中敌人坦克时,敌人的坦克就消失,如果能做出爆炸效果更好. …

c-CoSe2-CoN助力Zn-空气电池

硒化钴(CoSe2)的相变可有效调节其固有的电催化活性,但提高CoSe2的电导率和催化活性/稳定性还是一个挑战。异质结构工程可优化界面性能,促进CoSe2基催化剂上氧电催化的动力学。 基于此,黑龙江大学邹金龙教授等人报道了…

再谈Android重要组件——Handler(Native篇)

前言 最近工作比较忙,没怎么记录东西了。Android的Handler重要性不必赘述,之前也写过几篇关于hanlder的文章了: Handler有多深?连环二十七问Android多线程:深入分析 Handler机制源码(二) And…

pyspark连接mysql数据库报错

使用pyspark连接mysql数据库代码如下 spark_conf SparkConf().setAppName("MyApp").setMaster("local")spark SparkSession.builder.config(confspark_conf).getOrCreate()url "jdbc:mysql://localhost:3306/test?useUnicodetrue&characterE…

C语言习题整理①

一些C语言习题的整理。 目录 一、判断质数 二、判断回文数 三、判断水仙花数 四、输出乘法表 五、输出杨辉三角 一、判断质数 质数是指在大于1的自然数中,除了1和它本身以外不再有其他因数的自然数。质数又称素数。一个大于1的自然数,除了1和它自身…

为什么有了MAC地址,还需要IP地址?

解释 搞懂这个问题,首先需要了解交换机的功能 交换机内部有一张MAC地址映射表,记录着MAC地址和端口的对应关系。 如果A要给B发送一个数据包,构造如下格式的数据结构: 到达交换机时,交换机内部通过自己维护的 MAC 地…

Angular-07:组件生命周期

三个阶段: ① 挂载阶段1.1 constructor1.2 ngOnInit ② 更新阶段2.1 ngOnChanges2.2 ngAfterViewInit2.3 ngAfterContentInit2.4 ngDoCheck ③ 卸载阶段3.1 onOnDestroy ④ 在组件中添加所有方法并打印 该表按照执行顺序编写 编号函数名实现名说明1constructorcons…

uniapp自定义权限菜单,动态tabbar

已封装为组件&#xff0c;亲测4个菜单项目可以切换&#xff0c; 以下为示例&#xff0c;根据Storage 中 userType 的 值&#xff0c;判断权限菜单 <template><view class"tab-bar pb10"><view class"tabli" v-for"(tab, index) in ta…

DirectX3D 虚拟现实项目 三维物体的光照及着色(五个不同着色效果的旋转茶壶)

文章目录 任务要求原始代码CPP文件代码着色器文件代码 效果展示 任务要求 本篇文章是中国农业大学虚拟现实课程的一次作业内容&#xff0c;需要对五个茶壶模型使用不同的光照进行着色和渲染&#xff0c;然后旋转展示。 本人的代码也是在其他人的代码的基础上修改来的&#xf…

centos获取服务器公网ip

查看公网IP 用下面几个命令&#xff1a; #curl ifconfig.me #curl icanhazip.com #curl cip.cc

前端框架Vue学习 ——(五)前端工程化Vue-cli脚手架

文章目录 Vue-cliVue项目-创建Vue项目-目录结构Vue项目-启动Vue项目-配置端口Vue项目开发流程 Vue-cli 介绍&#xff1a;Vue-cli 是 Vue 官方提供的一个脚手架&#xff0c;用于快速生成一个 Vue 的项目模版 安装 NodeJS安装 Vue-cli npm install -g vue/cliVue项目-创建 图…

《深入理解分布式事务:原理与实战》读书笔记

Mysql事务 锁升级 行锁升级表锁&#xff1a;如果不是索引查找&#xff0c;或者索引失效&#xff0c;此时需要全表扫描&#xff0c;会升级为锁整张表。 为什么Mysql要把扫描到的每一行以及其间隙都加锁&#xff1f;这是为了防止幻读出现。幻读导致的问题是破坏了一致性声明&am…

三国志14信息查询小程序(历史武将信息一览)制作更新过程03-主要页面的设计

1&#xff0c;小程序的默认显示 分为三部分&#xff0c;头部的标题、中间的内容区和底部的标签栏。点击标签可以切换不同页面&#xff0c;这是在app.json文件中配置的。代码如下&#xff1a; //所有用到的页面都需要在 pages 数组中列出&#xff0c;否则小程序可能会出现错误或…

前端面试题之HTML篇

1、src 和 href 的区别 具有src的标签有&#xff1a;script、img、iframe 具有href的标签有&#xff1a;link、a 区别 src 是source的缩写。表示源的意思&#xff0c;指向资源的地址并下载应用到文档中。会阻塞文档的渲染&#xff0c;也就是为什么js脚本放在底部而不是头部的…

skynet学习笔记01— skynet开发环境搭建(超详细)与第一个skynet程序

00、参考资料 https://blog.csdn.net/qq769651718/category_7480207.html 01、前置准备 开发所在目录 mhzzjmhzzj-virtual-machine:~/work/skynetStudy$ pwd /home/mhzzj/work/skynetStudy前置准备 mhzzjmhzzj-virtual-machine:~/work/skynetStudy$ sudo apt install lua5…

CSS示例001:鼠标放div上,实现旋转、放大、移动等效果

GPT能够很好的应用到我们的代码开发中&#xff0c;能够提高开发速度。你可以利用其代码&#xff0c;做出一定的更改&#xff0c;然后实现效能。 css实战中&#xff0c;经常会看到这样的场景&#xff0c;鼠标放到一个图片或者一个div块状时候&#xff0c;会出现旋转、放大、移动…

地址的层次性

地址的层次性 当地址总数并不是很多的情况下&#xff0c;有了唯一地址就可以定位相互通信的主体。然而当地址的总数越来越多时&#xff0c;如何高效地从中找出通信的目标地址将成为一个重要的问题。为此人们发现地址除了具有唯一性还需要具有层次性。其实&#xff0c;在使用电…

基于跳蛛算法的无人机航迹规划-附代码

基于跳蛛算法的无人机航迹规划 文章目录 基于跳蛛算法的无人机航迹规划1.跳蛛搜索算法2.无人机飞行环境建模3.无人机航迹规划建模4.实验结果4.1地图创建4.2 航迹规划 5.参考文献6.Matlab代码 摘要&#xff1a;本文主要介绍利用跳蛛算法来优化无人机航迹规划。 1.跳蛛搜索算法 …

Verilog 基础知识(一) Verilog 基础语法与注意事项

基础知识 0.1 模块(Module) Verilog中的module可以看成一个具有输入输出端口的黑盒子&#xff0c;该黑盒子有输入和输出接口(信号)&#xff0c;通过把输入在盒子中执行某些操作来实现某项功能。(类似于C语言中的函数) 图1 模块示意图 0.1.1 模块描述 图1 所示的顶层模块(top…