深入浅出Pytorch宝典1.0

在这里插入图片描述

文章目录

前言

在学习 PyTorch 时,有许多其他重要的函数和概念值得了解,以便能够更全面地使用这个强大的深度学习库。以下是一些重要的概念和函数:

1. 张量操作

  • 索引、切片、连接、重塑torch.cat, torch.chunk, torch.index_select, torch.reshape, torch.squeeze, torch.stack, torch.transpose 等。
  • 数学运算torch.add, torch.sub, torch.mul, torch.div, torch.matmul 等。
  • 激活函数torch.sigmoid, torch.tanh, torch.softmax 等。

2. 自动微分

  • 反向传播tensor.backward() 用于计算梯度。
  • 梯度清零optimizer.zero_grad() 清除上一步的梯度,防止梯度累积。

3. 数据加载和处理

  • 数据集和数据加载器torch.utils.data.Datasettorch.utils.data.DataLoader 用于高效加载和批处理数据。
  • 数据转换torchvision.transforms 提供了图像的预处理和增强功能。

4. 模型构建和训练

  • 模型保存和加载torch.save, torch.load 用于保存和加载模型或参数。
  • GPU 加速.to(device).cuda() 方法用于将模型或张量移动到 GPU。

5. 预训练模型和迁移学习

  • 使用 torchvision.models 中的预训练模型进行迁移学习。

6. 调试和性能

  • 断点调试:在模型的关键部分使用 Python 的 pdb 或 IDE 断点。
  • 内存管理:使用 torch.cuda.memory_allocated 等函数监控 GPU 内存使用情况。

7. 高级特性

  • 分布式训练torch.nn.paralleltorch.distributed 用于多 GPU 和多节点训练。
  • 混合精度训练:使用 torch.cuda.amp 进行自动混合精度(AMP)训练,以加速训练并减少内存消耗。

总结

掌握这些基本的函数和概念对于深入理解和有效使用 PyTorch 非常重要。随着您在 PyTorch 的学习过程中的深入,您将逐渐接触到更多高级的特性和最佳实践。此外,阅读官方文档、教程和社区贡献的项目也是学习的好方法。

torch中主要的数据对象

PyTorch 是一个流行的开源机器学习库,广泛用于深度学习应用。张量是 PyTorch 中的一个基本组件,可被看作是一个高维数组或矩阵。在 PyTorch 中,主要的数据对象是 张量(Tensor)。张量是 PyTorch 的核心组件,用于存储和操作数据,尤其是在神经网络的操作中。张量类似于 NumPy 的多维数组(ndarray),但与此同时它们也提供了在 GPU 上进行操作的能力以加快计算速度。

主要特点和功能

  1. 多维数组:张量可以是一个数(标量)、一维数组(向量)、二维数组(矩阵)或更高维的数组。

  2. GPU 加速:与 NumPy 数组不同,PyTorch 张量可以在 GPU 上进行操作,这对于深度学习等需要大量计算的任务非常重要。

  3. 自动微分:张量支持自动微分,这对于训练神经网络至关重要。这是通过 requires_grad 属性来实现的,它允许 PyTorch 跟踪张量上的所有操作以便后续进行梯度计算。

  4. 与 NumPy 互操作性:PyTorch 张量可以轻松地与 NumPy 数组互相转换,使得两个库的结合使用变得非常方便。

  5. 动态计算图:PyTorch 使用动态计算图(称为动态计算图),这意味着图的结构是实时且动态的,可以根据需要在运行时改变。

张量的创建

张量可以通过多种方式创建,如直接从数据创建,通过现有张量创建,或使用特殊的函数(如 zeros, ones, rand 等)创建具有特定形状的张量。例如:

  • 直接从数据创建:

    import torch
    x = torch.tensor([1, 2, 3])
    
  • 通过现有张量创建:

    y = x.new_ones(3, 3)  # 创建一个大小为3x3且值为1的新张量
    
  • 使用特殊函数创建:

    z = torch.zeros(2, 2)  # 创建一个大小为2x2且值为0的张量
    

总体而言,张量是 PyTorch 中用于表示数据的主要对象,其灵活性和功能性使其成为深度学习和科学计算的有力工具。

数据处理和转换

1.torch.tensor() 创建一个新的张量(Tensor)

torch.tensor() 是 PyTorch 中的一个函数,用于创建一个新的张量(Tensor)。

以下是使用 torch.tensor() 的一些基本示例:

  1. 创建一个简单的张量

    import torch
    x = torch.tensor([1, 2, 3])
    

    这里创建了一个一维张量,包含元素 1, 2, 3。

  2. 创建一个二维张量(类似于矩阵)

    y = torch.tensor([[1, 2], [3, 4]])
    

    这里创建了一个 2x2 的张量,包含元素 1, 2; 3, 4。

  3. 指定数据类型

    z = torch.tensor([1, 2, 3], dtype=torch.float32)
    

    这里创建了一个一维张量,并明确指定了数据类型为 float32

torch.tensor() 可以接受各种数据类型的输入,如列表、NumPy 数组,并将其转换为张量。这个函数也允许你指定张量的数据类型,如 float32, int64 等。创建张量后,你可以用它来进行各种数学运算和深度学习操作。

2.torch.zeros()创建一个填充有零的张量(Tensor)

这个函数非常有用,特别是当你需要一个初始值全为零的张量作为算法的起点时。

函数的基本用法是指定张量的形状(即其各维度的大小),然后 torch.zeros() 会返回一个相应形状的新张量,其所有元素都被初始化为 0。

用法示例

假设你想创建一个 3x3 的零张量,你可以这样做:

import torch
x = torch.zeros(3, 3)

这段代码会创建一个 3 行 3 列的张量,其中每个元素都是 0。

可选参数

除了基本的形状参数外,torch.zeros() 还有一些可选参数,比如:

  • dtype: 用于指定张量的数据类型。例如,torch.float32torch.int64
  • layout: 用于指定张量的内存布局。
  • device: 用于指定张量所在的设备,如 CPU ('cpu') 或 GPU ('cuda:0').
  • requires_grad: 如果设置为 True,则 PyTorch 将会跟踪此张量上的所有操作,以便稍后在梯度下降优化中使用。这对于深度学习非常重要。

例如,创建一个数据类型为 float32 的 2x2 零张量,你可以这样做:

y = torch.zeros(2, 2, dtype=torch.float32)

torch.zeros() 是一个非常基础但极其有用的函数,在初始化模型参数、创建特定形状的占位符张量等方面非常常用。

3.torch.ones()创建一个填充有1的张量(Tensor)

这个函数在需要初始化所有元素为 1 的张量时非常有用,比如在某些算法初始化或者特定类型的数据处理中。

基本用法

torch.ones() 的基本用法是指定你希望创建的张量的形状,函数会返回一个相应形状的新张量,其所有元素都被初始化为 1。

示例

例如,创建一个 3x3 的全 1 张量:

import torch
x = torch.ones(3, 3)

这会创建一个 3 行 3 列,所有元素都是 1 的张量。

可选参数

除了形状参数,torch.ones() 还接受一些可选参数,包括:

  • dtype: 指定张量的数据类型,如 torch.float32torch.int64 等。
  • layout: 指定张量的内存布局。
  • device: 指定张量所在的设备,比如 CPU ('cpu') 或 GPU ('cuda:0')。
  • requires_grad: 如果设置为 True,PyTorch 将跟踪此张量上的所有操作,以便后续进行梯度计算。这对于深度学习应用非常重要。
示例

创建一个数据类型为 float32 的 2x2 全 1 张量:

y = torch.ones(2, 2, dtype=torch.float32)

torch.ones() 是 PyTorch 中的一个基础且常用的函数,广泛应用于初始化、设定默认值等多种场景。

4.torch.randn()

torch.randn() 是用于创建一个形状指定的张量,其元素从标准正态分布(均值为 0,标准差为 1)中随机抽取。这个函数在需要初始化具有随机值的张量时非常有用,尤其是在初始化神经网络权重时经常用到。

基本用法

要使用 torch.randn(),你需要指定张量的形状,函数会返回一个相应形状的新张量,其所有元素都是从标准正态分布中随机抽取的。

示例

例如,创建一个 2x3 的张量,其元素来自标准正态分布:

import torch
x = torch.randn(2, 3)

这段代码会创建一个 2 行 3 列的张量,每个元素都是随机的,来自于标准正态分布。

可选参数

torch.randn() 还接受一些可选参数,包括:

  • dtype: 指定张量的数据类型,如 torch.float32torch.int64 等。
  • layout: 指定张量的内存布局。
  • device: 指定张量所在的设备,例如 CPU ('cpu') 或 GPU ('cuda:0')。
  • requires_grad: 如果设置为 True,PyTorch 将跟踪此张量上的所有操作,以便后续进行梯度计算。这对于深度学习应用非常重要。

torch.randn() 在深度学习中经常用于初始化模型的权重参数,因为从正态分布中抽取的随机值可以防止模型在训练开始时过于确定性,帮助打破对称性,从而有助于更好的学习。

5.torch.from_numpy()

torch.from_numpy() 是用于将 NumPy 数组转换为 PyTorch 张量(Tensor)。这个函数非常有用,因为它允许你在 NumPy 和 PyTorch 之间无缝地转换数据,这在进行数据预处理或与使用 NumPy 的其他库交互时尤其重要。

