iT邦幫忙

2023 iThome 鐵人賽

DAY 10
1

簡介

建立深度學習的開發環境說是一場惡夢也不為過,無論是開發應用還是訓練模型,都有各自的大坑要踩。就算一開始環境都弄的好好的,也有可能用一陣子之後自己突然爆炸。今天來分享筆者自己如何建立開發環境以及一些踩過的坑,希望大家的環境都能夠平平安安、一帆風順的度過一生。

可愛貓貓 Day 10

(Powered By Microsoft Designer)

作業系統

談到開發環境使用的作業系統,小企鵝 Linux 肯定是首選,其次則是 Windows 的 WSL 子系統,本質上也是一種 Linux 作業系統。筆者推薦使用 Ubuntu 作為上手用的 Linux 環境,在社群支援方面也較廣泛,遇到問題比較容易在網路上搜尋到解決方法。

如果有兩台機器,可以考慮一台裝 Ubuntu Server 只有純文字介面,另外一台用 Windows 遠端過去開發。如果只有一台機器,則可以考慮裝雙系統。筆者在公司是用一台遠端一台訓練的模式,在家裡則是使用雙系統。

個人偏好使用 KDE Plasma 桌面環境,因此是選用 Kubuntu 22.04 作為作業系統。但桌面環境的影響通常不大,選擇自己順眼喜歡的就好。

NVIDIA 驅動程式

NVIDIA 驅動程式是運行整個 ML 相關開發應用最基礎也最重要的第一步,如果你是使用有桌面環境的 Ubuntu,通常能在系統設定找到 "Driver" 相關的頁面,打開來大概會像這樣:

Driver

可以看到有非常多選擇,其中 535, 525, 470 代表的是 Driver 的版本,如果你有勇氣的話可以選擇最新版來裝,如果追求穩定的話,通常選擇次新的版本會比較好。其次是如果你要保留桌面環境,那就不要裝有 "Server" 字樣的版本,通常那是給純文字環境使用的。

如果你是純文字環境下進行 NVIDIA Driver 的安裝,可以透過指令 apt search nvidia-driver 或者 apt-cache search nvidia-driver 來查看有哪些版本的 Driver 可以安裝,然後透過 apt 直接安裝即可,以安裝 535 版為例:

sudo apt install nvidia-driver-535

安裝完之後重新開機,然後開始祈禱電腦能順利重開 🙏

如果你的電腦能夠順利重開,可以輸入指令 nvidia-smi 來確認是否安裝成功:

nvidia-smi

安裝成功後,可以考慮將驅動程式的版本鎖定起來,避免他自動更新:

sudo apt-mark hold nvidia-driver-535
sudo apt-mark hold libnvidia-compute-535

NVIDIA 驅動程式就是踏入 ML 開發會遇到的第一個天坑,很有可能把 OS 裝好之後,放一個 NVIDIA Driver 上去,就再也打不開了,這都是很常遇到的事情。這邊建議把模型權重與訓練資料等等的,都放在另外一個硬碟。如果系統炸開,比較可以毫不猶豫的選擇重灌,會比起你解環境衝突來的好一些。

不過這一切都是建立在你能直接管理機器以及機器隨時都在你旁邊的狀況,在一些權限管理相對嚴格,或者訓練機房距離你萬里之遙的情況下就很麻煩了。如果你還能使用文字介面操作機器,建議先完整刪除 NVIDIA Driver 然後再重新安裝。筆者之前環境炸開時,紀錄了一份如何完全刪除 NVIDIA 驅動程式的筆記,給有需要的朋友參考看看。

Conda

在 Python 虛擬開發環境上,筆者推薦使用 Conda 家族的 Miniconda 來做管理。雖然 Miniconda 的老大哥 Anaconda 過去有些不好的評價,但其實經過了許多年的改進、社群的支持以及 Miniconda 的誕生,現在的 Conda 已經是個相當方便好用的工具了。不僅能用來管理 Python 套件,也能管理 Python 本身的版本以及 CUDA 的版本之類的。這個章節介紹 Conda 的安裝與基本用法。

安裝 Miniconda

前往 Miniconda 的頁面下載安裝檔並執行,中間會詢問你要安裝在哪個位置,預設是裝在 ~/miniconda3 裡面,但筆者偏好裝在 ~/.miniconda3 裡面,一般檢視資料夾時會被隱藏起來。

