一個由tf1.6.0引發的故事|從CUDA到gcc配置,非root使用者重灌舊版本TF環境

Teddyonthebench發表於2022-12-02

之前嘗試復現學姐前幾年的一個工作,但是因為框架有點古老而作罷。
然鵝,自己的實驗結果一直跑得十分奇怪,為了去學姐的程式碼中尋找參考,今天再次進行了嘗試。

我的需求是安裝Tensorflow_gpu_1.6.0(文中簡稱TensorFlow),目前機器已配置cuda11和cuda10,非root使用者。

前置疑問

Q1: 為什麼安裝TensorFlow需要重灌CUDA?

當我安裝完畢並執行Tensorflow_gpu_1.6.0時,出現了以下錯誤:

ImportError: libcublas.so.9.0: cannot open shared object file: No such file

這意味著TensorFlow需要對應版本的CUDA,對於CUDA、cuDNN和TensorFlow的關係,知乎有回答寫得很好:

CUDA看作是一個工作臺,上面配有很多工具,如錘子、螺絲刀等。
cuDNN是基於CUDA的深度學習GPU加速庫,有了它才能在GPU上完成深度學習的計算。
它就相當於工作的工具,比如它就是個扳手。但是CUDA這個工作臺買來的時候,並沒有送扳手。想要在CUDA上執行深度神經網路,就要安裝cuDNN,就像你想要擰個螺帽就要把扳手買回來。這樣才能使GPU進行深度神經網路的工作,工作速度相較CPU快很多。
基本上所有的深度學習框架都支援cuDNN這一加速工具,例如:Caffe、Caffe2、TensorFlow、Torch、Pytorch、Theano等。

Q2: nvidia-smi和nvcc -V顯示的不是同一版本CUDA?

來自CW大佬的文章,知道只要 nvcc -V 顯示的版本低於 nvidia-smi 顯示版本就沒有問題。

  1. CUDA有runtime APIdriver API,而nvcc -V顯示的是前者版本,nvidia-smi顯示的是後者版本。

  2. nvidia-smi展示的 driver API 代表機器能夠支援的最高版本CUDA,如果driver (nvidia-smi)的版本低於runtime (nvcc -V)版本,通常不會出現問題。

  3. 產生原因:安裝CUDA可以透過CUDA Toolkit installer 或 GPU driver installer,如果使用了單獨的GPU driver installer來安裝CUDA,就可能會出現版本不一致的現象,而透過CUDA Toolkit installer安裝則會保持一致。

Q3: 用conda裝CUDA不可以嗎?

我嘗試了透過conda安裝對應的CUDA9.0 以及cuDNN,安裝過程很順利,但Tensorflow並不能跑起來,提示缺少對應的.so檔案,而在/usr/local 下也並沒有找到我裝的cuda9.0。

參考知乎回答 , pytorch如何尋找CUDA:

  1. 先取預設 cuda 安裝目錄 /usr/local/cuda
  2. 如預設目錄不存在(例如安裝原生 cuda 到其他自定義位置),那麼搜尋 nvcc 所在的目錄
  3. 如果 nvcc 不存在,那麼直接尋找 cudart 庫檔案目錄(此時可能是透過 conda 安裝的 cudatoolkit,一般直接用 conda install cudatoolkit,就是在這裡搜尋到 cuda 庫的),庫檔案目錄的上級目錄就作為 CUDA_HOME。
  4. 如果最終未能得到 CUDA_HOME,那麼生成的 pytorch 將不使用 CUDA。

TensorFlow同理,但我並沒有成功修復呼叫順序的問題。那麼如果conda安裝正確,且機器沒有其他版本CUDA(如/usr/local/cuda),理論上能直接用?

結論

  1. 如果可以用docker,可以移步TF官網docker安裝教程-最簡單方法
  2. 如果可以用root,可以移步部落格TF安裝
  3. 如果conda能用,可以移步部落格TF安裝-conda

0 安裝之前——檢查機器配置(非root使用者)

查詢目標配置

