ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

实验室服务器深度学习环境配置实战:PyTorch、TensorFlow、远程连接全攻略

实验室服务器深度学习环境配置实战:PyTorch、TensorFlow、远程连接全攻略

实验室服务器这东西,用好了是提效神器,用不好就是大型掉发现场。我在服务器环境上摸爬滚打三年多,踩过的坑比吃过的盐还多,刚接手那阵子,装个PyTorch能装到怀疑人生,TensorFlow装了卸、卸了又装,visdom死活起不来,Jupyter要么连不上要么开了个寂寞,PyCharm远程更是玄学。这篇内容就是把这些年在实验室服务器上的实操经验整理出来,重点覆盖PyTorch、TensorFlow安装问题、visdom启动问题、本地远程调用服务器Jupyter、以及本地PyCharm远程连接服务器这几个核心场景,适合刚进实验室的研究生、需要远程跑实验的开发者,以及被服务器环境折腾到崩溃的同行。我不讲虚的,全是在命令行里实实在在敲出来的经验。

1. 先摸清服务器家底,环境准备少走半年弯路

1.1 GPU、驱动与CUDA版本的三角关系

很多人一上来就敲pip install,装完之后发现GPU用不了,最后查来查去,问题出在没搞清楚服务器上有什么硬件、什么驱动、什么上限。我每次在新服务器上动手之前,一定会先执行这串命令:

nvidia-smi python --version conda --version nproc free -h df -h

nvidia-smi的输出要重点看两行:第一行是GPU型号和显存大小,第二行是驱动版本以及驱动支持的最高CUDA版本。驱动上方显示的CUDA Version是一个上限值,它表示当前驱动可以兼容的最高CUDA runtime版本,不等于服务器上已经装了CUDA。

实验室里经常出现这种情况:驱动显示CUDA 12.0,但系统里压根没有装CUDA toolkit,或者装的是CUDA 11.4。这里面的核心认知是,你通过conda或pip安装深度学习框架时,框架会自带对应的CUDA runtime库,不依赖系统CUDA,只要你选的CUDA版本不高于驱动支持的上限,一般就能跑起来。所以选PyTorch版本时,先看nvidia-smi里那个上限,再去PyTorch官网选择对应CUDA版本的安装命令。

1.2 为什么我强烈建议用conda管理环境

实验室服务器通常多人共用,系统Python目录是公共资源,直接往里塞包,轻则版本冲突,重则搞坏别的同学的环境。我见过最惨的情况是一位同学强行升级了系统的numpy,结果另一个项目组所有代码直接跑不起来,查了一整天。

我的习惯是每台服务器都用Miniconda,每个实验建一个独立环境。这样做的理由很简单:第一是隔离,Python包互相不污染;第二是可复现,环境文件里记录版本,换机器也能还原;第三是权限,conda环境装在自己的home目录下,不需要sudo,不依赖管理员。

创建环境的命令看起来是小事,但初始规范特别重要。我会定死一套命名规则:

conda create -n tf24 python=3.9 conda create -n pt21 python=3.10 conda activate tf24

环境名带上框架核心版本号,时间久了不会忘记这个环境是干什么的。很多同学喜欢用env1、env2这种名字,两周之后自己都想不起来里面装了什么。

1.3 磁盘空间与数据目录规划

实验室服务器的磁盘坑远比想象中多。很多人的home目录只有几十个G,但数据集动不动就上百G。我见过有人把数据集放在home目录,训练到一半磁盘满了,整个进程直接crash,几个小时的训练白跑。

动手之前一定要用df -h看一下目录挂载情况。服务器的数据盘通常挂在/data、/ssd这类路径下。我习惯建一套固定的目录结构:

mkdir -p /data/yourname/datasets mkdir -p /data/yourname/codes mkdir -p /data/yourname/models mkdir -p /data/yourname/logs

然后把conda的缓存也指到数据盘,避免home文件膨胀到爆掉。操作方式是在.condarc里配置:

pkgs_dirs: - /data/yourname/conda_pkgs envs_dirs: - /data/yourname/conda_envs

这个细节看着不起眼,但能让home目录的压力小很多。处理服务器的第一原则就是:数据归数据,环境归环境,系统归系统,谁也别越界。