主要特点

  1. 共享内存:通过 torch.from_numpy() 创建的 PyTorch 张量会与原始的 NumPy 数组共享相同的内存。这意味着对张量的修改也会反映在原始数组上,反之亦然。这使得转换非常高效,但也需要注意数据的同步问题。

  2. 自动数据类型匹配:转换的张量会自动匹配 NumPy 数组的数据类型。

用法示例

假设你有一个 NumPy 数组 numpy_array,你可以这样将它转换为 PyTorch 张量:

import numpy as np
import torch

numpy_array = np.array([1, 2, 3])
tensor = torch.from_numpy(numpy_array)

现在,tensor 是一个 PyTorch 张量,其内容与 numpy_array 相同。

注意事项

  • 转换后的张量只支持 CPU 上的操作。如果需要在 GPU 上使用它,你需要先将张量复制到 GPU。
  • 只有 NumPy 的浮点数和整数类型的数组可以转换。对于其他数据类型,如字符串或对象,这个函数可能不适用。
  • 由于内存共享,改变原始 NumPy 数组的值会导致 PyTorch 张量的值也发生变化,反之亦然。

torch.from_numpy() 提供了一种方便快捷的方式,在 NumPy 数组和 PyTorch 张量之间进行转换,使得两者可以在不同的场景下互相补充和使用。

6.torch.mean()计算张量的均值

torch.mean() 是用于计算张量(Tensor)的均值。这个函数可以计算整个张量的均值,也可以计算张量某一个或多个维度的均值。

基本用法

当对整个张量调用 torch.mean() 时,它会返回所有元素的平均值。

示例
import torch
x = torch.tensor([1.0, 2.0, 3.0, 4.0])
mean_val = torch.mean(x)

这段代码将计算张量 x 的均值,并将结果存储在 mean_val 中。

指定维度

你也可以指定要计算均值的维度。在这种情况下,函数会沿着指定的维度计算均值,并返回一个降维后的张量。

示例
y = torch.tensor([[1.0, 2.0], [3.0, 4.0]])
mean_val_dim = torch.mean(y, dim=0)

这里,mean_val_dim 将会是一个一维张量,包含沿着**第一个维度(列)**的均值,即 [2.0, 3.0]

可选参数

  • dim: 指定要减少的维度。
  • keepdim: 如果设置为 True,输出张量将保持与输入张量相同的维数。
  • dtype: 指定返回的张量的数据类型。

torch.mean() 是在深度学习和数据分析中常用的函数,用于特征标准化、损失计算等。

7. torch.std()计算张量的标准差

torch.std() 是用于计算张量(Tensor)的标准差。标准差是衡量数据分散程度的一种方式,它表示数据的平均波动大小。在 PyTorch 中,这个函数可以用来计算整个张量的标准差,或者是沿特定维度的标准差。

基本用法

当对整个张量调用 torch.std() 时,它会计算并返回所有元素的标准差。

示例
import torch
x = torch.tensor([1.0, 2.0, 3.0, 4.0])
std_val = torch.std(x)

这段代码将计算张量 x 的标准差,并将结果存储在 std_val 中。

指定维度

你也可以指定计算标准差的维度。在这种情况下,函数会沿着指定的维度计算标准差,并返回一个降维后的张量。

示例
y = torch.tensor([[1.0, 2.0], [3.0, 4.0]])
std_val_dim = torch.std(y, dim=0)
# RESULT
# tensor([1.4142, 1.4142])

这里,std_val_dim 将会是一个一维张量,包含沿着第一个维度(列)的标准差。

可选参数

  • dim: 指定要减少的维度。
  • unbiased: 是否使用无偏估计(默认为 True)。无偏估计使用 N-1 作为分母(N 是元素的数量),而有偏估计使用 N。
  • keepdim: 如果设置为 True,输出张量将保持与输入张量相同的维数。

torch.std() 在数据分析和深度学习中非常有用,尤其是在特征标准化、性能评估等方面。通过计算标准差,可以更好地理解数据的波动情况和分布特性。

8.torch.var()

torch.var() 是 PyTorch 中的一个函数,用于计算张量(Tensor)的方差。方差是统计学中的一个基本概念,用于衡量数据点与其均值的平均距离的平方,是衡量数据分散程度的重要指标。在 PyTorch 中,这个函数可以用来计算整个张量的方差,或者是沿特定维度的方差。

基本用法

当对整个张量调用 torch.var() 时,它会计算并返回所有元素的方差。

示例
import torch

x = torch.tensor([1.0, 2.0, 3.0, 4.0])
var_val = torch.var(x)

这段代码将计算张量 x 的方差,并将结果存储在 var_val 中。

指定维度

你也可以指定计算方差的维度。在这种情况下,函数会沿着指定的维度计算方差,并返回一个降维后的张量。

示例
y = torch.tensor([[1.0, 2.0], [3.0, 4.0]])
var_val_dim = torch.var(y, dim=0)

这里,var_val_dim 将会是一个一维张量,包含沿着第一个维度(列)的方差。

可选参数

  • dim: 指定要减少的维度。
  • unbiased: 是否使用无偏估计(默认为 True)。无偏估计使用 N-1 作为分母(N 是元素的数量),而有偏估计使用 N。
  • keepdim: 如果设置为 True,输出张量将保持与输入张量相同的维数。

torch.var() 在数据分析和深度学习中非常有用,尤其是在特征标准化、性能评估等方面。通过计算方差,可以更好地理解数据的波动情况和分布特性。

9.6和7和8中 dim=0是啥意思

在 PyTorch 中,dim=0 指的是张量的第一个维度。在多维张量中,每个维度用一个索引表示,从 0 开始。对于二维张量(例如矩阵),dim=0 表示沿着行的方向,而 dim=1 表示沿着列的方向。

当你在函数(比如 torch.std())中指定 dim=0,意味着你希望该操作沿着第一个维度进行。具体来说:

  • 对于二维张量(如矩阵),dim=0 意味着对每一列执行操作,并返回每列的结果。
  • 对于三维张量,dim=0 意味着操作将在最外层维度(通常可以视为不同的矩阵或数据组)上进行。

例如,在计算标准差的情境下,如果你有一个形状为 (m, n) 的二维张量(即 m 行 n 列),使用 torch.std(tensor, dim=0) 会计算每列的标准差,并返回一个长度为 n 的一维张量,其中包含了每一列的标准差。这就是说,操作是跨行(沿着列)进行的。

控制变量法大家知道吗?就是几个方法或者实验的比较,保持只有一个因素是可变的,其他都是一致的,就是控制变量法,论文术语也叫消融实验。

dim的使用也是这样,只有dim指定的维度是可变的,其他都是固定不变的。

dim=0,指定的是行,那就是列不变,理解成:同一列中每一行之间的比较或者操作,是每一行的比较,因为行是可变的。

神经网络构建

1.torch.nn.Linear()全连接层

torch.nn.Linear() 是 PyTorch 中的一个类,用于创建一个线性层(也称为全连接层或密集层)。这个线性层是神经网络中的一个基本组件,它对输入数据进行线性变换。

基本用法

torch.nn.Linear() 需要两个主要参数:

  1. in_features: 输入特征的数量(输入张量的大小)。
  2. out_features: 输出特征的数量(输出张量的大小)。
示例
import torch.nn as nn

# 创建一个线性层,输入特征数为 5,输出特征数为 3
linear_layer = nn.Linear(in_features=5, out_features=3)

这个示例创建了一个线性层,它期望接收一个特征数为 5 的输入张量,并将其转换为一个特征数为 3 的输出张量。

工作原理

线性层基本上执行以下数学运算:
output = input × W T + b \text{output} = \text{input} \times W^T + b output=input×WT+b
[ \text{output} = \text{input} \times W^T + b ]

其中:

  • input 是输入张量。
  • W 是权重矩阵,由 in_featuresout_features 确定其形状。
  • b 是偏置向量。
  • output 是输出张量。

当你通过线性层传递输入时,它会应用这个线性变换。

权重和偏置

线性层中的权重和偏置是可学习的参数。在训练过程中,通过反向传播和优化器,PyTorch 会自动调整这些参数以最小化损失函数。

应用

线性层广泛用于各种神经网络结构中,包括简单的前馈神经网络、卷积神经网络(在全连接层中)以及更复杂的结构,如循环神经网络和变换器模型中。

2.torch.nn.Conv2d()

torch.nn.Conv2d() 是 PyTorch 中用于创建二维卷积层的一个类。这种层常用于处理图像数据,在卷积神经网络(CNNs)中扮演着核心角色。二维卷积层通过在输入图像上滑动多个过滤器(或卷积核),提取空间特征,例如边缘、纹理等。

基本用法

创建一个 Conv2d 层需要几个关键参数:

  1. in_channels:输入通道数。对于彩色图像,这通常是 3(红色、绿色、蓝色),对于灰度图像,这是 1。
  2. out_channels:输出通道数。这相当于要使用的卷积核的数量。
  3. kernel_size:卷积核的大小。可以是单个数字(表示方形核)或一个 (height, width) 的元组。
示例
import torch.nn as nn
# 创建一个卷积层,输入通道数为 3,输出通道数为 6,核大小为 5
conv_layer = nn.Conv2d(in_channels=3, out_channels=6, kernel_size=5)

这个示例创建了一个二维卷积层,期望接收一个具有 3 个通道的输入(如彩色图像),并输出 6 个通道,每个通道由一个 5x5 的卷积核生成。

附加参数

