昇思25天学习打卡营第23天|CV-ResNet50迁移学习

打卡

目录

打卡

迁移学习

实战练习

数据准备

数据处理

数据处理函数

数据处理流程

数据可视化

训练模型

构建Resnet50网络

固定特征进行训练

network 的输出

训练和评估

可视化模型预测 


通过本文,了解迁移学习的重点在于,了解你的模型结构,通过冻结不需要更新的模型参数,根据你的任务需求状况,修改模型的输出结构等部分,只更新所需参数,从而能从预训练的模型中快速训练出一个针对特定任务场景的模型,如N分类模型。

迁移学习

实际应用场景中,因为训练数据集不足,普遍的网络训练策略是,在一个非常大的基础数据集上训练得到一个预训练模型,然后用该模型来初始化网络的权重参数或作为固定特征提取器,从而应用于特定的任务中。

迁移学习详细内容见Stanford University CS231n

本文使用迁移学习的方法对 ImageNet 数据集中的狼和狗图像进行分类。

实战练习

数据准备

本次的 狗与狼分类数据集 中的图像来自于ImageNet,每个分类有大约 120 张训练图像与 30 张验证图像。数据下载方式如下。

from download import download

dataset_url = "https://mindspore-website.obs.cn-north-4.myhuaweicloud.com/notebook/datasets/intermediate/Canidae_data.zip"

download(dataset_url, "./datasets-Canidae", kind="zip", replace=True)

数据处理

用 mindspore.dataset.ImageFolderDataset接口来加载数据集,并进行相关图像增强操作。

数据处理函数

处理后的图像尺寸【channel, height, wight】

import mindspore as ms
import mindspore.dataset as ds
import mindspore.dataset.vision as vision
 

def create_dataset_canidae(dataset_path, workers, usage, batch_size, image_size):
    """数据加载"""
    ## 从树状结构的文件目录中读取图片构建源数据集。同一个文件夹中的所有图片将被分配相同的label。
    ### 生成的数据集有两列:[image, label]。数据类型分别为 uint8、uint32。
    data_set = ds.ImageFolderDataset(dataset_path,
                                     num_parallel_workers=workers,   ## 指定读取数据的工作线程数。默认None用全局默认线程数(8)。
                                     shuffle=True,)

    # 数据增强操作
    mean = [0.485 * 255, 0.456 * 255, 0.406 * 255]
    std = [0.229 * 255, 0.224 * 255, 0.225 * 255]
    scale = 32

    if usage == "train":
        # Define map operations for training dataset
        trans = [
            ## “裁剪”、”解码”和”调整尺寸大小”的组合处理。该操作将在随机位置裁剪输入图像,以 RGB 模式对裁剪后的图像进行解码,并调整解码图像的尺寸大小。针对 JPEG 图像进行了优化, 可以获得更好的性能。
            vision.RandomCropDecodeResize(size=image_size, ## 调整后图像的输出尺寸大小。大小值必须为正。
                                          scale=(0.08, 1.0),  ## 要裁剪的原始尺寸大小的各个尺寸的范围[min, max),必须为非负数。
                                          ratio=(0.75, 1.333)  ## 宽高比的范围 [min, max) 裁剪,必须为非负数。默认值: (3./4., 4./3.)。
                                          ),
            ## 对输入图像按给定的概率进行水平随机翻转。默认值: 0.5 。
            vision.RandomHorizontalFlip(prob=0.5),
            ## 根据均值和标准差对输入图像进行归一化。默认图像为HWC格式,否则设is_hwc 
            vision.Normalize(mean=mean, std=std),
            ## 将输入图像的shape从 <H, W, C> 转换为 <C, H, W>。 如果输入图像的shape为 <H, W> ,图像将保持不变。
            vision.HWC2CHW()
        ]
    else:
        # Define map operations for inference dataset
        trans = [
            ## 将输入的压缩图像解码为RGB格式。当前支持的图片类型:JPEG、BMP、PNG、TIFF、GIF(需要指定 to_pil=True)、WEBP(需要指定 to_pil=True)。
            vision.Decode(),
            ## 用给定的插值方式(默认mindspore.dataset.vision.Inter.LINEAR)去调整为给定图像的尺寸大小。第一个参数是图像的输出尺寸大小。若输入整型,将调整图像的较短边长度为 size ,且保持图像的宽高比不变;若输入是2元素组成的序列,其输入格式需要是 (高度, 宽度) 。
            vision.Resize(image_size + scale),
            ## 对输入图像应用中心区域裁剪。如果输入图像尺寸小于输出尺寸,则在裁剪前对输入图像边界填充0像素。
            vision.CenterCrop(image_size),
            vision.Normalize(mean=mean, std=std),
            vision.HWC2CHW()
        ]


    # 数据映射操作:给定一组数据增强列表,按顺序将数据增强作用在数据集对象上。
    data_set = data_set.map(
        operations=trans, input_columns='image',
        num_parallel_workers=workers  ## 指定map操作的多进程/多线程并发数,加快处理速度。
    )

    # 批量操作:将数据集中连续 batch_size 条数据组合为一个批数据
    data_set = data_set.batch(batch_size)

    return data_set

数据处理流程

import mindspore as ms
import mindspore.dataset as ds
import mindspore.dataset.vision as vision

batch_size = 18       # 批量大小
image_size = 224      # 训练图像空间大小
num_epochs = 5        # 训练周期数
lr = 0.001            # 学习率
momentum = 0.9        # 动量
workers = 4           # 并行线程个数


# 数据集目录路径
data_path_train = "./datasets-Canidae/data/Canidae/train/"
data_path_val = "./datasets-Canidae/data/Canidae/val/"


dataset_train = create_dataset_canidae(data_path_train, workers, "train", batch_size, image_size )
step_size_train = dataset_train.get_dataset_size()

dataset_val = create_dataset_canidae(data_path_val, workers, "val", batch_size, image_size  )
step_size_val = dataset_val.get_dataset_size()

如下图,训练集一共14个batch,验证集一共4个batch。

数据可视化

用 next 迭代访问数据迭代器。因为 batch_size 设为18,所以使用 next 一次可获取18个图像及标签数据。下面显示4个图像数据如下。

import matplotlib.pyplot as plt
import numpy as np

data = next(dataset_train.create_dict_iterator())
images = data["image"]
labels = data["label"]

print("Tensor of image", images.shape)  ##  (18, 3, 224, 224)
print("Labels:", labels)  ## 元素为 1 和 0 的长度为 18 的 list


# class_name对应label,按文件夹字符串从小到大的顺序标记label
class_name = {0: "dogs", 1: "wolves"}

plt.figure(figsize=(5, 5))
for i in range(4):
    # 获取图像及其对应的label
    data_image = images[i].asnumpy()
    data_label = labels[i]
    # 处理图像供展示使用
    ### 通过转置,其维度变为(高度, 宽度, 通道数),即(224, 224, 3),这样更符合图像处理的习惯。
    data_image = np.transpose(data_image, (1, 2, 0))
    ### 三个值对应于RGB三个通道的均值或标准差。这些均值或标准差通常是通过对大量图像数据集进行统计分析得到的,用于后续的标准化处理。 
    mean = np.array([0.485, 0.456, 0.406])
    std = np.array([0.229, 0.224, 0.225])
    ### 将图像数据从原始的像素值范围转换到均值为0,标准差为1的分布范围,这有助于模型的训练过程。
    data_image = std * data_image + mean
    ### 对标准化后的图像数据进行归一化处理。
    data_image = np.clip(data_image, 0, 1)
    # 显示图像
    plt.subplot(2, 2, i+1)
    plt.imshow(data_image)
    plt.title(class_name[int(labels[i].asnumpy())])
    plt.axis("off")

plt.show()

可视化一个batch_size 的 18 个图像输出如下。

训练模型

搭建好模型框架后,  下载ResNet50的预训练模型并将权重参数加载到网络中。

构建Resnet50网络

  • 代码调用逻辑为(从浅到深,其中代码调用顺序用>>表示,模型结构输入输出-->表示 ):
  1. func resnet50 >> func _resnet >> class ResNet >>  mindspore.load_checkpoint >>  mindspore.load_param_into_net
  2. class ResNet 结构
    1. conv1 --> norm  --> relu --> max_pool --> layer1 --> layer2 --> layer3 --> layer4 --> avg_pool --> flatten --> fc
    2. layerX 定义 >> func make_layer >> block=Union[ResidualBlockBase, ResidualBlock]
  3. class ResidualBlockBase 残差结构 [Building Block 结构]:conv1[3X3] --> norm  --> relu --> conv2[3X3] --> norm ( --> down_sample) --> +identity  --> relu.
  4. class ResidualBlock 残差块结构 [Bottleneck 结构]:conv1[1X1] --> norm1  --> relu --> conv2[3X3] --> norm2 --> relu --> conv3 [1X1]--> norm3 ( --> down_sample) --> +identity  --> relu.

关于ResNet50的文章介绍见本文

代码逻辑陈述:下载并加载模型预训练模型到 ResNet50 网络结构,其中 ResNet50 自定义实现,函数 make_layer 创建一个由残差块(Residual Block)组成的层。Block 表示残差块的类型,它可以是ResidualBlockBaseResidualBlock的子类。

from typing import Type, Union, List, Optional
from mindspore import nn, train
from mindspore.common.initializer import Normal
from mindspore import load_checkpoint, load_param_into_net


