全面PyTorch深度学习框架资源包

本文还有配套的精品资源,点击获取 menu-r.4af5f7ec.gif

简介:PyTorch是一个由Facebook AI研究团队开发的灵活、易用的开源深度学习框架。本资源包涵盖了PyTorch的核心特性,包括张量操作、自动求导、神经网络构建、优化器使用、数据加载、模型保存与加载、动态计算图、模型并行与数据并行、torchvision库的使用、自定义模块、TorchScript以及分布式训练等。这些知识点不仅为初学者提供了一个全面的学习资源,而且对进阶者也有深入理解和掌握PyTorch框架的帮助。 pytorch资源.zip

1. PyTorch简介及其核心特性

在深度学习领域,PyTorch已经成为研究人员和开发者的首选框架之一,它以动态计算图和易用性著称。在本章中,我们将介绍PyTorch的基本概念,并探讨其核心特性,为构建和训练深度神经网络打下坚实的基础。

1.1 PyTorch概述

PyTorch是一个开源的机器学习库,最初由Facebook的人工智能研究小组开发,广泛应用于计算机视觉、自然语言处理等领域的研究和产品开发。它允许开发者以直观的方式进行张量运算,并提供了动态计算图机制,使得模型的构建和调试更加灵活。

1.2 动态计算图与易用性

动态计算图是PyTorch的核心特性之一,它允许开发者在运行时动态构建计算图,从而轻松实现复杂的神经网络结构。这一特性相较于静态计算图框架如TensorFlow提供了更高的灵活性,但以牺牲一些性能为代价。

1.3 社区与生态系统

PyTorch拥有一个活跃的社区,提供大量的文档、教程、以及研究论文的实现。社区的活跃度保证了框架的持续更新和大量第三方库的支持,例如 torchvision torchaudio 等。这些库使得处理图像、视频、音频和文本数据变得更加容易。

通过掌握PyTorch的这些核心特性,开发者可以更加高效地在研究和开发中运用深度学习模型。接下来的章节,我们将深入探讨PyTorch如何实现张量操作、自动求导、构建神经网络等关键功能。

2. 张量操作和GPU支持

2.1 张量基础

2.1.1 张量的创建和属性

张量是PyTorch中用于表示多维数组的基石,类似于NumPy中的数组(ndarray),但张量可以在GPU上运行以加速计算。我们可以通过多种方式创建张量,包括直接初始化、转换现有数据结构和基于现有张量进行操作。

创建张量的代码示例:

import torch

# 从数据创建一个标量张量
scalar = torch.tensor(5)

# 创建一个向量
vector = torch.tensor([1, 2, 3])

# 创建一个矩阵
matrix = torch.tensor([[1, 2], [3, 4]])

# 创建一个三维张量
tensor_3d = torch.tensor([[[1, 2], [3, 4]], [[5, 6], [7, 8]]])

# 张量属性包含其数据类型、维度等
print(f"Data type of scalar: {scalar.dtype}")
print(f"Shape of vector: {vector.shape}")
print(f"Size of matrix: {matrix.size()}")
print(f"Number of dimensions in tensor_3d: {tensor_3d.ndim}")

在上述代码中,我们使用 torch.tensor 函数创建了不同类型的张量,并检查了它们的属性。张量的属性对于理解张量的结构以及后续进行索引和切片操作非常关键。

2.1.2 张量的运算

张量支持多种运算,包括基本的算数运算、矩阵运算和向量运算等。通过这些运算可以执行复杂的数学表达式,例如点积、叉积、张量乘法等。

张量运算的代码示例:

# 张量运算示例
a = torch.tensor([1, 2, 3])
b = torch.tensor([4, 5, 6])

# 点积
dot_product = torch.dot(a, b)

# 张量加法
addition = a + b

# 张量乘法
multiplication = a * b

# 张量的矩阵乘法
matrix_a = torch.tensor([[1, 2], [3, 4]])
matrix_b = torch.tensor([[5, 6], [7, 8]])
matrix_multiplication = torch.matmul(matrix_a, matrix_b)