除了这些基本参数外,Conv2d 还有一些其他可选参数,例如:

  • stride:卷积核滑动的步长。默认值是 1。
  • padding:输入边缘的填充量。默认为 0。填充可以帮助控制输出的空间维度。
  • dilation:卷积核中元素之间的间距。
  • groups:将输入和输出通道分组的数量,用于创建深度可分离卷积。
  • bias:是否添加偏置项。默认为 True

工作原理

在前向传递期间,Conv2d 层会将定义的卷积核应用于输入数据。卷积核会在输入数据的高度和宽度上滑动,计算局部区域的加权和。每个卷积核生成一个输出通道。

应用

卷积层是构建卷积神经网络的基础,广泛用于图像识别、图像分割、物体检测等计算机视觉任务。通过堆叠多个 Conv2d 层和其他层(如池化层、全连接层),可以构建出能够处理复杂视觉任务的深度神经网络。

3.torch.nn.ReLU()

torch.nn.ReLU() 是 PyTorch 中的一个类,用于创建一个激活函数层,实现了矩形线性单元(Rectified Linear Unit, ReLU)功能。ReLU 激活函数是深度学习中最常用的非线性激活函数之一,它用于增加网络的非线性特性,从而帮助网络学习更复杂的特征。

基本特征

ReLU 函数的数学表达式非常简单:f(x) = max(0, x)。这意味着它会将所有负值置为 0,而所有非负值保持不变。

使用方法

在 PyTorch 中,ReLU 可以作为一个层被添加到神经网络中。以下是使用 nn.ReLU() 的一个基本示例:

import torch.nn as nn
# 创建 ReLU 层
relu = nn.ReLU()
# 可以将这个层添加到神经网络的架构中

在实际应用中,ReLU 通常被用在卷积层或全连接层之后,以引入非线性。例如,在卷积神经网络(CNN)中,ReLU 层通常紧随卷积层之后。

优点

  • 减少梯度消失问题:与早期的激活函数(如 sigmoid 或 tanh)相比,ReLU 在正区间的梯度恒定,这有助于缓解深度神经网络中的梯度消失问题。
  • 计算效率高:由于 ReLU 函数数学上非常简单,它的计算效率高于其他复杂的激活函数。

注意事项

  • 死亡 ReLU 问题:在训练过程中,一些神经元可能只输出 0,这种现象被称为“死亡 ReLU”。为了解决这个问题,开发了一些 ReLU 的变体,如 Leaky ReLU 或 Parametric ReLU。

总的来说,ReLU 由于其简单性和有效性,在现代神经网络的设计中扮演着重要角色。

4.torch.nn.Sigmoid()

torch.nn.Sigmoid() 是用于创建一个激活函数层,实现了 Sigmoid 激活函数。Sigmoid 激活函数是一种经典的非线性激活函数,经常用于神经网络的输出层,尤其是在二分类问题中。

基本特征

Sigmoid 函数的数学形式为:f(x) = 1 / (1 + exp(-x))。它将任意实数值映射到 (0, 1) 区间内,因此特别适合用来做二元分类的输出(例如,表示概率)。

使用方法

在 PyTorch 中,Sigmoid 可以作为一个层被添加到神经网络中。以下是使用 nn.Sigmoid() 的一个基本示例:

import torch.nn as nn
# 创建 Sigmoid 层
sigmoid = nn.Sigmoid()
# 可以将这个层添加到神经网络的架构中

Sigmoid 激活函数通常用于神经网络的输出层,尤其是在处理二分类问题时。例如,如果你的任务是预测一个输入是否属于某个类别,可以在网络的最后使用 Sigmoid 函数将输出映射到 (0, 1) 区间,表示概率。

注意事项

  • 梯度消失问题:Sigmoid 函数在输入值非常大或非常小的时候,梯度会接近于零,这可能导致梯度消失问题,从而影响深层网络的训练。
  • 输出非零中心化:由于 Sigmoid 函数的输出始终是正的,所以它的输出不是以零为中心的,这可能会影响网络的收敛速度。

尽管有这些潜在的问题,Sigmoid 仍然是一个在特定场景下非常有用的激活函数,尤其是在早期的神经网络和某些特定类型的网络结构中。随着深度学习的发展,其他激活函数(如 ReLU 及其变体)因为性能和稳定性的优势,越来越多地被用于隐藏层。

5.torch.nn.functional.softmax

功能

Softmax 函数用于多类分类问题中,将神经网络输出的原始值(也称为 logits)转换为概率分布。对于给定的输入向量,softmax 函数会压缩其每个元素,使得输出元素的范围在 (0, 1) 之间,并且所有元素的和为 1。

在 PyTorch 中,softmax 函数可以通过两种方式调用:torch.softmaxtorch.nn.functional.softmax。实际上,并不存在 torch.nn.softmax 这个函数。

  1. torch.softmax

    • 这是直接在 torch 命名空间下的 softmax 函数。
    • 使用示例:
      output = torch.softmax(input, dim)
      
    • 其中 input 是你要应用 softmax 的数据,dim 是你要计算 softmax 的维度。
  2. torch.nn.functional.softmax

    • 这是在 torch.nn.functional 模块中的 softmax 函数,通常简称为 F.softmax(需要先导入 torch.nn.functional as F)。
    • 使用示例:
      import torch.nn.functional as F
      output = F.softmax(input, dim)
      
    • torch.softmax 的作用完全相同。

两者在功能上是等价的,你可以根据个人喜好和代码风格选择使用哪一个。在实际编码时,直接使用 torch.softmax 可能更为直观和方便,尤其是在不需要导入 torch.nn.functional 的情况下。而在使用许多其他神经网络相关函数(如激活函数、损失函数等)时,通常会导入并使用 torch.nn.functional,这时使用 F.softmax 可以保持代码风格的一致性。

示例

import torch
import torch.nn.functional as F

# 假设有一个批量大小为 3,类别数为 4 的 logits 张量
logits = torch.randn(3, 4)

# 计算 softmax
probabilities = F.softmax(logits, dim=1)
print(probabilities)

这个例子中,probabilities 包含了每个类别的预测概率,并且每行的总和为 1。

注意事项

在使用交叉熵损失函数 torch.nn.CrossEntropyLoss 时,通常不需要单独应用 softmax,因为 CrossEntropyLoss 已经内置了 softmax 操作。

训练和优化

1.torch.optim.Adam()

torch.optim.Adam() 是用于实现 Adam 优化算法。Adam(Adaptive Moment Estimation)是一种基于一阶和二阶矩估计的自适应学习率优化算法,它结合了梯度下降的动量(Momentum)和 RMSprop 的优点。由于其高效的迭代更新和对参数初始值较小的依赖性,Adam 在深度学习中非常受欢迎,并被广泛用于各种神经网络的训练。

基本用法

要使用 torch.optim.Adam,你需要首先导入它,然后创建一个 Adam 优化器对象,将神经网络中的参数传递给它。

示例
import torch.optim as optim
# 假设 model 是你的神经网络
model = ...
# 创建 Adam 优化器
optimizer = optim.Adam(model.parameters(), lr=0.001)

在这个例子中,model.parameters() 是传递给优化器的参数,lr=0.001 设置了学习率为 0.001,这是 Adam 优化器的一个重要参数。

参数

torch.optim.Adam 主要有以下参数:

  • params (iterable):需要优化的参数(通常是模型参数)。
  • lr (float, 可选):学习率(默认:1e-3)。
  • betas (Tuple[float, float], 可选):用于计算梯度以及梯度平方的运行平均值的系数(默认:(0.9, 0.999))。
  • eps (float, 可选):数值稳定性的小常数(默认:1e-8)。
  • weight_decay (float, 可选):权重衰减(L2惩罚)(默认:0)。
  • amsgrad (boolean, 可选):是否使用该算法的 AMSGrad 变体(默认:False)。

工作原理

Adam 通过计算梯度的一阶矩(均值)和二阶矩(未中心化的方差)来自适应地调整每个参数的学习率。这使得它在处理稀疏梯度(如在自然语言处理或计算机视觉任务中常见)或者在参数更新非常不均匀时(如使用循环神经网络时)表现得尤为出色。

应用

Adam 被广泛用于训练各种类型的神经网络,包括卷积神经网络(CNNs)、循环神经网络(RNNs)和变换器模型。由于其出色的性能和易用性,它成为了深度学习实践中的一个流行选择。

model.parameters()中一般有哪些参数

在 PyTorch 中,model.parameters() 是一个函数,用于返回神经网络模型中所有的可学习参数(通常是权重和偏置)。这些参数是模型训练过程中需要通过反向传播算法更新的。

对于一个典型的深度学习模型,model.parameters() 通常包含以下类型的参数:

  1. 卷积层参数

    • 权重(Weights):这些是卷积核的参数,用于提取输入数据的特征。
    • 偏置(Biases):每个卷积核可能有一个偏置项。
  2. 全连接层/线性层参数

    • 权重:这些是连接每个输入和输出节点的参数。
    • 偏置:每个输出节点可能有一个偏置项。
  3. 批归一化层参数(如果有的话):

    • 缩放系数(Scale factors):用于缩放归一化后的数据。
    • 偏移系数(Shift factors):用于在归一化后移动数据。
  4. 循环神经网络层参数(如 LSTM 或 GRU,如果使用的话):

    • 权重:这些包括控制门机制的各种权重参数。
    • 偏置:类似地,这些层也会有偏置参数。
  5. 其他自定义层参数

    • 如果你在模型中使用了自定义层,那么这些层的权重和偏置也会被包括在内。

