从代码学习深度学习 - 实战 Kaggle 比赛:图像分类 (CIFAR-10 PyTorch版)

news2025/5/16 18:50:25

文章目录

  • 前言
  • 1. 读取并整理数据集
    • 1.1 读取标签文件
    • 1.2 划分训练集和验证集
    • 1.3 整理测试集
    • 1.4 执行数据整理
  • 2. 图像增广
    • 2.1 训练集图像变换
    • 2.2 测试集(和验证集)图像变换
  • 3. 读取数据集
    • 3.1 创建 Dataset 对象
    • 3.2 创建 DataLoader 对象
  • 4. 定义模型
    • 4.1 获取 ResNet-18 模型
    • 4.2 损失函数
  • 5. 定义训练函数
  • 6. 训练和验证模型
  • 7. 在完整训练集上训练并预测测试集
  • 8. 辅助工具代码 (`utils_for_train.py` 和 `utils_for_huitu.py`)
    • 8.1 `utils_for_train.py`
    • 8.2 `utils_for_huitu.py`
  • 总结

前言

欢迎来到我们的深度学习实战系列!在本文中,我们将深入探讨一个经典的图像分类问题——CIFAR-10挑战,并通过一个实际的 Kaggle 比赛流程来学习。我们将从原始图像文件开始,一步步进行数据整理、图像增广、模型构建、训练、评估,并最终生成提交结果。本教程将全程使用 PyTorch 框架,并详细解释每一段代码的功能和背后的原理。

在以往的教程中,我们可能更多地依赖深度学习框架的高级API直接获取处理好的张量格式数据集。但在真实的比赛和项目中,我们往往需要从更原始的数据形态(如.jpg, .png 文件和标签列表)入手。本篇博客旨在弥补这一差距,带你体验一个相对完整的小型 Kaggle 比赛pipeline。

我们将使用一个名为 kaggle_cifar10_tiny 的数据集,它是 CIFAR-10 的一个小型子集,方便我们快速迭代和学习。让我们开始吧!

完整代码:下载链接

1. 读取并整理数据集

在任何机器学习项目中,数据准备都是至关重要的一步。对于图像分类任务,这通常意味着读取图像文件,将其与对应的标签关联起来,并组织成适合模型训练的结构。

1.1 读取标签文件

我们的原始数据包含一个 trainLabels.csv 文件,其中记录了训练集中每张图像的文件名及其类别标签。下面的代码定义了一个函数 read_csv_labels 来读取这个 CSV 文件,并将其内容转换为一个字典,方便后续查找。

import os

# 定义数据目录路径
data_dir = 'kaggle_cifar10_tiny'

def read_csv_labels(fname):
    """
    读取CSV文件并返回文件名到标签的映射字典
    
    参数:
        fname (str): CSV文件路径
        
    返回:
        dict: 包含文件名到标签的映射字典
            - 键 (str): 图像文件名 (不含扩展名)
            - 值 (str): 对应的类别标签
    """
    with open(fname, 'r') as f:
        # 跳过CSV文件的第一行(列名)
        lines = f.readlines()[1:]
    
    # 将每行按逗号分割成tokens
    # tokens是一个列表,每个元素是[文件名, 标签]形式的列表
    # 维度: tokens - list[list[str, str]], 长度为样本数量
    tokens = [l.rstrip().split(',') for l in lines]
    
    # 构建字典,将文件名映射到对应的标签
    # 维度: 返回的字典 - dict{str: str}, 键为文件名,值为标签
    return dict(((name, label) for name, label in tokens))

# 读取训练标签文件
# 维度: labels - dict{str: str}, 键为文件名,值为标签
labels = read_csv_labels(os.path.join(data_dir, 'trainLabels.csv'))

# 打印训练样本数量(即字典中键值对的数量)
# len(labels) - int, 表示训练样本的数量
print('# 训练样本:', len(labels))

# 打印类别数量(即标签种类的数量)
# len(set(labels.values())) - int, 表示不同类别的数量
print('# 类别:', len(set(labels.values())))

运行上述代码,我们会得到训练样本的总数和类别的总数。例如:

# 训练样本: 1000
# 类别: 10

这表明我们的微型数据集中有1000个训练样本,分布在10个类别中。

1.2 划分训练集和验证集

