第三次作业:卷积神经网络

OUC2022秋季软工09组第三次作业

声明

本博客为OUC2022秋季软件工程第三次作业

鄢凯瑞

一、视频学习

本次课程主要讲解了绪论、基本组成结构、卷积神经网络的典型结构。卷积神经网络应用于分类、识别、检测等,如一些比较成熟的技术人脸识别、表情识别、自动驾驶等等。深度学习三部曲:1. 搭建神经网络结构 2. 找到一个合适的损失函数 3. 找到一个合适的优化函数更新参数。卷积神经网络是由卷积层、池化层、全连接层交叉堆叠而成, 卷积是对两个实变函数的一种数学操作。池化是保留了主要特征的同时减少参数和计算量,防止过拟合,提高模型泛化的能力。它一般处于卷积层与卷积层之间,全连接层与全连接层之间,一般有最大值池化和平均值池化。全连接就是前一层网络的每个节点都与后面的节点相连。卷积神经网络结构有Alexnet、ZFNet、VGG、GoogleNet、ResNet。

  • AlexNet :大数据训练、非线性激活函数(ReLU)、防止过拟合、双GPU实现
  • ZFNet: 网路结构和AlexNet相同,仅仅是做了一些调参
  • VGG: 是一个更深网络, 8layers -> 16 ,错误率从11.7% -> 7.3%
  • GoogleNet:网络包含22个参数层,独立成块的层有100个,没有FC层
  • ResNet: 残差学习网络,深度152层,错误率6.7% -> 3.57%

二、代码练习

1. MNIST 数据集分类

加载数据(MINIST)
在这里插入图片描述
在这里插入图片描述
显示数据集中的部分:
在这里插入图片描述
创建网络:
在这里插入图片描述
在这里插入图片描述
定义训练和测试函数:
在这里插入图片描述
在小型全连接网络上训练(Fully-connected network):
在这里插入图片描述
在卷积神经网络上训练:
在这里插入图片描述
打乱像素顺序再次在两个网络上训练与测试:
在这里插入图片描述在这里插入图片描述
在这里插入图片描述
在全连接网络上训练与测试:
在这里插入图片描述
在卷积神经网络上训练与测试:
在这里插入图片描述
从打乱像素顺序的实验结果来看,全连接网络的性能基本上没有发生变化,但是 卷积神经网络的性能明显下降。

这是因为对于卷积神经网络,会利用像素的局部关系,但是打乱顺序以后,这些像素间的关系将无法得到利用。

2.CIFAR10 数据集分类

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
准确率还可以,通过改进网络结构,性能还可以进一步提升。在 Kaggle 的LeaderBoard上,准确率高的达到95%以上。

3.使用 VGG16 对 CIFAR10 分类

定义 dataloader:
在这里插入图片描述
VGG 网络定义:
在这里插入图片描述

在这里插入图片描述
网络训练:
报错cfg没有定义,检查代码发现前面是self.cfg, 改为self.cfg 没有报错。
在这里插入图片描述
但是在正向传播+反向传播+优化这里发生了错误,猜测可能是之前的改的不对,又将之前的self去了,但是还是不对,询问同学后知道不仅要加self而且要将2048改为512, 我修改后跑出结果如下:
在这里插入图片描述
在这里插入图片描述

然后进行测试, 测试结果为
在这里插入图片描述
在这里插入图片描述
可以看到,使用一个简化版的 VGG 网络,就能够显著地将准确率由 64%,提升到 84.92%。

三、相关问题解答

  1. dataloader 里面 shuffle 取不同值有什么区别?

dataloder是一个类,而shuffle(bool)是一个进行随机打乱顺序的函数, 参数为bool类型,默认情况下参数是false,此时每次迭代训练数据集时不会将输入数据顺序打乱;如果是true时每次迭代训练时都会将数据重新打乱。

  1. transform 里,取了不同值,这个有什么区别?