weight_init = Normal(mean=0, sigma=0.02)
gamma_init = Normal(mean=1, sigma=0.02)


class ResidualBlockBase(nn.Cell):
    expansion: int = 1  # 最后一个卷积核数量与第一个卷积核数量相等

    def __init__(self, in_channel: int, out_channel: int,
                 stride: int = 1, norm: Optional[nn.Cell] = None,
                 down_sample: Optional[nn.Cell] = None) -> None:
        super(ResidualBlockBase, self).__init__()
        if not norm:
            self.norm = nn.BatchNorm2d(out_channel)
        else:
            self.norm = norm

        self.conv1 = nn.Conv2d(in_channel, out_channel,
                               kernel_size=3, stride=stride,
                               weight_init=weight_init)
        self.conv2 = nn.Conv2d(in_channel, out_channel,
                               kernel_size=3, weight_init=weight_init)
        self.relu = nn.ReLU()
        self.down_sample = down_sample

    def construct(self, x):
        """ResidualBlockBase construct."""
        identity = x  # shortcuts分支

        out = self.conv1(x)  # 主分支第一层:3*3卷积层
        out = self.norm(out)
        out = self.relu(out)
        out = self.conv2(out)  # 主分支第二层:3*3卷积层
        out = self.norm(out)

        if self.down_sample is not None:
            identity = self.down_sample(x)
        out += identity  # 输出为主分支与shortcuts之和
        out = self.relu(out)

        return out


class ResidualBlock(nn.Cell):
    expansion = 4  # 最后一个卷积核的数量是第一个卷积核数量的4倍

    def __init__(self, in_channel: int, out_channel: int,
                 stride: int = 1, down_sample: Optional[nn.Cell] = None) -> None:
        super(ResidualBlock, self).__init__()

        self.conv1 = nn.Conv2d(in_channel, out_channel,
                               kernel_size=1, weight_init=weight_init)
        self.norm1 = nn.BatchNorm2d(out_channel)
        self.conv2 = nn.Conv2d(out_channel, out_channel,
                               kernel_size=3, stride=stride,
                               weight_init=weight_init)
        self.norm2 = nn.BatchNorm2d(out_channel)
        self.conv3 = nn.Conv2d(out_channel, out_channel * self.expansion,
                               kernel_size=1, weight_init=weight_init)
        self.norm3 = nn.BatchNorm2d(out_channel * self.expansion)

        self.relu = nn.ReLU()
        self.down_sample = down_sample

    def construct(self, x):

        identity = x  # shortscuts分支

        out = self.conv1(x)  # 主分支第一层:1*1卷积层
        out = self.norm1(out)
        out = self.relu(out)
        out = self.conv2(out)  # 主分支第二层:3*3卷积层
        out = self.norm2(out)
        out = self.relu(out)
        out = self.conv3(out)  # 主分支第三层:1*1卷积层
        out = self.norm3(out)

        if self.down_sample is not None:
            identity = self.down_sample(x)

        out += identity  # 输出为主分支与shortcuts之和
        out = self.relu(out)

        return out


def make_layer(last_out_channel, block: Type[Union[ResidualBlockBase, ResidualBlock]],
               channel: int, block_nums: int, stride: int = 1):
    down_sample = None  # shortcuts分支


    if stride != 1 or last_out_channel != channel * block.expansion:
        ## 如果stride不等于1或者上一个层的输出通道数与当前层的通道数乘以残差块的扩展因子expansion不相等,则需要创建一个下采样层。
        ## 下采样层由一个卷积层和一个批量归一化层组成。卷积层用于调整通道数和大小,批量归一化层用于归一化数据。
        down_sample = nn.SequentialCell([
            nn.Conv2d(last_out_channel, channel * block.expansion,
                      kernel_size=1, stride=stride, weight_init=weight_init),
            nn.BatchNorm2d(channel * block.expansion, gamma_init=gamma_init)
        ])

    ## 添加第一个残差块到列表中。第一个残差块可能包含下采样层。
    layers = []
    layers.append(block(last_out_channel, channel, stride=stride, down_sample=down_sample))

    ## 更新in_channel变量,表示下一个残差块的输入通道数是block.expansion倍的channel数量。
    in_channel = channel * block.expansion
    # 堆叠残差网络
    for _ in range(1, block_nums):
        layers.append(block(in_channel, channel))

    return nn.SequentialCell(layers)

 

class ResNet(nn.Cell):
    def __init__(self, block: Type[Union[ResidualBlockBase, ResidualBlock]],
                 layer_nums: List[int], num_classes: int, input_channel: int) -> None:
        super(ResNet, self).__init__()

        self.relu = nn.ReLU()
        # 第一个卷积层,输入channel为3(彩色图像),输出channel为64
        self.conv1 = nn.Conv2d(3, 64, kernel_size=7, stride=2, weight_init=weight_init)
        # 在四维输入(具有额外通道维度的小批量二维输入)上应用批归一化处理(Batch Normalization Layer),以避免内部协变量偏移。
        self.norm = nn.BatchNorm2d(64)
        # 最大池化层,缩小图片的尺寸
        self.max_pool = nn.MaxPool2d(kernel_size=3, stride=2, pad_mode='same')
        # 各个残差网络结构块定义,参数=上一个层的输出通道数+残差块+当前层的通道数+当前层中残差块的数量+第一个残差块的步长,默认1。
        self.layer1 = make_layer(64, block, 64, layer_nums[0])
        self.layer2 = make_layer(64 * block.expansion, block, 128, layer_nums[1], stride=2)
        self.layer3 = make_layer(128 * block.expansion, block, 256, layer_nums[2], stride=2)
        self.layer4 = make_layer(256 * block.expansion, block, 512, layer_nums[3], stride=2)
        # 平均池化层
        self.avg_pool = nn.AvgPool2d()
        # flattern层
        self.flatten = nn.Flatten()
        # 全连接层
        self.fc = nn.Dense(in_channels=input_channel, out_channels=num_classes)

    def construct(self, x):

        x = self.conv1(x)
        x = self.norm(x)
        x = self.relu(x)
        x = self.max_pool(x)

        x = self.layer1(x)
        x = self.layer2(x)
        x = self.layer3(x)
        x = self.layer4(x)

        x = self.avg_pool(x)
        x = self.flatten(x)
        x = self.fc(x)

        return x


def _resnet(model_url: str, block: Type[Union[ResidualBlockBase, ResidualBlock]],
            layers: List[int], num_classes: int, pretrained: bool, pretrianed_ckpt: str,
            input_channel: int):
    model = ResNet(block, layers, num_classes, input_channel)

    if pretrained:
        # 加载预训练模型
        download(url=model_url, path=pretrianed_ckpt, replace=True)
        param_dict = load_checkpoint(pretrianed_ckpt)
        load_param_into_net(model, param_dict)

    return model


def resnet50(num_classes: int = 1000, pretrained: bool = False):
    "ResNet50模型"
    resnet50_url = "https://mindspore-website.obs.cn-north-4.myhuaweicloud.com/notebook/models/application/resnet50_224_new.ckpt"
    resnet50_ckpt = "./LoadPretrainedModel/resnet50_224_new.ckpt"
    return _resnet(resnet50_url, ResidualBlock, [3, 4, 6, 3], num_classes,
                   pretrained, resnet50_ckpt, 2048)

固定特征进行训练

  • 使用固定特征进行训练的时候,需要冻结除最后一层之外的所有网络层。通过net_work.get_parameters() 获取模型的全部参数,并在所需层设置 requires_grad == False 冻结参数,以便不在反向传播中计算梯度。
  • 根据本次的2分类任务需求,将模型的最后一层全连接层重新设置为 nn.Dense(in_channels, 2),
  • mindspore 的模型训练或推理的高阶接口如下,它会根据用户传入的参数封装可训练或推理的实例: mindspore.train.Model(network, loss_fn=None, optimizer=None, metrics=None, eval_network=None, eval_indexes=None, amp_level='O0', boost_level='O0', **kwargs)
    • 参数含义:
      • network (Cell) - 用于训练或推理的神经网络。

      • loss_fn (Cell) - 损失函数。如果 loss_fn 为None,network 中需要进行损失函数计算。默认值: None 。

      • optimizer (Cell) - 用于更新网络权重的优化器。如果 optimizer 为None, 那么 network 的网络结构里需要包括反向传播和权重更新逻辑。默认值: None 。

      • metrics (Union[dict, set]) - 用于模型评估的一组评价函数。例如:{‘accuracy’, ‘recall’}。默认值: None 。

      • eval_network (Cell) - 用于评估的神经网络。未定义情况下,Model 会使用 network 和 loss_fn 封装一个 eval_network 。默认值: None 。

      • eval_indexes (list) - 在定义 eval_network 的情况下使用。如果 eval_indexes 为默认值None,Model 会将 eval_network 的所有输出传给 metrics 。如果配置 eval_indexes ,必须包含三个元素,分别为损失值、预测值和标签在 eval_network 输出中的位置,此时,损失值将传给损失评价函数,预测值和标签将传给其他评价函数。推荐使用评价函数的 mindspore.train.Metric.set_indexes() 代替 eval_indexes 。默认值: None 。

      • boost_level (str) - mindspore.boost 的可选参数,为boost模式训练等级。支持[“O0”, “O1”, “O2”]。默认值: "O0" 。

        • “O1”: 启用boost模式,性能将提升约20%,准确率保持不变。

        • “O2”: 启用boost模式,性能将提升约30%,准确率下降小于3%。

        • O0”: 不变化。