为了评估模型的泛化能力并进行超参数调整,我们需要从原始训练数据中划分出一部分作为验证集。下面的 reorg_train_valid 函数实现了这个功能。它会根据指定的验证集比例 valid_ratio,从每个类别中抽取一定数量的样本放入验证集,其余的放入新的训练集。同时,它会将所有原始训练图像(未划分前)也复制到一个单独的目录,以便后续在完整训练数据上训练模型。

copyfile 是一个辅助函数,用于将单个文件复制到目标目录,并在目标目录不存在时创建它。

import shutil
import collections
import math

def copyfile(filename, target_dir):
    """
    将文件复制到目标目录
    
    参数:
        filename (str): 源文件路径
        target_dir (str): 目标目录路径
    """
    # 创建目标目录(如果不存在)
    # target_dir - str, 目标目录路径
    os.makedirs(target_dir, exist_ok=True)
    
    # 复制文件到目标目录
    # filename - str, 源文件路径
    # target_dir - str, 目标目录路径
    shutil.copy(filename, target_dir)

def reorg_train_valid(data_dir, labels, valid_ratio):
    """
    将验证集从原始的训练集中拆分出来
    
    参数:
        data_dir (str): 数据集目录路径
        labels (dict): 文件名到标签的映射字典
        valid_ratio (float): 验证集比例,取值范围[0, 1]
    
    返回:
        int: 每个类别中分配给验证集的样本数量
    """
    # 获取样本数最少的类别中的样本数
    # collections.Counter(labels.values()) - Counter{str: int}, 统计每个标签出现的次数
    # most_common() - list[(str, int)], 按出现次数降序排列的(标签, 出现次数)列表
    # most_common()[-1] - tuple(str, int), 出现次数最少的(标签, 出现次数)对
    # most_common()[-1][1] - int, 出现次数最少的标签的出现次数
    # n - int, 样本数最少的类别中的样本数
    n = collections.Counter(labels.values()).most_common()[-1][1]
    
    # 计算验证集中每个类别应有的样本数
    # math.floor(n * valid_ratio) - int, 向下取整的验证集样本数
    # max(1, math.floor(n * valid_ratio)) - int, 确保每个类别至少有1个样本
    # n_valid_per_label - int, 每个类别分配给验证集的样本数
    n_valid_per_label = max(1, math.floor(n * valid_ratio))
    
    # 用于跟踪每个类别已分配到验证集的样本数
    # label_count - dict{str: int}, 键为标签,值为该标签在验证集中的样本数
    label_count = {
   }
    
    # 遍历训练目录中的所有文件
    # train_file - str, 训练集中的文件名
    for train_file in os.listdir(os.path.join(data_dir, 'train')):
        # 获取文件对应的标签
        # train_file.split('.')[0] - str, 去除文件扩展名的文件名
        # label - str, 文件对应的类别标签
        label = labels[train_file.split('.')[0]]
        
        # 构建完整的源文件路径
        # fname - str, 训练文件的完整路径
        fname = os.path.join(data_dir, 'train', train_file)
        
        # 将所有文件复制到train_valid目录下对应类别目录中
        # os.path.join(data_dir, 'train_valid_test', 'train_valid', label) - str, 目标目录路径
        copyfile(fname, os.path.join(data_dir, 'train_valid_test',
                                     'train_valid', label))
        
        # 如果该类别在验证集中的样本数未达到目标数量,则将文件添加到验证集
        if label not in label_count or label_count[label] < n_valid_per_label:
            # 复制文件到验证集目录对应类别文件夹下
            # os.path.join(data_dir, 'train_valid_test', 'valid', label) - str, 验证集目标目录路径
            copyfile(fname, os.path.join(data_dir, 'train_valid_test',
                                         'valid', label))
            # 更新该类别在验证集中的样本计数
            # label_count.get(label, 0) - int, 当前类别已有的验证集样本数,如果不存在则为0
            # label_count[label] - int, 更新后该类别在验证集中的样本数
            label_count[label] = label_count.get(label, 0) + 1
        else:
            # 如果该类别在验证集中的样本数已达到目标数量,则将文件添加到训练集
            # os.path.join(data_dir, 'train_valid_test', 'train', label) - str, 训练集目标目录路径
            copyfile(fname, os.path.join(data_dir, 'train_valid_test',
                                         'train', label))
    
    # 返回每个类别中分配给验证集的样本数量
    # n_valid_per_label - int
    return n_valid_per_label