2. PyTorch安装全流程与高频踩坑实录

2.1 官方命令之外,还要注意什么

PyTorch的安装最简单,但也最容易在版本选择上翻车。现在PyTorch官网首页会根据你的系统推荐一条pip命令,但实验室服务器的情况比较复杂,不能直接闭眼复制。

先说选版逻辑。打开官网的install页面,选择Linux、Conda、CUDA版本,官网会给出对应的安装命令。关键点在于CUDA版本的选择:如果你不追求极致性能,选CUDA 11.8或12.1这种稳定版本通常是最省心的,兼容性最好。如果你是做多卡训练、混合精度训练,再根据模型库的依赖要求选。

我强烈推荐用conda安装PyTorch,而不是pip。一个最实际的原因是conda会一并处理CUDA相关的依赖,比如cudatoolkit、cudnn这些非Python库,pip则经常漏掉。命令大概是这种形态:

conda install pytorch torchvision torchaudio pytorch-cuda=11.8 -c pytorch -c nvidia

如果你用的是国内镜像,那更要注意,pytorch的conda包比较大,经常下载到一半中断。我一般会在.condarc里配好镜像源,然后单独加一个-pytorch频道。千万不要把镜像源配置和-c pytorch参数混用,那样会有非常奇怪的冲突。

国内网络环境还有一个常用办法是pip加上国内镜像,然后指定torch的版本。比如:

pip install torch==2.1.0 torchvision==0.16.0 torchaudio==2.1.0 --index-url https://pypi.tuna.tsinghua.edu.cn/simple

这种方式能装,但装的通常是CPU版本,因为PyPI上默认的torch就是不带CUDA的。除非你明确指定了CUDA版本对应的轮子文件,否则装完发现torch.cuda.is_available()永远是False,这就是最常见的第一个坑。

2.2 安装后必做的三连验证

装完之后,我每次都会跑一套固定的验证流程,三个代码片段挨个过:

第一步,验证Python能不能正常导入:

python -c "import torch; print(torch.__version__)"

第二步,验证CUDA可用性:

python -c "import torch; print(torch.cuda.is_available()); print(torch.cuda.device_count())"

如果输出True和1,说明环境基本OK。第三步,真正跑一个小张量操作:

python -c "import torch; x = torch.randn(1000, 1000).cuda(); y = x @ x; print(y.sum().item())"

这种粗颗粒的验证比单纯看is_available靠谱得多。我踩过一种诡异情况:is_available返回True,但一跑.cuda()就报错CUDA error: initialization error,后来查出来是驱动版本与PyTorch内置的CUDA runtime不匹配。

注意:如果你用的是老服务器,显存比较小的那种,别一上来就跑大模型,先用小张量做通断测试是稳妥做法。

2.3 高频踩坑:显卡不可见、导入报错、网络中断

先说显卡不可见。有一种情况是nvidia-smi能看到GPU,但PyTorch就是检测不到。大多数原因是你通过conda或pip装的是CPU版本的PyTorch。修复方式很简单,卸载掉,重新装对应CUDA版本的包。

第二种情况是导入torch就报GLIBCXX相关错误。这是因为系统的libstdc++版本老旧,与PyTorch要求的版本不匹配。解决办法是装一个新一点的gcc工具链,或者更新系统的libstdc++。我的经验是直接conda install -c conda-forge libstdcxx-ng,把这个包装进当前环境,比动系统环境风险小很多。

第三种情况是下载安装包时网络中断。PyTorch的conda包动辄几个GB,网络稍不稳定就gg。我的解决方案是先用wget把包下载到本地,再conda install本地文件。或者提前下载好whl文件,pip install本地轮子。这套离线安装法在实验室里特别实用,毕竟不是每个实验室网络都稳定。

还有一个安装容易忽略的点是版本一致性。torch、torchvision、torchaudio三个包的版本必须严格对应,不能一个2.1另一个0.4,否则运行到一半可能报找不到某个模块。官网每个版本都有对应的映射列表,装之前看一眼比报错之后排查快得多。

3. TensorFlow安装:版本兼容是最大命门

3.1 先看清TensorFlow和CUDA的匹配关系