import mindspore as ms
import matplotlib.pyplot as plt
import os
import time

net_work = resnet50(pretrained=True)  ## 表示从预训练参数中加载模型

# 【最后一层】全连接层输入层的大小 
in_channels = net_work.fc.in_channels

# 输出通道数大小为狼狗分类数2
head = nn.Dense(in_channels, 2)

# 重置全连接层
net_work.fc = head

# 平均池化层kernel size为7
avg_pool = nn.AvgPool2d(kernel_size=7)
# 重置平均池化层
net_work.avg_pool = avg_pool

# 冻结除最后一层外的所有参数
for param in net_work.get_parameters():
    if param.name not in ["fc.weight", "fc.bias"]:
        param.requires_grad = False

# 定义优化器和损失函数
opt = nn.Momentum(params=net_work.trainable_params(), learning_rate=lr, momentum=0.5)
loss_fn = nn.SoftmaxCrossEntropyWithLogits(sparse=True, reduction='mean')


def forward_fn(inputs, targets):
    logits = net_work(inputs)
    loss = loss_fn(logits, targets)

    return loss

## 生成求导函数,用于计算给定函数的正向计算结果和梯度。
grad_fn = ms.value_and_grad(forward_fn, None, opt.parameters)

def train_step(inputs, targets):
    loss, grads = grad_fn(inputs, targets)
    opt(grads)
    return loss