1.3 整理测试集

测试集图像最初也存放在一个扁平的目录 test 中。为了方便后续使用 PyTorch 的 ImageFolder 类进行读取,我们需要将它们也组织到一个特定的目录结构下。reorg_test 函数将所有测试图像复制到 train_valid_test/test/unknown 目录下。这里将它们归类为 “unknown” 是因为在预测阶段,我们并不知道它们的真实标签。

import os
import shutil

def reorg_test(data_dir):
    """
    在预测期间整理测试集,以方便读取
    
    该函数将测试集图像文件复制到组织化的目录结构中,所有测试图像都归为"unknown"类别
    
    参数:
        data_dir (str): 数据集根目录路径
    """
    # 遍历测试目录中的所有文件
    # data_dir - str, 数据集根目录路径
    # os.path.join(data_dir, 'test') - str, 测试集目录的完整路径
    # os.listdir(...) - list[str], 测试集目录中的所有文件名列表
    # test_file - str, 当前处理的测试文件名
    for test_file in os.listdir(os.path.join(data_dir, 'test')):
        # 构建源文件路径
        # os.path.join(data_dir, 'test', test_file) - str, 测试文件的完整源路径
        # 构建目标目录路径,所有测试文件都放在'unknown'类别文件夹下
        # os.path.join(data_dir, 'train_valid_test', 'test', 'unknown') - str, 目标目录的完整路径
        
        # 将测试文件复制到组织化的目录结构中
        # 源文件: 原始测试集目录下的测试文件
        # 目标位置: train_valid_test/test/unknown/文件名
        copyfile(os.path.join(data_dir, 'test', test_file),
                 os.path.join(data_dir, 'train_valid_test', 'test',
                              'unknown'))

1.4 执行数据整理

现在,我们将上述函数整合到 reorg_cifar10_data 中,并执行它来完成整个数据集的重新组织。

import os
import shutil
import collections
import math

def reorg_cifar10_data(data_dir, valid_ratio):
    """
    重新组织CIFAR-10数据集,划分为训练集、验证集和测试集
    
    该函数完成CIFAR-10数据集的整体重组,包含三个主要步骤:
    1. 读取训练标签文件获取图像标签
    2. 重组训练集和验证集(从原始训练集中划分出验证集)
    3. 重组测试集(将所有测试图像归类为"unknown"类别)
    
    参数:
        data_dir (str): CIFAR-10数据集根目录路径
        valid_ratio (float): 验证集比例,取值范围[0, 1],表示从训练集中划分多少比例作为验证集
    """
    # 步骤1: 读取训练标签文件,获取文件名到标签的映射
    # data_dir - str, 数据集根目录路径
    # os.path.join(data_dir, 'trainLabels.csv') - str, 训练标签文件的完整路径
    # read_csv_labels(...) - 函数调用,读取CSV文件并返回字典
    # labels - dict{str: str}, 键为图像文件名,值为对应的类别标签
    labels = read_csv_labels(os.path.join(data_dir, 'trainLabels.csv'))
    
    # 步骤2: 重组训练集和验证集
    # 将原始训练集按照指定比例分为训练集和验证集,并保存到对应目录
    # data_dir - str, 数据集根目录路径
    # labels - dict{str: str}, 文件名到标签的映射字典
    # valid_ratio - float, 验证集比例
    # reorg_train_valid(...) - 函数调用,重组训练集和验证集
    reorg_train_valid(data_dir, labels, valid_ratio)
    
    # 步骤3: 重组测试集
    # 将测试集图像整理到组织化的目录结构中,便于后续处理
    # data_dir - str, 数据集根目录路径
    # reorg_test(...) - 函数调用,重组测试集
    reorg_test(data_dir)

我们设定批量大小 batch_size 为32,验证集比例 valid_ratio 为0.1(即10%的训练数据用作验证)。

import os
import shutil
import collections
import math


# batch_size - int, 训练和评估时的批量大小
batch_size = 32 

# 设置验证集比例
# valid_ratio - float, 验证集在原始训练集中的比例,取值范围[0, 1]
# 值为0.1表示将10%的原始训练数据划分为验证集
valid_ratio = 0.1