TensorFlow的安装比PyTorch更折腾,它对CUDA和cuDNN的版本有严格硬性要求。TensorFlow 2.10之前,GPU支持直接内置在里面,装完就能看到GPU。但2.10之后,Windows原生的GPU支持被移除了,Linux上也只能匹配特定版本的CUDA和cuDNN。

实验室服务器常见的是Linux系统,所以重点说Linux下的安装。在装TensorFlow之前,我习惯先去看官方给出的版本兼容表,核心匹配关系大概是TensorFlow 2.10对应CUDA 11.2、cuDNN 8.1;TensorFlow 2.12对应CUDA 11.8、cuDNN 8.6;TensorFlow 2.15对应CUDA 12.2、cuDNN 8.9。

这里有个容易混淆的点:TensorFlow这些版本要求的CUDA版本,指的是它编译时依赖的CUDA库版本,而你需要通过conda或pip安装配套的cudatoolkit和cudnn。如果用pip直接安装tensorflow,它不会自动帮你装CUDA库,你就得手动装好匹配版本。

我推荐的稳妥做法是:

conda create -n tf215 python=3.11 conda activate tf215 conda install -c conda-forge cudatoolkit=12.2 cudnn=8.9 pip install tensorflow==2.15

这段命令里的cudatoolkit和cudnn版本是配合TensorFlow 2.15来的。很多同学直接pip install tensorflow,然后马上跑GPU测试,测完发现是CPU,原因就在这里——缺少cudatoolkit和cudnn。

3.2 验证GPU状态的标准流程

TensorFlow验证GPU的命令和PyTorch不太一样:

python -c "import tensorflow as tf; print(tf.__version__)" python -c "import tensorflow as tf; print(tf.config.list_physical_devices('GPU'))"

如果输出的是一个空列表,说明GPU没有被检测到。如果列表里有GPU设备,但运行时有warning说CUDA/cuDNN版本不匹配,那多半就是cudatoolkit版本没对上。这种情况我遇到过太多次了,所以后来干脆固定使用conda安装全套依赖,不手动拼版本。

还有一种验证方式是跑一个矩阵乘:

python -c "import tensorflow as tf; print(tf.reduce_sum(tf.random.normal([1000,1000]) @ tf.random.normal([1000,1000])).numpy())"

跑完看GPU的占用情况,就能确认确实是GPU在算,而不是偷偷用CPU跑。

3.3 TensorFlow常见报错与排查思路

protobuf版本冲突是最常见的。TensorFlow对protobuf有严格的版本要求,当你环境里安装了新版protobuf,运行时会出现类似TypeError: Descriptors cannot not be created directly的报错。我踩过之后,直接把protobuf的版本降到官方建议的范围,比如3.20.x,问题立刻消失。

第二种是内存溢出,报OOM。实验室的共享服务器经常有人把显存占满,一张卡都不剩,TensorFlow启动时会尝试申请全部显存,直接爆掉。我建议在代码开头加一行限制:

import tensorflow as tf gpus = tf.config.experimental.list_physical_devices('GPU') for gpu in gpus: tf.config.experimental.set_memory_growth(gpu, True)

这个设置让TensorFlow按需申请显存,而不是一次性把显存吃满。插一句,PyTorch默认是动态申请显存的,所以它很少因为这一点触发OOM,但TensorFlow在这里特别刚。

第三种情况是运行时报找不到libcuda.so.1。这通常发生在你装了Conda的cudatoolkit,但系统的动态库搜索路径没有指向它。解决办法是在启动Python时把CUDA库路径加到环境变量里:

export LD_LIBRARY_PATH=$CONDA_PREFIX/lib:$LD_LIBRARY_PATH

我通常写进.bashrc或激活环境的脚本里,省得每次手动执行。

4. visdom安装与启动问题实录

4.1 visdom的定位和现状

虽然现在很多项目用wandb,但visdom在实验室里依然有大量代码在使用,尤其是在比较老的图像生成、强化学习项目里。visdom本质上是一个本地可视化服务器,通过网页展示训练曲线、图像、文本等。它的好处是不用注册账号、数据留存本地、延迟低。

但visdom有个让人头疼的毛病,就是安装老出问题,而且启动页面还经常白屏。我先说安装,再讲启动,最后说远程调用。

4.2 安装visdom的正确姿势