如果打算在 Dockerfile 裡面裝 Miniconda 的話,可以加上 -b 參數使用。其他還有滿多可用的參數,可以在執行安裝程式時加上 -h 來查看。

最後安裝程式會詢問你要不要對 Shell 做初始化,如果選擇做初始化的話,那每次打開 Shell 的時候,都會去讀取 Conda 的初始化環境,有些時候會造成嚴重的延遲。如果遇到這樣的問題,可以選擇不要初始化。如果已經初始化過了,可以到 ~/.bashrc 裡面將 >>> conda initialize >>> 的區塊註解掉即可。

但如果不做 Shell Init 的話,會不能直接使用 Conda 指令做操作,這時就要透過 source ~/miniconda3/bin/activate env_name 的指令來啟用虛擬環境了。

Conda 基本操作

使用 conda create 可以創建虛擬環境並指定 Python 版本:

# 使用 Python 3.9 的環境
conda create -n Hello39 python=3.9

# 使用 Python 3.10 的環境
conda create -n Hello310 python=3.10

建立完環境之後透過 conda activate 啟用環境:

conda activate Hello

在 Conda 虛擬環境裡面,可以透過 pip install 安裝套件,也可以透過 conda install 安裝套件。但並不是每個套件都支援從 Conda 安裝,建議參考該套件的安裝方法。一般情況下,筆者會優先選擇使用 pip 安裝,但是像 PyTorch 這種套件就有支援使用 conda 安裝的方法:

conda install pytorch pytorch-cuda=12.1 -c pytorch -c nvidia

如果創建虛擬環境時,忘記附上 python=X.YY 的參數,也可以用 conda install 裝回來:

conda install python=3.10

離開虛擬環境:

conda deactivate

如果你這環境不要了,打算整個刪掉,可以使用 conda env remove 指令:

conda env remove -n Hello

如果要清除 Conda 的快取,例如安裝套件的壓縮檔,可以使用 conda clean 指令:

conda clean -a  # 移除所有相關快取

Conda 進階操作

Conda 也能對環境進行備份與複製,例如:

# 將環境資訊輸出為 YAML 檔
conda env export > environment.yaml
# 從 YAML 檔複製環境
conda env create -f environment.yaml

# 將環境資訊輸出為 TXT 檔
conda list --explicit > requirements.txt
# 從 TXT 檔複製環境
conda create --name HelloCopy --file requirements.txt

# 直接複製環境
conda create --name HelloCopy --clone Hello

另外也能透過 Revision 功能操作環境的版本,但是 Conda 的 Revision 只會紀錄 conda install 相關的變化:

conda list --revisions  # 查看有哪些版本紀錄
conda install --revision 7  # Rollback 回第 7 版

其實在 Python 套件管理上,還有個相當受歡迎的 Poetry 套件。但是 Poetry 並不涉及 CUDA 這種系統工具的管理,因此也能用 Conda 管理系統工具,並在 Conda 的虛擬環境裡面使用 Poetry 之類的。

常用套件安裝

接下來介紹一些經常會用到的套件或工具的安裝方法。

CMake 安裝

在安裝套件的過程,經常會需要用到 CMake 這個編譯工具。但很多套件都會要求你 CMake 裝到 3.2X 版以上,如果你是 Ubuntu 20.04 之類的,透過 apt 安裝的 CMake 只能到 3.16 而已。這時你可以透過 pip install cmake 來把 CMake 裝到更高的版本,目前可以裝到 3.29.0.1 版。

pip 安裝 cmake 根本邪教 😱 但是真的太方便了!

CUDA

CUDA 是深度學習相當重要的技術,分成 Runtime 與 Toolkit 兩種,一般來說我們會使用到的都是 Toolkit 的部份,因此我們需要關注 NVIDIA Driver 與 CUDA Toolkit 的版本對應

例如你的 NVIDIA Driver 是 515 版,那就只能跑 CUDA 需求為 11.7 及以下的程式,如果有個專案需求為 11.8 以上,那就會不能跑,必須將 NVIDIA Driver 升級到 520 版才行。因此 Driver 的版本與 CUDA 的版本習習相關,兩者基本上都向下相容,所以:

NVIDIA Driver 支援的 CUDA 版本 >= CUDA Toolkit 版本 >= 開發應用的 CUDA 需求版本。