# 重组CIFAR-10数据集
# data_dir - str, CIFAR-10数据集的根目录路径
# valid_ratio - float, 验证集比例
# reorg_cifar10_data(...) - 函数调用,执行数据集重组操作
# 该函数会将原始CIFAR-10数据集重组为训练集、验证集和测试集三部分
# 重组后的数据结构为:
# - train_valid_test/train/: 训练集,按类别组织
# - train_valid_test/valid/: 验证集,按类别组织
# - train_valid_test/test/unknown/: 测试集,所有测试图像
# - train_valid_test/train_valid/: 原始训练集(包含验证部分),按类别组织
reorg_cifar10_data(data_dir, valid_ratio)

执行完毕后,data_dir 目录下会生成一个新的 train_valid_test 文件夹,其结构如下:

kaggle_cifar10_tiny/
├── trainLabels.csv
├── train/ (原始训练图像)
├── test/ (原始测试图像)
└── train_valid_test/
    ├── train/
    │   ├── airplane/
    │   ├── automobile/
    │   └── ... (其他8个类别)
    ├── valid/
    │   ├── airplane/
    │   ├── automobile/
    │   └── ... (其他8个类别)
    ├── train_valid/ (原始训练图像,按类别组织)
    │   ├── airplane/
    │   ├── automobile/
    │   └── ... (其他8个类别)
    └── test/
        └── unknown/ (所有测试图像)

2. 图像增广

图像增广(Data Augmentation)是一种通过对训练图像进行一系列随机变换来生成新样本的技术。它可以有效增加训练数据的多样性,减少模型过拟合,提高模型的泛化能力。

2.1 训练集图像变换

对于训练集,我们应用以下变换:

  1. Resize (40x40): 将图像调整到 40x40 像素。
  2. RandomResizedCrop (32x32, scale=(0.64, 1.0), ratio=(1.0, 1.0)): 随机裁剪一个面积为原图 64% 到 100% 的正方形区域,并将其缩放到 32x32 像素。这有助于模型学习到图像的不同部分。
  3. RandomHorizontalFlip: 以 50% 的概率水平翻转图像。
  4. ToTensor: 将 PIL 图像转换为 PyTorch 张量,并将像素值从 [0, 255] 归一化到 [0.0, 1.0]。
  5. Normalize: 使用 CIFAR-10 数据集的均值和标准差对图像进行标准化。这有助于加速模型收敛。
import torch
import torchvision
import torchvision.transforms as transforms