现代Python版本上,直接pip install visdom通常会报一个错,核心是老版本visdom兼容不了新版Python,最常见的是遇到win32相关导入错误。这个win32说的是一个Python标准的pywin32模块,在Linux环境下会导入失败。我在服务器上遇到的报错就指向visdom源码里的server模块。

我的解决办法是找一个visdom的修复版,或者手动改源码。网上有人维护了对应新版Python兼容的visdom fork,pip安装时用这类源:

pip install visdom

如果报错,就手动把错误文件里的win32导入删掉,或者注释掉对应的try except。具体来说,在visdom的py里找到类似import win32file、import win32api这种行,直接注释掉。linux系统根本不需要这两个模块,注释掉不影响功能。

装好之后启动服务:

python -m visdom.server -port 8097

如果看到It's Alive!字样的输出,说明服务起来了。如果什么输出都没有就卡住,通常是下载脚本资源卡在网络上了。visdom启动时会自动下载一些前端js资源,这些资源存放位置在~/.visdom目录下,网络不好的时候就会一直挂起。我的做法是手动下载这些静态文件放到~/.visdom里,具体路径可以看报错日志。更省事的办法是使用代理或把启动参数改成本地模式。

4.3 让本地浏览器访问服务器上的visdom

这是实验室最刚需的场景:代码在服务器上跑,训练曲线想在自己电脑浏览器上看。visdom默认监听服务器本地端口,你要做的是SSH隧道把远程的8097端口映射到本地的某个端口。

在本地电脑上执行:

ssh -L 18097:localhost:8097 username@server_ip

然后浏览器打开http://localhost:18097,就能看到服务器上的visdom页面。这个原理和后面Jupyter的远程访问一模一样,都是端口隧道转发。

我见过同学直接在服务器上开放防火墙端口然后用浏览器访问,这也能行,但安全性差很多,我不建议在公网环境下这么做。SSH隧道是加密链路,权限也受控,是实验室里最稳妥的方案。

4.4 visdom常见坑清单

第一个坑是服务起来之后页面白屏,刷新不出来。多数原因是浏览器访问的端口和visdom启动的端口不一致,或者隧道映射没成功。先在本机服务器上curl一下http://localhost:8097,把服务本身先确认能访问。

第二个坑是visdom页面能看到,但图表不更新。这个往往是代码里没有正确指定visdom环境,或者python端的visdom实例连错了端口。在代码里初始化时,要把env和server指向正确的地址:

from visdom import Visdom viz = Visdom(server='http://localhost', port=8097, env='main')

第三个坑是启动时报端口被占用。实验室里很多人同时用visdom,8097只有一个,后启动的人会失败。解决办法是用不同的端口,或者在visdom里设置不同的env名称来隔离数据,而不是硬抢8097端口。

5. 本地远程调用服务器Jupyter

5.1 SSH隧道是Jupyter远程访问的最佳姿势

Jupyter远程调用是实验室里的高频需求,但我见过太多人直接用服务器的IP加端口去浏览器访问,然后被防火墙拦住、被安全策略卡死,甚至不小心把Jupyter暴露在公网上。

我推荐的方案永远是SSH隧道。先在服务器上启动Jupyter,让它监听127.0.0.1地址,再在本地做一次端口转发。这样即使防火墙拦截外网端口,也不会影响你的访问。

服务器端:

jupyter notebook --no-browser --ip=127.0.0.1 --port=8888

本地端:

ssh -N -f -L 18888:127.0.0.1:8888 username@server_ip

然后浏览器访问http://localhost:18888,登录时输入token或密码。

这里有个细节,很多同学的服务器是跳板机架构,就是登录服务器要先经过一台堡垒机。这种情况下SSH隧道的写法要加上参数,指向最终目标主机。如果只有一跳,本地连接堡垒机时把隧道的目标IP写成内网地址即可,也可以使用本地配置里指定ProxyCommand的方式。我的建议是先在本地配置好SSH config,把跳板逻辑固化下来,而不是每次手动敲两个SSH。

5.2 配置Jupyter密码,告别token地狱

Jupyter默认启动时会生成一串超长的token,用token登录虽然安全,但体验很差。我建议配置一个固定密码,用jupyter server password命令来设置,它会生成一个hashed密码写入配置文件。

