Python精选200Tips:176-180

运行系统:macOS Sequoia 15.0
Python编译器:PyCharm 2024.1.4 (Community Edition)
Python版本:3.12
TensorFlow版本:2.17.0
Pytorch版本:2.4.1

往期链接:

1-56-1011-2021-3031-4041-50
51-60:函数61-70:类71-80:编程范式及设计模式
81-90:Python编码规范91-100:Python自带常用模块-1
101-105:Python自带模块-2106-110:Python自带模块-3
111-115:Python常用第三方包-频繁使用116-120:Python常用第三方包-深度学习
121-125:Python常用第三方包-爬取数据126-130:Python常用第三方包-为了乐趣
131-135:Python常用第三方包-拓展工具1136-140:Python常用第三方包-拓展工具2

Python项目实战

141-145146-150151-155156-160161-165166-170171-175

P176–LeNet-5【1988】

LeNet-5 是一种经典的卷积神经网络(CNN)架构,由 Yann LeCun 等人在 1998 年提出。它最初是为手写数字识别任务(如 MNIST 数据集)设计的,但其架构已成为许多后续深度学习模型的基础。

模型结构说明

LeNet-5 由以下几个主要层组成:

  • 输入层:28x28 像素的灰度图像。
  • 卷积层 1 (C1):使用 6 个 5x5 的卷积核,步长为 1,输出 6 个 24x24 的特征图。
  • 池化层 1 (S2):使用 2x2 的平均池化,步长为 2,输出 6 个 12x12 的特征图。
  • 卷积层 2 (C3):使用 16 个 5x5 的卷积核,输出 16 个 8x8 的特征图。
  • 池化层 2 (S4):使用 2x2 的平均池化,步长为 2,输出 16 个 4x4 的特征图。
  • 全连接层 1 (C5):将 16 个 4x4 的特征图展平为一个 400 维的向量,并与 120 个神经元全连接。
  • 全连接层 2 (F6):与 84 个神经元全连接。
  • 输出层:使用 Softmax 激活函数输出 10 个类别(0-9 的手写数字)。

模型结构代码

import matplotlib.pyplot as plt
from keras.utils import plot_model
from tensorflow.keras import layers, models

# 定义 LeNet-5 模型
def create_lenet5():
    model = models.Sequential()
    model.add(layers.Conv2D(6, (5, 5), activation='tanh', input_shape=(28, 28, 1)))
    model.add(layers.AveragePooling2D(pool_size=(2, 2)))  # 添加 pool_size 参数
    model.add(layers.Conv2D(16, (5, 5), activation='tanh'))
    model.add(layers.AveragePooling2D(pool_size=(2, 2)))  # 添加 pool_size 参数
    model.add(layers.Conv2D(120, (5, 5), activation='tanh'))
    model.add(layers.Flatten())
    model.add(layers.Dense(84, activation='tanh'))
    model.add(layers.Dense(10, activation='softmax'))
    return model

# 创建模型
model = create_lenet5()

# 保存模型结构
plot_model(model, to_file='lenet5_model.png', show_shapes=True, show_layer_names=True)

# 显示可视化结果
img = plt.imread('lenet5_model.png')
plt.figure(figsize=(10, 10))
plt.title('lenet5')
plt.imshow(img)
plt.axis('off')
plt.show()

模型结构可视化

在这里插入图片描述

P177–AlexNet【2012】

AlexNet 是一种深度卷积神经网络(CNN),由 Alex Krizhevsky、Ilya Sutskever 和 Geoffrey Hinton 在 2012 年提出。它在 ImageNet 竞赛中取得了显著的成功,推动了深度学习在计算机视觉领域的广泛应用。

模型结构及创新性说明