# 定义训练数据的图像变换流水线
# transform_train - torchvision.transforms.Compose对象, 包含多个按顺序应用的图像变换操作
transform_train = torchvision.transforms.Compose([
    # 步骤1: 调整图像大小
    # 将输入图像(任意大小)调整为40×40像素的正方形
    # 输入: 任意大小的PIL图像
    # 输出: 40×40像素的PIL图像
    torchvision.transforms.Resize(40),
    
    # 步骤2: 随机裁剪并调整大小
    # 随机裁剪一个面积为原始图像面积0.64~1倍(约为原图的80%~100%)的正方形区域,
    # 然后将该区域调整为32×32像素的图像
    # scale参数: (0.64, 1.0) - tuple(float, float), 表示裁剪区域面积占原图面积的比例范围
    # ratio参数: (1.0, 1.0) - tuple(float, float), 表示裁剪区域的宽高比范围,此处固定为1.0表示裁剪正方形
    # 输入: 40×40像素的PIL图像
    # 输出: 32×32像素的PIL图像
    torchvision.transforms.RandomResizedCrop(32, scale=(0.64, 1.0),
                                             ratio=(1.0, 1.0)),
    
    # 步骤3: 随机水平翻转
    # 以0.5的概率水平翻转图像,增加数据多样性
    # 输入: 32×32像素的PIL图像
    # 输出: 32×32像素的PIL图像,可能已水平翻转
    torchvision.transforms.RandomHorizontalFlip(),
    
    # 步骤4: 转换为张量
    # 将PIL图像转换为形状为[C, H, W]的张量,并将像素值范围从[0, 255]缩放到[0.0, 1.0]
    # 输入: 32×32像素的PIL图像
    # 输出: torch.Tensor, 形状为[3, 32, 32],值范围为[0.0, 1.0]
    torchvision.transforms.ToTensor(),
    
    # 步骤5: 标准化
    # 对每个通道应用标准化处理:(x - mean) / std
    

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2377075.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

嵌入式调试新宠!J-Scope:免费+实时数据可视化,让MCU调试效率飙升!

&#x1f4cc; 痛点直击&#xff1a;调试还在用“断点打印”&#xff1f; 嵌入式开发中&#xff0c;你是否也经历过这些崩溃瞬间&#xff1f; 想实时观察变量变化&#xff0c;代码里插满printf&#xff0c;结果拖垮系统性能&#xff1f; 断点调试打断程序运行&#xff0c;时序…

微信小程序学习之搜索框

1、第一步&#xff0c;我们在index.json中引入vant中的搜索框控件&#xff1a; {"usingComponents": {"van-search": "vant/weapp/search/index"} } 2、第二步&#xff0c;直接在index.wxml中添加布局&#xff1a; <view class"index…

Altium Designer AD如何输出PIN带网络名的PDF装配图

Altium Designer AD如何输出PIN带网络名的PDF装配图 文描述在Altium Designer版本中设置焊盘网络名时遇到的问题&#xff0c;网络名大小不一致&#xff0c;部分PAD的网络名称未显示&#xff0c;可能涉及字符大小设置和版本差异。 参考 1.AD导出PCB装配图 https://blog.csd…

VMware虚拟机 安装 CentOS 7

原文链接: VMware虚拟机 安装 CentOS 7 安装准备 软件: VMware Workstation Pro 17.6.3 镜像: CentOS-7.0-1406-x86_64-DVD.iso 我打包好放这了&#xff0c;VMware 和 CentOS7 &#xff0c;下载即可。 关于VMware Workstation Pro 17.6.3&#xff0c;傻瓜式安装即可。 CentO…

Python训练打卡Day22

复习日&#xff1a; 1.标准化数据&#xff08;聚类前通常需要标准化&#xff09; scaler StandardScaler() X_scaled scaler.fit_transform(X) StandardScaler() &#xff1a;这部分代码调用了 StandardScaler 类的构造函数。在Python中&#xff0c;当你在类名后面加上括号…

Cold Diffusion: Inverting Arbitrary Image Transforms Without Noise论文阅读

冷扩散&#xff1a;无需噪声的任意图像变换反转 摘要 标准扩散模型通常涉及两个核心步骤&#xff1a;图像降质 &#xff08;添加高斯噪声&#xff09;和图像恢复 &#xff08;去噪操作&#xff09;。本文发现&#xff0c;扩散模型的生成能力并不强烈依赖于噪声的选择&#xf…

嵌软面试每日一阅----通信协议篇(二)之TCP

一. TCP和UDP的区别 可靠性 TCP&#xff1a;✅ 可靠传输&#xff08;三次握手 重传机制&#xff09; UDP&#xff1a;❌ 不可靠&#xff08;可能丢包&#xff09; 连接方式 TCP&#xff1a;面向连接&#xff08;需建立/断开连接&#xff09; UDP&#xff1a;无连接&#xff0…

机器学习 --- 模型选择与调优

机器学习 — 模型选择与调优 文章目录 机器学习 --- 模型选择与调优一&#xff0c;交叉验证1.1 保留交叉验证HoldOut1.2 K-折交叉验证(K-fold)1.3 分层k-折交叉验证Stratified k-fold 二&#xff0c;超参数搜索三&#xff0c;鸢尾花数据集示例四&#xff0c;现实世界数据集示例…

AGI大模型(15):向量检索之调用ollama向量数据库

这里介绍将向量模型下载到本地,这里使用ollama,现在本地安装ollama,这里就不过多结束了。直接从下载开始。 1 下载模型 首先搜索模型,这里使用bge-large模型,你可以根据自己的需要修改。 点击进入,复制命令到命令行工具中执行。 安装后查看: 2 代码实现 先下载ollama…

什么是Agentic AI(代理型人工智能)?

什么是Agentic AI&#xff08;代理型人工智能&#xff09;&#xff1f; 一、概述 Agentic AI&#xff08;代理型人工智能&#xff09;是一类具备自主决策、目标导向性与持续行动能力的人工智能系统。与传统AI系统依赖外部输入和显式命令不同&#xff0c;Agentic AI在设定目标…

day 17 无监督学习之聚类算法

一、聚类流程 1. 利用聚类发现数据模式 无监督算法中的聚类&#xff0c;目的就是将数据点划分成不同的组或 “簇”&#xff0c;使得同一簇内的数据点相似度较高&#xff0c;而不同簇的数据点相似度较低&#xff0c;从而发现数据中隐藏的模式。 2. 对聚类后的类别特征进行可视…

时源芯微| KY键盘接口静电浪涌防护方案

KY键盘接口静电浪涌防护方案通过集成ESD保护元件、电阻和连接键&#xff0c;形成了一道有效的防护屏障。当键盘接口受到静电放电或其他浪涌冲击时&#xff0c;该方案能够迅速将过电压和过电流引导至地&#xff0c;从而保护后续电路免受损害。 ESD保护元件是方案中的核心部分&a…

CodeBuddy编程新范式

不会写&#xff1f;不想写&#xff1f; 腾讯推出的CodeBuddy彻底解放双手。 示例 以下是我对CodeBuddy的一个小体验。 我只用一行文字对CodeBuddy说明了一下我的需求&#xff0c;剩下的全部就交给了CodeBuddy&#xff0c;我需要做的就是验收结果即可。 1.首先CodeBuddy会对任…

小刚说C语言刷题—1088求两个数M和N的最大公约数

1.题目描述 求两个正整数 M 和 N 的最大公约数(M&#xff0c;N都在长整型范围内&#xff09; .输入 输入一行&#xff0c;包括两个正整数。 输出 输出只有一行&#xff0c;包括1个正整数。 样例 输入 45 60 输出 15 2.参考代码(C语言版) #include <stdio.h> …

【LLIE专题】基于码本先验与生成式归一化流的低光照图像增强新方法

GLARE: Low Light Image Enhancement via Generative Latent Feature based Codebook Retrieval&#xff08;2024&#xff0c;ECCV&#xff09; 专题介绍一、研究背景二、GLARE方法阶段一&#xff1a;正常光照代码本学习&#xff08;Normal-Light Codebook Learning&#xff09…

[MySQL数据库] SQL优化

&#x1f338;个人主页:https://blog.csdn.net/2301_80050796?spm1000.2115.3001.5343 &#x1f3f5;️热门专栏: &#x1f9ca; Java基本语法(97平均质量分)https://blog.csdn.net/2301_80050796/category_12615970.html?spm1001.2014.3001.5482 &#x1f355; Collection与…

网络编程epoll和udp

# epoll模型核心要点## 1. epoll核心概念### 1.1 高效IO多路复用- 监视列表与激活列表分离- 内核使用红黑树存储描述符- 边缘触发模式(EPOLLET)支持### 1.2 事件触发机制- **水平触发(LT)**&#xff1a;- 默认模式&#xff0c;类似select/poll- 数据未读完持续触发事件- **边缘…

【iOS】源码阅读(四)——isa与类关联的原理

文章目录 前言OC对象本质探索clang探索对象本质objc_setProperty源码探索 cls与类的关联原理为什么说bits与cls为互斥关系isa的类型isa_t原理探索isa与类的关联 总结 前言 本篇文章主要是笔者在学习和理解类与isa的关联关系时所写的笔记。 OC对象本质探索 在学习和理解类与isa…

uniapp 常用 UI 组件库

1. uView UI 特点&#xff1a; 组件丰富&#xff1a;提供覆盖按钮、表单、图标、表格、导航、图表等场景的内置组件。跨平台支持&#xff1a;兼容 App、H5、小程序等多端。高度可定制&#xff1a;支持主题定制&#xff0c;组件样式灵活。实用工具类&#xff1a;提供时间、数组操…

SCI写作开挂!把Grammarly语法修订嵌入word

详细分享如何把Grammarly嵌入Word&#xff0c;实现英文写作时的实时语法校改。 ①进入Grammarly官网 ②点击右上角的“Get Grammarly Its free”会直接跳转到注册或者登录界面&#xff0c;如果还没有账号先注册。 ③注册或登录后进入这个页面&#xff0c;点击“Support”。 ④…