安裝TensorFlow環境,需要配合對應版本的CUDA、cuDNN和python,而CUDA需要配合對應版本的gcc,
我的目標是裝tf_gpu_1.6.0,從官網查到匹配的環境資訊如下:官網-TensorFlow對應CUDA

版本 Python 版本 編譯器 構建工具 cuDNN CUDA
Tensorflow_gpu-1.6.0 2.7、3.3-3.6 GCC 4.8 Bazel 0.9.0 7 9

檢查機器配置

# 檢視命令

cat /etc/os-release  
# 或uname -a
gcc --version
nvcc -V 
nvidia-smi
python --version
版本 Python 版本 編譯器 構建工具 cuDNN CUDA
None 3.6.1 GCC 8.4.0 -- -- 10.1

安裝順序

按照依賴關係,gcc -> CUDA -> cuDNN -> python -> TensorFlow

1 安裝gcc

不同的CUDA支援的gcc版本不同,現在用的伺服器gcc版本比較新,實測cuda9.0在gcc8.4.0下安裝程式會失敗,所以需要降級。

首先在位置/home/[username]/, 建立目錄如下:參考部落格

.
├── gcc     # 新建,用於存放gcc版本
│   ├──gcc640   # 新建
│   └── gcc-6.4.0.tar.gz    # 下載
└── ... # 其他檔案

1.1 下載gcc (以gcc6.4.0為例)

(方式1) 手動下載:http://ftp.gnu.org/gnu/gcc/ 後傳輸到伺服器
(方式2) cmd:

wget http://ftp.gnu.org/gnu/gcc/gcc-6.4.0/gcc-6.4.0.tar.gz

1.2 configure

參考:官方gcc安裝教程

下載完成後進行解壓

cd /home/[username]/gcc
tar xzf gcc-6.4.0.tar.gz
cd gcc-6.4.0
./contrib/download_prerequisites  # 執行指令碼,自動安裝依賴
mkdir objdir  # 在gcc-6.4.0原始碼資料夾下,新建一個資料夾用於編譯
cd objdir 

使用configure生成 Makefile,為下一步的編譯做準備:

$PWD/../gcc-6.4.0/configure --prefix=$HOME/gcc/gcc640 --enable-checking=release --enable-languages=c,c++ --enable-threads=posix --disable-multilib

# --prefix=/usr/local/gcc6  指定安裝路徑
# --enable-languages=c,c++  支援的程式語言
# --enable-threads=posix    使用POSIX/Unix98作為執行緒支援庫
# --disable-multilib        取消多目標庫編譯(取消32位庫編譯)

1.3 編譯

接下來編譯

make  # 或者make -j8 使用8個執行緒並行編譯

可能問題1: make[3]: *** No rule to make target '../build-x86_64-pc-linux-gnu/libiberty/libiberty.a', needed by 'build/genmddeps'. Stop.

在解壓後需要在原始碼資料夾下新建一個資料夾,如/home/[username]/gcc/gcc-6.4.0/objdir,之後的make過程均在新建的編譯資料夾內

** 可能問題2: ./md-unwind-support.h:65:48: error: dereferencing pointer to incomplete type 'struct ucontext'**

參考部落格,原因是現在安裝版本的gcc語法不相容:

如果你機器上已經預設安裝的是gcc8.1,現在想要使用gcc6.x,那麼編譯gcc6.x原始碼過程中會出現好些錯誤,這些錯誤是因為gcc在升級過程中對標頭檔案的一些結構變數的定義的更改變化沒有做向後相容處理,或者某些語法的嚴格性要求不一樣(比較常見的是struct的定義和申明)

開啟報錯指出的檔案地址

/home/[username]/gcc/gcc-6.4.0/x86_64-pc-linux-gnu/libgcc/md-unwind-support.h

修改第61行的

struct ucontext * uc_ = context->cfa;

struct ucontext_t * uc_ = context->cfa; 

接下來繼續make

可能問題3: sanitizer_platform_limits_posix.cc:158:23: fatal error: sys/ustat.h: No such file or directory #include <sys/ustat.h>

參考部落格,修改程式碼檔案

vim libsanitizer/sanitizer_common/sanitizer_platform_limits_posix.cc