print(f"Dot product of a and b: {dot_product}")
print(f"Addition of a and b: {addition}")
print(f"Element-wise multiplication of a and b: {multiplication}")
print(f"Matrix multiplication of matrix_a and matrix_b: \n{matrix_multiplication}")

在这些代码段中,我们展示了张量的基础运算以及矩阵乘法的操作。理解这些基本运算对于进一步学习深度学习中的高级操作至关重要。

2.2 GPU加速计算

2.2.1 检测和使用GPU

PyTorch提供了强大的GPU支持,可以在NVIDIA的CUDA兼容GPU上进行加速计算。首先我们需要检测系统中是否有可用的GPU,并学习如何将张量和模型部署到GPU上。

检测和使用GPU的代码示例:

# 检测可用的GPU数量
if torch.cuda.is_available():
    print(f"GPU available: {torch.cuda.device_count()}")
    print("Current GPU: ", torch.cuda.current_device())
else:
    print("CUDA is not available on your system.")

# 将张量移动到GPU
tensor_on_gpu = tensor.to('cuda')

# 检查张量是否在GPU上
print(f"Tensor is on GPU: {tensor_on_gpu.is_cuda}")

# 将模型部署到GPU(假设我们有一个model对象)
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
model.to(device)

在以上代码中,我们首先检查了GPU是否可用,并展示了如何将一个张量移动到GPU上。同样的原则适用于神经网络模型,这使得模型能够利用GPU进行训练和推断,大大提高了计算效率。

2.2.2 张量与模型的GPU迁移

一旦模型或数据被移动到GPU上,所有的后续操作都将自动在GPU上执行,无需额外的代码修改。但是,在处理多GPU的情况时,有时需要显式地将数据在不同GPU之间进行传输。

张量与模型的GPU迁移的代码示例:

# 假设我们有多个GPU
gpus = [torch.device(f'cuda:{i}') for i in range(torch.cuda.device_count())]

# 将张量在不同GPU间迁移
tensor_on_gpu0 = tensor_on_gpu.to(gpus[0])
tensor_on_gpu1 = tensor_on_gpu.to(gpus[1])

# 模型的分布式数据并行处理
model = torch.nn.DataParallel(model, device_ids=gpus)
model.to(gpus[0])

# 模型的多GPU部署示例
model = torch.nn.parallel.DistributedDataParallel(model, device_ids=gpus)
model.to(gpus[0])

在这些代码段中,我们展示了如何在多个GPU之间迁移张量以及如何通过 DataParallel DistributedDataParallel 模块将模型部署到多个GPU上进行训练。

在上述章节中,我们介绍了PyTorch中张量的创建和属性,张量的基本运算,以及如何利用GPU进行加速计算。这为后续章节中更高级的深度学习概念和操作打下了坚实的基础。下一章节将探索PyTorch的自动求导系统和反向传播算法,它们是深度学习中不可或缺的两个组成部分。

3. 自动求导系统与反向传播

自动求导是深度学习框架的核心特性之一,它能够自动计算导数,极大地简化了神经网络的训练过程。本章节将深入探讨PyTorch中的自动求导系统,包括计算图的构建和梯度的计算,以及反向传播算法的实现和自定义梯度的计算。

3.1 自动求导机制

3.1.1 计算图与梯度

PyTorch采用动态计算图(Dynamic Computation Graphs),这种设计使得网络的构建更加灵活。计算图是一个有向无环图(DAG),其中的节点代表张量(tensor),边代表操作。在执行计算时,PyTorch会构建计算图,并根据链式法则自动计算导数。

import torch

# 创建张量
x = torch.tensor(1.0, requires_grad=True)
y = torch.tensor(2.0, requires_grad=True)

# 进行运算
z = x ** 2 + y ** 3

# 计算z关于x和y的导数
z.backward()

# 输出梯度
print("dz/dx:", x.grad)  # dz/dx: 2.0
print("dz/dy:", y.grad)  # dz/dy: 12.0

在上述代码中,我们创建了两个需要梯度的张量 x y ,然后定义了一个关于 x y 的计算表达式 z 。通过调用 backward() 方法,PyTorch会自动计算并存储 x y 关于 z 的梯度。

3.1.2 梯度计算与内存管理