transform可以对数据进行变换,可用于PIL Image 或者 Tensor Image,可以对图像进行裁剪、翻转和旋转、图像变换、图像格式转换、系列变换、通用变换、组合变化,如transforms.CenterCrop(size)可以裁剪图像,进行中心裁剪为size * size 大小的图像;transforms.RandomHorizontalFlip§ 水平翻转,默认值为0.5, 大于0.5就会左右翻转。

  1. epoch 和 batch 的区别?

Batch大小是在更新模型之前处理的多个样本。Epoch数是通过训练数据集的完整传递次数。批处理的大小必须大于或等于1且小于或等于训练数据集中的样本数。可以将epoch设置为1和无穷大之间的整数值。可以根据需要运行算法,甚至可以使用除固定数量的epoch之外的其他条件来停止算法。它们都是整数值,并且它们都是学习算法的超参数,例如学习过程的参数,而不是学习过程找到的内部模型参数。必须为学习算法指定batch大小和epoch数。

  1. 1x1的卷积和 FC 有什么区别?主要起什么作用?

FC作用:将前一层的输出通过加权隐射到label上去

1x2卷积作用:1*1的卷积可以用于降维(减少通道数),升维(增加通道数),代替fc成为一个分类器

区别:权值共享,参数量较同等功能的fc层相比少,使用了位置信息;fc层对于训练样本要求统一尺寸,但是1*1的卷积不会受该规定的限制

  1. residual leanring 为什么能够提升准确率?

通过使用residual learning我们可以加深网络层数,我们知道在不断加深神经网络深度时,模型的准确率会先上升然后达到饱和,此时如果再持续增加网络深度会使准确率下降,但如果在后面增加几个恒等映射层,这样即使增加网络深度,误差也不会增加,同时它也可以有效解决梯度消失问题。

  1. 代码练习二里,网络和1989年 Lecun 提出的 LeNet 有什么区别?

激活函数不同,LeNet使用sigmoid函数作为激活函数,代码练习二中使用ReLu作为激活函数。

  1. 代码练习二里,卷积以后feature map 尺寸会变小,如何应用 Residual Learning?

以使用带padding的大小为1*1的卷积来改变feature map尺寸使其可以应用Residual learning。

  1. 有什么方法可以进一步提升准确率?
  • 加深网络层数
  • 更换激活函数、损失函数
  • 增大数据集

王景琪

### 卷积神经网络(Convolutional Neural Network)

绪论
1.卷积神经网络应用
  1. 分类
  2. 检索
    • 人脸识别
    • 表情识别
  3. 检测
    • 人脸验证
  4. 分割(有点像抠图)
  5. 自动驾驶
2.传统神经网络vs卷积神经网络

深度学习三部曲
step1. 搭建神经网络结构
step2. 找到一个合适的损失函数
损失函数是衡量吻合度的,可以调整参数/权重W,使得映射的结果和实际类别吻合
传统神经网络也可以应用到计算机视觉,但是会有如下问题:参数太多,会造成过拟合(泛化性能差)
卷积神经网络:局部关联,参数共享
step3. 找到一个合适的优化函数,更新参数
相同之处:分层:卷积层,激活层,池化层,全连接层

基本组成结构

卷积:卷积是对两个实变函数的一种数学操作 实变函数:以实数为自变量的函数
二维卷积
一维卷积:常用在信号处理中,用于计算信号的延迟累计
滤波器(filter)
卷积核:信息的衰减率
基本概念:

  • 输入
  • 卷积核/滤波器
  • 权重
  • 感受野(receptive field)
  • 特征图(feature map)
  • padding(输入的两边补0)
  • 深度(channel)
  • 输出(output)
  • 步长(stride)

池化:(pooling layer):保留了主要特征的同时减少参数和计算量,防止过拟合,提高模型泛化的能力。它一般处于卷积层与卷积层之间,全连接层与全连接层之间。

  1. 最大值池化
  2. 平均值池化

全连接:(FC layer)两层之间的所有神经元都有权重链接,通常全连接层在卷积神经网络的尾部,全连接层参数量通常最大

小结:
  • 一个典型的卷积神经网络是由卷积层、池化层交叉堆叠而成,最后一层是全连接层
卷积神经网络典型结构
AlexNet