每个参数都是一个张量(Tensor),其形状取决于层的类型和配置。例如,卷积层的权重形状取决于卷积核的大小、输入通道数和输出通道数,而全连接层的权重形状取决于输入和输出特征的数量。

model.parameters() 是在训练过程中使用优化器(如 SGD, Adam 等)更新模型时必须的,因为优化器需要知道哪些参数需要更新以及如何更新它们。

2.torch.optim.SGD()

torch.optim.SGD() 是 PyTorch 中的一个类,用于实现随机梯度下降(Stochastic Gradient Descent, SGD)优化算法。SGD 是最基本的优化算法之一,广泛用于训练各种类型的神经网络。

基本用法

要使用 torch.optim.SGD,你需要首先导入它,然后创建一个 SGD 优化器对象,将神经网络中的参数传递给它。

示例
import torch.optim as optim
# 假设 model 是你的神经网络
model = ...
# 创建 SGD 优化器
optimizer = optim.SGD(model.parameters(), lr=0.01)

在这个例子中,model.parameters() 是传递给优化器的参数,lr=0.01 设置了学习率为 0.01,这是 SGD 优化器的一个重要参数。

参数

torch.optim.SGD 的构造函数接受以下参数:

  • params (iterable):需要优化的参数(通常是模型参数)。
  • lr (float):学习率。
  • momentum (float, 可选):动量因子,可以帮助加快模型训练的收敛速度,并减少震荡(默认:0,表示不使用动量)。
  • weight_decay (float, 可选):权重衰减(L2惩罚)(默认:0)。
  • dampening (float, 可选):动量的抑制因子(默认:0)。
  • nesterov (boolean, 可选):是否使用 Nesterov 动量(默认:False)。

工作原理

SGD 通过迭代地调整模型参数来最小化损失函数。在每次迭代中,它计算损失函数相对于参数的梯度,并用这个梯度来更新参数。使用动量的 SGD 可以在一定程度上克服局部最小值或鞍点的问题,并加速训练过程。

应用

SGD 虽然简单,但在很多情况下非常有效。它特别适用于大规模数据集和高维空间,因为每次更新只需要计算一小部分数据的梯度。然而,与一些更先进的优化器(如 Adam 或 RMSprop)相比,SGD 的收敛速度可能较慢,并且对超参数(如学习率)的选择更为敏感。

3.torch.autograd.backward()

torch.autograd.backward() 是 PyTorch 中的一个函数,用于自动计算张量(Tensor)的梯度。这个函数是 PyTorch 自动微分引擎的核心,它允许用户自动计算所有依赖于某些张量的梯度。

基本用法

在深度学习中,backward() 通常用于自动计算损失函数相对于网络参数的梯度。在调用此函数之后,所有具有 requires_grad=True 的张量将会有它们的 .grad 属性更新为存储对应的梯度。

示例

假设 loss 是一个标量张量(即单个数值的张量),计算的是模型的输出与真实标签之间的损失:

loss.backward()

在这个调用之后,所有参与 loss 计算且设置了 requires_grad=True 的张量(通常是模型的权重)将会有它们的梯度计算出来。

参数

torch.autograd.backward 可以接受以下参数:

  • tensors: 要计算梯度的张量。通常是损失函数的输出。
  • grad_tensors: 相对于每个张量的梯度。这对于非标量输出是必要的。
  • retain_graph: 是否保留计算图。在大多数情况下,在执行一次反向传播后,PyTorch 会自动清除计算图。如果你想对同一个图进行多次的反向传播,需要设置这个参数为 True
  • create_graph: 是否创建导数计算图。对于高阶导数来说是必要的。

工作原理

backward() 被调用时,PyTorch 会自动遍历所有依赖于输入张量的操作,使用链式法则计算梯度,并将这些梯度累积在各个张量的 .grad 属性中。

应用

backward() 函数是训练神经网络的核心。在定义网络、进行前向传播以及计算损失之后,backward() 被用来自动计算梯度。然后,这些梯度可以被用来更新模型的权重,通常是通过一个优化器(如 SGD、Adam 等)来完成。

4.具体案例

当然可以。让我们来看一个简单的 PyTorch 示例,这个示例将展示如何使用 torch.autograd.backward() 来进行自动梯度计算。这个过程通常包括以下步骤:

  1. 定义一个简单的神经网络。
  2. 生成一些输入数据和对应的目标数据。
  3. 进行前向传播以计算输出。
  4. 计算损失函数。
  5. 使用 backward() 进行反向传播以计算梯度。
  6. (可选)使用优化器更新模型的参数。
步骤1:定义一个简单的神经网络

首先,我们定义一个简单的单层线性神经网络。这个网络只包含一个线性层。

import torch
import torch.nn as nn

# 定义一个简单的线性模型
model = nn.Linear(in_features=1, out_features=1)
步骤2:生成输入和目标数据

接下来,我们生成一些模拟数据来作为输入,以及对应的目标数据。

# 创建输入和输出数据
x = torch.randn(10, 1)  # 输入数据:随机生成
y = torch.randn(10, 1)  # 目标数据:随机生成
步骤3:前向传播

现在,我们通过模型进行前向传播以计算输出。

# 前向传播
output = model(x)
步骤4:计算损失

我们使用一个简单的均方误差损失函数来计算预测和实际值之间的差异。

# 计算损失
criterion = nn.MSELoss()
loss = criterion(output, y)
步骤5:反向传播

使用 backward() 对损失进行反向传播,计算梯度。

# 反向传播以计算梯度
loss.backward()
步骤6:(可选)更新模型参数

通常,我们会使用优化器(例如 SGD 或 Adam)来根据计算出的梯度更新模型的参数。

# 使用 SGD 优化器
optimizer = torch.optim.SGD(model.parameters(), lr=0.01)

# 更新模型参数
optimizer.step()

# 清除梯度(这一步在每次更新后都必须进行)
optimizer.zero_grad()

以上代码展示了在 PyTorch 中进行神经网络训练的基本流程。在实际应用中,这些步骤会在数据集的多个批次上重复执行,通常是在一个循环中完成。

自动微分

tensor.backward()

在 PyTorch 中,tensor.backward() 是一个非常重要的方法,用于计算某个张量(tensor)的梯度。这个方法在神经网络的反向传播过程中扮演核心角色。

用法

当你调用 tensor.backward() 时,PyTorch 会自动计算该张量相对于其图中叶节点的梯度,并将这些梯度累积到叶节点的 .grad 属性中。

示例

假设你有一个关于标量张量(即只有一个元素的张量)的计算图,并且你想计算某个函数相对于其参数的梯度:

import torch
# 创建一个张量并设置 requires_grad=True 用来追踪其计算历史
x = torch.tensor([2.0], requires_grad=True)
# 定义一个函数
y = x * x * 3
# 反向传播,计算梯度
y.backward()
# 查看梯度
print(x.grad)  # dy/dx

在这个例子中,y 是关于 x 的函数。调用 y.backward() 后,x 的梯度将会被计算并存储在 x.grad 中。

注意事项

  1. 张量维度tensor.backward() 通常用于标量张量(例如损失函数的输出)。如果张量不是标量,需要提供一个与该张量形状相同的 gradient 参数来指定反向传播的梯度。

  2. 仅限可导函数:只能对可微分(differentiable)函数的结果张量调用 backward()

  3. 累积梯度:每次调用 backward() 时,梯度都会累积在 .grad 属性中,所以在多次反向传播之间可能需要清零梯度。

  4. 仅对具有 requires_grad=True 的张量有效:如果一个张量的 requires_grad 属性设置为 False,则不会跟踪其操作的计算历史,也就无法在其上执行 backward()

tensor.backward() 是实现神经网络训练的关键操作之一,通过它可以自动计算梯度,这是优化网络权重的基础。

tensor.backward()torch.autograd.backward()的区别

tensor.backward()torch.autograd.backward() 都是 PyTorch 中用于执行自动梯度计算的函数,它们在神经网络的反向传播过程中非常重要。虽然这两个函数在很多情况下可以互换使用,但它们之间存在一些关键的区别。

tensor.backward()

tensor.backward() 是应用于单个张量的方法,用于计算该张量的梯度。

  • 用途:当你有一个标量张量(例如,计算得到的损失函数的值),并希望计算相对于某些参数的梯度时使用。
  • 使用方式:直接调用张量的 .backward() 方法,PyTorch 会自动计算所有参与创建该张量的计算图中的梯度。
  • 限制:主要用于标量输出,因为标量输出相对于任何张量的梯度是明确的。

torch.autograd.backward()

torch.autograd.backward() 是一个更通用的函数,允许对张量的列表进行梯度计算。

  • 用途:当你有多个非标量张量,并且希望对这些张量进行梯度计算时使用。
  • 使用方式:传入一个张量或张量列表作为参数,PyTorch 会计算这些张量的梯度。
  • 灵活性:可以处理非标量张量,并允许更灵活的梯度计算。例如,可以指定每个张量相对于其梯度的权重。

示例

假设 loss 是一个计算得到的标量损失值,使用 tensor.backward()

loss.backward()

如果有多个损失值,或者需要计算非标量张量的梯度,使用 torch.autograd.backward()

torch.autograd.backward([loss1, loss2], [grad1, grad2])

其中,loss1loss2 是要计算梯度的张量,grad1grad2 是相对于这些张量的梯度。