在使用自动求导时,内存管理和梯度清零是非常重要的概念。 requires_grad=True 标记的张量在进行反向传播后,梯度会被累加到 .grad 属性中,而不是覆盖。因此,经常需要手动清除这些梯度以避免不必要的内存占用。

# 清除梯度
x.grad.zero_()
y.grad.zero_()

梯度清零后,再次调用 backward() 时,会从零开始累加梯度。

3.2 反向传播算法

3.2.1 反向传播流程

反向传播是通过链式法则来计算每个参数相对于损失函数的梯度的过程。在PyTorch中,这个过程是自动完成的。对于模型中的每个参数,可以通过调用 .backward() 来计算它对最终损失的梯度。

# 定义网络层
layer = torch.nn.Linear(10, 5)

# 前向传播
input = torch.randn(1, 10)
output = layer(input)

# 计算损失
loss = torch.nn.functional.mse_loss(output, torch.zeros(1, 5))

# 反向传播
loss.backward()

# 现在layer.weight的梯度已经计算完毕,可以用于参数更新

在上述代码中,我们进行了一次前向传播,并计算了损失。调用 backward() 后,PyTorch计算了损失函数关于网络参数的梯度,这对于后续的优化步骤至关重要。

3.2.2 自定义梯度计算

有时候,标准操作的梯度计算可能不满足需求,此时可以使用 torch.autograd.Function 来自定义梯度计算。自定义梯度计算需要实现 forward backward 两个方法。

class MyReLU(torch.autograd.Function):
    @staticmethod
    def forward(ctx, input):
        # 保存上下文信息
        ctx.save_for_backward(input)
        return input.clamp(min=0)

    @staticmethod
    def backward(ctx, grad_output):
        # 获取保存的信息
        input, = ctx.saved_tensors
        grad_input = grad_output.clone()
        grad_input[input < 0] = 0
        return grad_input

# 使用自定义的ReLU函数
custom_relu = MyReLU.apply

# 前向传播
input = torch.randn(1, 10, requires_grad=True)
output = custom_relu(input)

# 反向传播
output.backward(torch.ones_like(output))

# 此时input的梯度已经根据自定义的ReLU梯度计算逻辑得出

在这个例子中,我们定义了一个 MyReLU 类,它在前向传播时应用ReLU函数,在反向传播时根据ReLU的梯度公式计算梯度。通过这种方式,我们可以灵活地控制梯度计算的过程。

通过本章的介绍,您应该对PyTorch的自动求导系统有了更深入的了解,包括如何利用计算图自动计算导数,以及如何自定义梯度计算。下一章,我们将探讨如何构建复杂的神经网络模型,以及如何使用PyTorch提供的模块和组件来实现这些任务。

4. 神经网络构建与NN模块

4.1 神经网络层与块

4.1.1 顺序模型与模块化设计

在PyTorch中,构建神经网络的一个核心组件是 torch.nn.Module 。所有的网络层和模型都是继承自这个基类。模块化设计允许开发者构建复杂的神经网络架构,其中网络层被组织为模块,而这些模块又可以被嵌套进更大的模块中。这种设计模式使得代码易于理解和维护,同时也易于在不同的网络之间复用这些模块。

顺序模型是最简单的神经网络结构之一,它由一系列线性顺序的模块组成。PyTorch中的 torch.nn.Sequential 类就是用来实现这种顺序模型的。顺序模型的优点是简单直观,而且它的构建和使用都非常方便。

下面是一个简单的顺序模型示例:

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

# 定义一个简单的顺序模型
class SimpleSequentialNet(nn.Module):
    def __init__(self):
        super(SimpleSequentialNet, self).__init__()
        # 添加顺序模块
        self.layers = nn.Sequential(
            nn.Linear(784, 128),  # 输入特征到第一个隐藏层
            nn.ReLU(),
            nn.Linear(128, 64),
            nn.ReLU(),
            nn.Linear(64, 10)    # 最后一层是输出层
        )

    def forward(self, x):
        # 定义数据的前向传播路径
        x = x.view(x.size(0), -1)  # 将输入数据展平
        x = self.layers(x)         # 通过定义好的顺序模块
        return F.log_softmax(x, dim=1)  # 输出层使用log_softmax