AlexNet 的架构如下所示:

  • 输入层:224x224 像素的 RGB 图像。
  • 卷积层 1 (Conv1):使用 96 个 11x11 的卷积核,步长为 4。
  • 激活层 1 (ReLU):应用 ReLU 激活函数。
  • 池化层 1 (Max Pooling):使用 3x3 的最大池化,步长为 2。
  • 卷积层 2 (Conv2):使用 256 个 5x5 的卷积核。
  • 激活层 2 (ReLU):应用 ReLU 激活函数。
  • 池化层 2 (Max Pooling):使用 3x3 的最大池化,步长为 2。
  • 卷积层 3 (Conv3):使用 384 个 3x3 的卷积核。
  • 激活层 3 (ReLU):应用 ReLU 激活函数。
  • 卷积层 4 (Conv4):使用 384 个 3x3 的卷积核。
  • 激活层 4 (ReLU):应用 ReLU 激活函数。
  • 卷积层 5 (Conv5):使用 256 个 3x3 的卷积核。
  • 激活层 5 (ReLU):应用 ReLU 激活函数。
  • 池化层 3 (Max Pooling):使用 3x3 的最大池化,步长为 2。
  • 全连接层 1 (FC1):与 4096 个神经元全连接。
  • 激活层 6 (ReLU):应用 ReLU 激活函数。
  • Dropout 层 1:以 50% 的概率随机丢弃神经元。
  • 全连接层 2 (FC2):与 4096 个神经元全连接。
  • 激活层 7 (ReLU):应用 ReLU 激活函数。
  • Dropout 层 2:以 50% 的概率随机丢弃神经元。
  • 全连接层 3 (FC3):与 1000 个神经元全连接(对应 1000 个类别)。
  • 输出层:使用 Softmax 激活函数。

创新性
AlexNet 模型的创新性体现在多个方面,以下是其关键特点:

  1. 深层网络结构
    深度卷积网络:相比之前的浅层网络,AlexNet 采用了更深的结构,包含多个卷积层和全连接层,能够学习更复杂的特征。
  2. 激活函数的创新
    ReLU 激活函数:引入了 ReLU(修正线性单元)作为激活函数,解决了传统激活函数(如 Sigmoid 和 Tanh)在深层网络中造成的梯度消失问题,加速了训练过程。
  3. 数据增强和正则化
    数据增强:通过随机裁剪、翻转和颜色变换等方法扩充训练数据,增强模型的泛化能力。
  4. Dropout的加入
    在全连接层中使用 Dropout 技术,随机丢弃一部分神经元,减少过拟合,提高模型的鲁棒性。

模型结构代码

import matplotlib.pyplot as plt
from keras.utils import plot_model
from tensorflow.keras import layers, models

# macos系统显示中文
plt.rcParams['font.sans-serif'] = ['Arial Unicode MS']

# 定义 alexnet 模型
def create_alexnet(input_shape=(224, 224, 3), num_classes=1000):
    model = models.Sequential()

    # 卷积层1 激活函数ReLU 池化层1
    model.add(layers.Conv2D(96, kernel_size=(11, 11), strides=(4, 4), activation='relu', input_shape=input_shape))
    model.add(layers.MaxPooling2D(pool_size=(3, 3), strides=(2, 2)))

    # 卷积层2 激活函数ReLU 最大池化层2
    model.add(layers.Conv2D(256, kernel_size=(5, 5), activation='relu'))
    model.add(layers.MaxPooling2D(pool_size=(3, 3), strides=(2, 2)))

    # 卷积层3 激活函数ReLU
    model.add(layers.Conv2D(384, kernel_size=(3, 3), activation='relu'))

    # 卷积层4 激活函数ReLU
    model.add(layers.Conv2D(384, kernel_size=(3, 3), activation='relu'))

    # 卷积层5 激活函数ReLU 最大池化层5
    model.add(layers.Conv2D(256, kernel_size=(3, 3), activation='relu'))
    model.add(layers.MaxPooling2D(pool_size=(3, 3), strides=(2, 2)))

    # 展平层,为了后面的全连接层
    model.add(layers.Flatten())

    # 全连接层 激活函数ReLU,Dropout概率0.5
    model.add(layers.Dense(4096, activation='relu'))
    model.add(layers.Dropout(0.5))

    # 全连接层 Dropout概率0.5
    model.add(layers.Dense(4096, activation='relu'))
    model.add(layers.Dropout(0.5))

    # 全连接层,激活函数Softmax
    model.add(layers.Dense(num_classes, activation='softmax'))

    return model


# 创建 AlexNet 模型
model = create_alexnet()
model.summary()

# 保存模型结构
plot_model(model, to_file='AlexNet_model.png', show_shapes=True, show_layer_names=True)