非线性激活函数:ReLU函数:优点

  • 解决了梯度消失问题
  • 计算速度特别快,只需要判断输入是否大于0
  • 收敛速度远快于sigmoid
    AlexNet分层解析
ZFNet

网络结构与AlexNet相同

VGG

VGG是一个更深网络 8->16

GoogleNet
  • 网络包含22个带参数的层,独立成块的层总共有100个
  • 参数量大概是Alexnet的1/12
  • 没有FC层
    inception模块 用小的卷积核替代大的卷积核
ResNet(残差学习网络)

代码练习

MNIST 数据集分类
  1. 加载数据(MINIST)

  2. 创建网络

  3. 在小型全连接网络上训练(Fully-connected network)

  1. 在卷积神经网络上训练
# Training settings 
n_features = 6 # number of feature maps

model_cnn = CNN(input_size, n_features, output_size)
model_cnn.to(device)
optimizer = optim.SGD(model_cnn.parameters(), lr=0.01, momentum=0.5)
print('Number of parameters: {}'.format(get_n_params(model_cnn)))

train(model_cnn)
test(model_cnn)

在这里插入图片描述
通过上面的测试结果,可以发现,含有相同参数的 CNN 效果要明显优于 简单的全连接网络,是因为 CNN 能够更好的挖掘图像中的信息,主要通过两个手段:

  • 卷积:Locality and stationarity in images
  • 池化:Builds in some translation invariance
  1. 打乱像素顺序再次在两个网络上训练与测试

    • 在全连接网络上训练与测试

      Number of parameters: 6442
      Train: [0/60000 (0%)]	Loss: 2.264858
      Train: [6400/60000 (11%)]	Loss: 2.041490
      Train: [12800/60000 (21%)]	Loss: 1.552290
      Train: [19200/60000 (32%)]	Loss: 1.093537
      Train: [25600/60000 (43%)]	Loss: 0.799383
      Train: [32000/60000 (53%)]	Loss: 0.847221
      Train: [38400/60000 (64%)]	Loss: 0.756004
      Train: [44800/60000 (75%)]	Loss: 0.723714
      Train: [51200/60000 (85%)]	Loss: 0.438829
      Train: [57600/60000 (96%)]	Loss: 0.474032
      
      Test set: Average loss: 0.5751, Accuracy: 8348/10000 (83%)
      
    • 在卷积神经网络上训练与测试:

      Number of parameters: 6422
      Train: [0/60000 (0%)]	Loss: 2.300903
      Train: [6400/60000 (11%)]	Loss: 2.282535
      Train: [12800/60000 (21%)]	Loss: 2.261807
      Train: [19200/60000 (32%)]	Loss: 2.111837
      Train: [25600/60000 (43%)]	Loss: 1.717916
      Train: [32000/60000 (53%)]	Loss: 1.320999
      Train: [38400/60000 (64%)]	Loss: 0.960259
      Train: [44800/60000 (75%)]	Loss: 0.961738
      Train: [51200/60000 (85%)]	Loss: 0.636504
      Train: [57600/60000 (96%)]	Loss: 0.507474
      
      Test set: Average loss: 0.6227, Accuracy: 8003/10000 (80%)
      

      从打乱像素顺序的实验结果来看,全连接网络的性能基本上没有发生变化,但是 卷积神经网络的性能明显下降。

      这是因为对于卷积神经网络,会利用像素的局部关系,但是打乱顺序以后,这些像素间的关系将无法得到利用。

CIFAR10 数据集分类

接下来定义网络,损失函数和优化器:

class Net(nn.Module):
    def __init__(self):
        super(Net, self).__init__()
        self.conv1 = nn.Conv2d(3, 6, 5)
        self.pool = nn.MaxPool2d(2, 2)
        self.conv2 = nn.Conv2d(6, 16, 5)
        self.fc1 = nn.Linear(16 * 5 * 5, 120)
        self.fc2 = nn.Linear(120, 84)
        self.fc3 = nn.Linear(84, 10)

    def forward(self, x):
        x = self.pool(F.relu(self.conv1(x)))
        x = self.pool(F.relu(self.conv2(x)))
        x = x.view(-1, 16 * 5 * 5)
        x = F.relu(self.fc1(x))
        x = F.relu(self.fc2(x))
        x = self.fc3(x)
        return x