# 实例化模型并创建输入数据
model = SimpleSequentialNet()
input_data = torch.randn(100, 1, 28, 28)  # 一个batch的100张28x28图像

在这个例子中, SimpleSequentialNet 类通过 nn.Sequential 定义了一个包含三个全连接层的简单神经网络。 forward 方法定义了输入数据如何通过这个模型。

4.1.2 常见层的使用与配置

在PyTorch中,开发者可以使用不同的层来构建复杂的神经网络。以下是一些最常用的层及其配置方式的示例:

  • 全连接层( nn.Linear
  • 卷积层( nn.Conv2d
  • 池化层( nn.MaxPool2d
  • 循环神经网络层( nn.RNN , nn.LSTM , nn.GRU
  • 正则化层(如 nn.Dropout
  • 归一化层(如 nn.BatchNorm2d

例如,一个全连接层可以像这样定义和使用:

# 定义一个全连接层
fc_layer = nn.Linear(in_features=256, out_features=10)

# 使用全连接层对数据进行处理
output = fc_layer(input_data)

在这个例子中, fc_layer 是一个全连接层,它将256维的输入映射到10维的输出。当传入输入数据 input_data 时,全连接层将进行矩阵乘法,并根据需要添加偏置项。

卷积层的使用稍微复杂一些,因为它涉及到特征图(feature maps)的概念。下面是一个卷积层的定义和应用:

# 定义一个卷积层
conv_layer = nn.Conv2d(in_channels=3, out_channels=16, kernel_size=3, stride=1, padding=1)

# 使用卷积层处理输入图像
conv_output = conv_layer(input_data)

在这个例子中, conv_layer 是一个卷积层,它有3个输入通道,16个输出通道,核大小为3x3,步长为1,并在边缘进行了1个像素的填充。当传入输入数据 input_data 时,卷积层将应用指定的卷积核来提取特征。

在实际应用中,开发者需要根据具体任务的需求来选择和配置这些层。例如,在图像处理任务中可能会使用更多的卷积层和池化层,而在处理序列数据时,循环层或一维卷积层会是更好的选择。

4.2 模块与序列化

4.2.1 自定义模块的实现

在构建神经网络时,开发者经常需要实现自定义的模块以满足特定的需求。自定义模块通常继承自 nn.Module ,并需要实现 __init__ forward 方法。

下面是一个自定义模块的简单例子,它实现了一个简单的全连接层,其中包含正则化功能:

class CustomLinear(nn.Module):
    def __init__(self, in_features, out_features, bias=True, dropout=0.5):
        super(CustomLinear, self).__init__()
        self.linear = nn.Linear(in_features, out_features, bias=bias)
        self.dropout = nn.Dropout(dropout)

    def forward(self, x):
        x = self.linear(x)
        x = self.dropout(x)
        return x

# 使用自定义模块
custom_layer = CustomLinear(in_features=256, out_features=10)
output = custom_layer(input_data)

在这个例子中, CustomLinear 类继承自 nn.Module 。它在 __init__ 方法中定义了一个全连接层和一个Dropout层,而在 forward 方法中实现了数据的前向传播流程。

4.2.2 模块的保存和加载

模型训练完成后,经常需要保存模型的参数,以便后续加载和使用。PyTorch通过 torch.save 函数支持模型的保存,而通过 torch.load 支持模型的加载。模型的保存和加载对于模型部署和迁移学习尤为重要。

保存模型参数的代码示例如下:

# 保存模型
torch.save(model.state_dict(), 'model.pth')

加载模型参数的代码示例如下:

# 加载模型
model = SimpleSequentialNet()
model.load_state_dict(torch.load('model.pth'))

在这段代码中, torch.save 函数用于将模型 model 的状态字典(包含所有参数)保存到磁盘上的 model.pth 文件中。而 torch.load 函数则用于从磁盘加载模型参数,并将其赋值给 model 实例。

模块保存时,并不包括模型的结构信息,只包括参数。如果需要保存包括结构在内的完整信息,可以使用 torch.save 来保存整个模型:

# 保存整个模型
torch.save(model, 'model_full.pth')

加载整个模型的代码如下:

# 加载整个模型
model = torch.load('model_full.pth')

保存整个模型的好处是不需要重新定义模型结构,可以直接加载模型及其结构。这对于模型部署以及在不同设备间迁移模型非常有用。

4.2.3 自定义模块和序列化的工作流程图

graph LR
    A[开始] --> B[定义自定义模块]
    B --> C[实现__init__和forward方法]
    C --> D[实例化模型]
    D --> E[训练模型]
    E --> F[保存模型参数]
    E --> G[保存整个模型]
    F --> H[加载模型参数]
    G --> I[加载整个模型]
    H --> J[结束]
    I --> J

通过以上流程图,我们描绘了从定义自定义模块开始,经过训练模型,到保存和加载模型参数或整个模型的完整流程。这有助于理解自定义模块的实现和序列化过程中的关键步骤。

5. 优化算法与权重更新

在深度学习中,模型训练的核心是优化算法。这些算法用于调整模型权重,以减少模型在训练数据上的预测误差。本章将详细介绍优化算法的选择、配置,以及如何通过权重更新机制来提高模型的泛化能力。

5.1 优化算法详解

5.1.1 常用优化器的选择与配置

优化器是梯度下降算法的扩展,它根据损失函数相对于模型参数的梯度来更新这些参数。PyTorch内置了多种优化器,例如SGD(随机梯度下降)、Adam、RMSprop等。每种优化器都有其特定的超参数,这些超参数需要仔细调整才能获得最佳性能。

import torch.optim as optim

# 定义模型参数和损失函数
model = ...
criterion = ...

# 选择并配置SGD优化器
optimizer = optim.SGD(model.parameters(), lr=0.01, momentum=0.9)

# 选择并配置Adam优化器
optimizer = optim.Adam(model.parameters(), lr=0.001, betas=(0.9, 0.999), eps=1e-08)

在上述代码中, lr 是学习率,它控制着权重更新的幅度。 momentum betas eps 是其他优化器特有的超参数,它们有助于加速训练并减少震荡。

5.1.2 学习率调度策略

在训练过程中,静态的学习率设置往往不是最优的。学习率调度策略能够在训练的不同阶段动态调整学习率,以获得更好的性能。

# 使用StepLR学习率调度器
scheduler = optim.lr_scheduler.StepLR(optimizer, step_size=30, gamma=0.1)

# 在每个epoch后更新学习率
for epoch in range(num_epochs):
    ...
    scheduler.step()

在上面的例子中, StepLR 调度器会在每个 step_size 指定的周期性间隔后,将学习率乘以 gamma 。这有利于在训练开始时快速下降损失,在后期阶段进行精细调整。

5.2 权重更新机制

5.2.1 参数更新方法

权重更新是通过结合当前参数值、计算出的梯度以及学习率来实现的。PyTorch中的优化器封装了这一更新过程,但也可以自定义更新步骤。

# 手动更新参数
for group in optimizer.param_groups:
    for p in group['params']:
        if p.grad is not None:
            # 使用SGD更新规则
            d_p = p.grad.data
            p.data.add_(-group['lr'], d_p)

在手动更新参数的代码块中,我们首先检查梯度是否存在,然后根据SGD规则对参数进行更新。这种方式提供了对更新过程更细致的控制。

5.2.2 梯度裁剪与正则化

梯度裁剪是一种防止训练过程中出现梯度爆炸的技术,而正则化如L2惩罚则有助于防止模型过拟合。

# 梯度裁剪
torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm=1.0)

# 添加L2正则化
for param in model.parameters():
    param.grad.data.add_(reg * param.data)

在这里, clip_grad_norm_ 函数限制了模型参数梯度的范数,而L2正则化通过增加原始梯度来实现。 reg 是一个超参数,用于控制正则化项的权重。

权重更新是深度学习训练过程中的关键环节。通过理解和掌握优化算法、学习率调度策略、参数更新方法以及梯度裁剪和正则化的应用,可以有效提高模型的性能。本章所介绍的技巧和概念对于确保训练过程的稳定性和收敛性至关重要,并能够使您在面对复杂模型时更加自信和熟练。

6. 数据加载与批量处理

在深度学习项目中,数据的加载和处理方式直接影响着模型训练的效率和效果。合理组织和批量处理数据能够帮助提高模型训练速度,同时减少内存消耗。本章将深入探讨PyTorch中数据加载工具的使用,以及在训练过程中如何进行有效的批量处理。

6.1 数据加载工具

数据加载工具是实现高效数据处理的关键组件。PyTorch提供了 Dataset DataLoader 两个核心类,使得自定义数据加载变得简单而强大。

6.1.1 Dataset与DataLoader的使用

Dataset 类是所有数据集的抽象基类,用户需要继承这个类并实现 __len__ __getitem__ 两个方法来自定义数据集。

from torch.utils.data import Dataset

class MyDataset(Dataset):
    def __init__(self, data, targets):
        self.data = data
        self.targets = targets
    def __len__(self):
        return len(self.data)
    def __getitem__(self, idx):
        return self.data[idx], self.targets[idx]

DataLoader 为数据集提供了一个可迭代的接口,支持数据的批量加载、打乱、多线程加载等功能。

from torch.utils.data import DataLoader

dataset = MyDataset(data, targets)
dataloader = DataLoader(dataset, batch_size=32, shuffle=True, num_workers=4)
  • batch_size 指定了每个批次加载的数据数量。
  • shuffle=True 表示在每个epoch开始时打乱数据顺序。
  • num_workers 表示使用多少个子进程来加载数据,通过多线程可以加快数据的加载速度。

6.1.2 图像、文本数据的特殊处理

对于图像数据, torchvision.transforms 提供了丰富的图像预处理方法。对于文本数据,可以使用 torchtext 库来处理。

from torchvision import transforms

image_transforms = ***pose([
    transforms.Resize((224, 224)),
    transforms.ToTensor(),
    transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])
])

# 假设我们有一个图像路径列表
image_paths = ['path/to/image1.jpg', 'path/to/image2.png']
image_dataset = torchvision.datasets.ImageFolder(root='path/to/images', transform=image_transforms)
image_dataloader = DataLoader(image_dataset, batch_size=32, shuffle=True)

对于文本数据,可以使用 torchtext 库进行分词、构建词汇表、编码等操作。

6.2 批量处理技巧

批量处理是提高深度学习训练速度的关键步骤,它能显著减少模型更新参数的次数,从而降低计算资源消耗。

6.2.1 批量化训练的优势

批量化训练可以提升内存和计算资源的利用率。通过一次性向模型输入多个样本,我们能有效利用现代GPU的并行计算能力。

6.2.2 动态批量大小调整

在某些情况下,可能需要动态调整批量大小。例如,在训练初期使用较小的批量大小以保证收敛,在训练后期使用较大的批量大小来加速训练。

batch_size = 32  # 初始批量大小
for epoch in range(num_epochs):
    if epoch > start_epoch:  # 假设从某个epoch开始增加批量大小
        batch_size += delta_batch_size
    dataloader = DataLoader(dataset, batch_size=batch_size, shuffle=True)
    for data, target in dataloader:
        # 训练逻辑

在上述代码中, delta_batch_size 指定了批量大小的增量,而 start_epoch 指定了开始调整批量大小的epoch。

批量处理是深度学习中一项重要的技术,它不仅可以提高训练效率,还可以通过调整批量大小来平衡模型训练和性能之间的关系。在后续章节中,我们将进一步探讨如何将这些知识应用到实际的深度学习项目中。

本文还有配套的精品资源,点击获取 menu-r.4af5f7ec.gif

简介:PyTorch是一个由Facebook AI研究团队开发的灵活、易用的开源深度学习框架。本资源包涵盖了PyTorch的核心特性,包括张量操作、自动求导、神经网络构建、优化器使用、数据加载、模型保存与加载、动态计算图、模型并行与数据并行、torchvision库的使用、自定义模块、TorchScript以及分布式训练等。这些知识点不仅为初学者提供了一个全面的学习资源,而且对进阶者也有深入理解和掌握PyTorch框架的帮助。

本文还有配套的精品资源,点击获取 menu-r.4af5f7ec.gif

  • 4
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值