PyTorch使用教程(4)-torch.nn

torch.nn 是 PyTorch 深度学习框架中的一个核心模块,专门用于构建和训练神经网络。它提供了一系列用于构建神经网络所需的组件,包括层(Layers)、激活函数(Activation Functions)、损失函数(Loss Functions)等。orch.nn模块的核心是nn.Module类,它是所有神经网络组件的基类。通过继承nn.Module类并实现其forward方法,我们可以定义自己的神经网络模型。torch.nn的常用组件预览如下图:

一、基本功能

torch.nn 模块的主要功能是提供神经网络构建所需的各种类和函数。这些类和函数使得开发者能够轻松地定义、初始化和训练神经网络模型。无论是简单的全连接网络还是复杂的卷积神经网络(CNN)和循环神经网络(RNN),torch.nn 都能提供必要的组件和工具。

二、关键组件

2.1 层(Layers)

1.卷积层

在计算机视觉方面,卷积层最常用的就是torch.nn.Conv2d,函数原型如下:

python 复制代码
torch.nn.Conv2d(in_channels, out_channels, kernel_size, stride=1, padding=0, dilation=1, groups=1, bias=True, padding_mode='zeros', device=None, dtype=None)

对由多个输入平面组成的输入张量应用二维卷积,在最简单的情况下,具有输入大小 ( N , C i n , H , W ) (N,C_{in},H,W) (N,Cin,H,W)和输出 ( N , C o u t , H o u t , W o u t ) (N,C_{out},H_{out},W_{out}) (N,Cout,Hout,Wout)的层的输出值可以准确地描述为:

out ⁡ ( N i , C out ⁡ j ) = bias ⁡ ( C out ⁡ j ) + ∑ k = 0 C in ⁡ − 1 weight ⁡ ( C out ⁡ j , k ) ⋆ input ⁡ ( N i , k ) \operatorname{out}(N_i,C_{\operatorname{out}j})=\operatorname{bias}(C{\operatorname{out}j})+\sum{k=0}^{C_{\operatorname{in}-1}}\operatorname{weight}(C_{\operatorname{out}_j},k)\star\operatorname{input}(N_i,k) out(Ni,Coutj)=bias(Coutj)+k=0∑Cin−1weight(Coutj,k)⋆input(Ni,k)

b i a s bias bias是偏置量、 w e i g h t weight weight就是常说的卷积核权重参数。

常用参数

  • in_channels (int) -- 输入图像中的通道数
  • out_channels (int) -- 卷积产生的通道数
  • kernel_size (int 或 tuple) -- 卷积核的大小
  • stride (int 或 tuple, 可选) -- 卷积的步幅。默认值:1
  • padding (int, tuple 或 str, 可选) -- 添加到输入所有四边的填充。它可以是字符串 {'valid', 'same'} 或整数/整数元组,表示应用于两侧的隐式填充量。默认值:0
  • dilation (int 或 tuple, 可选) -- 内核元素之间的间距。默认值:1
  • groups (int, 可选) -- 从输入通道到输出通道的阻塞连接数。默认值:1
  • bias (bool, 可选) -- 如果为 True,则将可学习偏差添加到输出。默认值:True
  • padding_mode (str, 可选) -- 'zeros','reflect','replicate' 或 'circular'。默认值:'zeros'

示例

python 复制代码
#创建一个随机张量
>>> input = torch.rand(1,1,3,3)
>>> input
tensor([[[[0.7609, 0.8803, 0.2294],
          [0.8200, 0.8600, 0.5657],
          [0.2421, 0.0077, 0.9762]]]])
#使用默认的padding模式,创建一个输入为1个通道,输出为3个通道,卷积核尺寸为3的卷积层
>>> conv=torch.nn.Conv2d(1, 3, 3)
>>> conv(input)
tensor([[[[-0.0243]],
         [[-0.1504]],
         [[-1.2081]]]], grad_fn=<ConvolutionBackward0>)