# 网络放到GPU上
net = Net().to(device)
criterion = nn.CrossEntropyLoss()
optimizer = optim.Adam(net.parameters(), lr=0.001)

训练网络:

for epoch in range(10):  # 重复多轮训练
    for i, (inputs, labels) in enumerate(trainloader):
        inputs = inputs.to(device)
        labels = labels.to(device)
        # 优化器梯度归零
        optimizer.zero_grad()
        # 正向传播 + 反向传播 + 优化 
        outputs = net(inputs)
        loss = criterion(outputs, labels)
        loss.backward()
        optimizer.step()
        # 输出统计信息
        if i % 100 == 0:   
            print('Epoch: %d Minibatch: %5d loss: %.3f' %(epoch + 1, i + 1, loss.item()))

print('Finished Training')

网络在整个数据集上的表现:

Accuracy of the network on the 10000 test images: 62 %

使用 VGG16 对 CIFAR10 分类,链接:

VGG是由Simonyan 和Zisserman在文献《Very Deep Convolutional Networks for Large Scale Image Recognition》中提出卷积神经网络模型,其名称来源于作者所在的牛津大学视觉几何组(Visual Geometry Group)的缩写。

该模型参加2014年的 ImageNet图像分类与定位挑战赛,取得了优异成绩:在分类任务上排名第二,在定位任务上排名第一。

VGG16的网络结构如下图所示:
在这里插入图片描述

  1. 定义dataloader

    需要注意的是,这里的 transform,dataloader 和之前定义的有所不同

  2. 定义VGG网络

  3. 网络训练

    训练结果

    correct = 0
    total = 0
    
    for data in testloader:
        images, labels = data
        images, labels = images.to(device), labels.to(device)
        outputs = net(images)
        _, predicted = torch.max(outputs.data, 1)
        total += labels.size(0)
        correct += (predicted == labels).sum().item()
    
    print('Accuracy of the network on the 10000 test images: %.2f %%' % (
        100 * correct / total))
    

    Accuracy of the network on the 10000 test images: 84.92 %

    可以看到,使用一个简化版的 VGG 网络,就能够显著地将准确率由 64%,提升到 84.92%