# 显示可视化结果
img = plt.imread('AlexNet_model.png')
plt.figure(figsize=(10, 10))
plt.title('AlexNet 结构可视化')
plt.imshow(img)
plt.axis('off')
plt.show()

模型结构可视化

在这里插入图片描述

P178–VGGNet【2014】

VGGNet 是由牛津大学视觉几何组(Visual Geometry Group)在 2014 年提出的卷积神经网络(CNN)架构。VGGNet 在 ImageNet 大规模视觉识别挑战赛中取得了优异的成绩,并以其简单而有效的设计理念而闻名。VGGNet 有多个变体,最常用的包括 VGG16 和 VGG19,数字表示网络中的层数。本文以VGG19为例:

VGG19模型结构及创新性说明

  • 输入层:输入图像尺寸:224x224 像素,RGB 图像。
  • Conv Block 1:
    • Conv1-1: 3x3卷积,64个滤波器
    • Conv1-2: 3x3卷积,64个滤波器
    • Max Pooling: 2x2
  • Conv Block 2:
    • Conv2-1: 3x3卷积,128个滤波器
    • Conv2-2: 3x3卷积,128个滤波器
    • Max Pooling: 2x2
  • Conv Block 3:
    • Conv3-1: 3x3卷积,256个滤波器
    • Conv3-2: 3x3卷积,256个滤波器
    • Conv3-3: 3x3卷积,256个滤波器
    • Max Pooling: 2x2
  • Conv Block 4:
    • Conv4-1: 3x3卷积,512个滤波器
    • Conv4-2: 3x3卷积,512个滤波器
    • Conv4-3: 3x3卷积,512个滤波器
    • Max Pooling: 2x2
  • Conv Block 5:
    • Conv5-1: 3x3卷积,512个滤波器
    • Conv5-2: 3x3卷积,512个滤波器
    • Conv5-3: 3x3卷积,512个滤波器
    • Max Pooling: 2x2
  • 展开层
  • 全连接层:
    • FC1: 4096个神经元,ReLU 激活
    • FC2: 4096个神经元,ReLU 激活
    • FC3: 1000个神经元(对应 1000 个类别),Softmax 激活

创新性

VGGNet结构的创新性:

  • 简单而深层的结构
    VGGNet 使用简单的 3x3 卷积和 2x2 最大池化构建深层网络,证明了通过增加网络深度可以提高模型的性能。
  • 一致的卷积核大小
    所有卷积层均采用相同大小的卷积核(3x3),使网络结构更加简洁且易于理解。
  • 深度网络的有效性
    VGGNet 通过增加网络深度(16 或 19 层)来提高分类性能,展示了深度学习在复杂图像任务中的优势。
  • 可解释性
    尽管模型较深,但其结构相对简单,易于可视化和理解。

VGG19模型结构代码

import matplotlib.pyplot as plt
from keras.utils import plot_model
from tensorflow.keras import layers, models

# macos系统显示中文
plt.rcParams['font.sans-serif'] = ['Arial Unicode MS']

def create_vgg19(input_shape=(224, 224, 3), num_classes=1000):
    model = models.Sequential()

    # Conv Block 1
    model.add(layers.Conv2D(64, (3, 3), padding='same', activation='relu', input_shape=input_shape))
    model.add(layers.Conv2D(64, (3, 3), padding='same', activation='relu'))
    model.add(layers.MaxPooling2D((2, 2), strides=(2, 2)))

    # Conv Block 2
    model.add(layers.Conv2D(128, (3, 3), padding='same', activation='relu'))
    model.add(layers.Conv2D(128, (3, 3), padding='same', activation='relu'))
    model.add(layers.MaxPooling2D((2, 2), strides=(2, 2)))

    # Conv Block 3
    model.add(layers.Conv2D(256, (3, 3), padding='same', activation='relu'))
    model.add(layers.Conv2D(256, (3, 3), padding='same', activation='relu'))
    model.add(layers.Conv2D(256, (3, 3), padding='same', activation='relu'))
    model.add(layers.MaxPooling2D((2, 2), strides=(2, 2)))

    # Conv Block 4
    model.add(layers.Conv2D(512, (3, 3), padding='same', activation='relu'))
    model.add(layers.Conv2D(512, (3, 3), padding='same', activation='relu'))
    model.add(layers.Conv2D(512, (3, 3), padding='same', activation='relu'))
    model.add(layers.MaxPooling2D((2, 2), strides=(2, 2)))

    # Conv Block 5
    model.add(layers.Conv2D(512, (3, 3), padding='same', activation='relu'))
    model.add(layers.Conv2D(512, (3, 3), padding='same', activation='relu'))
    model.add(layers.Conv2D(512, (3, 3), padding='same', activation='relu'))
    model.add(layers.MaxPooling2D((2, 2), strides=(2, 2)))

    # Flattening Layer
    model.add(layers.Flatten())

    # Fully Connected Layers
    model.add(layers.Dense(4096, activation='relu'))
    model.add(layers.Dense(4096, activation='relu'))
    model.add(layers.Dense(num_classes, activation='softmax'))

    return model