#使用padding为'same'的模式
>>> conv=torch.nn.Conv2d(1, 3, 3,padding='same')  
>>> conv(input)                                  
tensor([[[[-0.0795, -0.2914, -0.4644],
          [-0.0520, -0.1241, -0.2314],
          [-0.1710, -0.1893,  0.2325]],

         [[-0.6988, -0.4558, -0.1104],
          [-0.8113, -0.6543, -0.4082],
          [-0.3180, -0.4468, -0.2224]],

         [[ 0.2227,  0.2641,  0.3581],
          [ 0.5351,  0.2648,  0.5343],
          [ 0.3264,  0.7865,  0.3547]]]], grad_fn=<ConvolutionBackward0>)

打印出卷积层中的 b i a s bias bias、 w e i g h t weight weight的参数值。

python 复制代码
>>> conv.bias
Parameter containing:
tensor([-0.2056,  0.0071,  0.0876], requires_grad=True)
>>> conv.weight
Parameter containing:
tensor([[[[ 0.0767,  0.2002, -0.2259],
          [-0.3162,  0.2678, -0.0168],
          [-0.0362, -0.0189, -0.0551]]],
          
        [[[ 0.1302, -0.0640, -0.2265],
          [-0.2353, -0.3108, -0.3259],
          [ 0.2809, -0.1426, -0.0763]]],

        [[[ 0.2515,  0.2941,  0.0281],
          [-0.1044, -0.1175,  0.2562],
          [ 0.2423,  0.3199, -0.3061]]]], requires_grad=True)

2. 池化层

池化层在卷积神经网络中起到了特征降维、特征提取、平移不变性、扩大感受野、减少计算量以及防止过拟合等多重作用。这些特性使得卷积神经网络在处理图像、视频等复杂数据时具有出色的性能。常见的池化操作包括最大池化(Max Pooling)和平均池化(Average Pooling)。这里以最大池化层为例详细说明。

最大池化层函数原型如下:

python 复制代码
torch.nn.MaxPool2d(kernel_size, stride=None, padding=0, dilation=1, return_indices=False, ceil_mode=False)

常用参数

  • kernel_size -- 需要进行最大值运算的窗口大小

  • stride -- 窗口的步长。默认值为 kernel_size

示例

python 复制代码
>>> input = torch.randn(1,1,4,4)        
>>> input  
tensor([[[[-0.3371,  0.1317,  0.9040,  1.3387],
          [-2.2840,  2.3960,  0.2795,  0.5269],
          [ 0.5753, -0.7660,  0.3067, -0.4039],
          [-0.8702, -0.2735,  0.7680, -2.3174]]]])
>>> maxpool=torch.nn.MaxPool2d(2) 
>>> maxpool(input)
tensor([[[[2.3960, 1.3387],
          [0.5753, 0.7680]]]])

3.线性层

线性层也称为全连接层(Fully Connected Layer),它对输入数据执行 y = x A T + b y=xA^T+b y=xAT+b。函数原型如下:

python 复制代码
torch.nn.Linear(in_features, out_features, bias=True, device=None, dtype=None)

参数

  • in_features (int) -- 每个输入样本的大小
  • out_features (int) -- 每个输出样本的大小
  • bias (bool) -- 如果设置为 False,则该层不会学习加性偏差。默认值:True

示例

python 复制代码
>>> m = nn.Linear(20, 30)
>>> input = torch.randn(128, 20)
>>> output = m(input)
>>> print(output.size())
torch.Size([128, 30])

2.2 激活函数(Activation Functions)

1. ReLU

函数原型如下:

python 复制代码
torch.nn.ReLU(inplace=False)

ReLU激活函数的数学定义如下:
R e L U ( x ) = ( x ) + = max ⁡ ( 0 , x ) \mathrm{ReLU}(x)=(x)^+=\max(0,x) ReLU(x)=(x)+=max(0,x)

