Ubuntu20.04にPythonとTensorFlowとCUDAとnvidia-driver(とcuDNN)を入れて機械学習GPU環境構築　libcudart.so.11のエラーなど

# 概要

大学に入って念願のゲーミングPCを手に入れたが持て余していたので、Ubuntuで機械学習（ディープラーニング）環境を整えようとしたら、仮想環境じゃLinuxがGPUを認識しないとか日本語remixだとネットにつながらないとかEFIが勝手にWindowsとデュアルブートになるとか色々あった。
TensorFlow周りでも苦労したので後日再度環境構築をするために手順をまとめる。


# 環境

* CPU：Intel Core i5-12600KF
* GPU：GeForce RTX 3060
* OS：Ubuntu 20.04


# 依存関係・バージョン確認

機械学習環境はPythonとTensorFlowとCUDAとnvidia-driverのバージョンが一致してないと動かないらしい。
実際、最新グラボだし全部新しいソフトなら動くだろうと思ったら動かなかった。（そしてこれが原因でのちにエラー祭り）

それぞれの依存関係が確認できる場所はいくつかある。[CUDAのWikipedia英語版](https://en.wikipedia.org/wiki/CUDA)とか、[CUDA Toolkitのドキュメント](https://docs.nvidia.com/cuda/cuda-toolkit-release-notes/index.html)とか。ただどうにも情報が古かったり錯綜していたので、自分は[TensoFlowのリファレンス](https://www.tensorflow.org/install/source#gpu)を参考にして決めた。
グラボがRTX 3060なのでCUDA 11.1以降でなければならず、

* Python 3.8
* TensorFlow 2.6
* CUDA 11.2
* nvidia-driver 460
* cuDNN 8.1

とした。


# pyenvによるPythonのインストール

Pythonのバージョン管理を（形だけでも）したかったので入れる。
Macと違って入れるのがまあまあ大変。

基本的なパッケージを入れてなかったら入れる。

```
sudo apt install git wget curl
```

`/etc/apt/sources.list`に

```
deb-src http://archive.ubuntu.com/ubuntu/ focal-updates main
```

を`sudo nano /etc/apt/sources.list`などで追加し、下記を実行。

```
sudo apt build-dep python3.8
```
[参考サイト](https://qiita.com/MusicScience37/items/4537233b840e2133cd32)によるとPythonをソースコードからビルドするのに必要なパッケージを`sources.list`のURLから参照して落としてるらしいが、これ3.9と3.8で違ったらどうなるのだろうか。

必要パッケージを手動で入れることも可。詳しくは[Python Developer's Guid](https://devguide.python.org/setup/)参照。

```
sudo apt install -y libffi-dev libssl-dev zlib1g-dev \
    liblzma-dev tk-dev libbz2-dev libreadline-dev \
    libsqlite3-dev libopencv-dev build-essential
```
`git clone`でソースコードを落とす。

```
git clone https://github.com/pyenv/pyenv.git ~/.pyenv
```
`.bashrc`にパスを追加。`source .bashrc`で再読込み。

```
export PYENV_ROOT="$HOME/.pyenv"
export PATH="$PYENV_ROOT/bin:$PATH"
eval "$(pyenv init -)"
```
可能なバージョン一覧

```
pyenv install --list
```

Pythonのインストール。

```
pyenv install 3.8.12
```
Pythonの入ってるバージョンを確認。*が現在。

```
pyenv versions

* system
  3.8.12
  3.9.10
```
使いたいバージョンに切り替え。

```
pyenv global 3.8.12
```
`global`じゃなくてディレクトリ下のみ変更の`local`もある。

実際に切り替わっているか確認。

```
python3 -V

Python 3.8.12
```
ここで切り替わってなかったらパス周りを疑う。

参考
[pyenv README](https://github.com/pyenv/pyenv#basic-github-checkout)
[Ubuntu 20.04 で pyenv を使用して Python を導入する](https://qiita.com/MusicScience37/items/4537233b840e2133cd32)（大変助かりました）
[ubuntu 20.04 / 18.04 に pyenv をインストール](https://zenn.dev/neruo/articles/install-pyenv-on-ubuntu)
[pyenvとpyenv-virtualenvの自分流使い方](https://qiita.com/ksato9700/items/5d9eba10fe6b8e064178)


# venvによるPythonライブラリの管理

TensorFlow公式が推奨していたのでやれたらやる。自分はやり忘れた。
これで直下にvenv用の環境ができる。

```
python3 -m venv [venv_name]
```

有効化。仮想環境に入ると先頭に(venv_name)が追加される。

```
source [venv_name]/bin/activate
```
無効化。

```
deactivate
```
参考
[venv: Python 仮想環境管理](https://qiita.com/fiftystorm36/items/b2fd47cf32c7694adc2e)


# TensorFlow 2.6のインストール

:::note warn
sudo pipをできるだけ使わない。pip単体を使う。
環境が破壊される可能性あり。
:::

pipで入れる。

```
pip install tensorflow==2.6
```
入ったか確認。

```
pip show tensorflow

Name: tensorflow
Version: 2.6.0
Summary: TensorFlow is an open source machine learning...
```

なおKerasのバグが2.6.0にはあるらしくKerasコード実行時に引っかかった。Kerasを使う場合は2.5へのダウングレードか修正版の2.6.2を推奨。
[How to fix error: Cannot register 2 metrics with the same name: /tensorflow/api/keras/optimizers](https://stackoverflow.com/questions/69694944/how-to-fix-error-cannot-register-2-metrics-with-the-same-name-tensorflow-api)


# CUDA 11.2のインストール

関門その１。
[公式サイト](https://developer.nvidia.com/)から、CUDA ToolkitというWebページの案内に従って条件を指定するとインストール用のコマンドが表示される。[CUDA Toolkit Archive](https://developer.nvidia.com/cuda-toolkit-archive)でほしいバージョンを探してもいいが、ググっても早い。
[CUDA Toolkit 11.2 Downloads](https://developer.nvidia.com/cuda-11.2.0-download-archive)

一番下のInstaller Typeは３つとも試したが、deb(network)はそのままコピペすると最新版が入る罠、runfile(local)は自分はあまりうまく行かなかったので、とくに根拠はないがdeb(local)を選ぶ。多分どれでもいい。
バージョン指定したい場合は最後を`cuda-11-2`とすればいい。

これはCUDA Toolkit 11.2のUbuntu20.04 x86_64のdeb(local)。

```
wget https://developer.download.nvidia.com/compute/cuda/repos/ubuntu2004/x86_64/cuda-ubuntu2004.pin
sudo mv cuda-ubuntu2004.pin /etc/apt/preferences.d/cuda-repository-pin-600
wget https://developer.download.nvidia.com/compute/cuda/11.2.0/local_installers/cuda-repo-ubuntu2004-11-2-local_11.2.0-460.27.04-1_amd64.deb
sudo dpkg -i cuda-repo-ubuntu2004-11-2-local_11.2.0-460.27.04-1_amd64.deb
sudo apt-key add /var/cuda-repo-ubuntu2004-11-2-local/7fa2af80.pub
sudo apt-get update
sudo apt-get -y install cuda
```

そしてどういう理屈か分からないが、上記のコードでは自分はうまく行かなかった。後述の方法でバージョンを確認すると、`nvidia-smi`と`dpkg -l | grep cuda`では確かに11.2が確認できるが、`/usr/local`に`cuda`から始まるディレクトリが一切なかった。あと`nvcc -V`が動かない。

結果自分の場合、最後のコードを、

```
sudo apt-get install cuda-toolkit-11-2
```

にするとうまく行った。
`/usr/local`に`cuda-11.2`というディレクトリがコマンド後に生成されたのも確認。どうもCUDA Toolkitにnvidia-driverも入ってて、そのおかげでうまくworkしたっぽいが、原因はよくわからない。[こちら](https://mathematical-free.com/?p=632)を参考にした。


`.bashrc`に下記を追加。CUDAのエラーはパス周りに多い気がする。

```
# ドキュメント準拠
export PATH=/usr/local/cuda-11.2/bin${PATH:+:${PATH}}
export LD_LIBRARY_PATH=/usr/local/cuda-11.2/lib64${LD_LIBRARY_PATH:+:${LD_LIBRARY_PATH}}

# nvidia-driverを入れる場合はこっちでも動く
export PATH="/usr/local/cuda/bin:$PATH"
export LD_LIBRARY_PATH="/usr/local/cuda/lib64:$LD_LIBRARY_PATH"
```
再起動後バージョン確認。

```
nvcc -V

nvcc: NVIDIA (R) Cuda compiler driver
Copyright (c) 2005-2021 NVIDIA Corporation
```

こちらも可。

```
dpkg -l | grep cuda

ii  cuda-command-line-tools-11-2               11.2.2-1                              amd64        CUDA command-line tools
ii  cuda-compiler-11-2                         11.2.2-1                              amd64        CUDA compiler
```
## `nvcc -V`時に`sudo apt install nvidia-cuda-toolkit`と帰ってきた場合

`nvcc -V`と入れたときに、`sudo apt install nvidia-cuda-toolkit`と出た場合、従わないことを推奨する。どうも古いCUDA 10を入れてしまうらしく２つのバージョンが同居して滅茶苦茶になる。ただ多分こんな現象うちだけだろう。

参考
[CUDA Toolkit 11.2 ドキュメント](https://docs.nvidia.com/cuda/archive/11.2.0/)


# nvidia-driver 460のインストール

前述の理由から、`sudo apt-get install cuda-toolkit-11-2`でCUDAを入れた場合、同梱されてて必要ない可能性もあるが一応入れる。

前述のCUDAを入れるコマンドのいくつかに、例えば`sudo dpkg -i cuda-repo-ubuntu2004-11-2-local_11.2.0-460.27.04-1_amd64.deb`の後半、７桁の数字がdriverのバージョンらしいので念の為それに合わせる。

```
sudo add-apt-repository ppa:graphics-drivers/ppa
sudo apt update
sudo apt install nvidia-driver-460
```
インストール後`/usr/local`に`cuda`、`cuda-11`という２つのディレクトリが創られるのを確認。
ただのこの２つと`CUDA-11.2`は中身が一緒っぽい。

再起動後バージョン確認。

```
nvidia-smi

+-----------------------------------------------------------------------------+
| NVIDIA-SMI 460.106.00   Driver Version: 460.106.00   CUDA Version: 11.2     |
|-------------------------------+----------------------+----------------------+
```
こちらも可。

```
dpkg -l | grep nvidia

ii  libnvidia-cfg1-460:amd64                   460.106.00-0ubuntu1                   amd64        NVIDIA binary OpenGL/GLX configuration library
ii  libnvidia-common-460                       460.106.00-0ubuntu1                   all          Shared files used by the NVIDIA libraries
```


# cuDNN 8.1のインストール

関門その２。
cuDNNはTensorFlowの学習速度の高速化ができるソフトウェアらしく、tfに必ずしも必要というわけではないが、参考にした大抵のブログでは入れていたので自分も入れる。唯一コマンドラインでインストールしないソフトウェアだが、これCUIのLinuxだとどうするんだろうか。

古い情報が多かったので[cuDNNのドキュメント](https://docs.nvidia.com/deeplearning/cudnn/install-guide/index.html#install-linux)を見る。

[cuDNN Archive](https://developer.nvidia.com/rdp/cudnn-archive)で目的のバージョンとCPUアーキテクチャに合致するファイルをすべて落とす。
今回は

* cuDNN Runtime Library for Ubuntu20.04 x86_64 (Deb)
* cuDNN Developer Library for Ubuntu20.04 x86_64 (Deb)

の２つ。

[ドキュメント](https://docs.nvidia.com/deeplearning/cudnn/install-guide/index.html#install-linux)を読むとUbuntu20.04なら２つで良さそうだが、多くの参考にしたサイトでは３つなので正直良くわからない。
ダウンロードしたファイルを実行。

```
sudo dpkg -i libcudnn8_8.1.0.77-1+cuda11.2_amd64.deb
sudo dpkg -i libcudnn8-dev_8.1.0.77-1+cuda11.2_amd64.deb
```
バージョン確認。

```
dpkg -l | grep cudnn

ii  libcudnn8                                  8.1.0.77-1+cuda11.2                   amd64        cuDNN runtime libraries
ii  libcudnn8-dev                              8.1.0.77-1+cuda11.2                   amd64        cuDNN development libraries and headers
```
参考
[cuDNN 8.1 ドキュメント](https://docs.nvidia.com/deeplearning/cudnn/index.html)


# 動作確認

下記コードを実行してエラーが出ずに動いているか確認。

```
python3 -c "import tensorflow as tf; print( tf.__version__ )"

YEAR-MONTH-DAY TIME: I tensorflow/stream_executor/platform/default/dso_loader.cc:53] Successfully opened dynamic library libcudart.so.11.0
2.6.0
```

TensorFlowがGPUを認識しているか確認。

```
python3 -c "from tensorflow.python.client import device_lib; print(device_lib.list_local_devices())"

...
device_type: "GPU"
...
```

GPUが表示されていれば成功。CPUのみなら失敗。

参考
[TensorFlow 2.7（GPU 対応可能）, Keras, scikit-learn, MatplotLib, Python 用 opencv-python 4.3 のインストール（Windows 上）](https://www.kkaneko.jp/tools/win/keras.html)


# Kerasで実験

Jupyter notebookを入れてKerasを適当に動かす。せっかくなのでCNNで。
コードは[Keras本](https://www.amazon.co.jp/Python%E3%81%A8Keras%E3%81%AB%E3%82%88%E3%82%8B%E3%83%87%E3%82%A3%E3%83%BC%E3%83%97%E3%83%A9%E3%83%BC%E3%83%8B%E3%83%B3%E3%82%B0-Francois-Chollet/dp/4839964262)から写経したものを流用。

TensorFlow 2.6.0だとKerasが動かないバグがあるので2.5か2.6.2に変える。
[How to fix error: Cannot register 2 metrics with the same name: /tensorflow/api/keras/optimizers](https://stackoverflow.com/questions/69694944/how-to-fix-error-cannot-register-2-metrics-with-the-same-name-tensorflow-api)


```python
from tensorflow.keras import layers
from tensorflow.keras import models
from tensorflow.keras.datasets import mnist
from tensorflow.keras.utils import to_categorical

model = models.Sequential()
model.add(layers.Conv2D(32, (3, 3), activation='relu', input_shape=(28, 28, 1)))
model.add(layers.MaxPooling2D((2, 2)))
model.add(layers.Conv2D(64, (3, 3), activation='relu'))
model.add(layers.MaxPooling2D((2, 2)))
model.add(layers.Conv2D(64, (3, 3), activation='relu'))

model.add(layers.Flatten())
model.add(layers.Dense(64, activation='relu'))
model.add(layers.Dense(10, activation='softmax'))

model.summary()

(train_images, train_labels), (test_images, test_labels) = mnist.load_data()
train_images = train_images.reshape((60000, 28, 28, 1))
train_images = train_images.astype('float32') / 255
test_images = test_images.reshape((10000, 28, 28, 1))
test_images = test_images.astype('float32') / 255
train_labels = to_categorical(train_labels)
test_labels = to_categorical(test_labels)

model.compile(optimizer='rmsprop',
             loss='categorical_crossentropy',
             metrics=['accuracy'])
model.fit(train_images, train_labels, epochs=5, batch_size=64)

test_loss, test_acc = model.evaluate(test_images, test_labels)

test_acc
```

1 epochあたり5-10s程度かかっている。Google colabでは同じコードで10-20程度。とてもはやい。

参考
[PythonとKerasによるディープラーニング](https://www.amazon.co.jp/Python%E3%81%A8Keras%E3%81%AB%E3%82%88%E3%82%8B%E3%83%87%E3%82%A3%E3%83%BC%E3%83%97%E3%83%A9%E3%83%BC%E3%83%8B%E3%83%B3%E3%82%B0-Francois-Chollet/dp/4839964262)（とてもよい本）


# トラブルシューティング（ソフトウェアの削除など）

## 便利コマンド

### バージョンの確認

```
# python
python3 -V
# tensorflow
pip show tensorflow

# nvidia-driver
nvidia-smi
# CUDA
nvcc -V

# cuda,nvidia,cudnn
dpkg -l | grep cuda
dpkg -l | grep nvidia
dpkg -l | grep cudnn
```
### TensorFlowの動作確認

```
# tensorflowの動作
python3 -c "import tensorflow as tf; print( tf.__version__ )"

# tensorflowがGPUを認識してるか
python3 -c "from tensorflow.python.client import device_lib; print(device_lib.list_local_devices())"
```

### ソフトウェアの削除

```
# python
pyenv uninstall 3.8.12

# tensoflow
pip uninstall tensorflow
# 依存関係ごと削除
pip-autoremove tensorflow

# CUDA
sudo apt-get --purge remove cuda-*
# CUDAドキュメント版
sudo apt-get --purge remove "*cublas*" "*cufft*" "*curand*"　"*cusolver*" "*cusparse*" "*npp*" "*nvjpeg*" "cuda*" "nsight*"

# nvidia-driver
sudo apt-get --purge remove nvidia-*
# CUDAドキュメント版
sudo apt-get --purge remove "*nvidia*"

# cuDNNは勝手に消えるっぽい
```

### 依存関係の削除

```
sudo apt autoremove
sudo apt autoclean
```

### アップデート/アップグレード

```
sudo apt update
sudo apt upgrade
```

## 遭遇したエラー

### `dlerror: libcurand.so.11: cannot open shared object file: No such file or directory`など

`python3 -c "import tensorflow as tf; print( tf.__version__ )"`と入れると帰ってきた。どうも[未だに直されてないバグの一つ](https://github.com/tensorflow/tensorflow/issues/45930)らしい。

`find`などでファイルを探しパスをつなぎ直すと解消できるらしいが、自分はそもそもそんなファイルが存在しなかった。

```
sudo apt-get install cuda-toolkit-11-2
```

で解決した。

### `sudo apt install nvidia-cuda-toolkit`

`nvcc -V`が作動しない場合はnvidia-driverの不良の可能性が高い。
同様に`sudo apt-get install cuda-toolkit-11-2`で解決。


# 参考にしたサイト

[ubuntuにCUDA、nvidiaドライバをインストールするメモ](https://qiita.com/porizou1/items/74d8264d6381ee2941bd)
[UbuntuにNVIDIAドライバ、CUDA、 cuDNNをインストールする方法(2020年8月)](https://mathematical-free.com/?p=632)
[【Ubuntu】NVIDIAドライバ・CUDA・CUDNNをインストールして深層学習環境を整える](https://guminote.sakura.ne.jp/archives/328#h2_6)
[TensorFlow on DockerでGPUを使えるようにする方法](https://www.kabuku.co.jp/developers/errors-with-tensorflow-on-gpu)
[Ubuntu18.04にNVIDIAとCUDAを入れ直すことに](https://qiita.com/Yoshitaka_Engineer/items/bd8edcb36396df878641)
[Ubuntu 18.04 で機械学習環境 ( cuda 10.0 + cudnn7.4 + Pytorch 1.7.1 ) の構築](https://qiita.com/RE-yura/items/73cbe460e387d98aaef2)
[深層学習　Ubuntu 20.04 に Tensorflow GPU をインストール](https://www.ecobioinfo.com/?p=1411)