# 创建 VGG19 模型
vgg19_model = create_vgg19()
vgg19_model.summary()

plot_model(vgg19_model, to_file='vgg19_model.png', show_shapes=True, show_layer_names=True, dpi=500)

# 显示可视化结果
img = plt.imread('vgg19_model.png')
plt.figure(figsize=(10, 10))
plt.title('VGG19 结构可视化')
plt.imshow(img)
plt.axis('off')
plt.show()

VGG19模型结构可视化

在这里插入图片描述

P179–ResNet【2015】

模型结构及创新性说明

ResNet(Residual Network)是由 Kaiming He 等人在 2015 年提出的一种深度卷积神经网络。其主要创新在于引入了残差学习(Residual Learning)框架,解决了深层网络训练中的梯度消失和退化问题。

模型结构

ResNet 的核心思想是通过引入跳跃连接(skip connections)来构建残差块(Residual Block)。以下是 ResNet 的基本结构:

  • 输入层:
    输入图像尺寸:通常为 224x224 像素的 RGB 图像。
  • 初始卷积层:
    使用 7x7 的卷积核,64 个滤波器,步长为 2,后跟最大池化层。
  • 残差块:
    由多个残差块组成,每个残差块包含两个或三个卷积层。每个残差块的输出通过跳跃连接与输入相加,形成残差学习。残差块可以分为以下几种类型:
    • Basic Block:适用于较小的特征图。
    • Bottleneck Block:适用于较大的特征图,使用 1x1 卷积减少维度,后接 3x3 卷积和再用 1x1 卷积恢复维度。
  • 全局平均池化层:
    在所有残差块之后,使用全局平均池化层减小特征图的尺寸。
  • 全连接层:
    最后一层使用 Softmax 激活函数输出类别概率。

ResNet 的创新性

  • 残差学习
    引入残差块,通过跳跃连接直接将输入添加到输出,允许网络学习残差而非直接学习目标函数,缓解了深层网络训练中的梯度消失问题。
  • 可以实现极深的网络结构
    ResNet 允许构建非常深的网络(例如 ResNet-50、ResNet-101、ResNet-152),在 2015 年 ImageNet 竞赛中表现优异。
  • 简单的构建模块
    残差块的设计使得构建深层网络变得简单,极大地推动了深度学习领域的发展。
  • 普适性
    ResNet 的设计思想被广泛应用于其他任务和模型,如目标检测、语义分割等。

ResNet-50、101、152模型结构代码

import matplotlib.pyplot as plt
from keras.utils import plot_model
from tensorflow.keras import layers, models

# macos系统显示中文
plt.rcParams['font.sans-serif'] = ['Arial Unicode MS']

def identity_block(x, filters, kernel_size=3):
    x_skip = x
    f1, f2, f3 = filters

    x = layers.Conv2D(f1, (1, 1), strides=(1, 1), padding='valid')(x)
    x = layers.BatchNormalization()(x)
    x = layers.Activation('relu')(x)

    x = layers.Conv2D(f2, kernel_size, strides=(1, 1), padding='same')(x)
    x = layers.BatchNormalization()(x)
    x = layers.Activation('relu')(x)

    x = layers.Conv2D(f3, (1, 1), strides=(1, 1), padding='valid')(x)
    x = layers.BatchNormalization()(x)

    x = layers.Add()([x, x_skip])
    x = layers.Activation('relu')(x)

    return x