ReLU函数的特点:

  • 非线性:ReLU函数通过引入非线性因素,使得神经网络能够学习更复杂的模式。尽管其定义简单,但ReLU实际上是非线性的,这增强了模型的表达能力。
  • 计算简单高效:ReLU函数的计算非常高效,因为它仅涉及简单的阈值判断,无需复杂的数学运算。这使得ReLU函数在处理大规模数据时具有显著优势。
  • 缓解梯度消失问题:在正区间内,ReLU函数的梯度为常数1,这有助于梯度的有效传递,从而缓解了梯度消失问题。这一特点使得ReLU函数在训练深层网络时表现更为出色。

示例

python 复制代码
>>> input = torch.randn(1,3) 
>>> input                    
tensor([[-1.1377,  0.1660, -0.8894]])
>>> relu = torch.nn.ReLU()        
>>> relu(input)             
tensor([[0.0000, 0.1660, 0.0000]])

2.LeakyReLU

函数原型如下:

python 复制代码
torch.nn.LeakyReLU(negative_slope=0.01, inplace=False)

LeakyReLU激活函数的数学定义如下:
L e a k y R e L U ( x ) = { x , i f x ≥ 0 negative slope × x , o t h e r w i s e \mathrm{LeakyReLU}(x)=\begin{cases}x,&\mathrm{if}x\geq0\\\text{negative slope}\times x,&\mathrm{otherwise}&\end{cases} LeakyReLU(x)={x,negative slope×x,ifx≥0otherwise

LeakyReLU激活函数的特点如下:

  • 在ReLU函数中,当输入值小于0时,输出值为0,这可能导致神经元在训练过程中"死亡"或停止学习。LeakyReLU函数通过允许负输入值有一个很小的斜率(通常为0.01),使得即使输入为负,输出也不会完全为零。这有助于避免神经元死亡的问题,使网络能够学习更多的特征1。
  • LeakyReLU函数在负值区域引入一个小的正斜率,这有助于梯度在网络中的传播,从而可以加快网络的收敛速度1。
  • LeakyReLU函数包含一个超参数α(alpha),它决定了负输入时的梯度大小。在实际应用中,α通常被设置为一个小于1的正数,如0.01。当α小于1时,LeakyReLU函数可以防止梯度消失;当α大于1时,它可以使梯度更快收敛;当α等于1时,LeakyReLU函数等价于ReLU激活函数1。

示例

python 复制代码
>>> input = torch.randn(4) 
>>> input
tensor([-0.3135, -0.3198,  0.2046,  2.0089])
>>> m = torch.nn.LeakyReLU(0.1) 
>>> m(input)
tensor([-0.0313, -0.0320,  0.2046,  2.0089])

3.Sigmoid

函数原型如下:

python 复制代码
torch.nn.Sigmoid(*args, **kwargs)

Sigmoid激活函数的数学定义如下:
S i g m o i d ( x ) = σ ( x ) = 1 1 + exp ⁡ ( − x ) \mathrm{Sigmoid}(x)=\sigma(x)=\frac{1}{1+\exp(-x)} Sigmoid(x)=σ(x)=1+exp(−x)1

Sigmoid激活函数的特点如下:

  • Sigmoid函数能够将任何实数输入映射到0和1之间的输出值。这一特性使得Sigmoid函数特别适合用于二分类问题,如"是"与"否"的判断,为分类任务提供了坚实的基础。
  • Sigmoid函数是平滑且连续的,这使得它在数学上易于处理,求导也相对简单。这一优点在神经网络的训练过程中尤为重要,因为激活函数的导数会用于权重的更新。
  • Sigmoid函数具有非线性特性,能够捕捉数据间复杂的非线性关系。这为神经网络提供了强大的建模能力,使其能够处理更加复杂的问题。

示例

python 复制代码
>>> m = torch.nn.Sigmoid() 
>>> input = torch.randn(2)
>>> input
tensor([ 0.2905, -0.1586])
>>> m(input)
tensor([0.5721, 0.4604])

4.SiLU

SiLU(Sigmoid-Weighted Linear Unit)激活函数,也被称为Swish,是一种结合了线性和非线性特性的现代激活函数,由Google的研究人员在2017年提出。

函数原型定义如下:

python 复制代码
torch.nn.SiLU(inplace=False)

SiLU激活函数的数学定义如下:
s i l u ( x ) = x ∗ σ ( x ) \mathrm{silu}(x)=x*\sigma(x) silu(x)=x∗σ(x)

SiLU激活函数的特点:

  • SiLU函数是连续且光滑的,其梯度也连续变化,避免了ReLU等激活函数中的不连续点。这种平滑性有助于优化算法更快地收敛,并且在整个实数域上都是可导的,使得在反向传播算法中梯度更加平滑和连续。
  • SiLU激活函数是非线性的,能够帮助神经网络学习复杂的非线性模式和特征。这种非线性特性是神经网络能够处理复杂任务的关键。
  • 随着输入值的增大,SiLU激活函数的输出值会趋向于线性变化,这有助于防止梯度消失或梯度爆炸问题。同时,通过x⋅σ(x)的形式,SiLU会动态调整激活值的大小,表现出平滑且动态的特性。

2.3 损失函数(Loss Functions)

损失函数(Loss Function)用于衡量模型预测结果与真实标签之间的差距,反映了模型的预测误差大小。在神经网络的训练和推理过程中,损失函数是一个关键概念,训练的目标就是最小化损失函数,以提高模型的准确度。损失函数的主要作用包括:

  • 评估模型的好坏‌:每一次前向传播后,将模型的输出与真实标签进行比较,计算损失值。模型再通过反向传播基于该损失调整参数,使其逐渐优化‌。
  • 指导模型参数的更新 ‌:损失函数告诉我们模型预测有多大误差,并指导模型参数的更新方向和幅度‌。
    这里,介绍几种常用的损失函数。

1. MSELoss均方误差损失

均方误差MSE作为损失函数常用于回归问题,特别是在需要优化图像像素级准确性的任务中。在PyTorch中,函数原型如下:

python 复制代码
torch.nn.MSELoss(size_average=None, reduce=None, reduction='mean')

均方误差MSE计算输入 x 和目标 y 中每个元素之间的均方误差(平方 L2 范数),数学表达如下:
ℓ ( x , y ) = L = { l 1 , ... , l N } ⊤ , l n = ( x n − y n ) 2 \ell(x,y)=L=\{l_1,\ldots,l_N\}^\top,\quad l_n=\left(x_n-y_n\right)^2 ℓ(x,y)=L={l1,...,lN}⊤,ln=(xn−yn)2

其中 N N N 是批次大小。如果 reduction 不是 'none'(默认值为 'mean'),则
ℓ ( x , y ) = { m e a n ( L ) , if reduction = ′ m e a n ′ ; s u m ( L ) , if reduction = ′ s u m ′ . \ell(x,y)=\begin{cases}\mathrm{mean}(L),&\text{if reduction}=\mathrm{'mean'};\\\mathrm{sum}(L),&\text{if reduction}=\mathrm{'sum'}.&\end{cases} ℓ(x,y)={mean(L),sum(L),if reduction=′mean′;if reduction=′sum′.

2. CrossEntropyLoss交叉熵损失

交叉熵损失函数(Cross-Entropy Loss)是深度学习中分类问题常用的损失函数,特别适用于多分类问题。在PyTorch中,函数原型如下:

python 复制代码
torch.nn.CrossEntropyLoss(weight=None, size_average=None, ignore_index=-100, reduce=None, reduction='mean', label_smoothing=0.0)

交叉熵损失函数是深度学习中分类问题常用的损失函数,特别适用于多分类问题。它通过度量预测分布与真实分布之间的差异,来衡量模型输出的准确性。数学定义如下:
C r o s s E n t r o y L o s s = − ∑ i = 1 N y i ⋅ l o g ( y ^ i ) CrossEntroyLoss=-\sum_{i=1}^Ny_i\cdot log(\hat{y}_i) CrossEntroyLoss=−i=1∑Nyi⋅log(y^i)

  • N:类别数
  • y i y_i yi:真实的标签(用 one-hot 编码表示,只有目标类别对应的位置为 1,其他位置为 0)。
  • y ^ i \hat{y}_i y^i :模型的预测概率,即 softmax 的输出值。

3. L1Loss平均绝对误差

MAE计算输入 x x x 和目标 y y y 中每个元素之间的平均绝对误差 (MAE)。在PyTorch中,函数原型如下:

python 复制代码
torch.nn.L1Loss(size_average=None, reduce=None, reduction='mean')

L1 Loss(也称为绝对误差损失或曼哈顿损失)是一种常用的损失函数,广泛应用于回归任务中,其主要目的是衡量模型预测值与真实值之间的差异。数学定义如下:
ℓ ( x , y ) = L = { l 1 , ... , l N } ⊤ , l n = ∣ x n − y n ∣ , \ell(x,y)=L=\{l_1,\ldots,l_N\}^\top,\quad l_n=|x_n-y_n|, ℓ(x,y)=L={l1,...,lN}⊤,ln=∣xn−yn∣,

2.4 容器模块(Container Modules)

在PyTorch中,容器模块(Container Modules)用于组织和管理神经网络中的各个层。这些容器模块使得模型的构建和管理变得更加灵活和方便。以下是一些主要的PyTorch容器模块:
1. nn.Sequentia

这是一个非常常用的容器模块,它按照在构造函数中添加它们的顺序来组织多个子模块(通常是网络层)。使用nn.Sequential,每个添加的模块或层的输出自动成为下一个模块的输入,这简化了模型的构建过程,使代码更加清晰和易于理解。它适用于大多数前馈神经网络(feed-forward neural networks),如简单的卷积神经网络、全连接网络等。但对于需要复杂数据流的模型,如具有跳跃连接或多输入/多输出的网络,可能不太适合。
示例

python 复制代码
model = nn.Sequential(
          nn.Conv2d(1,20,5),
          nn.ReLU(),
          nn.Conv2d(20,64,5),
          nn.ReLU()
        )

2. nn.ModuleLis

这是一个简单的列表容器,可以包含多个子模块。与Python的常规列表不同,nn.ModuleList中的模块会被正确注册,从而确保它们能够参与到模型的参数更新和保存/加载过程中。
示例

python 复制代码
class MyModule(nn.Module):
    def __init__(self) -> None:
        super().__init__()
        #使用列表容器构造模型
        self.linears = nn.ModuleList([nn.Linear(10, 10) for i in range(10)])

    def forward(self, x):
        # ModuleList can act as an iterable, or be indexed using ints
        for i, l in enumerate(self.linears):
            x = self.linears[i // 2](x) + l(x)
        return x

3.nn.ModuleDic

类似于nn.ModuleList,但它是基于字典的容器,可以通过键值对的形式存储子模块。这对于需要根据名称动态访问模块的情况非常有用。
示例

python 复制代码
class MyModule(nn.Module):
    def __init__(self) -> None:
        super().__init__()
        self.choices = nn.ModuleDict({
                'conv': nn.Conv2d(10, 10, 3),
                'pool': nn.MaxPool2d(3)
        })
        self.activations = nn.ModuleDict([
                ['lrelu', nn.LeakyReLU()],
                ['prelu', nn.PReLU()]
        ])
	#在推理时,可以根据choice传入名字选择网络层进行推理
    def forward(self, x, choice, act):
        x = self.choices[choice](x)
        x = self.activations[act](x)
        return x

三、 nn.Module

nn.Module 类是 PyTorch 中构建所有神经网络的基类。它提供了模型构建所需的基本功能,包括参数的注册、前向传播的定义、以及模型保存和加载等。

以下是 nn.Module 类的一些关键特性和方法:
1. 参数注册

在 nn.Module 子类中,你可以通过 self.register_parameter 方法注册模型的参数(通常是权重和偏置)。PyTorch 会自动跟踪这些参数,并在训练过程中进行更新。

python 复制代码
import torch
import torch.nn as nn

class MyModule(nn.Module):
    def __init__(self):
        super(MyModule, self).__init__()
        # 创建一个参数,并注册它
        self.weight = nn.Parameter(torch.randn(3, 3))

    def forward(self, x):
        return x @ self.weight

# 实例化模块
model = MyModule()
print(list(model.parameters()))  # 输出包含已注册参数的列表

2.前向传播

你需要重写 forward 方法来定义模型的前向传播逻辑。

在调用模型时,PyTorch 会自动调用 forward 方法,并将输入数据传递给它。

python 复制代码
class SimpleNN(nn.Module):
    def __init__(self):
        super(SimpleNN, self).__init__()
        self.fc = nn.Linear(10, 1)  # 添加一个全连接层

    def forward(self, x):
        return self.fc(x)

# 实例化模型
model = SimpleNN()
input_data = torch.randn(1, 10)  # 创建一个输入张量
output = model(input_data)  # 调用模型,自动执行 forward 方法
print(output)

3.子模块

nn.Module 支持将其他 nn.Module 实例作为子模块。子模块可以通过 self.add_module 方法添加,但更常见的是直接在构造函数中通过赋值给 self 的属性来创建和添加子模块。

python 复制代码
class ComplexModule(nn.Module):
    def __init__(self):
        super(ComplexModule, self).__init__()
        self.submodule1 = nn.Linear(10, 20)
        self.submodule2 = nn.ReLU()

    def forward(self, x):
        x = self.submodule1(x)
        x = self.submodule2(x)
        return x

# 实例化模型
model = ComplexModule()
input_data = torch.randn(1, 10)
output = model(input_data)
print(output)

4. 模型保存和读取

nn.Module 提供了 save 和 load_state_dict 方法,用于保存和加载模型的参数。通常,会保存模型的 state_dict(一个包含所有参数和缓冲区的字典),而不是直接保存模型对象。
模型保存

python 复制代码
# 假设你有一个已经训练好的模型
torch.save(model.state_dict(), 'model.pth')

模型读取

python 复制代码
# 实例化一个新的模型对象
new_model = SimpleNN()
# 加载参数
new_model.load_state_dict(torch.load('model.pth'))

5.设备迁移

你可以使用 to 方法将模型移动到不同的设备(如 CPU、GPU)上。这对于在多设备环境下进行模型训练和推理非常有用。

python 复制代码
# 将模型移动到 GPU 上(如果可用)
if torch.cuda.is_available():
    model = model.to('cuda')

四、小结

torch.nn模块是PyTorch深度学习框架中不可或缺的一部分,它为开发者提供了构建和训练神经网络所需的所有工具和功能。通过深入了解torch.nn模块的使用方法和技巧,开发者可以更好地应用深度学习技术来解决实际问题。

相关推荐
i鹰斯坦爱吃红烧you1 分钟前
探索 Transformer²:大语言模型自适应的新突破
人工智能·pytorch·python·深度学习·语言模型·自然语言处理·transformer
瞻邈3 分钟前
DETR论文阅读
深度学习·计算机视觉
云天徽上6 分钟前
【机器学习案列】基于朴素贝叶斯的垃圾短信分类
人工智能·机器学习·分类
Jozky8611 分钟前
自动驾驶&占用网格预测
人工智能·机器学习·自动驾驶
day day-up14 分钟前
1.17组会汇报
人工智能
油泼辣子多加43 分钟前
Prompt-人工智能领域的核心技术与创新理念
人工智能·prompt
蜉蝣1号1 小时前
【已解决】我和ollama运行的qwen2.5大模型通信,总是返回GGGG?
人工智能·ai·语言模型·aigc
AI趋势预见1 小时前
AAPM:基于大型语言模型代理的资产定价模型,夏普比率提高9.6%
人工智能·深度学习·神经网络·语言模型·自然语言处理
安科瑞蒋静1 小时前
关于安科瑞Acrel-1000DP分布式光伏监控系统的实际案例分析-安科瑞 蒋静
大数据·人工智能
yzx9910132 小时前
OpenCV基础
人工智能·opencv·计算机视觉