(1)首先註釋158行#include <sys/ustat.h>

(2)將250行語句unsigned struct_ustat_sz = sizeof(struct ustat);註釋,修改為如下內容:

  // Use pre-computed size of struct ustat to avoid <sys/ustat.h> which
  // has been removed from glibc 2.28.
#if defined(__aarch64__) || defined(__s390x__) || defined (__mips64) \
  || defined(__powerpc64__) || defined(__arch64__) || defined(__sparcv9) \
  || defined(__x86_64__)
#define SIZEOF_STRUCT_USTAT 32
#elif defined(__arm__) || defined(__i386__) || defined(__mips__) \
  || defined(__powerpc__) || defined(__s390__)
#define SIZEOF_STRUCT_USTAT 20
#else
#error Unknown size of struct ustat
#endif
  unsigned struct_ustat_sz = SIZEOF_STRUCT_USTAT;

(3)儲存後,重新make

可能問題4 :../../.././libsanitizer/sanitizer_common/sanitizer_stoptheworld_linux_libcdep.cc:270:22: error: aggregate ‘sigaltstack handler_stack’ has incomplete type and cannot be defined

(1)開啟libsanitizer/sanitizer_common/sanitizer_linux.h檔案,註釋第22行的struct sigaltstack;

(2)修改31.32行:

uptr internal_sigaltstack(const struct sigaltstack* ss,
                          struct sigaltstack* oss);

uptr internal_sigaltstack(const void* ss, void* oss);

(3)修改libsanitizer/sanitizer_common/sanitizer_linux.cc檔案549、550行