# 实例化模型
model1 = train.Model(net_work, loss_fn, opt, metrics={"Accuracy": train.Accuracy()})
network 的输出
ResNet<
  (relu): ReLU<>
  (conv1): Conv2d<input_channels=3, output_channels=64, kernel_size=(7, 7), stride=(2, 2), pad_mode=same, padding=0, dilation=(1, 1), group=1, has_bias=False, weight_init=<mindspore.common.initializer.Normal object at 0xffff901e02e0>, bias_init=None, format=NCHW>
  (norm): BatchNorm2d<num_features=64, eps=1e-05, momentum=0.9, gamma=Parameter (name=norm.gamma, shape=(64,), dtype=Float32, requires_grad=False), beta=Parameter (name=norm.beta, shape=(64,), dtype=Float32, requires_grad=False), moving_mean=Parameter (name=norm.moving_mean, shape=(64,), dtype=Float32, requires_grad=False), moving_variance=Parameter (name=norm.moving_variance, shape=(64,), dtype=Float32, requires_grad=False)>
  (max_pool): MaxPool2d<kernel_size=3, stride=2, pad_mode=SAME>
  (layer1): SequentialCell<
    (0): ResidualBlock<
      (conv1): Conv2d<input_channels=64, output_channels=64, kernel_size=(1, 1), stride=(1, 1), pad_mode=same, padding=0, dilation=(1, 1), group=1, has_bias=False, weight_init=<mindspore.common.initializer.Normal object at 0xffff901e02e0>, bias_init=None, format=NCHW>
      (norm1): BatchNorm2d<num_features=64, eps=1e-05, momentum=0.9, gamma=Parameter (name=layer1.0.norm1.gamma, shape=(64,), dtype=Float32, requires_grad=False), beta=Parameter (name=layer1.0.norm1.beta, shape=(64,), dtype=Float32, requires_grad=False), moving_mean=Parameter (name=layer1.0.norm1.moving_mean, shape=(64,), dtype=Float32, requires_grad=False), moving_variance=Parameter (name=layer1.0.norm1.moving_variance, shape=(64,), dtype=Float32, requires_grad=False)>
      (conv2): Conv2d<input_channels=64, output_channels=64, kernel_size=(3, 3), stride=(1, 1), pad_mode=same, padding=0, dilation=(1, 1), group=1, has_bias=False, weight_init=<mindspore.common.initializer.Normal object at 0xffff901e02e0>, bias_init=None, format=NCHW>
      (norm2): BatchNorm2d<num_features=64, eps=1e-05, momentum=0.9, gamma=Parameter (name=layer1.0.norm2.gamma, shape=(64,), dtype=Float32, requires_grad=False), beta=Parameter (name=layer1.0.norm2.beta, shape=(64,), dtype=Float32, requires_grad=False), moving_mean=Parameter (name=layer1.0.norm2.moving_mean, shape=(64,), dtype=Float32, requires_grad=False), moving_variance=Parameter (name=layer1.0.norm2.moving_variance, shape=(64,), dtype=Float32, requires_grad=False)>
      (conv3): Conv2d<input_channels=64, output_channels=256, kernel_size=(1, 1), stride=(1, 1), pad_mode=same, padding=0, dilation=(1, 1), group=1, has_bias=False, weight_init=<mindspore.common.initializer.Normal object at 0xffff901e02e0>, bias_init=None, format=NCHW>
      (norm3): BatchNorm2d<num_features=256, eps=1e-05, momentum=0.9, gamma=Parameter (name=layer1.0.norm3.gamma, shape=(256,), dtype=Float32, requires_grad=False), beta=Parameter (name=layer1.0.norm3.beta, shape=(256,), dtype=Float32, requires_grad=False), moving_mean=Parameter (name=layer1.0.norm3.moving_mean, shape=(256,), dtype=Float32, requires_grad=False), moving_variance=Parameter (name=layer1.0.norm3.moving_variance, shape=(256,), dtype=Float32, requires_grad=False)>
      (relu): ReLU<>
      (down_sample): SequentialCell<
        (0): Conv2d<input_channels=64, output_channels=256, kernel_size=(1, 1), stride=(1, 1), pad_mode=same, padding=0, dilation=(1, 1), group=1, has_bias=False, weight_init=<mindspore.common.initializer.Normal object at 0xffff901e02e0>, bias_init=None, format=NCHW>
        (1): BatchNorm2d<num_features=256, eps=1e-05, momentum=0.9, gamma=Parameter (name=layer1.0.down_sample.1.gamma, shape=(256,), dtype=Float32, requires_grad=False), beta=Parameter (name=layer1.0.down_sample.1.beta, shape=(256,), dtype=Float32, requires_grad=False), moving_mean=Parameter (name=layer1.0.down_sample.1.moving_mean, shape=(256,), dtype=Float32, requires_grad=False), moving_variance=Parameter (name=layer1.0.down_sample.1.moving_variance, shape=(256,), dtype=Float32, requires_grad=False)>
        >
      >
    (1): ResidualBlock<
      (conv1): Conv2d<input_channels=256, output_channels=64, kernel_size=(1, 1), stride=(1, 1), pad_mode=same, padding=0, dilation=(1, 1), group=1, has_bias=False, weight_init=<mindspore.common.initializer.Normal object at 0xffff901e02e0>, bias_init=None, format=NCHW>
      (norm1): BatchNorm2d<num_features=64, eps=1e-05, momentum=0.9, gamma=Parameter (name=layer1.1.norm1.gamma, shape=(64,), dtype=Float32, requires_grad=False), beta=Parameter (name=layer1.1.norm1.beta, shape=(64,), dtype=Float32, requires_grad=False), moving_mean=Parameter (name=layer1.1.norm1.moving_mean, shape=(64,), dtype=Float32, requires_grad=False), moving_variance=Parameter (name=layer1.1.norm1.moving_variance, shape=(64,), dtype=Float32, requires_grad=False)>
      (conv2): Conv2d<input_channels=64, output_channels=64, kernel_size=(3, 3), stride=(1, 1), pad_mode=same, padding=0, dilation=(1, 1), group=1, has_bias=False, weight_init=<mindspore.common.initializer.Normal object at 0xffff901e02e0>, bias_init=None, format=NCHW>
      (norm2): BatchNorm2d<num_features=64, eps=1e-05, momentum=0.9, gamma=Parameter (name=layer1.1.norm2.gamma, shape=(64,), dtype=Float32, requires_grad=False), beta=Parameter (name=layer1.1.norm2.beta, shape=(64,), dtype=Float32, requires_grad=False), moving_mean=Parameter (name=layer1.1.norm2.moving_mean, shape=(64,), dtype=Float32, requires_grad=False), moving_variance=Parameter (name=layer1.1.norm2.moving_variance, shape=(64,), dtype=Float32, requires_grad=False)>
      (conv3): Conv2d<input_channels=64, output_channels=256, kernel_size=(1, 1), stride=(1, 1), pad_mode=same, padding=0, dilation=(1, 1), group=1, has_bias=False, weight_init=<mindspore.common.initializer.Normal object at 0xffff901e02e0>, bias_init=None, format=NCHW>
      (norm3): BatchNorm2d<num_features=256, eps=1e-05, momentum=0.9, gamma=Parameter (name=layer1.1.norm3.gamma, shape=(256,), dtype=Float32, requires_grad=False), beta=Parameter (name=layer1.1.norm3.beta, shape=(256,), dtype=Float32, requires_grad=False), moving_mean=Parameter (name=layer1.1.norm3.moving_mean, shape=(256,), dtype=Float32, requires_grad=False), moving_variance=Parameter (name=layer1.1.norm3.moving_variance, shape=(256,), dtype=Float32, requires_grad=False)>
      (relu): ReLU<>
      >
    (2): ResidualBlock<
      (conv1): Conv2d<input_channels=256, output_channels=64, kernel_size=(1, 1), stride=(1, 1), pad_mode=same, padding=0, dilation=(1, 1), group=1, has_bias=False, weight_init=<mindspore.common.initializer.Normal object at 0xffff901e02e0>, bias_init=None, format=NCHW>
      (norm1): BatchNorm2d<num_features=64, eps=1e-05, momentum=0.9, gamma=Parameter (name=layer1.2.norm1.gamma, shape=(64,), dtype=Float32, requires_grad=False), beta=Parameter (name=layer1.2.norm1.beta, shape=(64,), dtype=Float32, requires_grad=False), moving_mean=Parameter (name=layer1.2.norm1.moving_mean, shape=(64,), dtype=Float32, requires_grad=False), moving_variance=Parameter (name=layer1.2.norm1.moving_variance, shape=(64,), dtype=Float32, requires_grad=False)>
      (conv2): Conv2d<input_channels=64, output_channels=64, kernel_size=(3, 3), stride=(1, 1), pad_mode=same, padding=0, dilation=(1, 1), group=1, has_bias=False, weight_init=<mindspore.common.initializer.Normal object at 0xffff901e02e0>, bias_init=None, format=NCHW>
      (norm2): BatchNorm2d<num_features=64, eps=1e-05, momentum=0.9, gamma=Parameter (name=layer1.2.norm2.gamma, shape=(64,), dtype=Float32, requires_grad=False), beta=Parameter (name=layer1.2.norm2.beta, shape=(64,), dtype=Float32, requires_grad=False), moving_mean=Parameter (name=layer1.2.norm2.moving_mean, shape=(64,), dtype=Float32, requires_grad=False), moving_variance=Parameter (name=layer1.2.norm2.moving_variance, shape=(64,), dtype=Float32, requires_grad=False)>
      (conv3): Conv2d<input_channels=64, output_channels=256, kernel_size=(1, 1), stride=(1, 1), pad_mode=same, padding=0, dilation=(1, 1), group=1, has_bias=False, weight_init=<mindspore.common.initializer.Normal object at 0xffff901e02e0>, bias_init=None, format=NCHW>
      (norm3): BatchNorm2d<num_features=256, eps=1e-05, momentum=0.9, gamma=Parameter (name=layer1.2.norm3.gamma, shape=(256,), dtype=Float32, requires_grad=False), beta=Parameter (name=layer1.2.norm3.beta, shape=(256,), dtype=Float32, requires_grad=False), moving_mean=Parameter (name=layer1.2.norm3.moving_mean, shape=(256,), dtype=Float32, requires_grad=False), moving_variance=Parameter (name=layer1.2.norm3.moving_variance, shape=(256,), dtype=Float32, requires_grad=False)>
      (relu): ReLU<>
      >
    >
  (layer2): SequentialCell<
    (0): ResidualBlock<
      (conv1): Conv2d<input_channels=256, output_channels=128, kernel_size=(1, 1), stride=(1, 1), pad_mode=same, padding=0, dilation=(1, 1), group=1, has_bias=False, weight_init=<mindspore.common.initializer.Normal object at 0xffff901e02e0>, bias_init=None, format=NCHW>
      (norm1): BatchNorm2d<num_features=128, eps=1e-05, momentum=0.9, gamma=Parameter (name=layer2.0.norm1.gamma, shape=(128,), dtype=Float32, requires_grad=False), beta=Parameter (name=layer2.0.norm1.beta, shape=(128,), dtype=Float32, requires_grad=False), moving_mean=Parameter (name=layer2.0.norm1.moving_mean, shape=(128,), dtype=Float32, requires_grad=False), moving_variance=Parameter (name=layer2.0.norm1.moving_variance, shape=(128,), dtype=Float32, requires_grad=False)>
      (conv2): Conv2d<input_channels=128, output_channels=128, kernel_size=(3, 3), stride=(2, 2), pad_mode=same, padding=0, dilation=(1, 1), group=1, has_bias=False, weight_init=<mindspore.common.initializer.Normal object at 0xffff901e02e0>, bias_init=None, format=NCHW>
      (norm2): BatchNorm2d<num_features=128, eps=1e-05, momentum=0.9, gamma=Parameter (name=layer2.0.norm2.gamma, shape=(128,), dtype=Float32, requires_grad=False), beta=Parameter (name=layer2.0.norm2.beta, shape=(128,), dtype=Float32, requires_grad=False), moving_mean=Parameter (name=layer2.0.norm2.moving_mean, shape=(128,), dtype=Float32, requires_grad=False), moving_variance=Parameter (name=layer2.0.norm2.moving_variance, shape=(128,), dtype=Float32, requires_grad=False)>
      (conv3): Conv2d<input_channels=128, output_channels=512, kernel_size=(1, 1), stride=(1, 1), pad_mode=same, padding=0, dilation=(1, 1), group=1, has_bias=False, weight_init=<mindspore.common.initializer.Normal object at 0xffff901e02e0>, bias_init=None, format=NCHW>
      (norm3): BatchNorm2d<num_features=512, eps=1e-05, momentum=0.9, gamma=Parameter (name=layer2.0.norm3.gamma, shape=(512,), dtype=Float32, requires_grad=False), beta=Parameter (name=layer2.0.norm3.beta, shape=(512,), dtype=Float32, requires_grad=False), moving_mean=Parameter (name=layer2.0.norm3.moving_mean, shape=(512,), dtype=Float32, requires_grad=False), moving_variance=Parameter (name=layer2.0.norm3.moving_variance, shape=(512,), dtype=Float32, requires_grad=False)>
      (relu): ReLU<>
      (down_sample): SequentialCell<
        (0): Conv2d<input_channels=256, output_channels=512, kernel_size=(1, 1), stride=(2, 2), pad_mode=same, padding=0, dilation=(1, 1), group=1, has_bias=False, weight_init=<mindspore.common.initializer.Normal object at 0xffff901e02e0>, bias_init=None, format=NCHW>
        (1): BatchNorm2d<num_features=512, eps=1e-05, momentum=0.9, gamma=Parameter (name=layer2.0.down_sample.1.gamma, shape=(512,), dtype=Float32, requires_grad=False), beta=Parameter (name=layer2.0.down_sample.1.beta, shape=(512,), dtype=Float32, requires_grad=False), moving_mean=Parameter (name=layer2.0.down_sample.1.moving_mean, shape=(512,), dtype=Float32, requires_grad=False), moving_variance=Parameter (name=layer2.0.down_sample.1.moving_variance, shape=(512,), dtype=Float32, requires_grad=False)>
        >
      >
    (1): ResidualBlock<
      (conv1): Conv2d<input_channels=512, output_channels=128, kernel_size=(1, 1), stride=(1, 1), pad_mode=same, padding=0, dilation=(1, 1), group=1, has_bias=False, weight_init=<mindspore.common.initializer.Normal object at 0xffff901e02e0>, bias_init=None, format=NCHW>
      (norm1): BatchNorm2d<num_features=128, eps=1e-05, momentum=0.9, gamma=Parameter (name=layer2.1.norm1.gamma, shape=(128,), dtype=Float32, requires_grad=False), beta=Parameter (name=layer2.1.norm1.beta, shape=(128,), dtype=Float32, requires_grad=False), moving_mean=Parameter (name=layer2.1.norm1.moving_mean, shape=(128,), dtype=Float32, requires_grad=False), moving_variance=Parameter (name=layer2.1.norm1.moving_variance, shape=(128,), dtype=Float32, requires_grad=False)>
      (conv2): Conv2d<input_channels=128, output_channels=128, kernel_size=(3, 3), stride=(1, 1), pad_mode=same, padding=0, dilation=(1, 1), group=1, has_bias=False, weight_init=<mindspore.common.initializer.Normal object at 0xffff901e02e0>, bias_init=None, format=NCHW>
      (norm2): BatchNorm2d<num_features=128, eps=1e-05, momentum=0.9, gamma=Parameter (name=layer2.1.norm2.gamma, shape=(128,), dtype=Float32, requires_grad=False), beta=Parameter (name=layer2.1.norm2.beta, shape=(128,), dtype=Float32, requires_grad=False), moving_mean=Parameter (name=layer2.1.norm2.moving_mean, shape=(128,), dtype=Float32, requires_grad=False), moving_variance=Parameter (name=layer2.1.norm2.moving_variance, shape=(128,), dtype=Float32, requires_grad=False)>
      (conv3): Conv2d<input_channels=128, output_channels=512, kernel_size=(1, 1), stride=(1, 1), pad_mode=same, padding=0, dilation=(1, 1), group=1, has_bias=False, weight_init=<mindspore.common.initializer.Normal object at 0xffff901e02e0>, bias_init=None, format=NCHW>
      (norm3): BatchNorm2d<num_features=512, eps=1e-05, momentum=0.9, gamma=Parameter (name=layer2.1.norm3.gamma, shape=(512,), dtype=Float32, requires_grad=False), beta=Parameter (name=layer2.1.norm3.beta, shape=(512,), dtype=Float32, requires_grad=False), moving_mean=Parameter (name=layer2.1.norm3.moving_mean, shape=(512,), dtype=Float32, requires_grad=False), moving_variance=Parameter (name=layer2.1.norm3.moving_variance, shape=(512,), dtype=Float32, requires_grad=False)>
      (relu): ReLU<>
      >
    (2): ResidualBlock<
      (conv1): Conv2d<input_channels=512, output_channels=128, kernel_size=(1, 1), stride=(1, 1), pad_mode=same, padding=0, dilation=(1, 1), group=1, has_bias=False, weight_init=<mindspore.common.initializer.Normal object at 0xffff901e02e0>, bias_init=None, format=NCHW>
      (norm1): BatchNorm2d<num_features=128, eps=1e-05, momentum=0.9, gamma=Parameter (name=layer2.2.norm1.gamma, shape=(128,), dtype=Float32, requires_grad=False), beta=Parameter (name=layer2.2.norm1.beta, shape=(128,), dtype=Float32, requires_grad=False), moving_mean=Parameter (name=layer2.2.norm1.moving_mean, shape=(128,), dtype=Float32, requires_grad=False), moving_variance=Parameter (name=layer2.2.norm1.moving_variance, shape=(128,), dtype=Float32, requires_grad=False)>
      (conv2): Conv2d<input_channels=128, output_channels=128, kernel_size=(3, 3), stride=(1, 1), pad_mode=same, padding=0, dilation=(1, 1), group=1, has_bias=False, weight_init=<mindspore.common.initializer.Normal object at 0xffff901e02e0>, bias_init=None, format=NCHW>
      (norm2): BatchNorm2d<num_features=128, eps=1e-05, momentum=0.9, gamma=Parameter (name=layer2.2.norm2.gamma, shape=(128,), dtype=Float32, requires_grad=False), beta=Parameter (name=layer2.2.norm2.beta, shape=(128,), dtype=Float32, requires_grad=False), moving_mean=Parameter (name=layer2.2.norm2.moving_mean, shape=(128,), dtype=Float32, requires_grad=False), moving_variance=Parameter (name=layer2.2.norm2.moving_variance, shape=(128,), dtype=Float32, requires_grad=False)>
      (conv3): Conv2d<input_channels=128, output_channels=512, kernel_size=(1, 1), stride=(1, 1), pad_mode=same, padding=0, dilation=(1, 1), group=1, has_bias=False, weight_init=<mindspore.common.initializer.Normal object at 0xffff901e02e0>, bias_init=None, format=NCHW>
      (norm3): BatchNorm2d<num_features=512, eps=1e-05, momentum=0.9, gamma=Parameter (name=layer2.2.norm3.gamma, shape=(512,), dtype=Float32, requires_grad=False), beta=Parameter (name=layer2.2.norm3.beta, shape=(512,), dtype=Float32, requires_grad=False), moving_mean=Parameter (name=layer2.2.norm3.moving_mean, shape=(512,), dtype=Float32, requires_grad=False), moving_variance=Parameter (name=layer2.2.norm3.moving_variance, shape=(512,), dtype=Float32, requires_grad=False)>
      (relu): ReLU<>
      >
    (3): ResidualBlock<
      (conv1): Conv2d<input_channels=512, output_channels=128, kernel_size=(1, 1), stride=(1, 1), pad_mode=same, padding=0, dilation=(1, 1), group=1, has_bias=False, weight_init=<mindspore.common.initializer.Normal object at 0xffff901e02e0>, bias_init=None, format=NCHW>
      (norm1): BatchNorm2d<num_features=128, eps=1e-05, momentum=0.9, gamma=Parameter (name=layer2.3.norm1.gamma, shape=(128,), dtype=Float32, requires_grad=False), beta=Parameter (name=layer2.3.norm1.beta, shape=(128,), dtype=Float32, requires_grad=False), moving_mean=Parameter (name=layer2.3.norm1.moving_mean, shape=(128,), dtype=Float32, requires_grad=False), moving_variance=Parameter (name=layer2.3.norm1.moving_variance, shape=(128,), dtype=Float32, requires_grad=False)>
      (conv2): Conv2d<input_channels=128, output_channels=128, kernel_size=(3, 3), stride=(1, 1), pad_mode=same, padding=0, dilation=(1, 1), group=1, has_bias=False, weight_init=<mindspore.common.initializer.Normal object at 0xffff901e02e0>, bias_init=None, format=NCHW>
      (norm2): BatchNorm2d<num_features=128, eps=1e-05, momentum=0.9, gamma=Parameter (name=layer2.3.norm2.gamma, shape=(128,), dtype=Float32, requires_grad=False), beta=Parameter (name=layer2.3.norm2.beta, shape=(128,), dtype=Float32, requires_grad=False), moving_mean=Parameter (name=layer2.3.norm2.moving_mean, shape=(128,), dtype=Float32, requires_grad=False), moving_variance=Parameter (name=layer2.3.norm2.moving_variance, shape=(128,), dtype=Float32, requires_grad=False)>
      (conv3): Conv2d<input_channels=128, output_channels=512, kernel_size=(1, 1), stride=(1, 1), pad_mode=same, padding=0, dilation=(1, 1), group=1, has_bias=False, weight_init=<mindspore.common.initializer.Normal object at 0xffff901e02e0>, bias_init=None, format=NCHW>
      (norm3): BatchNorm2d<num_features=512, eps=1e-05, momentum=0.9, gamma=Parameter (name=layer2.3.norm3.gamma, shape=(512,), dtype=Float32, requires_grad=False), beta=Parameter (name=layer2.3.norm3.beta, shape=(512,), dtype=Float32, requires_grad=False), moving_mean=Parameter (name=layer2.3.norm3.moving_mean, shape=(512,), dtype=Float32, requires_grad=False), moving_variance=Parameter (name=layer2.3.norm3.moving_variance, shape=(512,), dtype=Float32, requires_grad=False)>
      (relu): ReLU<>
      >
    >
  (layer3): SequentialCell<
    (0): ResidualBlock<
      (conv1): Conv2d<input_channels=512, output_channels=256, kernel_size=(1, 1), stride=(1, 1), pad_mode=same, padding=0, dilation=(1, 1), group=1, has_bias=False, weight_init=<mindspore.common.initializer.Normal object at 0xffff901e02e0>, bias_init=None, format=NCHW>
      (norm1): BatchNorm2d<num_features=256, eps=1e-05, momentum=0.9, gamma=Parameter (name=layer3.0.norm1.gamma, shape=(256,), dtype=Float32, requires_grad=False), beta=Parameter (name=layer3.0.norm1.beta, shape=(256,), dtype=Float32, requires_grad=False), moving_mean=Parameter (name=layer3.0.norm1.moving_mean, shape=(256,), dtype=Float32, requires_grad=False), moving_variance=Parameter (name=layer3.0.norm1.moving_variance, shape=(256,), dtype=Float32, requires_grad=False)>
      (conv2): Conv2d<input_channels=256, output_channels=256, kernel_size=(3, 3), stride=(2, 2), pad_mode=same, padding=0, dilation=(1, 1), group=1, has_bias=False, weight_init=<mindspore.common.initializer.Normal object at 0xffff901e02e0>, bias_init=None, format=NCHW>
      (norm2): BatchNorm2d<num_features=256, eps=1e-05, momentum=0.9, gamma=Parameter (name=layer3.0.norm2.gamma, shape=(256,), dtype=Float32, requires_grad=False), beta=Parameter (name=layer3.0.norm2.beta, shape=(256,), dtype=Float32, requires_grad=False), moving_mean=Parameter (name=layer3.0.norm2.moving_mean, shape=(256,), dtype=Float32, requires_grad=False), moving_variance=Parameter (name=layer3.0.norm2.moving_variance, shape=(256,), dtype=Float32, requires_grad=False)>
      (conv3): Conv2d<input_channels=256, output_channels=1024, kernel_size=(1, 1), stride=(1, 1), pad_mode=same, padding=0, dilation=(1, 1), group=1, has_bias=False, weight_init=<mindspore.common.initializer.Normal object at 0xffff901e02e0>, bias_init=None, format=NCHW>
      (norm3): BatchNorm2d<num_features=1024, eps=1e-05, momentum=0.9, gamma=Parameter (name=layer3.0.norm3.gamma, shape=(1024,), dtype=Float32, requires_grad=False), beta=Parameter (name=layer3.0.norm3.beta, shape=(1024,), dtype=Float32, requires_grad=False), moving_mean=Parameter (name=layer3.0.norm3.moving_mean, shape=(1024,), dtype=Float32, requires_grad=False), moving_variance=Parameter (name=layer3.0.norm3.moving_variance, shape=(1024,), dtype=Float32, requires_grad=False)>
      (relu): ReLU<>
      (down_sample): SequentialCell<
        (0): Conv2d<input_channels=512, output_channels=1024, kernel_size=(1, 1), stride=(2, 2), pad_mode=same, padding=0, dilation=(1, 1), group=1, has_bias=False, weight_init=<mindspore.common.initializer.Normal object at 0xffff901e02e0>, bias_init=None, format=NCHW>
        (1): BatchNorm2d<num_features=1024, eps=1e-05, momentum=0.9, gamma=Parameter (name=layer3.0.down_sample.1.gamma, shape=(1024,), dtype=Float32, requires_grad=False), beta=Parameter (name=layer3.0.down_sample.1.beta, shape=(1024,), dtype=Float32, requires_grad=False), moving_mean=Parameter (name=layer3.0.down_sample.1.moving_mean, shape=(1024,), dtype=Float32, requires_grad=False), moving_variance=Parameter (name=layer3.0.down_sample.1.moving_variance, shape=(1024,), dtype=Float32, requires_grad=False)>
        >
      >
    (1): ResidualBlock<
      (conv1): Conv2d<input_channels=1024, output_channels=256, kernel_size=(1, 1), stride=(1, 1), pad_mode=same, padding=0, dilation=(1, 1), group=1, has_bias=False, weight_init=<mindspore.common.initializer.Normal object at 0xffff901e02e0>, bias_init=None, format=NCHW>
      (norm1): BatchNorm2d<num_features=256, eps=1e-05, momentum=0.9, gamma=Parameter (name=layer3.1.norm1.gamma, shape=(256,), dtype=Float32, requires_grad=False), beta=Parameter (name=layer3.1.norm1.beta, shape=(256,), dtype=Float32, requires_grad=False), moving_mean=Parameter (name=layer3.1.norm1.moving_mean, shape=(256,), dtype=Float32, requires_grad=False), moving_variance=Parameter (name=layer3.1.norm1.moving_variance, shape=(256,), dtype=Float32, requires_grad=False)>
      (conv2): Conv2d<input_channels=256, output_channels=256, kernel_size=(3, 3), stride=(1, 1), pad_mode=same, padding=0, dilation=(1, 1), group=1, has_bias=False, weight_init=<mindspore.common.initializer.Normal object at 0xffff901e02e0>, bias_init=None, format=NCHW>
      (norm2): BatchNorm2d<num_features=256, eps=1e-05, momentum=0.9, gamma=Parameter (name=layer3.1.norm2.gamma, shape=(256,), dtype=Float32, requires_grad=False), beta=Parameter (name=layer3.1.norm2.beta, shape=(256,), dtype=Float32, requires_grad=False), moving_mean=Parameter (name=layer3.1.norm2.moving_mean, shape=(256,), dtype=Float32, requires_grad=False), moving_variance=Parameter (name=layer3.1.norm2.moving_variance, shape=(256,), dtype=Float32, requires_grad=False)>
      (conv3): Conv2d<input_channels=256, output_channels=1024, kernel_size=(1, 1), stride=(1, 1), pad_mode=same, padding=0, dilation=(1, 1), group=1, has_bias=False, weight_init=<mindspore.common.initializer.Normal object at 0xffff901e02e0>, bias_init=None, format=NCHW>
      (norm3): BatchNorm2d<num_features=1024, eps=1e-05, momentum=0.9, gamma=Parameter (name=layer3.1.norm3.gamma, shape=(1024,), dtype=Float32, requires_grad=False), beta=Parameter (name=layer3.1.norm3.beta, shape=(1024,), dtype=Float32, requires_grad=False), moving_mean=Parameter (name=layer3.1.norm3.moving_mean, shape=(1024,), dtype=Float32, requires_grad=False), moving_variance=Parameter (name=layer3.1.norm3.moving_variance, shape=(1024,), dtype=Float32, requires_grad=False)>
      (relu): ReLU<>
      >
    (2): ResidualBlock<
      (conv1): Conv2d<input_channels=1024, output_channels=256, kernel_size=(1, 1), stride=(1, 1), pad_mode=same, padding=0, dilation=(1, 1), group=1, has_bias=False, weight_init=<mindspore.common.initializer.Normal object at 0xffff901e02e0>, bias_init=None, format=NCHW>
      (norm1): BatchNorm2d<num_features=256, eps=1e-05, momentum=0.9, gamma=Parameter (name=layer3.2.norm1.gamma, shape=(256,), dtype=Float32, requires_grad=False), beta=Parameter (name=layer3.2.norm1.beta, shape=(256,), dtype=Float32, requires_grad=False), moving_mean=Parameter (name=layer3.2.norm1.moving_mean, shape=(256,), dtype=Float32, requires_grad=False), moving_variance=Parameter (name=layer3.2.norm1.moving_variance, shape=(256,), dtype=Float32, requires_grad=False)>
      (conv2): Conv2d<input_channels=256, output_channels=256, kernel_size=(3, 3), stride=(1, 1), pad_mode=same, padding=0, dilation=(1, 1), group=1, has_bias=False, weight_init=<mindspore.common.initializer.Normal object at 0xffff901e02e0>, bias_init=None, format=NCHW>
      (norm2): BatchNorm2d<num_features=256, eps=1e-05, momentum=0.9, gamma=Parameter (name=layer3.2.norm2.gamma, shape=(256,), dtype=Float32, requires_grad=False), beta=Parameter (name=layer3.2.norm2.beta, shape=(256,), dtype=Float32, requires_grad=False), moving_mean=Parameter (name=layer3.2.norm2.moving_mean, shape=(256,), dtype=Float32, requires_grad=False), moving_variance=Parameter (name=layer3.2.norm2.moving_variance, shape=(256,), dtype=Float32, requires_grad=False)>
      (conv3): Conv2d<input_channels=256, output_channels=1024, kernel_size=(1, 1), stride=(1, 1), pad_mode=same, padding=0, dilation=(1, 1), group=1, has_bias=False, weight_init=<mindspore.common.initializer.Normal object at 0xffff901e02e0>, bias_init=None, format=NCHW>
      (norm3): BatchNorm2d<num_features=1024, eps=1e-05, momentum=0.9, gamma=Parameter (name=layer3.2.norm3.gamma, shape=(1024,), dtype=Float32, requires_grad=False), beta=Parameter (name=layer3.2.norm3.beta, shape=(1024,), dtype=Float32, requires_grad=False), moving_mean=Parameter (name=layer3.2.norm3.moving_mean, shape=(1024,), dtype=Float32, requires_grad=False), moving_variance=Parameter (name=layer3.2.norm3.moving_variance, shape=(1024,), dtype=Float32, requires_grad=False)>
      (relu): ReLU<>
      >
    (3): ResidualBlock<
      (conv1): Conv2d<input_channels=1024, output_channels=256, kernel_size=(1, 1), stride=(1, 1), pad_mode=same, padding=0, dilation=(1, 1), group=1, has_bias=False, weight_init=<mindspore.common.initializer.Normal object at 0xffff901e02e0>, bias_init=None, format=NCHW>
      (norm1): BatchNorm2d<num_features=256, eps=1e-05, momentum=0.9, gamma=Parameter (name=layer3.3.norm1.gamma, shape=(256,), dtype=Float32, requires_grad=False), beta=Parameter (name=layer3.3.norm1.beta, shape=(256,), dtype=Float32, requires_grad=False), moving_mean=Parameter (name=layer3.3.norm1.moving_mean, shape=(256,), dtype=Float32, requires_grad=False), moving_variance=Parameter (name=layer3.3.norm1.moving_variance, shape=(256,), dtype=Float32, requires_grad=False)>
      (conv2): Conv2d<input_channels=256, output_channels=256, kernel_size=(3, 3), stride=(1, 1), pad_mode=same, padding=0, dilation=(1, 1), group=1, has_bias=False, weight_init=<mindspore.common.initializer.Normal object at 0xffff901e02e0>, bias_init=None, format=NCHW>
      (norm2): BatchNorm2d<num_features=256, eps=1e-05, momentum=0.9, gamma=Parameter (name=layer3.3.norm2.gamma, shape=(256,), dtype=Float32, requires_grad=False), beta=Parameter (name=layer3.3.norm2.beta, shape=(256,), dtype=Float32, requires_grad=False), moving_mean=Parameter (name=layer3.3.norm2.moving_mean, shape=(256,), dtype=Float32, requires_grad=False), moving_variance=Parameter (name=layer3.3.norm2.moving_variance, shape=(256,), dtype=Float32, requires_grad=False)>
      (conv3): Conv2d<input_channels=256, output_channels=1024, kernel_size=(1, 1), stride=(1, 1), pad_mode=same, padding=0, dilation=(1, 1), group=1, has_bias=False, weight_init=<mindspore.common.initializer.Normal object at 0xffff901e02e0>, bias_init=None, format=NCHW>
      (norm3): BatchNorm2d<num_features=1024, eps=1e-05, momentum=0.9, gamma=Parameter (name=layer3.3.norm3.gamma, shape=(1024,), dtype=Float32, requires_grad=False), beta=Parameter (name=layer3.3.norm3.beta, shape=(1024,), dtype=Float32, requires_grad=False), moving_mean=Parameter (name=layer3.3.norm3.moving_mean, shape=(1024,), dtype=Float32, requires_grad=False), moving_variance=Parameter (name=layer3.3.norm3.moving_variance, shape=(1024,), dtype=Float32, requires_grad=False)>
      (relu): ReLU<>
      >
    (4): ResidualBlock<
      (conv1): Conv2d<input_channels=1024, output_channels=256, kernel_size=(1, 1), stride=(1, 1), pad_mode=same, padding=0, dilation=(1, 1), group=1, has_bias=False, weight_init=<mindspore.common.initializer.Normal object at 0xffff901e02e0>, bias_init=None, format=NCHW>
      (norm1): BatchNorm2d<num_features=256, eps=1e-05, momentum=0.9, gamma=Parameter (name=layer3.4.norm1.gamma, shape=(256,), dtype=Float32, requires_grad=False), beta=Parameter (name=layer3.4.norm1.beta, shape=(256,), dtype=Float32, requires_grad=False), moving_mean=Parameter (name=layer3.4.norm1.moving_mean, shape=(256,), dtype=Float32, requires_grad=False), moving_variance=Parameter (name=layer3.4.norm1.moving_variance, shape=(256,), dtype=Float32, requires_grad=False)>
      (conv2): Conv2d<input_channels=256, output_channels=256, kernel_size=(3, 3), stride=(1, 1), pad_mode=same, padding=0, dilation=(1, 1), group=1, has_bias=False, weight_init=<mindspore.common.initializer.Normal object at 0xffff901e02e0>, bias_init=None, format=NCHW>
      (norm2): BatchNorm2d<num_features=256, eps=1e-05, momentum=0.9, gamma=Parameter (name=layer3.4.norm2.gamma, shape=(256,), dtype=Float32, requires_grad=False), beta=Parameter (name=layer3.4.norm2.beta, shape=(256,), dtype=Float32, requires_grad=False), moving_mean=Parameter (name=layer3.4.norm2.moving_mean, shape=(256,), dtype=Float32, requires_grad=False), moving_variance=Parameter (name=layer3.4.norm2.moving_variance, shape=(256,), dtype=Float32, requires_grad=False)>
      (conv3): Conv2d<input_channels=256, output_channels=1024, kernel_size=(1, 1), stride=(1, 1), pad_mode=same, padding=0, dilation=(1, 1), group=1, has_bias=False, weight_init=<mindspore.common.initializer.Normal object at 0xffff901e02e0>, bias_init=None, format=NCHW>
      (norm3): BatchNorm2d<num_features=1024, eps=1e-05, momentum=0.9, gamma=Parameter (name=layer3.4.norm3.gamma, shape=(1024,), dtype=Float32, requires_grad=False), beta=Parameter (name=layer3.4.norm3.beta, shape=(1024,), dtype=Float32, requires_grad=False), moving_mean=Parameter (name=layer3.4.norm3.moving_mean, shape=(1024,), dtype=Float32, requires_grad=False), moving_variance=Parameter (name=layer3.4.norm3.moving_variance, shape=(1024,), dtype=Float32, requires_grad=False)>
      (relu): ReLU<>
      >
    (5): ResidualBlock<
      (conv1): Conv2d<input_channels=1024, output_channels=256, kernel_size=(1, 1), stride=(1, 1), pad_mode=same, padding=0, dilation=(1, 1), group=1, has_bias=False, weight_init=<mindspore.common.initializer.Normal object at 0xffff901e02e0>, bias_init=None, format=NCHW>
      (norm1): BatchNorm2d<num_features=256, eps=1e-05, momentum=0.9, gamma=Parameter (name=layer3.5.norm1.gamma, shape=(256,), dtype=Float32, requires_grad=False), beta=Parameter (name=layer3.5.norm1.beta, shape=(256,), dtype=Float32, requires_grad=False), moving_mean=Parameter (name=layer3.5.norm1.moving_mean, shape=(256,), dtype=Float32, requires_grad=False), moving_variance=Parameter (name=layer3.5.norm1.moving_variance, shape=(256,), dtype=Float32, requires_grad=False)>
      (conv2): Conv2d<input_channels=256, output_channels=256, kernel_size=(3, 3), stride=(1, 1), pad_mode=same, padding=0, dilation=(1, 1), group=1, has_bias=False, weight_init=<mindspore.common.initializer.Normal object at 0xffff901e02e0>, bias_init=None, format=NCHW>
      (norm2): BatchNorm2d<num_features=256, eps=1e-05, momentum=0.9, gamma=Parameter (name=layer3.5.norm2.gamma, shape=(256,), dtype=Float32, requires_grad=False), beta=Parameter (name=layer3.5.norm2.beta, shape=(256,), dtype=Float32, requires_grad=False), moving_mean=Parameter (name=layer3.5.norm2.moving_mean, shape=(256,), dtype=Float32, requires_grad=False), moving_variance=Parameter (name=layer3.5.norm2.moving_variance, shape=(256,), dtype=Float32, requires_grad=False)>
      (conv3): Conv2d<input_channels=256, output_channels=1024, kernel_size=(1, 1), stride=(1, 1), pad_mode=same, padding=0, dilation=(1, 1), group=1, has_bias=False, weight_init=<mindspore.common.initializer.Normal object at 0xffff901e02e0>, bias_init=None, format=NCHW>
      (norm3): BatchNorm2d<num_features=1024, eps=1e-05, momentum=0.9, gamma=Parameter (name=layer3.5.norm3.gamma, shape=(1024,), dtype=Float32, requires_grad=False), beta=Parameter (name=layer3.5.norm3.beta, shape=(1024,), dtype=Float32, requires_grad=False), moving_mean=Parameter (name=layer3.5.norm3.moving_mean, shape=(1024,), dtype=Float32, requires_grad=False), moving_variance=Parameter (name=layer3.5.norm3.moving_variance, shape=(1024,), dtype=Float32, requires_grad=False)>
      (relu): ReLU<>
      >
    >
  (layer4): SequentialCell<
    (0): ResidualBlock<
      (conv1): Conv2d<input_channels=1024, output_channels=512, kernel_size=(1, 1), stride=(1, 1), pad_mode=same, padding=0, dilation=(1, 1), group=1, has_bias=False, weight_init=<mindspore.common.initializer.Normal object at 0xffff901e02e0>, bias_init=None, format=NCHW>
      (norm1): BatchNorm2d<num_features=512, eps=1e-05, momentum=0.9, gamma=Parameter (name=layer4.0.norm1.gamma, shape=(512,), dtype=Float32, requires_grad=False), beta=Parameter (name=layer4.0.norm1.beta, shape=(512,), dtype=Float32, requires_grad=False), moving_mean=Parameter (name=layer4.0.norm1.moving_mean, shape=(512,), dtype=Float32, requires_grad=False), moving_variance=Parameter (name=layer4.0.norm1.moving_variance, shape=(512,), dtype=Float32, requires_grad=False)>
      (conv2): Conv2d<input_channels=512, output_channels=512, kernel_size=(3, 3), stride=(2, 2), pad_mode=same, padding=0, dilation=(1, 1), group=1, has_bias=False, weight_init=<mindspore.common.initializer.Normal object at 0xffff901e02e0>, bias_init=None, format=NCHW>
      (norm2): BatchNorm2d<num_features=512, eps=1e-05, momentum=0.9, gamma=Parameter (name=layer4.0.norm2.gamma, shape=(512,), dtype=Float32, requires_grad=False), beta=Parameter (name=layer4.0.norm2.beta, shape=(512,), dtype=Float32, requires_grad=False), moving_mean=Parameter (name=layer4.0.norm2.moving_mean, shape=(512,), dtype=Float32, requires_grad=False), moving_variance=Parameter (name=layer4.0.norm2.moving_variance, shape=(512,), dtype=Float32, requires_grad=False)>
      (conv3): Conv2d<input_channels=512, output_channels=2048, kernel_size=(1, 1), stride=(1, 1), pad_mode=same, padding=0, dilation=(1, 1), group=1, has_bias=False, weight_init=<mindspore.common.initializer.Normal object at 0xffff901e02e0>, bias_init=None, format=NCHW>
      (norm3): BatchNorm2d<num_features=2048, eps=1e-05, momentum=0.9, gamma=Parameter (name=layer4.0.norm3.gamma, shape=(2048,), dtype=Float32, requires_grad=False), beta=Parameter (name=layer4.0.norm3.beta, shape=(2048,), dtype=Float32, requires_grad=False), moving_mean=Parameter (name=layer4.0.norm3.moving_mean, shape=(2048,), dtype=Float32, requires_grad=False), moving_variance=Parameter (name=layer4.0.norm3.moving_variance, shape=(2048,), dtype=Float32, requires_grad=False)>
      (relu): ReLU<>
      (down_sample): SequentialCell<
        (0): Conv2d<input_channels=1024, output_channels=2048, kernel_size=(1, 1), stride=(2, 2), pad_mode=same, padding=0, dilation=(1, 1), group=1, has_bias=False, weight_init=<mindspore.common.initializer.Normal object at 0xffff901e02e0>, bias_init=None, format=NCHW>
        (1): BatchNorm2d<num_features=2048, eps=1e-05, momentum=0.9, gamma=Parameter (name=layer4.0.down_sample.1.gamma, shape=(2048,), dtype=Float32, requires_grad=False), beta=Parameter (name=layer4.0.down_sample.1.beta, shape=(2048,), dtype=Float32, requires_grad=False), moving_mean=Parameter (name=layer4.0.down_sample.1.moving_mean, shape=(2048,), dtype=Float32, requires_grad=False), moving_variance=Parameter (name=layer4.0.down_sample.1.moving_variance, shape=(2048,), dtype=Float32, requires_grad=False)>
        >
      >
    (1): ResidualBlock<
      (conv1): Conv2d<input_channels=2048, output_channels=512, kernel_size=(1, 1), stride=(1, 1), pad_mode=same, padding=0, dilation=(1, 1), group=1, has_bias=False, weight_init=<mindspore.common.initializer.Normal object at 0xffff901e02e0>, bias_init=None, format=NCHW>
      (norm1): BatchNorm2d<num_features=512, eps=1e-05, momentum=0.9, gamma=Parameter (name=layer4.1.norm1.gamma, shape=(512,), dtype=Float32, requires_grad=False), beta=Parameter (name=layer4.1.norm1.beta, shape=(512,), dtype=Float32, requires_grad=False), moving_mean=Parameter (name=layer4.1.norm1.moving_mean, shape=(512,), dtype=Float32, requires_grad=False), moving_variance=Parameter (name=layer4.1.norm1.moving_variance, shape=(512,), dtype=Float32, requires_grad=False)>
      (conv2): Conv2d<input_channels=512, output_channels=512, kernel_size=(3, 3), stride=(1, 1), pad_mode=same, padding=0, dilation=(1, 1), group=1, has_bias=False, weight_init=<mindspore.common.initializer.Normal object at 0xffff901e02e0>, bias_init=None, format=NCHW>
      (norm2): BatchNorm2d<num_features=512, eps=1e-05, momentum=0.9, gamma=Parameter (name=layer4.1.norm2.gamma, shape=(512,), dtype=Float32, requires_grad=False), beta=Parameter (name=layer4.1.norm2.beta, shape=(512,), dtype=Float32, requires_grad=False), moving_mean=Parameter (name=layer4.1.norm2.moving_mean, shape=(512,), dtype=Float32, requires_grad=False), moving_variance=Parameter (name=layer4.1.norm2.moving_variance, shape=(512,), dtype=Float32, requires_grad=False)>
      (conv3): Conv2d<input_channels=512, output_channels=2048, kernel_size=(1, 1), stride=(1, 1), pad_mode=same, padding=0, dilation=(1, 1), group=1, has_bias=False, weight_init=<mindspore.common.initializer.Normal object at 0xffff901e02e0>, bias_init=None, format=NCHW>
      (norm3): BatchNorm2d<num_features=2048, eps=1e-05, momentum=0.9, gamma=Parameter (name=layer4.1.norm3.gamma, shape=(2048,), dtype=Float32, requires_grad=False), beta=Parameter (name=layer4.1.norm3.beta, shape=(2048,), dtype=Float32, requires_grad=False), moving_mean=Parameter (name=layer4.1.norm3.moving_mean, shape=(2048,), dtype=Float32, requires_grad=False), moving_variance=Parameter (name=layer4.1.norm3.moving_variance, shape=(2048,), dtype=Float32, requires_grad=False)>
      (relu): ReLU<>
      >
    (2): ResidualBlock<
      (conv1): Conv2d<input_channels=2048, output_channels=512, kernel_size=(1, 1), stride=(1, 1), pad_mode=same, padding=0, dilation=(1, 1), group=1, has_bias=False, weight_init=<mindspore.common.initializer.Normal object at 0xffff901e02e0>, bias_init=None, format=NCHW>
      (norm1): BatchNorm2d<num_features=512, eps=1e-05, momentum=0.9, gamma=Parameter (name=layer4.2.norm1.gamma, shape=(512,), dtype=Float32, requires_grad=False), beta=Parameter (name=layer4.2.norm1.beta, shape=(512,), dtype=Float32, requires_grad=False), moving_mean=Parameter (name=layer4.2.norm1.moving_mean, shape=(512,), dtype=Float32, requires_grad=False), moving_variance=Parameter (name=layer4.2.norm1.moving_variance, shape=(512,), dtype=Float32, requires_grad=False)>
      (conv2): Conv2d<input_channels=512, output_channels=512, kernel_size=(3, 3), stride=(1, 1), pad_mode=same, padding=0, dilation=(1, 1), group=1, has_bias=False, weight_init=<mindspore.common.initializer.Normal object at 0xffff901e02e0>, bias_init=None, format=NCHW>
      (norm2): BatchNorm2d<num_features=512, eps=1e-05, momentum=0.9, gamma=Parameter (name=layer4.2.norm2.gamma, shape=(512,), dtype=Float32, requires_grad=False), beta=Parameter (name=layer4.2.norm2.beta, shape=(512,), dtype=Float32, requires_grad=False), moving_mean=Parameter (name=layer4.2.norm2.moving_mean, shape=(512,), dtype=Float32, requires_grad=False), moving_variance=Parameter (name=layer4.2.norm2.moving_variance, shape=(512,), dtype=Float32, requires_grad=False)>
      (conv3): Conv2d<input_channels=512, output_channels=2048, kernel_size=(1, 1), stride=(1, 1), pad_mode=same, padding=0, dilation=(1, 1), group=1, has_bias=False, weight_init=<mindspore.common.initializer.Normal object at 0xffff901e02e0>, bias_init=None, format=NCHW>
      (norm3): BatchNorm2d<num_features=2048, eps=1e-05, momentum=0.9, gamma=Parameter (name=layer4.2.norm3.gamma, shape=(2048,), dtype=Float32, requires_grad=False), beta=Parameter (name=layer4.2.norm3.beta, shape=(2048,), dtype=Float32, requires_grad=False), moving_mean=Parameter (name=layer4.2.norm3.moving_mean, shape=(2048,), dtype=Float32, requires_grad=False), moving_variance=Parameter (name=layer4.2.norm3.moving_variance, shape=(2048,), dtype=Float32, requires_grad=False)>
      (relu): ReLU<>
      >
    >
  (avg_pool): AvgPool2d<kernel_size=7, stride=1, pad_mode=VALID>
  (flatten): Flatten<>
  (fc): Dense<input_channels=2048, output_channels=2, has_bias=True>
  >