一般來說你可以裝 535 的驅動(支援到 CUDA 12 版)然後裝 CUDA Toolkit 11.8 跑一個 CUDA 需求為 11.7 的程式。但一般來說會建議讓 CUDA Toolkit 與程式的 CUDA 需求一樣,因為 Conda 可以管理 CUDA 的版本,所以就會看到類似這種情況:

$ conda env list

# conda environments:
#
base
py311cu118
py311cu121
py312cu121
...

如果一開始不知道怎麼決定要安裝哪個 CUDA 版本的話,推薦可以參考 PyTorch 官網目前支援到哪個版本,例如目前 PyTorch 支援 11.8 與 12.1 兩版,那筆者就會建議裝 CUDA 12.1 來用。這只是個參考,一切還是以應用需求為主。

我們可以在 Anaconda 的頁面上查看目前可以安裝哪些版本的 CUDA 可以安裝,例如我們要安裝 CUDA 12.1 可以使用以下指令:

conda install -y nvidia/label/cuda-12.1.1::cuda

這行指令代表安裝標籤為 nvidia/label/cuda-12.1.1cuda 套件。除了 cuda 以外,還有個名稱為 cuda-toolkit 的套件,兩者其實都會幫你安裝 CUDA 函式庫。過去筆者其實一直分不清楚兩者的差異,於是將兩邊的安裝內容仔細的比較了一下,發現選擇安裝 cuda 套件的話,會比 cuda-toolkit 多出 cuda, cuda-demo-suite, cuda-runtime 等三個項目,但實際上只有 cuda-demo-suite 套件會多安裝一些新檔案。所以不管安裝 cudacuda-toolkit 都是可以的,但推薦按照 NVIDIA 官方文件的建議安裝 cuda 套件即可。

有時其他套件會需要自行編譯一些 CUDA 函式庫,例如常用的分散式訓練框架 DeepSpeed,它可能會去搜尋環境底下的 lib64 而不是 lib 資料夾,但 Conda 通常是把相關函式庫安裝在 lib 裡面,因此可以直接 Link 過去:

ln -s ~/.miniconda3/envs/env_name/lib ~/.miniconda3/envs/env_name/lib64

另外像是 NVCCcuDNN 這類的工具和函式庫也能透過 Conda 安裝,前者通常會跟著 CUDA 一起安裝。這邊建議先裝 cuDNN 再裝 CUDA,原因在下個章節細談:

conda install -y anaconda::cudnn
conda install -y nvidia/label/cuda-12.1.1::cuda
conda install -y pytorch pytorch-cuda=12.1 -c pytorch -c nvidia

安裝好之後,我們可以透過 nvcc -V 來確認安裝是否正確:

$ nvcc -V

nvcc: NVIDIA (R) Cuda compiler driver
Copyright (c) 2005-2023 NVIDIA Corporation
Built on Mon_Apr__3_17:16:06_PDT_2023
Cuda compilation tools, release 12.1, V12.1.105
Build cuda_12.1.r12.1/compiler.32688072_0

OMG Conda & cuDNN

在安裝完 cuDNN 之後,重新調用 PyTorch 可能會遇到以下問題:

Traceback (most recent call last):
  File "/home/user/lab/script.py", line 1, in <module>
    import torch
  File "/home/user/.miniconda3/envs/Py310/lib/python3.10/site-packages/torch/__init__.py", line 229, in <module>
    from torch._C import *  # noqa: F403
ImportError: /home/user/.miniconda3/envs/Py310/lib/python3.10/site-packages/torch/lib/libtorch_cuda.so: undefined symbol: cudaGraphInstantiateWithFlags, version libcudart.so.11.0

這是因為從 Conda 安裝 cuDNN 時,系統很「熱心」的幫我們多裝了一個 CUDA 的函式庫,這時我們前往 Conda 環境底下的 lib 資料夾查看:

$ cd ~/.miniconda3/envs/Py310/lib
$ ll | grep cuda

-rw-rw-r-- 1.2K cudatoolkit_config.yaml
-rw-rw-r-- 832K libcudadevrt.a
lrwxrwxrwx   21 libcudart.so -> libcudart.so.11.3.109
lrwxrwxrwx   21 libcudart.so.11.0 -> libcudart.so.11.3.109
-rwxrwxr-x 623K libcudart.so.11.3.109
-rwxrwxr-x 680K libcudart.so.11.8.89
-rw-rw-r-- 1.2M libcudart_static.a