def convolutional_block(x, filters, kernel_size=3, stride=2):
    x_skip = x
    f1, f2, f3 = filters

    x = layers.Conv2D(f1, (1, 1), strides=(stride, stride), padding='valid')(x)
    x = layers.BatchNormalization()(x)
    x = layers.Activation('relu')(x)

    x = layers.Conv2D(f2, kernel_size, strides=(1, 1), padding='same')(x)
    x = layers.BatchNormalization()(x)
    x = layers.Activation('relu')(x)

    x = layers.Conv2D(f3, (1, 1), strides=(1, 1), padding='valid')(x)
    x = layers.BatchNormalization()(x)

    x_skip = layers.Conv2D(f3, (1, 1), strides=(stride, stride), padding='valid')(x_skip)
    x_skip = layers.BatchNormalization()(x_skip)

    x = layers.Add()([x, x_skip])
    x = layers.Activation('relu')(x)

    return x

def ResNet(input_shape, num_classes, blocks):
    inputs = layers.Input(shape=input_shape)

    x = layers.ZeroPadding2D(padding=(3, 3))(inputs)
    x = layers.Conv2D(64, (7, 7), strides=(2, 2))(x)
    x = layers.BatchNormalization()(x)
    x = layers.Activation('relu')(x)
    x = layers.MaxPooling2D((3, 3), strides=(2, 2))(x)

    x = convolutional_block(x, [64, 64, 256], kernel_size=3, stride=1)
    for _ in range(blocks[0] - 1):
        x = identity_block(x, [64, 64, 256], kernel_size=3)

    x = convolutional_block(x, [128, 128, 512], kernel_size=3, stride=2)
    for _ in range(blocks[1] - 1):
        x = identity_block(x, [128, 128, 512], kernel_size=3)

    x = convolutional_block(x, [256, 256, 1024], kernel_size=3, stride=2)
    for _ in range(blocks[2] - 1):
        x = identity_block(x, [256, 256, 1024], kernel_size=3)

    x = convolutional_block(x, [512, 512, 2048], kernel_size=3, stride=2)
    for _ in range(blocks[3] - 1):
        x = identity_block(x, [512, 512, 2048], kernel_size=3)

    x = layers.GlobalAveragePooling2D()(x)
    x = layers.Dense(num_classes, activation='softmax')(x)

    model = models.Model(inputs, x)
    return model

ResNet-50代码
# ResNet50
resnet50 = ResNet((224, 224, 3), 1000, [3, 4, 6, 3])
resnet50.summary()

plot_model(resnet50, to_file='resnet50.pdf', show_shapes=True,
           show_layer_names=True)
ResNet-101代码
# ResNet101
resnet101 = ResNet((224, 224, 3), 1000, [3, 4, 23, 3])
resnet101.summary()

plot_model(resnet101, to_file='resnet101.pdf', show_shapes=True,
           show_layer_names=True)
ResNet-152代码
# ResNet152
resnet152 = ResNet((224, 224, 3), 1000, [3, 8, 36, 3])
resnet152.summary()

plot_model(resnet152, to_file='resnet152.pdf', show_shapes=True,
           show_layer_names=True)

ResNet系列模型结构可视化

ResNet50、101、152模型结构PDF下载地址

P180–DenseNet【2017】

模型结构及创新性

DenseNet(Densely Connected Convolutional Networks)是由 Gao Huang 等人在 2017 年提出的一种卷积神经网络架构。DenseNet 的核心思想是通过密集连接(Dense Connectivity)来构建网络,使得每一层都与之前所有层相连。

模型结构

  • 输入层: 输入图像尺寸:通常为 224x224 像素的 RGB 图像。
  • 初始卷积层: 使用 7x7 的卷积核,64 个滤波器,步长为 2,后跟最大池化层。
  • 密集块(Dense Block):
    网络由多个密集块组成,每个密集块内部包含多个卷积层。每个卷积层的输出被直接连接到后续的所有卷积层,形成了密集连接。通过将所有前面层的特征图作为输入,DenseNet 能够有效地传递特征并减少梯度消失。
  • 过渡层(Transition Layer):
    每个密集块后面有一个过渡层,用于减少特征图的尺寸和通道数。过渡层通常包含 1x1 的卷积和 2x2 的平均池化。
  • 全局平均池化层:
    在最后一个密集块之后,使用全局平均池化层减小特征图的尺寸。
  • 全连接层: 最后一层使用 Softmax 激活函数输出类别概率。

