ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Ubuntu PyTorch/TensorFlow GPU 环境搭建与版本匹配

Ubuntu PyTorch/TensorFlow GPU 环境搭建与版本匹配 Ubuntu 上把 TensorFlow 和 PyTorch 装到能用、好用、GPU 跑得起来这件事说起来一句话做起来能卡人一整天。我自己前前后后在不同机器、不同版本上折腾过十几次从实验室的双系统台式机到云上的裸机实例踩过的坑足够写一本小册子。这篇就把整个流程从头到尾捋一遍从系统准备、显卡驱动、CUDA 版本推算到 PyTorch 和 TensorFlow 两套框架的具体安装、验证、共存隔离再到排查思路和日常维护。内容偏向实战适合刚上手的新手也适合已经装过几次但总在版本匹配上翻车的朋友。看完你至少能做到两件事知道每一步为什么这么做以及出问题的时候知道去哪查。1. 开工前的整体思路与方案选型配环境这件事最怕的就是上来就敲命令。命令敲得再快方向错了照样白干。所以我习惯先把整条链路想清楚我要跑什么模型需求决定框架框架决定 CUDA 版本CUDA 版本决定驱动版本驱动版本又反过来受显卡型号和系统内核约束。这条链是单向的从下往上补比从上往下改省事得多。1.1 为什么大家偏爱在 Ubuntu 上做深度学习很多人会问Windows 上不是也能装 PyTorch 吗为什么非要折腾 Ubuntu。原因不在于哪个系统更高级而在于生态的默认假设。绝大多数开源仓库的 README、CI 脚本、Docker 镜像都是围绕 Linux 写的很多冷门算子、C 扩展、自定义 CUDA kernel 在 Windows 上要么没有预编译包要么编译报错一堆链接问题。你在 Windows 上装完 PyTorch 跑通 MNIST 没问题但一旦要编译某个第三方库比如涉及nvcc的扩展Windows 的 MSVC 和 CUDA 工具链配合就够你喝一壶。Ubuntu 的另一个好处是包管理和依赖链条清晰。apt装系统级依赖conda 管 Python 层依赖两者分工明确不会互相污染。还有一点很实际服务器、集群、云实例绝大多数跑的是 Ubuntu你在本地练熟的操作迁移到远程机几乎零成本。这一点对以后要上多卡训练的人来说提前适应是值得的。那选哪个版本我的建议是 LTS 版本比如 22.04 LTS 或者 24.04 LTS。非 LTS 版本支持周期短显卡驱动和内核更新频繁容易出现驱动和新内核不兼容的情况。新硬件比如很新的显卡可能反而需要较新的内核才能识别这种情况可以先用 24.04实在不行再考虑升级内核而不是直接上非 LTS。1.2 Anaconda、Miniconda 和原生 pip 到底怎么选环境管理这块我的结论很明确用 Miniconda不用完整版 Anaconda。Anaconda 装完几个 G预装了上百个包其中大部分你一辈子用不到还容易在后续conda install时因为基础环境的包冲突而卡住。Miniconda 只有 conda 和 Python干净利落需要什么装什么。为什么一定要用虚拟环境因为深度学习项目的依赖冲突是常态。项目 A 要 PyTorch 1.13 配 CUDA 11.7项目 B 要 PyTorch 2.3 配 CUDA 12.1你不可能让它们共用一个 Python 环境。conda 环境的好处是它不仅能隔离 Python 包还能隔离 CUDA 运行时库。这点很关键当你用conda install pytorch-cuda12.1的时候conda 会把对应版本的 CUDA 运行时装进这个环境内部不依赖系统全局的 CUDA。这样不同环境可以共存不同的 CUDA 版本互不干扰。pip 和 conda 混用是另一个高频翻车点。原则是在同一个环境里能用 conda 装的用 condaconda 没有的再用 pip而且装完 pip 包之后尽量不要再conda install新东西否则 conda 可能会覆盖掉 pip 装的依赖版本。我个人的习惯是PyTorch 全家桶走 conda因为 conda 版本会帮你把 CUDA 运行时一起处理了TensorFlow 走 pip因为官方对 pip 支持最全conda 的 TF 版本更新偏慢。1.3 显卡、驱动、CUDA、cuDNN 四层关系一次讲透很多人搞混这几个概念我用一个类比说清楚。把 GPU 想象成一台机器显卡驱动是这台机器的电源和说明书负责让操作系统认识它、能给它通电CUDA 是操作这台机器的专用语言和工具库cuDNN 是 CUDA 之上专门为深度学习优化的一层加速库卷积、池化这些运算的底层实现都在里面。关键点在于驱动是全局的一个系统只装一个CUDA 和 cuDNN 是可以按环境隔离的。nvidia-smi右上角显示的那个 CUDA Version: 12.4意思是当前驱动最高支持到 CUDA 12.4它不代表你装了 CUDA 12.4。这个数字经常把人误导以为版本不对其实它只是个上限提示。所以正确的顺序是先确认显卡型号和它能支持的最高 CUDA 版本再决定框架要哪个 CUDA 版本然后确认驱动版本是否达标最后才是装框架。这个顺序理清楚了后面基本不会出大问题。2. 系统准备与基础依赖安装环境搭建的地基在系统层。地基没打好后面框架装得再漂亮GPU 认不出来照样白搭。这一节讲系统怎么装、驱动怎么上、装完怎么验证。2.1 系统安装方式双系统、虚拟机还是裸机先回答一个很多人纠结的问题虚拟机能不能跑深度学习能装、能跑 CPU 版但 GPU 直通极其麻烦性能损耗也大除非你的机器支持并正确配置了 PCI 直通否则不推荐用虚拟机做正经训练。虚拟机适合干什么适合你在 Windows 上先熟悉 Ubuntu 的命令行、文件系统、apt 操作用它练手没问题。但真要跑模型还是双系统或者纯 Linux 裸机。双系统安装的流程大致是下载 Ubuntu 的 ISO 镜像用工具写入 U 盘做成启动盘进 BIOS 关掉安全启动Secure Boot它会阻止未签名的驱动加载调整启动顺序然后分区安装。分区这块给个参考/根分区给 100G 以上/home按需给大一点数据集和模型权重很占地方swap给内存的 1 到 2 倍就行。如果机器内存够大64G 以上swap 可以给少一点甚至不给。装完之后有几个系统级的小事顺手做了更新软件源和系统包sudo apt update sudo apt upgrade、装基础编译工具sudo apt install build-essential cmake git、装中文输入法方便查文档fcitx 框架配一个输入法引擎即可。这些不影响 GPU但能让后续开发顺手很多。2.2 显卡驱动安装的三条路线与取舍驱动安装有三条路我按推荐程度排序。第一条是系统自带的附加驱动工具软件和更新里的 Additional Drivers。对新手最友好图形界面选一下推荐的专有驱动版本点应用等它装完重启就行。缺点是可选版本有限遇到新显卡可能没有匹配项。第二条是官方 apt 源。先加 NVIDIA 的仓库然后sudo apt install nvidia-driver-XXX。这条路的优点是版本全、更新方便XXX换成具体版本号。装完重启用nvidia-smi验证。这是我目前最常用的方式因为它可复现方便写成脚本。第三条是官网下载.run文件手动安装。功能上没差别但最容易出问题装之前必须禁用 nouveau 开源驱动装的时候要和内核头文件版本匹配内核一升级驱动就可能失效需要重装。除非有特殊需求我不建议走这条路。有个坑要提前说装驱动时不要同时装系统仓库里的 CUDA toolkit。sudo apt install cuda这种命令会连带装一大堆东西还容易和驱动版本打架。系统级只装驱动CUDA 交给 conda 环境处理这是我坚持的原则。2.3 用 nvidia-smi 判断驱动是否真的就位装完驱动重启第一件事就是敲nvidia-smi。看到那张表格说明驱动加载成功了。这张表信息量不小我逐块拆一下。----------------------------------------------------------------------------- | NVIDIA-SMI 535.104.05 Driver Version: 535.104.05 CUDA Version: 12.2 | |--------------------------------------------------------------------------- | GPU Name Persistence-M| Bus-Id Disp.A | Volatile Uncorr. ECC | | Fan Temp Perf Pwr:Usage/Cap| Memory-Usage | GPU-Util Compute M. | || | 0 NVIDIA GeForce ... Off | 00000000:01:00.0 On | N/A | | 30% 45C P8 20W / 250W | 512MiB / 12288MiB | 0% Default | ---------------------------------------------------------------------------上半部分是驱动版本和它支持的 CUDA 上限。中间那一行是显卡型号、温度、功耗、显存占用和 GPU 利用率。装环境阶段重点看两个数驱动版本对后面 CUDA 匹配有用和显存大小决定你能跑多大的 batch 和多大规模模型。如果提示command not found说明驱动没装好或者没生效先别急着装 CUDA回头把驱动问题解决掉。如果显示驱动版本但报 Failed to initialize NVML常见原因是内核更新后驱动模块没重新加载最简单的处理是重启一次。3. CUDA 与 cuDNN 版本匹配的完整推算过程版本匹配是深度学习环境里最容易出错的一环也是我见过最多人翻车的地方。这一节我把推算过程完整写出来你照着套就行。3.1 从框架反推 CUDA 版本的正确姿势假设我要装 PyTorch 2.3那我先去 PyTorch 官网的安装页面或者官网文档的 Previous Versions 页看它提供哪些 CUDA 版本的构建。以 PyTorch 2.3 为例官方提供 CUDA 11.8 和 CUDA 12.1 两个构建。那我就二选一。怎么选看驱动。假如我的驱动是 535.x那 CUDA 12.1 完全没问题CUDA 12.1 要求驱动 530 以上。如果驱动比较老比如 470 系列那只能选 CUDA 11.8要求驱动 450 以上。驱动版本决定你能选哪个 CUDA而不是反过来。TensorFlow 这边规则不太一样而且近两年变化很大。老版本2.15 及以前需要系统级装好 CUDA 和 cuDNN比如 TensorFlow 2.13 需要 CUDA 11.8 加 cuDNN 8.6。而 2.16 之后官方推荐用pip install tensorflow[and-cuda]它会把需要的 CUDA 库作为 pip 包直接装进虚拟环境你根本不需要在系统里装 CUDA。这个变化极大简化了流程也避免了和 PyTorch 的 CUDA 版本打架。整理一张对照表放在这里方便你查。这是基于官方文档常见版本的整理具体以你安装时的官网为准。框架版本推荐 CUDA对应 cuDNN最低驱动Linux安装方式PyTorch 2.0 - 2.211.7 / 11.8自带450.80.02conda / pipPyTorch 2.3 - 2.511.8 / 12.1自带525conda / pipTensorFlow 2.13 - 2.1511.88.6450.80.02pip 系统 CUDATensorFlow 2.1612.3pip 内置8.9525pip[and-cuda]注意这张表里的最低驱动是指该 CUDA 版本要求的驱动下限。你的驱动只要不低于这个数就能跑对应的 CUDA 版本。3.2 系统级安装 CUDA 还是用 conda 内置运行时这个问题我纠结过很久现在的答案是能用 conda 内置的就别装系统级的。原因有三。第一隔离干净。conda 会把cudatoolkit、cudnn这些包装进环境环境删掉它们也就没了不会在系统里留一堆卸载不干净的残留。第二避免版本冲突。当你有多个项目需要不同 CUDA 版本时系统级安装根本没法共存而 conda 环境天然支持。第三省事。conda install pytorch-cuda12.1一句命令就搞定不需要手动配环境变量、不需要下 cuDNN 压缩包、不需要拷贝头文件。那什么时候需要系统级 CUDA当你需要编译自定义的 CUDA 扩展、需要nvcc编译器的时候。这种情况下装一个系统级 CUDA 是合理的但要注意它和 conda 环境里的版本尽量保持一致否则编译出来的东西运行时可能对不上。3.3 环境变量配错的几个典型症状如果你确实装了系统级 CUDA那环境变量一定要配对。通常是在~/.bashrc里加这么几行export CUDA_HOME/usr/local/cuda export PATH$CUDA_HOME/bin:$PATH export LD_LIBRARY_PATH$CUDA_HOME/lib64:$LD_LIBRARY_PATH配完之后source ~/.bashrc然后nvcc -V看编译器版本。如果报找不到命令要么路径写错了要么/usr/local/cuda这个软链接没建。常见的错配症状有几个nvcc -V显示 11.8但 PyTorch 里torch.version.cuda显示 12.1这说明你的环境变量指向了一个 CUDA实际框架用的是另一个。还有一种是LD_LIBRARY_PATH里残留了旧版本的路径导致运行时加载了错误的库报一堆 undefined symbol。遇到这种先把环境变量里的 CUDA 相关行清理干净重启终端再试。4. PyTorch 环境搭建实操前面铺垫了这么多现在开始动手。PyTorch 这边我推荐 conda 安装因为 CUDA 运行时会一并处理好。4.1 创建虚拟环境并安装 PyTorch 的逐步解析第一步创建一个独立的 conda 环境指定 Python 版本。Python 版本别太新也别太旧3.10 或 3.11 是目前兼容性最好的区间太新比如 3.13有些包还没跟上太旧会影响一些新库的安装。conda create -n pt2 python3.10 -y conda activate pt2第二步安装 PyTorch。以 CUDA 12.1 为例官方推荐的命令是conda install pytorch torchvision torchaudio pytorch-cuda12.1 -c pytorch -c nvidia -y逐段解释一下这段命令。pytorch torchvision torchaudio是三个核心包PyTorch 主体、视觉工具库、音频工具库。pytorch-cuda12.1是关键它告诉 conda 要装 CUDA 12.1 对应的运行时库也不需要你系统里预先装好 CUDA。-c pytorch -c nvidia是指定这两个官方 channel保证拿到的是官方构建而不是第三方野包。如果你不用 conda用 pip 也可以pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu121这个--index-url指向 PyTorch 官方的 whl 仓库不同 CUDA 版本对应不同的地址后缀cu121 就是 CUDA 12.1cu118 就是 11.8。用错后缀会导致装了 CPU 版或者版本对不上。提示如果你网络环境在国内PyTorch 的下载可能会很慢。conda 可以配置国内镜像源加速pip 也可以临时指定镜像源。配置镜像源是常规操作能省不少等待时间具体在第 7 节展开。4.2 三步验证 GPU 是否真的被调用装完别急着写模型先验证。写一个check_torch.pyimport torch print(PyTorch version:, torch.__version__) print(CUDA available:, torch.cuda.is_available()) print(CUDA version:, torch.version.cuda) print(Device count:, torch.cuda.device_count()) if torch.cuda.is_available(): print(Device name:, torch.cuda.get_device_name(0)) x torch.randn(1000, 1000).cuda() y torch.randn(1000, 1000).cuda() z x y print(Compute OK, result shape:, z.shape)这里有几个判断点。cuda.is_available()必须是True如果是False说明 PyTorch 装成了 CPU 版或者驱动有问题。torch.version.cuda会告诉你这个构建用的是哪个 CUDA 版本。最后两行是真正在 GPU 上做一次矩阵乘法能算出结果说明整条链路是通的这比单纯看is_available更靠谱。如果is_available是 False排查顺序是先看nvidia-smi是否正常再看装的是不是 CPU 版conda list | grep pytorch看看有没有pytorch-cuda最后看驱动版本是否达标。4.3 用一个小 Transformer 例子把环境跑穿环境验证完用一段能跑的代码确认它真的能干正事。这里用一个最小的 Transformer 编码器既能验证 PyTorch 的 GPU 路径也能顺便看看你对这套东西的直观感受。import torch import torch.nn as nn class TinyTransformer(nn.Module): def __init__(self, d_model128, nhead8, num_layers2): super().__init__() self.embed nn.Linear(32, d_model) encoder_layer nn.TransformerEncoderLayer( d_modeld_model, nheadnhead, batch_firstTrue ) self.encoder nn.TransformerEncoder(encoder_layer, num_layersnum_layers) self.head nn.Linear(d_model, 2) def forward(self, x): x self.embed(x) x self.encoder(x) return self.head(x.mean(dim1)) device cuda if torch.cuda.is_available() else cpu model TinyTransformer().to(device) data torch.randn(16, 20, 32).to(device) out model(data) print(Output shape:, out.shape, on, device)这段代码把d_model、nhead这些参数设小是为了跑得快batch_firstTrue是个容易踩的点Transformer 默认输入形状是(seq_len, batch, feature)设成 True 之后变成(batch, seq_len, feature)符合大多数人的直觉。跑通之后你就有一个可用的最小骨架了后面换数据集、加层数都是在这个基础上改。5. TensorFlow 环境搭建实操TensorFlow 和 PyTorch 装在一起是很多人的需求一个是论文复现常用一个是老项目维护常用。关键是要隔离好。5.1 两个框架共存的隔离策略我的做法是一个框架一个 conda 环境不放在一起。原因很简单TensorFlow 依赖的 CUDA 版本和 PyTorch 经常不一致即使某一时刻一致升级其中一个就可能破坏另一个。分开之后环境 A 跑 PyTorch环境 B 跑 TensorFlow切换只需要conda activate。如果你一定要在同一个环境里装两个比如某个项目代码里同时import torch和import tensorflow那就要保证它们对 CUDA 的要求一致。PyTorch 走 conda、TensorFlow 走 pip这种情况下用 TensorFlow 2.16 以上版本比较好因为它自带 CUDA 库、不依赖系统 CUDA和 conda 装的 PyTorch 冲突面小一些。5.2 TensorFlow 安装与 GPU 识别验证新版本2.16的安装非常简单conda create -n tf2 python3.11 -y conda activate tf2 pip install tensorflow[and-cuda][and-cuda]这个 extras 就是关键它会把 CUDA 12.3 相关的库作为 pip 包一起装进来。装完验证import tensorflow as tf print(TensorFlow version:, tf.__version__) gpus tf.config.list_physical_devices(GPU) print(GPUs:, gpus) if gpus: tf.config.set_visible_devices(gpus[0], GPU) tf.config.experimental.set_memory_growth(gpus[0], True) with tf.device(/GPU:0): a tf.random.normal([1000, 1000]) b tf.random.normal([1000, 1000]) c tf.matmul(a, b) print(Compute OK:, c.shape)如果你的 TensorFlow 是 2.15 或更早的版本那就要在系统里装好对应版本的 CUDA 和 cuDNN然后pip install tensorflow。这种情况下 CUDA 的版本要求不能错差一个小版本都可能报Could not load dynamic library libcudart.so。我在第 6 节把这个报错的排查方法写进去。set_memory_growth(True)这行建议一开始就加上。默认情况下 TensorFlow 会一次性占满所有显存导致你没法在同一张卡上跑第二个任务。设置成按需增长之后显存是用了才占多任务共存舒服很多。5.3 两个框架混用时容易翻车的点即使隔离好了有些坑还是要注意。第一不要在系统环境里pip install任何框架所有框架都装进 conda 环境系统环境保持干净否则容易出现激活环境后还是用到了系统里的包这种情况。第二环境变量里的LD_LIBRARY_PATH别指向某个特定框架的 CUDA 库这会污染另一个环境。第三如果两个环境要用不同驱动版本那你只能升级驱动到能兼容两者的那个版本因为驱动是全局的没法隔离。还有一个细节TensorFlow 和 PyTorch 对 cuDNN 的版本要求可能不同。如果你用的是老版本、需要系统级 cuDNN那就得权衡。新版本自带 cuDNN 之后这个问题基本消失了这也是我推荐新版本的原因之一。6. 常见问题与排查速查前面都是顺风顺水的流程实际动手时总会卡住。这一节把最高频的问题整理出来做成速查的形式。6.1 高频报错速查表报错信息关键词大概率原因处理方式torch.cuda.is_available()为 False装成 CPU 版检查 conda list 里有无 pytorch-cuda重装 GPU 版could not load dynamic library libcudart.soCUDA 版本不匹配或路径没配核对框架要求的 CUDA 版本检查 LD_LIBRARY_PATHnvidia-smicommand not found驱动没装或内核模块没加载重装驱动或重启CUDA out of memorybatch 太大或显存被占满减小 batch设置显存按需增长检查残留进程undefined symbol库版本混用加载了错误版本清理环境变量重装对应包nvcc找不到命令系统级 CUDA 没装或路径没加装 CUDA 或把 bin 目录加进 PATHconda 安装卡在 Solving environment依赖解析慢或源有问题换用 mamba 或配置国内镜像源SSH 连不上防火墙、服务未启动、网络配置检查 sshd 服务状态和端口核对网络配置注意Could not load dynamic library这类报错看起来严重实际上大多数情况下只是找不到库文件而已。TensorFlow 遇到这种情况一般会降级到 CPU 运行不会直接崩但你要留意它有没有打印 successful NUMA node read 之后的 GPU 信息如果 GPU 列表是空的那它就是在用 CPU 跑。6.2 我踩过的坑与独家避坑经验第一个坑驱动升级后忘了重启。有一次我更新了驱动nvidia-smi却报错折腾了半小时才发现是内核模块还是旧的重启就好了。所以驱动相关操作之后别省那一次重启。第二个坑conda 环境里装了 pip 版 PyTorch又用 conda 装了别的包结果把 PyTorch 依赖覆盖了。症状是原本能用装了个不相干的包之后突然import torch报错。经验是装任何包之前养成习惯先看conda install的 dry-run 输出加--dry-run参数确认它不会动 PyTorch 的依赖。第三个坑数据集放在/home之外权限没给对。深度学习环境配好了结果读取数据时报Permission denied白白怀疑了半天环境。建议是数据目录统一放在一个有明确权限的位置动手前先用ls -l看一眼属主。第四个坑磁盘空间不够。数据集、模型权重、conda 环境三样加起来非常吃空间尤其是 conda 的 pkgs 缓存目录。定期用conda clean -a清理没用的包和缓存能省出好几个 G。我见过有人训练到一半报 No space left on device白白浪费一轮。7. 日常维护与效率提升环境装好只是开始怎么让它长期稳定、怎么迁移、怎么远程开发这些决定了你的日常工作流顺不顺。7.1 环境导出、复现与迁移一个装好的环境最好能一键复现。conda 提供了两个命令用途不一样。conda env export environment.yml会导出完整的依赖树包括构建号好处是复现精确坏处是换机器可能因为构建号不匹配而失败。conda env export --no-builds environment.yml只导出包名和版本跨平台友好我一般用这个。但要注意environment.yml只记录 conda 装的包如果你在环境里用 pip 装过东西它不一定完整记录。所以更稳妥的做法是同时导出 pip 的列表pip freeze requirements.txt。迁移到新机器时先conda env create -f environment.yml再pip install -r requirements.txt。补充一个小技巧如果你的环境大部分是 pip 装的直接用requirements.txt重建更简单没必要走 conda 那一套。选择哪种方式取决于你环境里 conda 和 pip 的比例。7.2 远程开发SSH 与编辑器的配合实际工作里训练机往往在机房或者云上你人在本地。这时候 SSH 是主力。配置好 SSH 之后本地用 VS Code 的 Remote 功能连上去就像在本机编辑一样。这种方式的好处是代码在服务器上训练和调试都在服务器进行本地只是个终端不吃本地资源。SSH 连不上是高频问题排查顺序是这样的先在服务器本地确认 sshd 服务在跑systemctl status ssh再看端口是否被防火墙拦了最后确认网络配置。如果之前能连、突然连不上多半是网络变了或者 IP 变了。另外如果长时间 idle 后自动断开可以在本地 SSH 配置里加上ServerAliveInterval 60定期发心跳保持连接。7.3 镜像源配置与下载加速国内环境下配置镜像源能省大量下载时间。conda 的镜像源配置写在~/.condarc里把默认源指向国内镜像即可。pip 的镜像源可以写在~/.pip/pip.conf里或者临时用-i参数指定。配置镜像源要注意两点。第一PyTorch 的安装尽量还是用官方源因为它的 CUDA 版本 whl 是特殊的镜像源不一定同步得及时可能出现找不到对应版本的情况。第二镜像源配置完之后记得验证跑一次下载看速度是不是真的上来了有时候配置写错了会走回默认源。如果你需要编译 C/CUDA 扩展别忘了cmake的版本也要确认一下。老版本的 cmake 可能不支持新的 CUDA 工具链直接apt装的可能偏旧必要时去官网下新版。这也是环境配好之后容易被忽略的一环。我个人这些年折腾下来最大的体会是深度学习环境配置的难点不在于命令本身而在于版本之间的约束关系。命令就那么多背都背下来了但每一次版本组合都可能带来新问题。所以与其记住某一次的安装命令不如记住那套推导逻辑——从显卡和驱动出发反推框架需要的 CUDA再决定装法。这套逻辑一旦形成无论换什么机器、升什么版本你都能自己推出来不用每次都在网上搜别人能跑的方案。另外环境配好之后立刻导出environment.yml存一份是性价比最高的一步操作能帮未来的你省下好几个小时。
返回列表