原本應該是 CUDA 11.8 的環境,被 Link 成 11.3 了,但因為當初 PyTorch 是安裝 CUDA 11.8 的版本,所以才會報錯。其中一種解決方法,是在當初安裝環境時,先安裝 cuDNN 再安裝 CUDA 來避免連結被蓋掉:

conda install -y anaconda::cudnn
conda install -y nvidia/label/cuda-12.1.1::cuda
conda install -y pytorch pytorch-cuda=12.1 -c pytorch -c nvidia

但我們也可以手動解決這個問題,把不正確的連結刪掉,重新連結正確的函式庫即可:

rm libcudart.so.11.0 libcudart.so
ln -s libcudart.so.11.8.89 libcudart.so.11.0
ln -s libcudart.so.11.8.89 libcudart.so

NVCC

NVCC 其實是個編譯用的工具,我們可以使用 C 語言撰寫一些 CUDA 小工具來用,例如我們想要查看 GPU 的 CUDA 相容性:

// DemoCUDA.cpp
// Include Path: ~/.miniconda3/envs/env_name/include/**

#include <cuda_runtime.h>
#include <stdio.h>

int main() {
    int nDevices;
    cudaGetDeviceCount(&nDevices);

    for (int i = 0; i < nDevices; i++) {
        cudaDeviceProp p;
        cudaGetDeviceProperties(&p, i);

        printf("Device %d\n", i);
        printf("  Device Name: %s\n", p.name);
        printf("  CUDA Capability: %d.%d\n", p.major, p.minor);
    }

    return 0;
}

透過以下指令編譯並執行:

nvcc DemoCUDA.cpp -o DemoCUDA && ./DemoCUDA

執行結果如下:

Device 0
  Device Name: NVIDIA GeForce RTX 3090
  CUDA Capability: 8.6

這個 CUDA Capability 在使用某些比較先進的高速運算時會有影響,例如比較早期的顯卡可能就不支援快速的 8-Bit 矩陣運算,在一些使用量化 (Quantization) 技術的應用裡面,速度優勢就會相對小一點。