训练和评估

开始训练模型,因为不用计算部分梯度,时间较快。保存评估精度最高的ckpt文件于当前路径的./BestCheckpoint/resnet50-best-freezing-param.ckpt。

import mindspore as ms
import matplotlib.pyplot as plt
import os
import time 


dataset_train = create_dataset_canidae(data_path_train, "train")
step_size_train = dataset_train.get_dataset_size()

dataset_val = create_dataset_canidae(data_path_val, "val")
step_size_val = dataset_val.get_dataset_size()

num_epochs = 5

# 创建迭代器
data_loader_train = dataset_train.create_tuple_iterator(num_epochs=num_epochs)
data_loader_val = dataset_val.create_tuple_iterator(num_epochs=num_epochs)
best_ckpt_dir = "./BestCheckpoint"
best_ckpt_path = "./BestCheckpoint/resnet50-best-freezing-param.ckpt"



# 开始循环训练
print("Start Training Loop ...")

best_acc = 0  ## 保存最好的精度
for epoch in range(num_epochs):
    losses = []  ## 保存当前epoch中每个batch_size下的损失
    net_work.set_train()

    epoch_start = time.time()

    # 为每轮训练读入数据
    for i, (images, labels) in enumerate(data_loader_train):
        labels = labels.astype(ms.int32)
        ## 包含反向传播算法的训练步骤。
        loss = train_step(images, labels)
        losses.append(loss)

    # 每个epoch结束后,验证准确率

    acc = model1.eval(dataset_val)['Accuracy']

    epoch_end = time.time()
    epoch_seconds = (epoch_end - epoch_start) * 1000
    step_seconds = epoch_seconds/step_size_train

    print("-" * 20) 
    ### 输出当前epoch中所有batch的平均损失和最好的验证集的精度。
    print("Epoch: [%3d/%3d], Average Train Loss: [%5.3f], Accuracy: [%5.3f]" % (
        epoch+1, num_epochs, sum(losses)/len(losses), acc
    ))
    ## 输出当前 epoch 训练耗时,以及每个batch的平均耗时。
    print("epoch time: %5.3f ms, per step time: %5.3f ms" % (
        epoch_seconds, step_seconds
    ))

    ## 更新保存最好的精度值下的网络权重文件
    if acc > best_acc:
        best_acc = acc
        if not os.path.exists(best_ckpt_dir):
            os.mkdir(best_ckpt_dir)
        ms.save_checkpoint(net_work, best_ckpt_path)