总结

  • tensor.backward() 适用于单个标量张量的简单情况。
  • torch.autograd.backward() 提供了更多的灵活性和控制,适用于复杂的情况,如非标量张量和多张量的梯度计算。

optimizer.zero_grad()

在 PyTorch 中,optimizer.zero_grad() 是一个非常重要的方法,用于清除(重置)所有被优化的张量(通常是模型参数)的梯度。这个方法通常在每个训练迭代的开始处调用。

为什么需要调用 zero_grad()

在 PyTorch 中,梯度是累积的。这意味着,在每次进行反向传播并调用 .backward() 方法时,梯度会被累加到之前的梯度上,而不是被替换。虽然这在某些情况下是有益的(比如在 RNN 训练中),但在大多数情况下,我们希望在每次迭代中从零开始计算梯度。为了实现这一点,在每次迭代的开始,我们需要手动将梯度归零。

使用方式

zero_grad() 应该在每次更新参数之前调用。通常情况下,这个调用放在训练循环的开始处:

for epoch in range(num_epochs):
    for batch in data_loader:
        optimizer.zero_grad()
        output = model(batch)
        loss = loss_function(output, target)
        loss.backward()
        optimizer.step()

在这个训练循环中,optimizer.zero_grad() 被用于清除上一步的梯度,确保这次的梯度计算是针对当前步骤的输出和目标。

注意事项

  • 忘记调用 zero_grad() 可能导致模型以意外的方式表现,因为梯度被错误地累积。
  • 在一些特殊的训练配置中,可能故意不清零梯度,以实现特定的效果(例如梯度累积),但这种做法不常见,并且需要特别的考虑。

总之,optimizer.zero_grad() 是 PyTorch 训练循环中的一个标准步骤,用于确保每次迭代中梯度的正确计算。

数据加载和处理

1.torch.utils.data.Dataset

torch.utils.data.Dataset 是 PyTorch 中的一个抽象类,用于表示数据集。在 PyTorch 中,创建自定义数据集通常涉及继承 Dataset 类并重写两个方法:__len____getitem__。这样做的目的是为了方便地使用 PyTorch 的数据加载和处理工具,特别是与 torch.utils.data.DataLoader 配合,实现多线程数据加载和批处理。

基本结构

以下是创建自定义数据集的基本结构:

import torch
from torch.utils.data import Dataset

class CustomDataset(Dataset):
    def __init__(self, data, labels):
        # 初始化函数,读取数据等初始化操作
        self.data = data
        self.labels = labels

    def __len__(self):
        # 返回数据集中样本的数量
        return len(self.data)

    def __getitem__(self, index):
        # 根据索引号返回样本和标签
        sample = self.data[index]
        label = self.labels[index]
        return sample, label

在这个示例中,CustomDataset 类继承自 Dataset 类。它接受数据和标签作为输入,并实现了 __len____getitem__ 方法。__len__ 方法返回数据集的大小,而 __getitem__ 根据给定的索引返回对应的数据和标签。

使用示例

假设您已经有了一些数据和标签,以下是如何使用自定义数据集和数据加载器的示例:

from torch.utils.data import DataLoader

# 假设 data 和 labels 是你的数据和标签
data = ...
labels = ...

# 创建数据集
dataset = CustomDataset(data, labels)

# 创建数据加载器
data_loader = DataLoader(dataset, batch_size=4, shuffle=True)

# 使用数据加载器
for batch in data_loader:
    batch_data, batch_labels = batch
    # 进行训练或其他操作

在这个示例中,DataLoader 负责批量加载数据集,shuffle=True 表示在每个 epoch 开始时,数据将被打乱。通过这种方式,可以方便地在神经网络训练中使用自定义数据集。

2.torch.utils.data.DataLoader

torch.utils.data.DataLoader 是 PyTorch 中的一个类,它提供了快速、灵活和内存高效的数据加载方式,特别适用于大规模数据集。DataLoader 能够处理各种不同类型的数据集,并支持自动批处理(batching)、数据打乱(shuffling)、多线程数据加载等功能。

基本用法

要使用 DataLoader,你通常需要将一个 Dataset 对象传递给它。Dataset 提供了一个数据集的接口,而 DataLoader 负责将这些数据集中的数据转换成小批次,以便于神经网络进行处理。

示例
from torch.utils.data import DataLoader

# 假设你已经有了一个 Dataset 对象
dataset = ...

# 创建 DataLoader
data_loader = DataLoader(dataset, batch_size=32, shuffle=True)

在这个例子中,我们创建了一个 DataLoader 实例,它从 dataset 中每次取出 32 个样本作为一个批次,并且在每个 epoch 开始时打乱数据。

参数

DataLoader 的主要参数包括:

  • dataset:要加载的数据集。
  • batch_size (int, 可选):每个批次的样本数量。默认值为 1。
  • shuffle (bool, 可选):是否在每个 epoch 开始时打乱数据。默认为 False。
  • num_workers (int, 可选):用于数据加载的子进程数。0 表示数据将在主进程中加载。默认为 0。
  • collate_fn (callable, 可选):将数据列表合并为一个批次的函数。
  • drop_last (bool, 可选):如果数据集大小不能被批次大小整除,设置为 True 时将丢弃最后一个不完整的批次。

使用 DataLoader

DataLoader 通常用在训练循环中,如下所示:

for batch_idx, (data, targets) in enumerate(data_loader):
    # 在这里进行模型训练的操作
    ...

在每次迭代中,data_loader 会自动提供一个批次的数据(data)和对应的标签或目标(targets)。

通过使用 DataLoader,你可以轻松地实现数据的批处理和多线程加载,这对于加速训练过程和高效地利用计算资源非常重要。

模型保存和加载

1.torch.save()

torch.save() 是用于将对象(如模型的状态字典、优化器的状态等)序列化并保存到文件中。这个函数非常有用,特别是在保存和加载神经网络模型的训练状态时。

基本用法

要使用 torch.save(),你需要指定要保存的对象以及保存的文件路径。

示例:保存模型
import torch

# 假设 model 是你的神经网络模型
model = ...

# 保存模型的状态字典
torch.save(model.state_dict(), 'model.pth')

在这个例子中,我们将模型的状态字典(model.state_dict())保存到了名为 ‘model.pth’ 的文件中。状态字典包含了模型的参数(权重和偏置)。

示例:保存更多内容

你也可以保存更多的内容,如模型的状态字典、优化器的状态字典等。

# 假设 optimizer 是你使用的优化器
optimizer = ...

# 保存模型和优化器的状态
torch.save({
    'model_state_dict': model.state_dict(),
    'optimizer_state_dict': optimizer.state_dict(),
}, 'model_and_optimizer.pth')

格式

torch.save() 默认使用 Python 的 pickle 模块进行序列化。因此,保存的文件可以使用 Python 的任何版本加载,前提是该版本兼容用于保存文件的 PyTorch 版本。

注意事项

  • 保存和加载设备:当你保存并随后加载模型时,需要注意原模型是在 CPU 还是 GPU 上训练的。加载模型时可能需要将模型的参数映射到 CPU 或 GPU。
  • 版本兼容性:保存的模型可能与不同版本的 PyTorch 不兼容。尽量确保保存和加载模型的 PyTorch 版本一致。

使用 torch.save() 保存模型或其他对象是 PyTorch 中实现模型持久化的一个重要功能,特别是在需要暂停和恢复训练过程或者进行模型部署时。

2.torch.load()

torch.load() 是用于加载通过 torch.save() 保存的对象。这个函数主要用于加载模型的状态字典、优化器的状态等,是模型训练和部署中的关键步骤。

基本用法

要使用 torch.load(),你只需要提供保存的文件路径。如果保存的对象是模型的状态字典,通常需要先创建一个相同结构的模型,然后将状态字典加载到这个模型中。

示例:加载模型
import torch
import torch.nn as nn

# 假设 MyModel 是你的模型类
class MyModel(nn.Module):
    # 模型的定义...

# 创建模型的实例
model = MyModel()

# 加载模型状态
model.load_state_dict(torch.load('model.pth'))

在这个例子中,我们首先创建了模型的一个实例,然后加载了之前保存的模型状态。

加载到指定设备

在加载时,你可以指定将数据加载到哪个设备(CPU 或 GPU)。

示例:加载到 CPU 或 GPU
# 加载到 CPU
model.load_state_dict(torch.load('model.pth', map_location=torch.device('cpu')))

# 或者,如果你想加载到 GPU(假设可用)
model.load_state_dict(torch.load('model.pth', map_location=torch.device('cuda')))

加载包含多个对象的文件

如果你保存了一个包含多个对象的文件(例如,模型状态和优化器状态),你可以按照相同的方式加载它们。

示例:加载模型和优化器状态
# 假设 optimizer 是你的优化器
optimizer = ...

checkpoint = torch.load('model_and_optimizer.pth')
model.load_state_dict(checkpoint['model_state_dict'])
optimizer.load_state_dict(checkpoint['optimizer_state_dict'])

注意事项

  • 确保加载的模型结构与保存时的模型结构相同。
  • 注意模型保存和加载时的设备一致性,特别是在使用 GPU 时。
  • 当在不同的 PyTorch 版本间传输模型时,要注意版本兼容性。

torch.load() 是恢复模型状态、迁移学习或模型部署的基础,使得模型的持久化和重用成为可能。

3.torch.to(device)

