驗(yàn)室服務(wù)器深度學(xué)習(xí)環(huán)境配置:PyTorch/TensorFlow與遠(yuǎn)程連接排坑指南)
實(shí)驗(yàn)室服務(wù)器這個(gè)東西我接手實(shí)驗(yàn)室管理快三年了每年開學(xué)都能看到新同學(xué)在同一個(gè)地方栽跟頭PyTorch裝上跑不了GPUTensorFlow和CUDA版本對不上visdom啟動(dòng)后瀏覽器一片空白Jupyter在服務(wù)器上起了但本地死活打不開最后還有PyCharm遠(yuǎn)程連不上的連環(huán)坑。這篇文章就把我這些年處理過的實(shí)驗(yàn)室服務(wù)器使用問題系統(tǒng)整理一遍從PyTorch、TensorFlow安裝到visdom可視化服務(wù)再到本地調(diào)用服務(wù)器的Jupyter和PyCharm遠(yuǎn)程連接每一步的坑和正確操作都寫清楚適合所有剛進(jìn)實(shí)驗(yàn)室、準(zhǔn)備在公共服務(wù)器上動(dòng)手跑實(shí)驗(yàn)的同學(xué)參考。先說個(gè)基本判斷這類問題百分之八十不是玄學(xué)而是環(huán)境不一致導(dǎo)致的。服務(wù)器上裝的驅(qū)動(dòng)、CUDA版本、Python版本、包管理器任何一個(gè)環(huán)節(jié)和你的預(yù)期偏差一點(diǎn)后面就是連環(huán)報(bào)錯(cuò)。所以這篇文章的核心思路是先幫你把環(huán)境基線摸清楚再逐個(gè)擊破安裝和遠(yuǎn)程連接問題。1. 先搞清楚服務(wù)器環(huán)境的三件事能避開一半的坑1.1 服務(wù)器權(quán)限邊界你不是一個(gè)人在戰(zhàn)斗實(shí)驗(yàn)室服務(wù)器通常是多用戶共享的這意味著你大概率沒有root權(quán)限系統(tǒng)級(jí)的/usr/bin/python、系統(tǒng)級(jí)的pip都不是你能隨便動(dòng)的。很多安裝問題本質(zhì)上是你在沒有權(quán)限的系統(tǒng)目錄里嘗試寫文件被拒絕后又換了個(gè)奇怪的方式硬裝最后把環(huán)境搞亂。我的建議很簡單永遠(yuǎn)優(yōu)先使用conda創(chuàng)建自己的虛擬環(huán)境。conda不需要root安裝到自己的home目錄下環(huán)境隔離干凈刪了重來也不影響別人。很多同學(xué)問我為什么我pip裝個(gè)包老是Permission denied一看就是在系統(tǒng)目錄下執(zhí)行了pip install。如果是用conda環(huán)境這類權(quán)限問題基本不會(huì)出現(xiàn)。如果你連conda都沒有先去你個(gè)人的home目錄下裝一個(gè)Miniconda安裝的時(shí)候全選默認(rèn)路徑即可包管理器會(huì)自動(dòng)配置到你的~/.bashrc里。裝完之后重新登錄conda -V能輸出版本號(hào)就說明環(huán)境隔離的基礎(chǔ)打好了。1.2 CUDA、顯卡驅(qū)動(dòng)、深度學(xué)習(xí)框架的三角關(guān)系很多同學(xué)把安裝CUDA理解成裝一個(gè)軟件實(shí)際上服務(wù)器上的CUDA體系分成兩層驅(qū)動(dòng)層NVIDIA驅(qū)動(dòng)由服務(wù)器管理員安裝普通用戶不能動(dòng)。用nvidia-smi看到的信息屬于這一層。CUDA Toolkit層包含了nvcc等編譯工具和CUDA運(yùn)行庫可以裝多個(gè)版本也可以由conda在虛擬環(huán)境內(nèi)安裝不需要root。PyTorch和TensorFlow運(yùn)行時(shí)依賴的是驅(qū)動(dòng)層和它自己打包的CUDA運(yùn)行庫。它們并不直接調(diào)用系統(tǒng)里的nvcc所以關(guān)鍵在于驅(qū)動(dòng)版本是否支持你想要的CUDA runtime版本。用nvidia-smi看右上角的CUDA Version這里顯示的其實(shí)是驅(qū)動(dòng)支持的最高CUDA版本不是當(dāng)前生效的版本。只要驅(qū)動(dòng)支持的CUDA版本高于你想要安裝的框架所要求的CUDA版本一般就能跑。舉個(gè)例子nvidia-smi顯示CUDA 12.2你裝PyTorch的cu121或cu118版本都沒問題因?yàn)轵?qū)動(dòng)向下兼容。最煩的情況是舊驅(qū)動(dòng)搭配新版框架比如驅(qū)動(dòng)只支持CUDA 11.8你非裝默認(rèn)的cu124版PyTorch框架一加載CUDA就報(bào)driver version is too old。遇到這個(gè)報(bào)錯(cuò)不用問去裝對應(yīng)低CUDA的PyTorch版本。1.3 conda環(huán)境一套環(huán)境一套依賴別混裝我強(qiáng)烈建議你為每個(gè)項(xiàng)目單獨(dú)建一個(gè)conda環(huán)境而不是在同一個(gè)環(huán)境里既裝PyTorch又裝TensorFlow還裝一堆別的包??缈蚣艿囊蕾嚊_突是真實(shí)存在的尤其是numpy和cudatoolkit這類基礎(chǔ)庫PyTorch對numpy版本有要求TensorFlow的numpy限制更嚴(yán)格混裝之后不是你覆蓋我就是我覆蓋你最典型的就是numpy.core.multiarray failed to import這類詭異報(bào)錯(cuò)?;静僮魇莄onda create -n myenv python3.9 conda activate myenv然后把該項(xiàng)目的所有依賴通過requirements文件一次性裝好。如果后面發(fā)現(xiàn)裝壞了直接conda env remove -n myenv重建五分鐘就能回到干凈狀態(tài)比在系統(tǒng)環(huán)境里慢慢修復(fù)高效得多。2. PyTorch安裝問題不是pip install就完事了2.1 先看驅(qū)動(dòng)再選cu版本最后定安裝命令PyTorch的安裝命令官網(wǎng)會(huì)給得很清楚但問題是很多人不看官網(wǎng)直接pip install torch這通常會(huì)裝上CPU版本或者一個(gè)和你服務(wù)器驅(qū)動(dòng)不匹配的GPU版本。你裝完一測torch.cuda.is_available()返回False就開始懷疑人生。正確的順序是在服務(wù)器上執(zhí)行nvidia-smi看驅(qū)動(dòng)版本和它支持的CUDA版本。去PyTorch官網(wǎng)選擇你需要的CUDA版本比如cu118、cu121、cu124。復(fù)制官網(wǎng)給出的pip命令在conda環(huán)境下執(zhí)行。舉個(gè)例子驅(qū)動(dòng)支持CUDA 11.8那你應(yīng)該用pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu118如果你已經(jīng)在官網(wǎng)選了對應(yīng)版本它生成的命令里會(huì)帶上--index-url不要自己改。安裝完成后用三行代碼驗(yàn)收import torch print(torch.__version__) print(torch.cuda.is_available()) print(torch.cuda.get_device_name(0))第一行確認(rèn)版本第二行返回True說明檢測到顯卡第三行能看到你的顯卡型號(hào)。這三行都正常PyTorch的GPU支持就算通了。2.2 下載慢、超時(shí)中斷的問題怎么破實(shí)驗(yàn)室網(wǎng)絡(luò)環(huán)境經(jīng)常不穩(wěn)定從官方源下載幾個(gè)GB的PyTorch包跑到一半斷掉是家常便飯。pip默認(rèn)遇到超時(shí)會(huì)直接失敗所以建議加長超時(shí)時(shí)間并開啟重試pip install --timeout 120 --retries 5 torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu118如果網(wǎng)絡(luò)實(shí)在太差可以試試先下載whl文件到本地再用pip安裝。找到對應(yīng)版本和平臺(tái)的wheel文件下載完成后執(zhí)行pip install ./torch-xxx.whl這種方法不依賴網(wǎng)絡(luò)持續(xù)穩(wěn)定斷了就重新下。另外pip下載的緩存在~/.cache/pip同一個(gè)包你已經(jīng)下過一次沒裝成功第二次安裝會(huì)直接用緩存速度會(huì)快很多。如果你想強(qiáng)制重新下載可以加--no-cache-dir但通常沒必要。2.3 裝完跑不起來先看這五個(gè)常見報(bào)錯(cuò)我遇到過太多次明明裝好了但就是不能用把最常見的幾個(gè)報(bào)錯(cuò)和解決辦法列成表你們可以對照自查。報(bào)錯(cuò)信息原因解決方案ModuleNotFoundError: No module named torch包沒裝進(jìn)當(dāng)前環(huán)境檢查conda環(huán)境和pip對應(yīng)關(guān)系torch.cuda.is_available()為False裝成CPU版本或驅(qū)動(dòng)不支持卸載后按CUDA版本重裝CUDA driver version is too old驅(qū)動(dòng)版本太低裝對應(yīng)低CUDA版的PyTorchlibcudnn.so.8: cannot open shared object filecuDNN版本不匹配用官網(wǎng)命令重裝或安裝對應(yīng)cudnnOutOfMemoryError顯存不足減小batch size或模型規(guī)模其中第一個(gè)坑最隱蔽。你在conda環(huán)境里執(zhí)行pip install但實(shí)際安裝到了base環(huán)境或系統(tǒng)環(huán)境這是因?yàn)閜ip命令被別名指向了其他地方。遇到這個(gè)情況檢查which pip確認(rèn)是不是指向了你當(dāng)前環(huán)境的bin目錄。用python -m pip install這種寫法可以繞開路徑混亂問題。3. TensorFlow安裝的坑版本對齊比想象中難3.1 TensorFlow 2.x的CUDA要求官方說了算TensorFlow對CUDA和cuDNN的版本要求比PyTorch嚴(yán)格得多。PyTorch把CUDA運(yùn)行庫打包進(jìn)wheel安裝包裝完基本能跑TensorFlow則是運(yùn)行時(shí)動(dòng)態(tài)加載系統(tǒng)的CUDA庫版本不匹配直接給一長串warning然后告訴你Could not load dynamic library libcudnn.so.8。如果你用conda裝TensorFlow最簡單的辦法是讓conda自動(dòng)解決依賴。在conda環(huán)境中執(zhí)行conda install tensorflow-gpuconda會(huì)幫你裝好匹配的cudatoolkit和cudnn。如果你堅(jiān)持用pip那就必須嚴(yán)格對照官方表。比如TensorFlow 2.10.0官方對應(yīng)的是CUDA 11.2和cuDNN 8.1。TensorFlow從2.11開始Linux版的pip包默認(rèn)不帶GPU支持了需要裝tensorflow[and-cuda]這個(gè)版本別搞混了。3.2 和PyTorch共存時(shí)的依賴沖突別頭鐵我在第一節(jié)提過強(qiáng)烈建議PyTorch和TensorFlow分環(huán)境裝。這里再補(bǔ)充一個(gè)真實(shí)場景如果非要在同一環(huán)境裝兩個(gè)框架最常見的沖突點(diǎn)是numpy。TensorFlow 2.10要求numpy1.24某個(gè)小版本而PyTorch 2.x可能要求更高的numpy。一旦numpy被更新TensorFlow直接報(bào)numpy.AxisError之類的問題。遇到這種情況不要試圖手動(dòng)鎖定一個(gè)numpy版本去滿足兩邊因?yàn)楦炬i不住。最干凈的操作是conda create -n tf_env python3.9 conda create -n torch_env python3.9兩個(gè)環(huán)境各自獨(dú)立使用的時(shí)候conda activate切換互不干擾。磁盤占用多幾個(gè)GB但換來的是心平氣和。3.3 從報(bào)錯(cuò)信息快速定位三分靠猜七分靠查TensorFlow的報(bào)錯(cuò)信息長但關(guān)鍵信息往往在最后幾行。一個(gè)常見的錯(cuò)誤模式是長長的DNN library is missing這基本就是cuDNN沒找到。另一種是tf.test.is_gpu_available()返回False需要先跑一下診斷import tensorflow as tf print(tf.config.list_physical_devices(GPU))如果列表是空的接著執(zhí)行l(wèi)dconfig -p | grep libcudnn看看服務(wù)器上裝了哪些cuDNN版本。沒有輸出說明系統(tǒng)層沒有cuDNN。如果是conda環(huán)境執(zhí)行conda list | grep cudnn看環(huán)境內(nèi)是否包含cuDNN。這兩步查下來問題出在哪一層就清楚了。4. Visdom安裝后打不開可視化服務(wù)的前世今生4.1 visdom的正確啟動(dòng)方式很多人第一步就錯(cuò)了visdom是Facebook開源的深度學(xué)習(xí)可視化工具在PyTorch社區(qū)很流行。它由兩部分組成Python客戶端和Web服務(wù)端。你安裝完pip install visdom之后必須單獨(dú)啟動(dòng)一個(gè)服務(wù)進(jìn)程瀏覽器才能看到可視化面板。很多人裝完直接跑訓(xùn)練代碼然后瀏覽器訪問http://localhost:8097看到空白或者連不上就開始懷疑安裝有問題。正確啟動(dòng)方式是在服務(wù)器上執(zhí)行python -m visdom.server默認(rèn)監(jiān)聽8097端口??吹捷敵鯥ts Alive!之類的信息就說明服務(wù)啟動(dòng)成功了。這時(shí)候再訪問http://服務(wù)器IP:8097就能看到visdom的Web界面。注意要訪問服務(wù)器的IP不是localhost除非你就在服務(wù)器本機(jī)操作。4.2 啟動(dòng)報(bào)錯(cuò)與端口占用最常見的兩個(gè)故障visdom啟動(dòng)時(shí)最常見的報(bào)錯(cuò)是端口被占用。你可以用netstat -tlnp | grep 8097查看端口占用情況如果有進(jìn)程占了換個(gè)端口啟動(dòng)python -m visdom.server -port 8098然后你的客戶端代碼里也要對應(yīng)修改import visdom vis visdom.Visdom(port8098)另一個(gè)高頻報(bào)錯(cuò)是ConnectionRefusedError這通常是服務(wù)端沒有成功啟動(dòng)。有時(shí)和網(wǎng)絡(luò)代理沖突有關(guān)項(xiàng)目里如果設(shè)了HTTP_PROXY之類的環(huán)境變量容易干擾本地回環(huán)連接可以試試unset HTTP_PROXY HTTPS_PROXY python -m visdom.server4.3 visdom的替代方案與遷移建議visdom本身已經(jīng)維護(hù)不太活躍如果你是新項(xiàng)目我建議考慮替代方案。TensorBoard其實(shí)也能看PyTorch的指標(biāo)曲線配合torch.utils.tensorboard體驗(yàn)更穩(wěn)定。另一個(gè)好用的工具是wandb云端記錄訓(xùn)練曲線不需要自己維護(hù)服務(wù)團(tuán)隊(duì)協(xié)作方便。如果只是偶爾看個(gè)損失曲線matplotlib直接畫圖存文件也比啟動(dòng)一個(gè)服務(wù)省事得多。當(dāng)然如果你確實(shí)需要用visdom上面的啟動(dòng)方法足夠應(yīng)對。記著一點(diǎn)visdom的服務(wù)端進(jìn)程要一直開著訓(xùn)練腳本里的客戶端負(fù)責(zé)往服務(wù)端推數(shù)據(jù)兩者分開理解問題就少一半。5. 本地瀏覽器遠(yuǎn)程調(diào)用服務(wù)器JupyterSSH隧道與端口映射5.1 遠(yuǎn)程訪問Jupyter的原理一張白紙說清楚服務(wù)器上的Jupyter默認(rèn)監(jiān)聽8888端口但服務(wù)器的8888端口通常不對公網(wǎng)開放。你需要做的是把服務(wù)器的8888端口搬到本地電腦上然后訪問http://localhost:8888數(shù)據(jù)會(huì)通過SSH加密通道傳送。這就是SSH隧道也叫端口轉(zhuǎn)發(fā)是實(shí)驗(yàn)室遠(yuǎn)程訪問Jupyter最標(biāo)準(zhǔn)的方式安全系數(shù)高配置也簡單。5.2 一條命令搞定臨時(shí)隧道在本地電腦上執(zhí)行ssh -L 8888:localhost:8888 your_usernameserver_ip這條命令的含義是把本地的8888端口通過SSH連接映射到服務(wù)器的localhost:8888。執(zhí)行后會(huì)登錄到服務(wù)器你先正常執(zhí)行jupyter notebook --port8888 --no-browser啟動(dòng)Jupyter然后保持這個(gè)SSH會(huì)話不要關(guān)閉。另開一個(gè)本地瀏覽器訪問http://localhost:8888就能看到Jupyter界面。如果你想免去每次輸密碼的麻煩把本地公鑰加到服務(wù)器的~/.ssh/authorized_keys中。這樣后面的SSH連接都不用輸密碼隧道命令體驗(yàn)順暢很多。5.3 用~/.ssh/config固化配置一條別名走天下如果你每次都敲一長串SSH命令很容易膩。更優(yōu)雅的做法是在本地~/.ssh/config里寫一個(gè)配置段Host labserver HostName 192.168.1.100 User your_username Port 22 LocalForward 8888 localhost:8888配置好之后本地終端只要執(zhí)行ssh labserver就會(huì)自動(dòng)建立隧道并登錄服務(wù)器。然后服務(wù)器上照常啟動(dòng)Jupyter本地訪問localhost:8888就能用。這種方式的額外好處是你在公司或宿舍都能用同一個(gè)別名連接IP變了就改一下配置文件。5.4 Jupyter的token與密碼問題新版Jupyter啟動(dòng)時(shí)會(huì)給一個(gè)token你需要在瀏覽器里粘貼token才能登錄。如果覺得麻煩可以先在服務(wù)器上設(shè)置密碼jupyter notebook password設(shè)置完成后Jupyter會(huì)使用密碼登錄本地訪問時(shí)直接輸入密碼就行。另外Jupyter默認(rèn)只允許本機(jī)訪問如果你通過SSH隧道訪問這個(gè)限制完全沒問題因?yàn)樗淼涝诒镜乜磥砭褪潜緳C(jī)訪問。還要提醒一點(diǎn)不要在服務(wù)器上直接執(zhí)行jupyter notebook --ip0.0.0.0來開放所有IP訪問除非你非常清楚自己在做什么。實(shí)驗(yàn)室公共網(wǎng)絡(luò)環(huán)境下裸奔式開放的Jupyter容易被掃描器盯上輕則挖礦重則數(shù)據(jù)泄露。SSH隧道是安全性和便利性最均衡的方案。6. PyCharm遠(yuǎn)程連接服務(wù)器把編輯器搬進(jìn)服務(wù)器環(huán)境6.1 遠(yuǎn)程解釋器配置三分鐘搞定代碼編寫與運(yùn)行PyCharm Professional版本支持遠(yuǎn)程解釋器功能可以在本地寫代碼、本地調(diào)試實(shí)際運(yùn)行在服務(wù)器環(huán)境。個(gè)人我用這個(gè)功能很久了配合前面說的conda虛擬環(huán)境體驗(yàn)接近本地開發(fā)。配置路徑是Settings - Project - Python Interpreter - Add Interpreter - On SSH。填寫服務(wù)器的IP、用戶名、密碼后下一步會(huì)讓你選擇遠(yuǎn)程Python解釋器路徑。如果你用的是conda環(huán)境比如環(huán)境名是myenv解釋器路徑一般是~/miniconda3/envs/myenv/bin/python選好后PyCharm會(huì)掃描遠(yuǎn)程環(huán)境中的包并在本地顯示。你可以直接在本地新建Python文件選好解釋器后運(yùn)行進(jìn)程實(shí)際是在服務(wù)器上跑的。代碼可以同步到服務(wù)器的某個(gè)目錄比如~/projects/myproject。6.2 文件同步與路徑映射別讓代碼和可執(zhí)行文件分家遠(yuǎn)程解釋器默認(rèn)會(huì)把本地項(xiàng)目文件上傳到服務(wù)器某目錄但如果你遠(yuǎn)程目錄和本地目錄結(jié)構(gòu)不一致會(huì)導(dǎo)致ModuleNotFoundError。我建議把遠(yuǎn)程目錄設(shè)置成和本地項(xiàng)目一致的相對路徑。比如本地項(xiàng)目是D:/myproject遠(yuǎn)程就設(shè)成~/myproject兩邊目錄層級(jí)一致import就很少出問題。在映射配置里PyCharm有一項(xiàng)Deployment你可以選擇自動(dòng)上傳或手動(dòng)上傳。我習(xí)慣用自動(dòng)上傳模式這樣本地保存一次遠(yuǎn)程文件就更新一次省心。6.3 調(diào)試器、終端和環(huán)境變量的聯(lián)動(dòng)PyCharm遠(yuǎn)程調(diào)試的體驗(yàn)接近本地你可以打斷點(diǎn)、單步執(zhí)行、查看變量值關(guān)鍵是遠(yuǎn)程解釋器必須有pydevd-pycharm這個(gè)包這個(gè)包一般在你連接解釋器時(shí)PyCharm會(huì)自動(dòng)安裝到遠(yuǎn)程環(huán)境。還有一個(gè)容易忽略的點(diǎn)遠(yuǎn)程環(huán)境的環(huán)境變量。比如你的訓(xùn)練腳本要讀取數(shù)據(jù)集的路徑這個(gè)路徑是服務(wù)器上的絕對路徑而你在本地運(yùn)行調(diào)試時(shí)PyCharm會(huì)把當(dāng)前工作目錄切換為遠(yuǎn)程項(xiàng)目目錄這時(shí)候相對路徑其實(shí)不太可靠。我建議所有涉及文件路徑的地方都用絕對路徑或者從環(huán)境變量讀取避免本地和服務(wù)器之間來回踩坑。6.4 連接失敗的排查清單PyCharm遠(yuǎn)程連接失敗的報(bào)錯(cuò)通常比較直白但原因多種多樣。我按經(jīng)驗(yàn)把排查順序列出來ssh: Connection refused確認(rèn)服務(wù)器SSH服務(wù)在運(yùn)行端口默認(rèn)22。Server host key not found首次連接會(huì)詢問是否信任主機(jī)點(diǎn)是就行。Authentication failed密碼或用戶名錯(cuò)誤檢查是否有拼寫問題。Permission denied可能是服務(wù)器限制密碼登錄需要改用密鑰認(rèn)證。Interpreter not found你填的Python路徑不對在服務(wù)器上先執(zhí)行which python確認(rèn)路徑。還有一個(gè)比較高發(fā)的問題服務(wù)器上Python環(huán)境是激活的conda環(huán)境但PyCharm連接時(shí)用的是.bashrc里沒有conda初始化的非交互shell導(dǎo)致解釋器找不到。這種情況可以在遠(yuǎn)程環(huán)境的啟動(dòng)腳本里加上source ~/.bashrc或者在解釋器路徑里直接把conda環(huán)境Python的絕對路徑寫死。7. 最后分享兩個(gè)我自己用下來的小技巧一個(gè)是安裝任何大型包之前先在conda環(huán)境里查一下已安裝的包版本用conda list大概了解下基礎(chǔ)環(huán)境避免裝到一半發(fā)現(xiàn)核心庫版本被改動(dòng)。另一個(gè)是寫一個(gè)env_check.sh腳本把nvidia-smi、python --version、pip list這些關(guān)鍵信息一次性輸出到一個(gè)日志文件里遇到問題先看日志再解決省去反復(fù)排查的時(shí)間。這套流程我每年都會(huì)整理給實(shí)驗(yàn)室的新同學(xué)照著走下來PyTorch、TensorFlow、visdom、Jupyter遠(yuǎn)程訪問、PyCharm遠(yuǎn)程連接這五座大山基本都能翻過去。真遇到?jīng)]見過的報(bào)錯(cuò)也別慌把完整報(bào)錯(cuò)貼到搜索框里先看最后三行再往上翻上下文絕大多數(shù)坑都有前人踩過。祝你在實(shí)驗(yàn)室的服務(wù)器上跑得順利早點(diǎn)把時(shí)間留給真正要做的實(shí)驗(yàn)。