NVIDIA Driverのアップデート手順とTensorflowの再構築

## はじめに

UbuntuのNVIDIA GPU driverを430 => 435にアップデートしたらTensorflowがGPUモードで動作しなくなりました。435環境でTensorflowとCUDAを復活させるのに結構苦労したため、Driverのアップデート手順を書いてみました。

既にNVIDA DriverやCUDA一式をインストール済みの状態から始めていますので、クリーンな状態からインストールする場合は挙動が違うかも知れません。

### 動作環境

 - Ubuntu 19.10
 - GTX 1080 Ti
 - NVIDIA driver 430  <= 今回導入
 - CUDA 10.1
 - cuDNN 7.6.5
 - Tensorflow 2.1.0
 - PyTorch 1.4.0
 
NVIDIA Driverを更新した以外は既に導入済です。TensorflowやPyTorchはCUDA 10.1を要求するため、最新のCUDA 10.2は使えません。

### NVIDA Driverの更新

先ずは、以下のコマンドで使用可能なドライバを確認します。

`ubuntu-drivers devices`

```
$ ubuntu-drivers devices
== /sys/devices/pci0000:00/0000:00:01.0/0000:01:00.0 ==
modalias : pci:v000010DEd00001B06sv00001462sd00003602bc03sc00i00
vendor   : NVIDIA Corporation
model    : GP102 [GeForce GTX 1080 Ti]
driver   : nvidia-driver-390 - distro non-free
driver   : nvidia-driver-440 - third-party free recommended
driver   : nvidia-driver-435 - distro non-free
driver   : nvidia-driver-430 - distro non-free
driver   : nvidia-driver-410 - third-party free
driver   : xserver-xorg-video-nouveau - distro free builtin
```