具体流程是:

jupyter server password Enter password: 你设置的密码

然后启动时指定配置,之后登录只需要输入密码就行。注意新版Jupyter的配置文件名是jupyter_server_config.py,老版本是jupyter_notebook_config.py。用哪套看你的Jupyter版本,我建议直接用jupyter server password,它可以自动处理配置文件路径。

5.3 让Jupyter正确使用conda环境

这是实验室里要求最高的功能,我把它列成单独一节,因为真的太多人卡在这里。你在服务器上用conda激活了某个环境,在终端里启动Jupyter,但打开网页新建Notebook时,里面只有1个默认的Python内核,根本看不到你那个环境。

原因是Jupyter内核与conda环境是两回事。需要在conda环境里安装ipykernel,并把内核注册到Jupyter中:

conda activate pt21 pip install ipykernel python -m ipykernel install --user --name=pt21 --display-name="PyTorch-2.1"

之后启动Jupyter,新建Notebook的下拉菜单里就会出现PyTorch-2.1这个内核。选它之后,Notebook里运行的代码就是基于pt21环境的。

我在实际使用中还发现一个细节:如果你修改了conda环境里的包,但Jupyter内核还是旧的,那是因为内核注册信息里记录的是Python解释器路径,而不是环境路径。所以内核注册时一定要确保当前激活的目标环境。另外,如果Jupyter本身装在base环境,那内核注册时也尽量在base环境里执行ipykernel install,避免路径错乱。

5.4 Jupyter远程连接常见问题

第一个问题:打开页面之后出现404或Kernel not found。这通常是因为Jupyter的进程是在某一个conda环境下启动的,而你要用的内核没有注册到这一环境。解决方案就是把目标环境的ipykernel重新install一下,或者在启动Jupyter之前先activate对应环境。

第二个问题:内核连不上,一直在等待。排查方式很简单,看服务器上Jupyter进程的日志输出,它会显示内核启动失败的堆栈信息。常见原因有conda环境被删除、Python版本不兼容、jupyter_client版本不匹配。

第三个问题:多人同时共用一台服务器,8888端口冲突。这不是大问题,每个人启动时指定不同端口即可,比如--port=8889。我用一个取巧的办法,在SSH隧道里只做本地端口映射,远程端口按自己习惯固定下来,比如我用8888、A同学用8889、B同学用8890,大家互不干扰。

6. 本地PyCharm远程连接服务器

6.1 选对连接模式:Deployment加远程解释器

PyCharm远程连接服务器是提高效率的重要工具,它让我可以本地写代码、本地看UI、远程跑训练,代码和文件在本地和服务器之间同步。很多同学以为PyCharm远程只能通过Python Console来玩,实际上完整的方案是Deployment(文件部署)+远程解释器(Remote Interpreter)组合。

先说Deployment,它是把本地代码同步到服务器上的机制。在PyCharm的Settings里找到Build, Execution, Deployment下的Deployment,点加号添加配置。类型选SFTP,输入服务器的地址、用户名和密码。

然后是映射配置,本地目录和服务器目录的映射关系必须搞清楚。我的习惯是把本地项目根目录映射到服务器上固定的专用目录,比如/data/yourname/codes/project_name。不要映射到home下的某个临时目录,因为数据盘成本低、空间大、性能好。

配置远程解释器时,在Settings里找到Project Interpreter,点Add Interpreter,选择On SSH。这里有个关键点:解释器路径要指向conda环境里的Python二进制文件,而不是系统的/usr/bin/python。一般路径是:

/data/yourname/conda_envs/pt21/bin/python

如果找不到这个路径,先用which python确认一下。

6.2 完整配置流程实操

我把具体步骤拆开讲,按这个顺序配置不会乱:

第一步,先在PyCharm里打开项目,然后在Settings里配置Deployment,添加一个SFTP服务器,名字随意,比如lab_server。

第二步,设置Root Path,这是服务器上的根目录。服务器端项目目录如果是/data/yourname/codes/myproj,那Root Path就填这个绝对路径。