在 PyTorch 中,.to(device) 是一个非常重要的方法,用于将张量(tensors)或整个模型(models)移动到不同的设备上。这个设备可以是 CPU 或者 GPU,这取决于你的系统配置和需求。

基本用法

使用 .to(device) 时,你需要指定目标设备。这个设备可以是 'cpu' 或者 'cuda'(对于 NVIDIA GPU)。如果你的系统有多个 GPU,你还可以指定 'cuda:0', 'cuda:1', 等等,来选择特定的 GPU。

将模型移动到 GPU
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
model = MyModel()  # 假设 MyModel 是某个定义的神经网络模型
model.to(device)

这段代码首先检查 CUDA(GPU 支持)是否可用。如果可用,它会创建一个 'cuda' 设备对象;否则,它会回退到 'cpu'。然后,模型通过 .to(device) 被移动到指定的设备。

将张量移动到 GPU
tensor = torch.randn(3, 4)
tensor = tensor.to(device)

在这个例子中,一个随机张量被创建并移动到了之前指定的设备上。

为什么使用 .to(device)

  1. 性能:GPU(特别是现代的 GPU)比 CPU 提供更快的计算速度,特别是对于并行计算任务,如大规模的矩阵运算和深度学习模型。

  2. 灵活性.to(device) 方法允许代码在有无 GPU 支持的环境中都能灵活运行,无需改变代码结构。

  3. 优化资源使用:允许开发者根据需求和资源情况,选择最合适的计算资源。

注意事项

  • 确保在进行任何计算之前将模型和输入数据移动到同一设备上,否则会引发错误。
  • 在使用 GPU 训练模型时,记得也要将输入数据和目标数据移动到 GPU 上。
  • 当使用多个 GPU 时,你还可能需要使用 torch.nn.DataParalleltorch.nn.parallel.DistributedDataParallel 来并行处理数据。

4.torch.cuda.is_available()检查CUDA是否可⽤

torch.cuda.is_available() 是用于检查你的系统是否支持 CUDA,也就是说,它用于检测你的机器是否装有 NVIDIA 的 GPU,并且是否可以用于加速 PyTorch 的运算。

用途

在编写 PyTorch 代码时,常常需要根据系统是否支持 CUDA 来决定是否使用 GPU 加速。使用 torch.cuda.is_available() 可以帮助你的代码在有 GPU 支持的情况下自动利用 GPU,没有 GPU 时自动回退到 CPU。

使用示例

import torch

# 检查 CUDA 是否可用
if torch.cuda.is_available():
    device = torch.device("cuda")
    print("CUDA is available. Using GPU.")
else:
    device = torch.device("cpu")
    print("CUDA is not available. Using CPU.")

# 然后可以将模型和数据移动到检测到的设备
model.to(device)
data = data.to(device)

在这个例子中,首先检查 CUDA 是否可用。如果可用,程序会选择使用 GPU("cuda"),否则使用 CPU("cpu")。然后,模型和数据将被移动到相应的设备上,以利用 GPU 加速(如果可用)。

注意事项

  • 在没有 NVIDIA GPU 或者相应的 CUDA 驱动未安装的机器上,torch.cuda.is_available() 将返回 False
  • 如果你在 GPU 上运行模型或数据,但你的 PyTorch 安装没有 CUDA 支持,你的代码会抛出错误。
  • 使用 torch.cuda.is_available() 使得代码在不同的硬件环境下更加灵活和健壮。

函数式接口

1.torch.nn.functional