uptr internal_sigaltstack(const struct sigaltstack *ss,                                      
                                struct sigaltstack *oss) {

uptr internal_sigaltstack(const void *ss, void *oss) {

(4)修改libsanitizer/sanitizer_common/sanitizer_stoptheworld_linux_libcdep.cc270行

struct sigaltstack handler_stack;

stack_t handler_stack;

(5)修改libsanitizer/tsan/tsan_platform_linux.cc294行

__res_state *statp = (__res_state*)state;

struct __res_state *statp = (struct __res_state*)state;

(6)繼續make

可能問題5: ../../.././libsanitizer/sanitizer_common/sanitizer_internal_defs.h:254:72: error: size of array ‘assertion_failed__1139’ is negative

(1)修改 /gcc-6.3.0/configure檔案
(2)刪除檔案中 target-libsanitizer \,大概在2700多行
(3)繼續make

1.4 安裝

make install

檢查包含以下目錄,即安裝成功:/home/[username]/gcc/gcc640

1.4.1 覆蓋路徑

[要安裝GCC的路徑]:/home/[username]/gcc/gcc640
#路徑要在環境變數前

export PATH=[要安裝GCC的路徑]/bin:[要安裝GCC的路徑]/lib64:$PATH
export LD_LIBRARY_PATH=[要安裝GCC的路徑]/lib/:$LD_LIBRARY_PATH

最後重新整理

source ~/.bashrc

1.4.2 檢查gcc設定

gcc --version

2 安裝CUDA

CUDA 下載地址:https://developer.nvidia.com/cuda-toolkit-archive

.
├── cuda      # 新建,用於存放不同的cuda版本,即安裝目錄
│   ├── cuda-9.0     # 新建
│   └── tem      # 新建,用於cuDNN的解壓,安裝完成後就刪除該目錄
│   │     ├── cudnn-9.1-linux-x64-v7.1.tgz     # 後續下載
│   └── cuda_9.1.85_387.26_linux.run       # 後續下載
└── ... # 其他檔案

2.1 給cuda可執行許可權

chmod +x cuda_9.1.85_387.26_linux.run 

2.2 執行安裝

sh cuda_9.1.85_387.26_linux.run 

安裝過程記錄:

-----------------
Do you accept the previously read EULA?
accept/decline/quit: accept

You are attempting to install on an unsupported configuration. Do you wish to continue?
(y)es/(n)o [ default is no ]: y

# 已有Driver API,不安裝
Install NVIDIA Accelerated Graphics Driver for Linux-x86_64 384.81?
(y)es/(n)o/(q)uit: n

Install the CUDA 9.0 Toolkit?
(y)es/(n)o/(q)uit: y

# 非root使用者,所以安裝在自定義目錄
Enter Toolkit Location
 [ default is /usr/local/cuda-9.0 ]: /home/[username]/cuda/cuda-9.0

# 非root使用者,嘗試過無法連結到/usr/local
Do you want to install a symbolic link at /usr/local/cuda?
(y)es/(n)o/(q)uit: n

Install the CUDA 9.0 Samples?
(y)es/(n)o/(q)uit: y

Enter CUDA Samples Location
 [ default is /home/[username] ]: /home/[username]/cuda/cuda-9.0

Installing the CUDA Toolkit in /home/[username]/cuda/cuda-9.0 ...
Missing recommended library: libGLU.so
Missing recommended library: libXi.so
Missing recommended library: libXmu.so

Installing the CUDA Samples in /home/[username]/cuda/cuda-9.0 ...
Copying samples to /home/[username]/cuda/cuda-9.0/NVIDIA_CUDA-9.0_Samples now...
Finished copying samples.

2.3 解除安裝CUDA

參考部落格
安裝程式最後已經準備好了解除安裝的介面,解除安裝程式在/[安裝目錄]/bin下,需要注意的是cuda10.0及之前的版本解除安裝程式名為uninstall_cuda_xx.x.pl,而cuda10.1及之後的版本解除安裝程式名為cuda-uninstaller。

找到之後執行解除安裝程式即可,這裡的xx.x表示自己的cuda版本。

3 安裝cuDNN

參考部落格
cudnn下載地址:https://developer.nvidia.com/rdp/cudnn-archive

3.1 解壓安裝檔案

 tar -zxvf cudnn-9.1-linux-x64-v7.1.tgz 

3.2 複製檔案到cuda

cp /home/[username]/cuda/include/cudnn.h  /home/[username]/cuda/cuda-9.0/include
cp /home/[username]/cuda/lib64/libcudnn*  /home/[username]/cuda/cuda-9.0/lib64

3.3 新增許可權

chmod a+r /home/[username]/cuda/cuda-9.0/include/cudnn.h  /home/[username]/cuda/cuda-9.0/lib64/libcudnn*

3.4 配置使用者環境變數

修改個人使用者目錄下的.bashrc檔案(用vi ~/.bashrc編輯),在檔案最後面加入以下指令並儲存:

export PATH=//home/[username]/cuda/cuda-9.0/bin${PATH:+:${PATH}}
export LD_LIBRARY_PATH=/home/[username]/cuda/cuda-9.0/lib64${LD_LIBRARY_PATH:+:${LD_LIBRARY_PATH}}

最後輸入命令source .bashrc 使新配置的環境變數生效

4 安裝TensorFlow

可能問題: Could not find a version that satisfies the requirement tensorflow-1.6.0 (from versions: none)

參考:https://stackoverflow.com/questions/38896424/tensorflow-not-found-using-pip

一般情況下是python版本不對應,但目前python版本是正確的,以下兩種解決方案:

(1) 把pip install替換成pip3 install --upgrade

pip3 install --upgrade TF_BINARY_URL 

(2) 檢查自己的語法(事實證明是我錯寫成tensorflow-gpu-1.6.0了)

pip3 install tensorflow-gpu==1.6.0

5 檢測新環境

import tensorflow as tf
import os
os.environ['CUDA_VISIBLE_DEVICES']='4'   # tensorflow預設佔用機器所有卡,所以記得事先選擇
from tensorflow.python.client import device_lib
print(device_lib.list_local_devices())   # 檢查TF可用的卡

Note: TensorFlow預設會對卡從0開始重新編號

Ref:
[1] https://blog.csdn.net/wanggao_1990/article/details/120989070
[2] https://stackoverflow.com/questions/9450394/how-to-install-gcc-piece-by-piece-with-gmp-mpfr-mpc-elf-without-shared-libra
[3] https://blog.csdn.net/XCCCCZ/article/details/80958414

相關文章