第三步,在Mappings标签页里,把Local Path填本地项目路径,Deployment Path填一个相对路径,代表相对于Root Path的目录。我这里通常填一个项目名,比如myproj,最终同步到服务器的路径就是/data/yourname/codes/myproj/myproj,这有点冗余。为了让路径干净,我会把Deployment Path留空或者填一个点,表示根目录本身。

第四步,配置远程解释器。Settings -> Project -> Python Interpreter,点击齿轮,Add Interpreter -> On SSH。选择Existing server configuration,直接选刚才的Connection配置,然后填远程Python路径。

第五步,调整Path mappings。这一步最容易忽略。远程解释器跑的时候,PyCharm会把本地脚本路径映射到服务器脚本路径。如果映射不对,运行代码时会提示找不到文件。我通常会在Path mappings里把本地项目根目录映射到服务器项目根目录。

完成这些之后,你先手动点一次Tools -> Deployment -> Upload to lab_server,确认文件能传上去,再试运行代码。

6.3 同步与调试的小技巧

文件同步有两种模式:手动上传和自动上传。我建议代码修改频繁时开启自动上传,在Tools -> Deployment -> Automatic Upload里打开。但有一个坑:自动上传会即时覆盖服务器上的文件,如果你在服务器上也改了同一个文件,两边会发生覆盖冲突。

我习惯使用一个简单约定:本地代码为主,服务器运行环境为准。所有代码修改在本地完成,然后自动上传,服务端脚本只负责执行,不再单独修改。这样可以避免大部分文件冲突问题。

运行脚本时,PyCharm的Run配置里要选择远程解释器。我的习惯是新建一个Run/Debug Configuration,选好脚本,然后直接点运行,PyCharm会把本地脚本同步到服务器,再通过远程解释器执行。此时服务器上的数据集路径、训练输出路径都要在脚本里用绝对路径写好,不能依赖于相对路径。

调试时注意,PyCharm的远程调试需要在服务器上安装调试辅助包pydevd-pycharm。如果你不需要断点调试,只是纯跑脚本,可以跳过这一步。但如果要做断点调试,就得确保服务器环境里装了对应版本。

6.4 卡顿、权限、同步失败的解决办法

PyCharm远程连接最常见的痛点是卡顿、响应慢。这通常与SFTP同步大量文件、索引远程文件有关。解决办法是把本地项目的Excluded Paths设置好,把数据目录、缓存目录、虚拟环境目录全部排除,不要同步,不要索引。比如:

/data/yourname/datasets /data/yourname/conda_envs/pt21

在Deployment配置的Excluded Paths里加上这些路径,速度会明显提升。

第二个问题是权限不足。有时在服务器上运行脚本时发现无法创建日志文件、无法写入模型文件,这是因为Deployment同步过来的文件所有者是当前登录用户,但远程解释器的当前用户可能没有相应目录的写权限。解决办法是检查服务器上数据目录和日志目录的权限,用chown或chmod调整权限。服务器上的数据目录通常会有一个组权限,你要确保自己是这个组的成员。

第三个问题是远程解释器无法定位conda环境。我见过这样一幕:用户在PyCharm里填了conda环境的Python路径,但运行代码时ImportError找不到torch。原因在于PyCharm启动远程进程时没有激活conda环境。解决方法是让远程解释器直接指向conda环境的python,而不是base环境的python。甚至可以在运行脚本之前写一段shell脚本,把conda环境变量的路径设置好。

还有一个不算问题但常被误报的情况:PyCharm提示Python package missing。这是因为本地解释器没有装这个包,但远程解释器装了。PyCharm在本地做静态分析时会认为缺少包,实际上运行用的是远程解释器,完全不受影响。这个提示直接用忽略处理,或者把本地解释器也指成远程,就没有这个告警了。

最后再分享一条小技巧

说一条我后来一直坚持的运维习惯:每台服务器的环境创建和关键操作,我用一个shell脚本记录在项目根目录里,比如setup_env.sh。脚本里写清楚conda环境创建命令、PyTorch或TensorFlow安装命令、visdom和Jupyter的启动方式、SSH隧道命令。这样无论是换机器还是换同学维护,只要照着脚本执行,五分钟就能恢复一套可用环境。我在实际维护中省了无数时间,所以强烈建议你也把环境操作沉淀成文档或脚本,而不是让它们只存在自己的记忆里。

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进