torch.nn.functional 是 PyTorch 中的一个模块,提供了许多神经网络相关的函数。这个模块包含了各种激活函数、损失函数、卷积操作等,是构建神经网络时的重要组成部分。与 torch.nn 模块中的类不同,torch.nn.functional(通常简称为 F提供的是函数接口

主要特点

  • 无状态torch.nn.functional 中的函数不包含可学习的参数。例如,当你使用 F.conv2d 时,你需要手动传入权重和偏置。
  • 灵活性:由于这些函数是无状态的,它们可以在任何地方被调用,不需要事先定义一个类的实例。这使得在快速原型制作或定制化操作时非常灵活。

常用函数

  1. 激活函数:例如 F.relu, F.sigmoid, F.tanh
  2. 损失函数:例如 F.mse_loss(均方误差损失),F.cross_entropy(交叉熵损失)。
  3. 卷积操作:例如 F.conv2d, F.conv1d
  4. 池化操作:例如 F.max_pool2d, F.avg_pool2d
  5. 归一化操作:例如 F.batch_norm, F.layer_norm

使用示例

下面是一个使用 torch.nn.functional 的示例:

import torch
import torch.nn.functional as F
# 假设 input 是神经网络的输入
input = torch.randn(1, 16, 12, 12)
# 卷积操作,需要手动指定权重和偏置
weight = torch.randn(33, 16, 3, 3)
output = F.conv2d(input, weight)
# ReLU 激活函数
output = F.relu(output)

在这个示例中,我们先进行了卷积操作,然后应用了 ReLU 激活函数。

选择 torch.nntorch.nn.functional

在 PyTorch 中,很多操作既可以用 torch.nn 模块中的类实现,也可以用 torch.nn.functional 中的函数实现。一般来说:

  • 如果操作包含可学习的参数(如卷积层、全连接层),最好使用 torch.nn 模块中的类,因为它们会自动管理这些参数。
  • 对于没有学习参数的操作(如激活函数、池化操作),使用 torch.nn.functional 中的函数更为方便。

选择哪种方式主要取决于个人喜好和特定的用例。

1中使用示例讲解

这段代码展示了如何使用 PyTorch 中的 torch.nn.functional 模块进行二维卷积操作。让我们逐步解析这段代码:

  1. 输入张量(input)

    input = torch.randn(1, 16, 12, 12)
    

    这里,input 是一个随机初始化的四维张量,其维度为 [1, 16, 12, 12]。这四个维度分别代表:

    • 批次大小(Batch size):1,表示每次处理一个样本。
    • 通道数(Channels):16,表示输入特征图(或称为输入通道)的数量。
    • 高度:12,输入特征图的高度。
    • 宽度:12,输入特征图的宽度。
  2. 权重张量(weight)

    weight = torch.randn(33, 16, 3, 3)
    

    weight 是一个随机初始化的四维张量,其维度为 [33, 16, 3, 3]。这四个维度分别代表:

    • 输出通道数:33,表示卷积层输出特征图的数量。
    • 输入通道数:16,这应与输入张量的通道数匹配。
    • 卷积核的高度:3。
    • 卷积核的宽度:3。
  3. 卷积操作

    output = F.conv2d(input, weight)
    

    这行代码执行二维卷积操作。F.conv2d 接受输入张量和权重张量,并对输入张量执行卷积操作。在这个例子中,它将 input 张量与 33 个大小为 3x3 的卷积核进行卷积。由于没有指定偏置(bias)和其他参数(如步长(stride)和填充(padding)),所以默认不使用偏置,步长为 1,且没有填充。

  4. 输出
    output 是卷积操作的结果。其大小取决于输入的大小、卷积核的大小、步长和填充。在这个例子中,由于步长为 1,且没有填充,输出特征图的空间维度会小于输入特征图。具体的输出尺寸可以通过卷积操作的公式计算得出。

  5. 激活函数:

output = F.relu(output)

在这行代码中,我们对 output 张量应用 ReLU 激活函数。如果 output 中的某些元素是负数,这些负数将被置为 0,而正数则保持不变。这样的操作有助于在神经网络中引入非线性,从而使网络能够学习和建模更复杂的函数。

2.torch.nn.Module

torch.nn.Module 是 PyTorch 中所有神经网络模块的基类。几乎所有的神经网络构建块(比如各种层、损失函数等)都继承自这个类。它提供了一种组织网络层和计算的方式,使得构建复杂的神经网络变得更加简单和直观。

基本特性

  1. 封装参数torch.nn.Module 自动跟踪所有分配给其子类的可训练参数(比如层的权重和偏置),这些参数可以通过 parameters()named_parameters() 方法访问。

  2. 管理子模块:可以将其他 nn.Module 对象作为属性添加到一个 nn.Module 对象中,这样做的好处是它可以自动处理这些子模块的参数。

  3. 定义前向传播:通过定义 forward 方法,nn.Module 允许你定义模型的前向传播路径。

创建自定义模块

要创建自定义的神经网络模型,通常需要继承 nn.Module 并定义自己的构造函数(__init__)和前向传播函数(forward)。

示例
import torch.nn as nn
import torch.nn.functional as F

class MyModel(nn.Module):
    def __init__(self):
        super(MyModel, self).__init__()
        # 定义模型层
        self.conv1 = nn.Conv2d(1, 20, 5)
        self.conv2 = nn.Conv2d(20, 50, 5)
        self.fc1 = nn.Linear(4*4*50, 500)
        self.fc2 = nn.Linear(500, 10)

    def forward(self, x):
        # 定义前向传播
        x = F.relu(self.conv1(x))
        x = F.relu(self.conv2(x))
        x = x.view(-1, 4*4*50)  # 展平操作
        x = F.relu(self.fc1(x))
        x = self.fc2(x)
        return x

model = MyModel()

在这个例子中,MyModel 类继承了 nn.Module。在构造函数中定义了几个层,然后在 forward 方法中定义了数据如何通过这些层的具体路径。

优点

  • 模块化和重用性:可以轻松地在不同的模型中重用或替换特定的层或操作。
  • 易于理解和维护:定义清晰的前向传播路径有助于理解模型的工作方式,使得代码更容易维护和调试。
  • 与 PyTorch 生态系统集成nn.Module 提供了与 PyTorch 其他功能(如优化器、序列化等)的无缝集成。

附加功能

  • 钩子(Hooks)nn.Module 提供了注册前向和后向钩子的能力,这对于理解模型内部的操作和调试非常有用。
  • 参数冻结和解冻:可以轻松地冻结或解冻模型的某些部分,这在微调(fine-tuning)预训练模型时非常有用。
  • 参数初始化:可以自定义参数的初始化方式。
  • 设备管理:通过 .to(device) 方法,可以轻松地将模型的所有参数移动到 GPU 或 CPU。

使用注意事项

  • 一定要调用 super().__init__() 初始化父类 nn.Module,以确保网络层正确注册。
  • 所有需要学习的参数都应该是 nn.Module 的属性。
  • forward 方法定义了模型的前向传播路径,它是唯一必须由用户定义的方法。
  • 不要在 forward 方法中使用 torch.tensor 直接创建新的张量,这可能会导致设备不一致或梯度计算问题。

总的来说,torch.nn.Module 是 PyTorch 中构建神经网络的基石,它提供了灵活性和强大的功能来帮助你设计和实现各种复杂的网络架构。

Pytorch中的高级网络架构

介绍

在 PyTorch 中,高级网络架构通常指的是那些已经预先定义好并在多个任务中表现优秀的神经网络模型。这些架构通常是由研究社区经过大量实验验证的,并在各种比赛和基准测试中取得了良好的成绩。PyTorch 提供了许多这样的预训练模型,它们主要在 torchvision 库中,这个库专注于图像处理。

以下是一些在 PyTorch 中常见的高级网络架构:

1. 卷积神经网络(CNNs)架构

用于图像识别、分类和其他视觉任务。

  • ResNet(残差网络):通过残差连接解决了深层网络中的梯度消失问题。
  • VGG:通过使用多个较小的卷积核代替大的卷积核来提升网络性能。
  • Inception(GoogLeNet):包含多个并行卷积层的模块化架构。
  • DenseNet:通过连接每一层的输出到后面所有层来促进特征重用。

2. 循环神经网络(RNNs)架构

用于处理序列数据,如自然语言处理或时间序列分析。

  • LSTM(长短期记忆):能够学习长期依赖关系的 RNN 变体。
  • GRU(门控循环单元):比 LSTM 简单,但在许多任务中同样有效。

3. 变换器(Transformer)架构

主要用于自然语言处理任务。

  • BERT(双向编码器表示):通过预训练的双向 Transformer 编码器来提高下游任务的性能。
  • GPT(生成预训练变换器):自回归模型,用于文本生成任务。

4. 检测和分割网络架构

用于对象检测和图像分割。

  • Faster R-CNN:用于快速高效的对象检测。
  • Mask R-CNN:在 Faster R-CNN 的基础上添加了一个分支,用于像素级别的对象分割。

使用预训练模型

这些模型通常以预训练的形式提供,可以很容易地加载并用于各种任务,例如特征提取、微调(fine-tuning

)等。在 PyTorch 中,使用预训练模型通常只需要几行代码。例如,加载预训练的 ResNet-50 模型可以像这样:

import torchvision.models as models

# 加载预训练的 ResNet-50 模型
resnet = models.resnet50(pretrained=True)

总结

​ 这些高级网络架构在解决各种复杂的机器学习问题时提供了极大的便利,特别是对于那些不具备足够资源来从头开始训练大型模型的应用场景。通过使用这些预训练模型,你可以利用深度学习社区的集体智慧,并在你自己的特定任务上实现快速的性能提升。

torch.nn.GRU() (了解一下)

torch.nn.GRU() 是 PyTorch 中用于创建一个门控循环单元(Gated Recurrent Unit, GRU)层的类。GRU 是一种流行的循环神经网络(RNN)架构,类似于长短时记忆网络(Long Short-Term Memory, LSTM),但结构上更简单。它在处理序列数据方面非常有效,广泛应用于自然语言处理、语音识别等领域。

基本用法

创建 GRU 层时,可以指定几个参数,包括输入的特征维度、隐藏层的维度等。

示例
import torch.nn as nn

# 创建 GRU 层
# input_size:输入特征的维度
# hidden_size:隐藏层的维度
# num_layers:堆叠的 GRU 层数
# batch_first:如果设置为 True,则输入和输出的批次维度为第一个维度
gru = nn.GRU(input_size=10, hidden_size=20, num_layers=2, batch_first=True)

参数

torch.nn.GRU 的主要参数包括:

  • input_size:输入特征的维度。
  • hidden_size:隐藏层中的特征数量。
  • num_layers(可选):GRU 层的堆叠数,默认为 1。
  • batch_first(可选):若设置为 True,则输入输出张量的形状为 (batch, seq, feature),默认为 False,即 (seq, batch, feature)
  • dropout(可选):如果堆叠了多个 GRU 层,则此参数为除最后一层外的每一层的 dropout 概率,默认为 0。
  • bidirectional(可选):若设置为 True,将会变成一个双向 GRU,默认为 False

输入和输出

GRU 层的输入和输出包括:

  • 输入:形状为 (seq_len, batch, input_size) 的张量,或者如果 batch_first=True,则为 (batch, seq_len, input_size)
  • 隐藏状态:形状为 (num_layers * num_directions, batch, hidden_size) 的张量。
  • 输出:包括每个序列步骤的输出特征(h_t),形状为 (seq_len, batch, num_directions * hidden_size),或者如果 batch_first=True,则为 (batch, seq_len, num_directions * hidden_size)

应用

GRU 由于其较为简单的结构,相比于 LSTM 有更少的参数,因此在某些情况下会更高效。它通常用于处理序列数据,如时间序列分析、文本处理、语音识别等任务中。GRU 能够有效地捕捉序列中的时间动态信息,并处理那些具有长距离依赖的数据。

与 LSTM 的比较

  • 结构简单:GRU 有两个门(更新门和重置门),而 LSTM 有三个门(遗忘门、输入门和输出门)。因此,GRU 的结构相对更简单,参数更少。
  • 性能:在很多任务中,GRU 和 LSTM 的性能相近。选择哪一种模型通常取决于特定任务的需求和数据的特性。

使用 GRU 时需要注意的一点是,虽然它在处理长期依赖方面比传统的 RNN 更有效,但在非常长的序列中仍然可能遇到困难。对于这些情况,可能需要考虑更复杂的结构,如 Transformer。

优化与调参

torch.optim.lr_scheduler调整学习率

torch.optim.lr_scheduler 提供了在 PyTorch 中调整学习率的方法。学习率调度器(Learning Rate Scheduler)在训练过程中根据特定策略调整学习率,这对于提高模型的训练效果和加快收敛速度是非常有用的。不同的调度器提供不同的学习率调整策略。

常用的学习率调度器

以下是一些在 PyTorch 中常见的学习率调度器:

  1. StepLR:在固定的周期(step)内以固定的因子降低学习率。

    scheduler = torch.optim.lr_scheduler.StepLR(optimizer, step_size=30, gamma=0.1)
    
  2. MultiStepLR:在预定义的若干个 epoch 下降学习率。

    scheduler = torch.optim.lr_scheduler.MultiStepLR(optimizer, milestones=[30,80], gamma=0.1)
    
  3. ExponentialLR:按指数衰减的方式调整学习率。

    scheduler = torch.optim.lr_scheduler.ExponentialLR(optimizer, gamma=0.9)
    
  4. ReduceLROnPlateau:当指标停止改进时降低学习率。

    scheduler = torch.optim.lr_scheduler.ReduceLROnPlateau(optimizer, 'min')
    
  5. CosineAnnealingLR:按余弦函数周期性调整学习率。

    scheduler = torch.optim.lr_scheduler.CosineAnnealingLR(optimizer, T_max=50, eta_min=0)
    

使用方式

调度器通常在每个训练周期(epoch)后更新学习率,示例如下:

for epoch in range(100):
    train(...)  # 训练过程
    validate(...)  # 验证过程
    scheduler.step()  # 更新学习率

对于 ReduceLROnPlateau 调度器,你需要在每个 epoch 后提供一个指标(如验证集损失):

for epoch in range(100):
    train(...)
    val_loss = validate(...)
    scheduler.step(val_loss)

注意事项

  • 在使用学习率调度器时,需要确保它与相应的优化器(optimizer)匹配。
  • 学习率调度策略应根据具体任务和模型的需要进行选择。
  • 对于某些调度器(如 ReduceLROnPlateau),你可能需要根据模型的性能指标来调整学习率。

学习率调度器是优化深度学习模型训练过程中

一个重要的组成部分,它可以帮助模型更快地收敛并提高最终性能。正确地使用学习率调度器可以在训练过程中动态地微调学习率,使得模型在初期快速学习,在后期稳定下来,以达到更好的训练效果。

torch.optim.RMSprop

torch.optim.RMSprop 是 PyTorch 中实现 RMSprop(Root Mean Square Propagation)优化算法的一个类。RMSprop 是一种自适应学习率方法,非常适合处理非平稳目标和很小的批量数据的训练。它是由 Geoff Hinton 提出的,旨在解决 Adagrad 方法在学习过程中学习率不断减小导致的学习停滞问题

RMSprop 通过保持一个移动(指数衰减)平均值来调整每个参数的学习率,从而使学习率在参数空间的不同方向上适应性调整。

基本用法

创建一个 RMSprop 优化器实例的基本方法如下:

import torch.optim as optim

optimizer = optim.RMSprop(model.parameters(), lr=0.01, alpha=0.99, eps=1e-08, weight_decay=0, momentum=0, centered=False)

其中,model.parameters() 是需要优化的参数(通常是某个模型的所有参数),lr 是学习率。

参数说明

  • params (iterable):待优化参数的迭代器,或定义参数组的字典。
  • lr (float):学习率(默认:1e-2)。
  • alpha (float):平滑常数(默认:0.99)。这个值用于计算平均平方梯度的指数衰减。
  • eps (float):为了提高数值稳定性而加到分母的项(默认:1e-8)。
  • weight_decay (float):权重衰减(L2惩罚)(默认:0)。
  • momentum (float):动量因子(默认:0)。
  • centered (bool):如果为 True,则计算中心化的二阶矩(默认:False)。

应用场景

RMSprop 优化器适用于处理非平稳目标和小批量数据的情况,经常被用于训练深度神经网络。与其他自适应学习率方法(如 Adam 或 Adagrad)相比,RMSprop 提供了一种不同的方式来调整每个参数的学习率,有助于在训练复杂的模型时加快收敛速度和提高性能。

其他

输入张量的维度一般是

在神经网络中,输入张量的维度通常取决于特定的应用场景和网络类型。以下是一些常见场景中输入张量维度的概述:

1. 全连接网络(Feedforward Neural Networks)

对于简单的全连接网络,输入通常是一维张量(即向量)。在这种情况下,张量的维度通常是 [batch_size, features],其中:

  • batch_size 是批次大小,表示一次处理多少个样本。
  • features 是每个输入样本的特征数量。

2. 卷积神经网络(Convolutional Neural Networks, CNNs)

对于处理图像的卷积神经网络,输入通常是四维张量。这些维度是 [batch_size, channels, height, width],其中:

  • batch_size 是批次大小。
  • channels 是输入图像的通道数(对于彩色图像通常是 3 — 红、绿、蓝;对于灰度图像是 1)。
  • heightwidth 是图像的高度和宽度。

3. 循环神经网络(Recurrent Neural Networks, RNNs)

对于处理序列数据(如文本或时间序列)的循环神经网络,输入通常是三维张量。这些维度是 [batch_size, sequence_length, features],其中:

  • batch_size 是批次大小。
  • sequence_length 是序列的长度。
  • features 是每个时间步的特征数量。

4. 自然语言处理(Natural Language Processing, NLP)

在 NLP 应用中,尤其是在使用词嵌入(word embeddings)时,输入张量通常也是三维的,其维度是 [batch_size, sequence_length, embedding_size],其中 embedding_size 是词嵌入的维度。

总结

输入张量的具体维度取决于处理的数据类型和所选用的神经网络架构。理解和正确设置这些维度对于构建有效的神经网络模型至关重要。

权重张量的维度一般是

权重张量的维度取决于所使用的神经网络层的类型和配置。不同类型的层有不同的权重维度要求。以下是一些常见神经网络层的权重张量维度:

1. 全连接层(Linear Layers)

全连接层(也称为线性层或密集层)的权重通常是二维张量。其维度是 [output_features, input_features],其中:

  • input_features 是输入特征的数量。
  • output_features 是输出特征的数量。

如果使用偏置项(bias),偏置是一个一维张量,其长度等于 output_features

2. 卷积层(Convolutional Layers)

卷积层的权重是四维张量,其维度是 [output_channels, input_channels, kernel_height, kernel_width],其中:

  • input_channels 是输入通道数。
  • output_channels 是输出通道数(也即卷积核的数量)。
  • kernel_heightkernel_width 是卷积核的高度和宽度。

对于 3D 卷积,还会有一个额外的维度表示卷积核的深度。

3. 循环神经网络层(RNN Layers)

对于循环神经网络(如 LSTM 或 GRU),权重张量的维度较为复杂,通常包含多个二维张量,分别对应于不同的门控制机制和状态。一般情况下,每个 RNN 单元都有两组权重:

  • 权重连接输入和隐藏层([hidden_size, input_size])。
  • 权重连接隐藏层的前一状态到当前状态([hidden_size, hidden_size])。

4. 词嵌入层(Embedding Layers)

词嵌入层的权重是二维张量,其维度是 [num_embeddings, embedding_dim],其中:

  • num_embeddings 是嵌入字典的大小(即词汇表的大小)。
  • embedding_dim 是嵌入的维度。

总结

不同类型的层有不同的权重维度配置,这些配置取决于层的作用和设计。正确理解和设置这些维度对于构建有效的神经网络模型非常重要。在实际应用中,这些维度通常在定义模型时通过层的构造函数参数指定。

torch.no_grad()

torch.no_grad() 是 PyTorch 中的一个上下文管理器,用于禁用在其作用域内的所有张量的梯度计算。这意味着在 torch.no_grad() 块内部执行的操作不会跟踪用于梯度计算的历史,也就是说,它们不会对反向传播产生影响。这样做的好处是减少内存消耗并提高计算速度。

使用场景

torch.no_grad() 主要用于以下场景:

  1. 进行推理(Inference):当你只需要进行前向传播(比如在模型评估或预测时),并不需要进行反向传播时,使用 torch.no_grad() 可以减少内存消耗并提高计算速度。

  2. 冻结部分模型参数:在微调预训练模型时,如果你只想更新部分参数,可以使用 torch.no_grad() 来冻结其他参数。

  3. 计算不需要梯度的量:例如,你可能想计算某些指标(比如准确率)而不影响梯度。

使用示例

以下是使用 torch.no_grad() 的一个简单示例:

import torch

model = ...  # 某个模型
inputs = ...  # 输入数据

# 使用 torch.no_grad() 进行推理
with torch.no_grad():
    outputs = model(inputs)
    # 在这里,outputs 的计算不会跟踪梯度

在这个例子中,模型对输入进行前向传播,但由于使用了 torch.no_grad(),所以这个操作不会在计算图中留下梯度信息,从而节省了内存,并可能提高了计算速度。

注意事项

  • 在训练模型时不应使用 torch.no_grad(),因为这会阻止 PyTorch 跟踪梯度,从而无法进行有效的模型优化。
  • 通常在模型评估和预测时使用 torch.no_grad(),以提高效率。
    数量。

如果使用偏置项(bias),偏置是一个一维张量,其长度等于 output_features

2. 卷积层(Convolutional Layers)

卷积层的权重是四维张量,其维度是 [output_channels, input_channels, kernel_height, kernel_width],其中:

  • input_channels 是输入通道数。
  • output_channels 是输出通道数(也即卷积核的数量)。
  • kernel_heightkernel_width 是卷积核的高度和宽度。

对于 3D 卷积,还会有一个额外的维度表示卷积核的深度。

3. 循环神经网络层(RNN Layers)

对于循环神经网络(如 LSTM 或 GRU),权重张量的维度较为复杂,通常包含多个二维张量,分别对应于不同的门控制机制和状态。一般情况下,每个 RNN 单元都有两组权重:

  • 权重连接输入和隐藏层([hidden_size, input_size])。
  • 权重连接隐藏层的前一状态到当前状态([hidden_size, hidden_size])。

4. 词嵌入层(Embedding Layers)

词嵌入层的权重是二维张量,其维度是 [num_embeddings, embedding_dim],其中:

  • num_embeddings 是嵌入字典的大小(即词汇表的大小)。
  • embedding_dim 是嵌入的维度。

总结

不同类型的层有不同的权重维度配置,这些配置取决于层的作用和设计。正确理解和设置这些维度对于构建有效的神经网络模型非常重要。在实际应用中,这些维度通常在定义模型时通过层的构造函数参数指定。

torch.no_grad()

torch.no_grad() 是 PyTorch 中的一个上下文管理器,用于禁用在其作用域内的所有张量的梯度计算。这意味着在 torch.no_grad() 块内部执行的操作不会跟踪用于梯度计算的历史,也就是说,它们不会对反向传播产生影响。这样做的好处是减少内存消耗并提高计算速度。

使用场景

torch.no_grad() 主要用于以下场景:

  1. 进行推理(Inference):当你只需要进行前向传播(比如在模型评估或预测时),并不需要进行反向传播时,使用 torch.no_grad() 可以减少内存消耗并提高计算速度。

  2. 冻结部分模型参数:在微调预训练模型时,如果你只想更新部分参数,可以使用 torch.no_grad() 来冻结其他参数。

  3. 计算不需要梯度的量:例如,你可能想计算某些指标(比如准确率)而不影响梯度。

使用示例

以下是使用 torch.no_grad() 的一个简单示例:

import torch

model = ...  # 某个模型
inputs = ...  # 输入数据

# 使用 torch.no_grad() 进行推理
with torch.no_grad():
    outputs = model(inputs)
    # 在这里,outputs 的计算不会跟踪梯度

在这个例子中,模型对输入进行前向传播,但由于使用了 torch.no_grad(),所以这个操作不会在计算图中留下梯度信息,从而节省了内存,并可能提高了计算速度。

注意事项

  • 在训练模型时不应使用 torch.no_grad(),因为这会阻止 PyTorch 跟踪梯度,从而无法进行有效的模型优化。
  • 通常在模型评估和预测时使用 torch.no_grad(),以提高效率。
  • 13
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

knighthood2001

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值