雖然我們寫不出偉大的 CUDA 演算法,但我們還是可以寫一些小工具佯裝自己會點 CUDA 程式 ((x

雖然 nvidia-smi 已經有提供了,但我們可以透過 NVML 來撰寫取得顯卡溫度的程式。首先安裝 NVML 函式庫:

conda install -c conda-forge nvidia-ml

撰寫取得溫度的程式碼:

#include <nvml.h>
#include <stdio.h>

int main() {
    nvmlInit();
    unsigned int device_count;
    nvmlDeviceGetCount(&device_count);

    for (int i = 0; i < device_count; i++) {
        unsigned int temp;
        nvmlDevice_t device;
        nvmlDeviceGetHandleByIndex(i, &device);
        nvmlDeviceGetTemperature(device, NVML_TEMPERATURE_GPU, &temp);
        printf("Device %d - GPU Temperature: %u C\n", i, temp);
    }

    nvmlShutdown();

    return 0;
}

這裡可以直接用 gcc 編譯,在編譯之前,要先確認 nvml.hnvidia-ml.so 的所在路徑:

find ~/.miniconda3/envs/env_name | grep nvml.h
find ~/.miniconda3/envs/env_name | grep nvidia-ml.so

以筆者的環境而言,這兩個檔案分別位在:

~/.miniconda3/envs/env_name/include/nvml.h
~/.miniconda3/envs/env_name/lib/stubs/libnvidia-ml.so

編譯時,前者要放在參數 -I 後面,後者的資料夾(不需要 .so)要放在 -L 後面,完整的編譯指令如下:

gcc \
    -L ~/.miniconda3/envs/env_name/lib/stubs \
    -I ~/.miniconda3/envs/env_name/include \
    DemoNVML.cpp -o DemoNVML -l nvidia-ml

執行 DemoNVML 結果如下:

Device 0 - GPU Temperature: 55 C

GPU 小工具 DIY 輕鬆寫意~

Colab

如果你已經厭倦了與 Python 環境奮鬥,每次一個新的實驗都要搭一個環境做測試,有的實驗可能也存活不了一兩天,不妨試試看 Google Colab 筆記本。

Colab 的介面與 Jupyter Notebook 很相似,但運算是跑在 Google 的機器上面。Google 非常大方的免費借我們一張 Tesla T4 GPU 使用,雖然只有 16 GB 的 GPU 記憶體,但是做一些小型實驗已經相當夠用。在這個環境裡面,完全不用煩惱 NVIDIA Driver 與 CUDA 的問題。重點是,那個環境的網路速度有夠快的啦!

Colab

離峰時段網速經常破百 MB/s,平均也有個五六十 MB/s 以上,對於經常在下載大模型的 ML 開發者而言,是個相當方便的網路環境。唯一的缺點是不能在裡面操作 Docker,個人猜測 Colab 本身應該也是容器服務的緣故。

如果要在 Colab 裡面安裝其他套件,只要在程式碼區塊裡面輸入 !pip install ... 即可,這點與 Jupyter 相同:

!pip install transformers

可以在上方選單的「執行階段 > 變更執行階段類型」選擇使用 T4 GPU,另外也有 TPU 之類的可以選,但是 TPU 的用法跟一般 GPU 不太一樣,筆者也不太清楚如何調用就是了。

另外在設定裡面還有貓貓狗狗模式跟爆炸特效可以選擇,替沉悶的開發過程增添一點活潑的氣氛:

Colab

不過 Colab 也並非完全無限制的讓你使用,只要筆記本閒置一段時間就會被中斷連線,而連續運作時間通常也不能超過半天,比較適合那種一兩個小時就能結束的小型實驗,或者要寫 Demo Code 給別人看的時候能方便分享。如果想要更長時間的使用,或者調用更高級的顯卡,可以考慮購買 Colab Pro 會員資格。

Docker

若是熟悉 Docker 的開發者,可以選擇使用 PyTorch 官方的 Image 進行開發。使用官方 Docker Image 的好處在於 CUDA 相關的工具都已經配置好了,不太需要擔心環境配置炸開的問題。也能夠進一步透過 Dockerfile 來控制開發環境所依賴的套件,無論是需要透過 pipapt 安裝的套件,使環境建置更容易被重現。

若是 Tensorflow 則可以考慮 tensorflow/tensorflow:2.15.0-gpu 或者 tensorflow/tensorflow:2.15.0-gpu-jupyter 這兩個映像檔,後者附帶了 Jupyter 開發環境可以使用,相對好上手一些。

GPU

最後再來分享一些 GPU 型號的資訊。在 NVIDIA 底下的 GPU,每一代的架構都會有個代號,例如 GTX 10 開頭的顯卡是 Pascal 架構、RTX 20 開頭的則是 Turing 架構、RTX 30 為 Ampere、最新的 RTX 40 則為 Ada Lovelace 架構。這些代號都在向數學與資訊史上的名人致敬。

作為深度學習領域的開發者,選購顯卡時除了運算速度不同,更重要的是顯卡的記憶體,如果模型放不下,算的再快都是白搭。然而消費級的顯卡,例如 RTX 3090, RTX 4090,其 GPU 記憶體最高都只做到 24 GB 而已。如果想要再往上,就會用到工作站等級的顯卡。現今最有名的就是 A100

A100V100 的繼任者,採用 Ampere 架構,有 40 GB/80 GB 兩種規格的 GPU 記憶體。有趣的是他能切割記憶體,一張 A100 看起來會像有 7 張 GPU 一樣:

A100

(感謝強者我朋友鐵貓提供的截圖)

上圖是一台四張 A100 的機器,其中 4 號顯卡被切成 7 份的樣子。

其他還有 RTX A6000, RTX 6000 Ada 這種 48 GB 級的,和 A100 的繼任者 H100 等這些訓練用的顯卡。還有 L40S 這種推論優化的顯卡。但無論哪一種,都不是一般人買得起的 💸💸💸

雖然現在這些顯卡非常缺貨,有錢也買不起,只能說老黃賺爛了 💰

結論

今天分享了架設與管理環境的方法,中間還很熱血的寫起了 CUDA 小程式,並介紹了 Colab 與一些 NVIDIA 顯卡的資訊。其實每個人都有自己的一套管理方法,只要自己能夠輕鬆駕馭,而且方便配合團隊即可。完成環境搭建之後,明天就要來介紹 Hugging Face Transformers 套件啦 🤗

參考


上一篇
LLM Note Day 9 - LLM 訓練流程
下一篇
LLM Note Day 11 - 擁抱開源的微笑 Hugging Face Transformers
系列文
LLM 學習筆記33
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言