思考
  1. dataloader 里面 shuffle 取不同值有什么区别?

    Dataloader中设置了shuffle是True,所以每次加载的数据都是随机的。

    洗牌。默认设置为False。在每次迭代训练时是否将数据洗牌,默认设置是False。将输入数据的顺序打乱,是为了使数据更有独立 性,但如果数据是有序列特征的,就不要设置成True了

  2. transform 里,取了不同值,这个有什么区别?

    transform的作用:对图像进行一定的预处理,相当于扩展数据集。

    transform的处理方法

    1 裁剪-Crop

    2 翻转和旋转——Flip and Rotation

    3 图像变换

    4 对transforms操作,使数据增强更灵活

  3. epoch 和 batch 的区别?

    epoch:当一个完整的数据集经过神经网络一次,并返回一次,这个过程称为一个epoch。

    batch:当数据集很大的时候,对于每个epoch,很难将所有的数据集一次读入到内存中,这是需要将数据集分为几次读入,每次称为一个batch。

  4. 1x1的卷积和 FC 有什么区别?主要起什么作用?

    1*1的卷积:

    1. 添加非线性特性

    即保持特征图尺寸不发生变化且维持上一层的通道数,在增加网络的深度的同时令网络能够学习更为复杂的函数(特征信息)。

    1. 对通道数实现升维降维:一般用降维来减少计算量

    FC(全连接层):起到分类器的作用。对前层的特征进行一个加权和,(卷积层是将数据输入映射到隐层特征空间)将特征空间通过线性变换映射到样本标记空间(也就是label)

    区别:

    全连接是把特征图拆开组成一个一维向量,再乘以一个权重向量,这两个向量中的元素一一对应所以输出结果是一个值。

    11的卷积核那就是表明只由一个权重组成,如果特征图尺寸也是11的话,那输出就是一个值,此时与全连接完全一样。但是如果特征图尺寸不是11,而是wh的话,那么11的卷积输出就不是一个值而是wh的一个矩阵。

    用11卷积代替全连接应该是基于输入尺寸的考虑,全连接的输入是特征图所有元素乘以权重再求和,但是这个权重向量是在设计网络的时候就需要固定的,所以全连接没办法适应输入尺寸的变化只能固定。但是11卷积的输出与输入尺寸是一样大的,输出尺寸可以随着输入尺寸的变化而变化,所以1*1卷积无需固定输出尺寸。

  5. residual leanring 为什么能够提升准确率?

    残差的思想都是去掉相同的主体部分,从而突出微小的变化,引入残差后的映射对输出的变化更敏感。很明显,在残差网络中输出的变化对权重的调整影响更大,也就是说反向传播的梯度值更大,训练就更加容易。

  6. 代码练习二里,网络和1989年 Lecun 提出的 LeNet 有什么区别?

    • LeNet :最早用于手写数字识别的CNN网络
  7. 有什么方法可以进一步提升准确率?

    1. 增加更多数据

    2. 2.处理缺失值和异常值

    3. 特征工程学:这一步骤有助于从现有数据中提取更多信息。新信息作为新特征被提取出来。这些特征可能会更好地解释训练集中的差异变化。因此能改善模型的准确率。

    4. 特征选择:特征选择是寻找众多属性的哪个子集合,能够最好的解释目标变量与各个自变量的关系的过程。

      你可以根据多种标准选取有用的特征

    5. 使用多种算法

    6. 交叉验证:如果想解决这个问题,我们必须使用交叉验证技术(cross validation)。交叉验证是数据建模领域最重要的概念之一。它是指,保留一部分数据样本不用来训练模型,而是在完成模型前用来验证。

王义钧

一、视频学习部分

1、AlexNet

    第一个现代卷积网络是AlexNet,也正是AlexNet在ImageNet竞赛上的成功,使得卷积神经网络以及深度学习开始风靡,引发了这一轮的深度学习热潮。AlexNet的基本模式同LeNet相同,其成功的关键原因有两个,一是网络时代大量增加的数据,二是GPU提供的算力支持了大型网络的训练。在LeNet的时代,网络尚未完全普及,在图像领域的机器学习研究通常使用小样本,而且样本的维度也不大。在这样的情况下,神经网络并不比传统的机器学习方法更有优势,而且神经网络的训练和调参难度也比传统机器学习方法大,因此神经网络没有获得多少青睐。而后来互联网的普及使得数据集的规模有了增大的可能,而gpu的使用,使得训练更大更深的神经网络更加容易。在这两个条件的基础上,AlexNet取得了成功。
Alt

2、VGG

    下一个学习的网络结构是有名的VGG Net。其创新点在于提出了卷积块的概念,将若干个卷积层以及激活函数打包成一个块,通过多个块的堆叠构造神经网络,方便了网络结构的设计,并且也取得了很好的效果。其另一个发现是多个33的卷积堆叠,效果要好于使用大尺寸的卷积,例如77,11*11,即使用窄且深的网络比宽且浅的网络好。
    一个vgg块包含若干个卷积和ReLU,每个块的第一个卷积会将输入的通道进行改变(或不改变),后续的所有卷积都采用尺寸为3,步长为1,padding为1的卷积,输入的通道和输出的通道数一致,因此在一个vgg块内部,特征图的尺寸不会发生变化,直到块的末尾。

3、残差网络ResNet

    如果AlexNet让深度学习风靡,那么ResNet带来了真正的深度。残差连接思想的提出使得我们训练极深的网络成为了可能,残差连接几乎成为了现在深度学习不可缺少的技术。
    残差链接使得网络层数的加深和复杂可以包含原有的网络,即模型的效果最坏不会坏于加深之前的效果。具体的计算方式是,每次将输入直接与输出相加,使得网络变成x+f(x)的形式,这样网络最差也是恒等映射,y=x,不会让效果变得更差。更重要的是,残差连接使得梯度可以通过“捷径”从深处传递到前面,缓解了梯度弥散的问题,使得深层的网络更容易训练。