print("=" * 80)
print(f"End of validation the best Accuracy is: {best_acc: 5.3f}, "
      f"save the best ckpt file in {best_ckpt_path}", flush=True)

训练过程截图:

如上图,第一轮用时最多,这里面可能是因为初次模型加载到显存比较费时吧,后面就非常快了。每一个batch 18个图片,总共14个batch 243张图片,差不多1秒左右就完成一轮训练了。

可视化模型预测 

使用固定特征得到的最好的模型权重文件 "./BestCheckpoint/resnet50-best-freezing-param.ckpt" 对验证集的狼和狗图像数据进行预测。若预测字体为蓝色即为预测正确,若预测字体为红色则预测错误,如图,可以看到准确率还是非常高的。

import matplotlib.pyplot as plt
import mindspore as ms

def visualize_model(best_ckpt_path, val_ds):
    net = resnet50()
    # 全连接层输入层的大小
    in_channels = net.fc.in_channels
    # 输出通道数大小为狼狗分类数2
    head = nn.Dense(in_channels, 2)
    # 重置全连接层
    net.fc = head
    # 平均池化层kernel size为7
    avg_pool = nn.AvgPool2d(kernel_size=7)
    # 重置平均池化层
    net.avg_pool = avg_pool
    # 加载模型参数
    param_dict = ms.load_checkpoint(best_ckpt_path)
    ms.load_param_into_net(net, param_dict)
    model = train.Model(net)
    # 加载验证集的数据进行验证
    data = next(val_ds.create_dict_iterator())
    images = data["image"].asnumpy()
    labels = data["label"].asnumpy()
    class_name = {0: "dogs", 1: "wolves"}
    # 预测图像类别
    output = model.predict(ms.Tensor(data['image']))
    pred = np.argmax(output.asnumpy(), axis=1)

    # 显示图像及图像的预测值
    plt.figure(figsize=(5, 5))
    for i in range(4):
        plt.subplot(2, 2, i + 1)
        # 若预测正确,显示为蓝色;若预测错误,显示为红色
        color = 'blue' if pred[i] == labels[i] else 'red'
        plt.title('predict:{}'.format(class_name[pred[i]]), color=color)
        picture_show = np.transpose(images[i], (1, 2, 0))
        mean = np.array([0.485, 0.456, 0.406])
        std = np.array([0.229, 0.224, 0.225])
        picture_show = std * picture_show + mean
        picture_show = np.clip(picture_show, 0, 1)
        plt.imshow(picture_show)
        plt.axis('off')

    plt.show()


visualize_model(best_ckpt_path, dataset_val)

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值