DenseNet 的创新性

密集连接:
每一层都与前面的所有层相连,这种连接方式增强了特征的传播和重用,解决了深层网络中的梯度消失问题。
参数效率:
DenseNet 通过重用特征图,显著减少了模型参数的数量,相比于传统的卷积网络,DenseNet 具有更高的参数效率。
特征重用:
通过密集连接,DenseNet 能够在不同层之间共享特征,使得网络更加高效,并且有助于学习更丰富的特征表示。
适应性强:
DenseNet 在不同的任务和数据集上表现出色,适用于图像分类、目标检测等多种计算机视觉任务。
网络深度:
DenseNet 可构建非常深的网络(如 DenseNet-121、DenseNet-169、DenseNet-201),在多个基准数据集上取得了优秀的结果。

DenseNet-121、169、201模型结构代码

import matplotlib.pyplot as plt
import tensorflow as tf
from keras.utils import plot_model
from tensorflow.keras import layers, models

# macos系统显示中文
plt.rcParams['font.sans-serif'] = ['Arial Unicode MS']


def dense_block(x, units):
    for i in range(units):
        x1 = layers.BatchNormalization()(x)
        x1 = layers.ReLU()(x1)
        x1 = layers.Conv2D(32, (1, 1), padding='same')(x1)

        x2 = layers.BatchNormalization()(x1)
        x2 = layers.ReLU()(x2)
        x2 = layers.Conv2D(32, (3, 3), padding='same')(x2)

        x = layers.Concatenate()([x, x2])
    return x


def transition_layer(x, reduction):
    x = layers.BatchNormalization()(x)
    x = layers.ReLU()(x)
    x = layers.Conv2D(int(tf.keras.backend.int_shape(x)[-1] * reduction), (1, 1), padding='same')(x)
    x = layers.AveragePooling2D((2, 2), strides=(2, 2))(x)
    return x


def create_densenet(input_shape=(224, 224, 3), num_classes=1000, dense_blocks=(6, 12, 24, 16)):
    inputs = layers.Input(shape=input_shape)

    x = layers.Conv2D(64, (7, 7), strides=2, padding='same')(inputs)
    x = layers.BatchNormalization()(x)
    x = layers.ReLU()(x)
    x = layers.MaxPooling2D((3, 3), strides=2)(x)

    for i, block_size in enumerate(dense_blocks):
        x = dense_block(x, units=block_size)
        if i < len(dense_blocks) - 1:
            x = transition_layer(x, reduction=0.5)

    x = layers.GlobalAveragePooling2D()(x)
    outputs = layers.Dense(num_classes, activation='softmax')(x)

    model = models.Model(inputs, outputs)
    return model
DenseNet-121代码
# 创建 DenseNet-121 模型
densenet121_model = create_densenet(dense_blocks=(6, 12, 24, 16))
densenet121_model.summary()

plot_model(densenet121_model, to_file='densenet121_model.pdf', show_shapes=True,
           show_layer_names=True)
DenseNet-169代码
# 创建 DenseNet-169 模型
densenet169_model = create_densenet(dense_blocks=(6, 12, 32, 32))
densenet169_model.summary()

plot_model(densenet169_model, to_file='densenet169_model.pdf', show_shapes=True,
           show_layer_names=True)
DenseNet-201代码
# 创建 DenseNet-201 模型
densenet201_model = create_densenet(dense_blocks=(6, 12, 48, 32))
densenet201_model.summary()

plot_model(densenet201_model, to_file='densenet201_model.pdf', show_shapes=True,
           show_layer_names=True)

DenseNet系列模型结构可视化

DenseNet121、169、201模型PDF高清结构下载地址

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

AnFany

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

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

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

打赏作者

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

抵扣说明:

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

余额充值