Alt

二、代码练习部分

1、MNIST 数据集分类:构建简单的CNN对 mnist 数据集进行分类。

训练前准备:引入pytorch包 计算模型中有多少参数 同时更改训练为GPU。

import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
from torchvision import datasets, transforms
import matplotlib.pyplot as plt
import numpy

# 一个函数,用来计算模型中有多少参数
def get_n_params(model):
    np=0
    for p in list(model.parameters()):
        np += p.nelement()
    return np

# 使用GPU训练,可以在菜单 "代码执行工具" -> "更改运行时类型" 里进行设置
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")

①加载数据集(MNIST)
    使用datasets.MNIST方法 从training.pt创建数据集,放入root文件夹下。两个batch大小不同,但均将数据集打乱。

input_size  = 28*28   # MNIST上的图像尺寸是 28x28
output_size = 10      # 类别为 09 的数字,因此为十类

train_loader = torch.utils.data.DataLoader(
    datasets.MNIST('./data', train=True, download=True,
        transform=transforms.Compose(
            [transforms.ToTensor(),
             transforms.Normalize((0.1307,), (0.3081,))])),
    batch_size=64, shuffle=True)

test_loader = torch.utils.data.DataLoader(
    datasets.MNIST('./data', train=False, transform=transforms.Compose([
             transforms.ToTensor(),
             transforms.Normalize((0.1307,), (0.3081,))])),
    batch_size=1000, shuffle=True)

运行结果:
在这里插入图片描述

显示数据集中的部分图像

plt.figure(figsize=(8, 5))
for i in range(20):
    plt.subplot(4, 5, i + 1)
    image, _ = train_loader.dataset.__getitem__(i)
    plt.imshow(image.squeeze().numpy(),'gray')
    plt.axis('off');

在这里插入图片描述

②创建网络
    定义网络时,需要继承nn.Module,并实现它的forward方法,把网络中具有可学习参数的层放在构造函数init中。

class FC2Layer(nn.Module):
    def __init__(self, input_size, n_hidden, output_size):
        # nn.Module子类的函数必须在构造函数中执行父类的构造函数
        # 下式等价于nn.Module.__init__(self)        
        super(FC2Layer, self).__init__()
        self.input_size = input_size
        # 这里直接用 Sequential 就定义了网络,注意要和下面 CNN 的代码区分开
        self.network = nn.Sequential(
            nn.Linear(input_size, n_hidden), 
            nn.ReLU(), 
            nn.Linear(n_hidden, n_hidden), 
            nn.ReLU(), 
            nn.Linear(n_hidden, output_size), 
            nn.LogSoftmax(dim=1)
        )
    def forward(self, x):
        # view一般出现在model类的forward函数中,用于改变输入或输出的形状
        # x.view
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
卷积神经网络(Convolutional Neural Network,CNN)是一种深度学模型,常用于图像识别和计算机视觉任务。卷积神经网络的基本结构包括卷积层、池化层和全连接层[1]。典型的卷积神经网络结构有AlexNet、ZFNet、VGG、GoogleNet和ResNet等。 其中,ResNet是引入了残差学习网络的一种深度神经网络模型,它解决了梯度消失的问题,可以用来训练非常深的网络。 在使用卷积神经网络进行图像分类时,需要定义网络结构。可以通过继承nn.Module类并实现其forward方法来定义网络。可学习参数的层应该放在构造函数的init方法中。 总的来说,卷积神经网络是一种用于图像识别和计算机视觉任务的深度学习模型,具有卷积层、池化层和全连接层。ResNet是一种引入了残差学习网络的深度神经网络模型,解决了梯度消失问题。在定义卷积神经网络时,需要继承nn.Module类,并实现其forward方法。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [【OUC_SE_2022】第三周作业卷积神经网络基础](https://blog.csdn.net/OUC_SE_GROUP18/article/details/127340419)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值