インストール済みのバージョンは435ですが、440が使えます。では、440をインストール。
(注): 440は"third-party"と表記されているため、Ubuntu 19.10のデフォルトリポジトリだけだと見えないと思います。なぜ440が見えるかですが、後述するCUDA再インストールのために[NVIDIAのリポジトリ](http://developer.download.nvidia.com/compute/cuda/repos/ubuntu1804/x86_64/)を追加しているためだと思われます。

`sudo apt-get install nvidia-driver-440`

再起動して、いらなくなった古いパッケージを削除。

`sudo apt autoremove`

`nvidia-smi`を実行するとdriverは440ですが、CUDAのバージョンが10.2だと言っています。435では10.1と表示されていたのですが・・

```
$ nvidia-smi
Sun Mar 29 11:33:33 2020       
+-----------------------------------------------------------------------------+
| NVIDIA-SMI 440.64.00    Driver Version: 440.64.00    CUDA Version: 10.2     |
|-------------------------------+----------------------+----------------------+
| GPU  Name        Persistence-M| Bus-Id        Disp.A | Volatile Uncorr. ECC |
| Fan  Temp  Perf  Pwr:Usage/Cap|         Memory-Usage | GPU-Util  Compute M. |
|===============================+======================+======================|
|   0  GeForce GTX 108...  On   | 00000000:01:00.0  On |                  N/A |
|  0%   34C    P8    12W / 280W |    229MiB / 11171MiB |      0%      Default |
+-------------------------------+----------------------+----------------------+
```

nvccのバージョン（= CUDAのバージョン）は10.1と表示されます。

```
~$ nvcc -V
nvcc: NVIDIA (R) Cuda compiler driver
Copyright (c) 2005-2019 NVIDIA Corporation
Built on Sun_Jul_28_19:07:16_PDT_2019
Cuda compilation tools, release 10.1, V10.1.243
```

[StackOvlowのポスト](https://stackoverflow.com/questions/53422407/different-cuda-versions-shown-by-nvcc-and-nvidia-smi)によると、nvidia-smiコマンドはNVIDIA Driverと一緒にインストールされ、CUDA driver APIのバージョンを表示するようです。一方で、`nvcc -V`はCUDA runtime APIのバージョンを表示する。つまり、driver APIとruntime APIを管理しているライブラリが異なるため、NVIDIA DriverとCUDAと別々にインストールするとこうなる模様です。

ということで、この現象に関しては放置としました。

### Tensorflowの再構築

ドライバの更新後にtensorflowを動かすと以下のエラーが出てCPUモードになってしまいます。

```
$ python
Python 3.7.7 (default, Mar 26 2020, 15:48:22) 
[GCC 7.3.0] :: Anaconda, Inc. on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import tensorflow as tf
2020-03-29 11:32:12.334692: W tensorflow/stream_executor/platform/default/dso_loader.cc:55] Could not load dynamic library 'libnvinfer.so.6'; dlerror: libnvinfer.so.6: cannot open shared object file: No such file or directory; LD_LIBRARY_PATH: :/usr/local/cuda/lib64:/usr/local/cuda/extras/CUPTI/lib64
2020-03-29 11:32:12.334773: W tensorflow/stream_executor/platform/default/dso_loader.cc:55] Could not load dynamic library 'libnvinfer_plugin.so.6'; dlerror: libnvinfer_plugin.so.6: cannot open shared object file: No such file or directory; LD_LIBRARY_PATH: :/usr/local/cuda/lib64:/usr/local/cuda/extras/CUPTI/lib64
2020-03-29 11:32:12.334784: W tensorflow/compiler/tf2tensorrt/utils/py_utils.cc:30] Cannot dlopen some TensorRT libraries. If you would like to use Nvidia GPU with TensorRT, please make sure the missing libraries mentioned above are installed properly.
>>> 
```

当初はここで、CUDA 10.1が認識されなくなったと勘違いし、CUDAの再インストールに走って状況を悪化させました。これ、実はCUDAの問題ではなく、TensorRTのライブラリが消えたのが原因でした。以下のエラーメッセージに出てくるライブラリはTensorRTのものです。

`・Could not load dynamic library 'libnvinfer.so.6'`
`・Could not load dynamic library 'libnvinfer_plugin.so.6'`

どうも、TensorFlow 2.1はGPUモードでの起動時に[TensorRTのライブラリをチェックする](https://github.com/tensorflow/tensorflow/issues/35968#issuecomment-577329399)ようです。
ということでlibnvinferを再インストールします。先ずは、公式の手順で。

```
$ sudo apt-get install --no-install-recommends libnvinfer6=6.0.1-1+cuda10.1 \
        libnvinfer-dev=6.0.1-1+cuda10.1 \
        libnvinfer-plugin6=6.0.1-1+cuda10.1

Reading package lists... Done
Building dependency tree       
Reading state information... Done
E: Unable to locate package libnvinfer6
E: Unable to locate package libnvinfer-dev
E: Unable to locate package libnvinfer-plugin6
```

登録済みのリポジトリには上記のパッケージがないようです・・
仕方がないので、以下のNVIDAのリポジトリから必要なファイルをダウンロードしてマニュアルでインストールします。

`https://developer.download.nvidia.com/compute/machine-learning/repos/ubuntu1804/x86_64/`

ダウンロードするファイルはTensorRT 6対応の以下のファイルです（TensorRT 7はCUDA 10.2が必要なため使えません）

 - libnvinfer6_6.0.1-1+cuda10.1_amd64.deb
 - libnvinfer-dev_6.0.1-1+cuda10.1_amd64.deb
 - libnvinfer-plugin6_6.0.1-1+cuda10.1_amd64.deb
 - libnvinfer-plugin-dev_6.0.1-1+cuda10.1_amd64.deb

ライブラリには依存関係があるため、適当なサブディレクトリに4つのファイルを格納して、一気にインストールします。

`sudo dpkg -i ./libnvinfer/*.deb`

これでTensorFlow（Keras）がめでたく復活しました！

### CUDA 10.1の再インストール（おまけ）

最初にNVIDIA Driver 435を入れたときは、先の「libnvinfer問題」をCUDAが認識されなくなったと勘違いして、CUDAを一旦消して再インストールに走りました。CUDAを再インストールする手順についても、いつも迷うのでメモとして書いておきます。

[NVIDAの公式手順](https://developer.nvidia.com/cuda-10.1-download-archive-update2?target_os=Linux&target_arch=x86_64&target_distro=Ubuntu&target_version=1804&target_type=debnetwork)ではCUDAのインストール方法には以下のオプションがあります。

 - runfiles(local) : CUDA一式とインストール用のシェルスクリプトをダウンロードしてローカルでインストール → シェルスクリプトがCUDA 10.1でデフォルトのDriver 418をインストールしようとするようで、既に他のドライバをインストール済みの場合は警告が出ます。無視してインストールを強行するとシステムがおかしくなりそうなので止めました。
 - deb(local or network) : aptのリポリトリを追加してインストールする

deb方式で、NVIDA公式にある`sudo apt-install cuda`を実行すると、CUDA 10.2をインストールしようとして依存性のチェックでエラーとなります。Driver 440が入っているとデフォルトでは10.2をインストールしようとするのかもしれません。そこで、
`sudo apt install cuda-10-1`
で、バージョンを指定してインストールします。このコマンドでは依存パッケージとして；

 - cuda-toolkit-10-1
 - cuda-runtime-10-1
 - cuda-demo-suite-10-1 

をインストールしようとし、

```
$ apt show cuda-10-1
Package: cuda-10-1
Version: 10.1.243-1
Priority: optional
Section: multiverse/devel
Source: cuda
Maintainer: cudatools <cudatools@nvidia.com>
Installed-Size: 25.6 kB
Depends: cuda-toolkit-10-1 (>= 10.1.243), cuda-runtime-10-1 (>= 10.1.243), cuda-demo-suite-10-1 (>= 10.1.243)
```

さらに、cuda-runtime-10-1は依存パッケージとして`cuda-drivers`を含むため、ドライバを再インストールしてしまいます。

```
$ apt show cuda-runtime-10-1
Package: cuda-runtime-10-1
Version: 10.1.243-1
Priority: optional
Section: multiverse/devel
Source: cuda
Maintainer: cudatools <cudatools@nvidia.com>
Installed-Size: 25.6 kB
Depends: cuda-drivers (>= 418.87), cuda-libraries-10-1 (>= 10.1.243), cuda-license-10-1 (>= 10.1.243)
```

ただし、apt install cuda-driversでインストールされるドライバは440なので悪さをすることはなさそうです。

```
$ apt show cuda-drivers
Package: cuda-drivers
Version: 440.64.00-1
Priority: optional
```

440より前のドライバを使っており、それを更新したくない場合は、`cuda-toolkit-10-1`のみをインストールしておく手もありそうです。

```
$ apt show cuda-toolkit-10-1
Package: cuda-toolkit-10-1
Version: 10.1.243-1
Priority: optional
Section: multiverse/devel
Source: cuda
Maintainer: cudatools <cudatools@nvidia.com>
Installed-Size: 25.6 kB
Depends: cuda-compiler-10-1 (>= 10.1.243), cuda-tools-10-1 (>= 10.1.243), cuda-samples-10-1 (>= 10.1.243), cuda-documentation-10-1 (>= 10.1.243), cuda-libraries-dev-10-1 (>= 10.1.243), cuda-nvml-dev-10-1 (>= 10.1.243), cuda-license-10-1 (>= 10.1.243)
```

cuda-runtime-10-1のインストールをスキップすると、cuda-libraries-10-1が入りませんが、Tensorflowの動作には影響はなさそう。

### おわりに

NVIDIA DriverとCUDA、さらにはTensorflowの依存関係がたまにおかしくなります（余計なアップデートをしなければよいだけの話だと思いますが・・）。そのたびに再インストールでハマるため、メモとして一連の手順をまとめてみました。

この３つは依存関係が複雑ですので、Dockerでサクッとやるのが一番手っ取り早いのかも知れません。