TensorFlow をソースからコンパイルして CUDA 10 対応にする

この記事は以下の記事の一部分です。長くなるのでここは別記事にしました。
[Ubuntu 18.04.1 LTS に Keras をインストールする](https://qiita.com/chlochan/items/1f4ae0af2091b95c600d "Ubuntu 18.04.1 LTS に Keras をインストールする")
CUDA 10.0、cuDNN のインストール、環境設定、は上記の記事を参照してください。

# 概要
CUDA 10 に対応させたいので、TensorFlow をソースからコンパイルします。本家は以下になります。
[Build from source](https://www.tensorflow.org/install/install_sources#ConfigureInstallation)
以下が参考になります。ただし情報が古くなっているところがあります。
[How to install Tensorflow GPU with CUDA 10.0 for python on Ubuntu](https://www.python36.com/how-to-install-tensorflow-gpu-with-cuda-10-0-for-python-on-ubuntu/2/)
日本語では以下が参考になります。ただし情報が古くなっているところがあります。
[TensorFlow をソースからコンパイル 20180917版](https://qiita.com/Guwashi/items/d26f06ed45f9d740ffa2)

# 事前準備
CPUコア16個ではビルドに1時間以上かかりました。24個までしかやっていないですが24個にすると1時間以下になりました。CPUコア数と同じだけ同時コンパイルする様なので、クラウドの利点を生かして、コンパイルの前に最大限にVMのCPUの数を増やしておいた方が良いです。試してないですが、例えば64個とかにすると20分ぐらいで終わるのでは、と思います。

# Bazel をインストール
以下に従って自分のホームディレクトリにインストールします。
[Installing Bazel on Ubuntu](https://docs.bazel.build/versions/master/install-ubuntu.html)
なお、本家には書いてないのですが、最新バージョンの Bazel を使うとなぜかビルドエラーになるので、旧バージョンの 0.18.1 をインストールします。上記のインストール例では自分のディレクトリにインストールしていますのでそれを踏襲します。.bashrc に path を追加しています。(12/1追記：公式にはTensorFlow 1.12.0 は Bazel 0.15.0 でビルドテストされている)

```bash
$ wget https://github.com/bazelbuild/bazel/releases/download/0.18.1/\
bazel-0.18.1-installer-linux-x86_64.sh
$ chmod +x bazel-0.18.1-installer-linux-x86_64.sh
$ ./bazel-0.18.1-installer-linux-x86_64.sh --user
$ sed -i -e '$ a export PATH="$PATH:$HOME/bin"' .bashrc
$ source .bashrc
```
# 必要なパッケージをインストール
下の３つのバージョン指定はビルドエラー回避で必要なようです。一番下は本家には書いてないのですが、必要なようです。

```bash
$ sudo pip3 install pip six numpy wheel mock
$ sudo pip3 install keras_applications==1.0.5 --no-deps
$ sudo pip3 install keras_preprocessing==1.0.3 --no-deps
$ sudo pip3 install h5py==2.8.0
```

# GitHubから最新のtensorflowを持ってくる
以下で持ってきます。

```bash
$ git clone https://github.com/tensorflow/tensorflow
$ cd tensorflow
$ git checkout r1.12
```

# ./configure
以下の中の、"Configure the build" を参考に ./configure します。
[Build from source](https://www.tensorflow.org/install/install_sources#ConfigureInstallation)

```bash
$ ./configure
```
configureの途中で Compute Capability を聞かれます。GCP で使われている Tesla GPU の Compute Capability は以下の様になっています。使用している GPU の値を入れます。使っている GPU を検出して自動的にデフォルト値が入る様なので、Enterでいけます。

|  GPU | Compute Capability |
|:----:|:------------------:|
|  T4  |         7.5        |
| V100 |         7.0        |
| P100 |         6.0        |
|  P4  |         6.1        |
|  K80 |         3.7        |

その他は以下の4箇所だけ入力して、それ以外は Enter でいけました。

```
Please specify the location of python. [Default is /usr/bin/python]: /usr/bin/python3
```
ここは、自分は python3 しか使わないので、/usr/bin/python3

```
Do you wish to build TensorFlow with CUDA support? [y/N]: y
```
ここは当然 y です。

```
Please specify the CUDA SDK version you want to use. [Leave empty to default to CUDA 9.0]: 10
```
ここは CUDA 10 をインストールしたので 10 です。

```
Please specify the NCCL version you want to use. \
If NCCL 2.2 is not installed, then you can use version 1.3 that can be fetched\
 automatically but it may have worse performance with multiple GPUs. [Default is 2.2]: 1.3
```
デフォルトは2.2との事ですが、そもそも CUDA 10 対応の 2.2 はありませんでした。最新版の 2.3 をインストールしています。試しに 2.3 といれたらエラーになりました。2.3.7と入れてもやっぱりダメでした。1.3 が使える、との事なので 1.3 と入力しました。

# pip パッケージビルド
以下の中の、"Build the pip package" を参考にビルドします。
[Build from source](https://www.tensorflow.org/install/install_sources#ConfigureInstallation)

```bash
$ time bazel build --verbose_failures --config=opt --config=cuda\
 //tensorflow/tools/pip_package:build_pip_package
```
以下の様な感じで、CPU使用率100%に張り付いた状態で、約50分かかりました。CPUコア数と同じだけ同時コンパイルする様です。クラウドの利点を生かして、コンパイルの時は最大限にCPUの数を増やした方が良さそうです。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/307576/be70c9be-adf9-c256-4dfb-2a1e9fb8aafa.png)
なお、使用したマシンタイプは以下です。
マシンタイプ : vCPU x 24、メモリ 156GB
CPU プラットフォーム : Intel Skylake

次にpipのパッケージを作ります。オプション --project_name でパッケージ名称を設定できます。
ここでは、"--project_name tensorflow_gpu_cuda_10.0" としてみました。 

```bash
$ bazel-bin/tensorflow/tools/pip_package/build_pip_package /tmp/tensorflow_pkg\
 --project_name tensorflow_gpu_cuda_10.0
```
後ろに文字が自動的に追加されて、
tensorflow_gpu_cuda_10.0-1.12.0-cp36-cp36m-linux_x86_64.whl
というファイルになりました。

# pip パッケージインストール
既にインストールしてある場合は、今インストールされているものをアンインストールしてからインストールします。

```bash
$ sudo pip3 uninstall tensorflow  # remove current version
$ sudo pip3 install /tmp/tensorflow_pkg/tensorflow_gpu_cuda_10.0-1.12.0-cp36-cp36m-linux_x86_64.whl
```
# CUDA 10.0 対応 tensorflow の動作確認
動作確認はビルドしたディレクトリから出て行う必要があります。出ないとエラーが出て実行できません。
まず、バージョンを確認してみます。 1.12.0 と表示されるはずです。

```bash
$ python3 -c "import tensorflow as tf; print(tf.__version__)"
```
接続されているGPUを確認します。
GPUの種類、メモリ量、compute capability、などが表示されます。

```bash
$ python3 -c "import tensorflow as tf; print(tf.contrib.eager.num_gpus())"
```
Hello, TensorFlow! してみます。

```python3
import tensorflow as tf
hello = tf.constant('Hello, TensorFlow!')
sess = tf.Session()
print(sess.run(hello))
```
以下にある、tensorflow.org のチュートリアルをやってみます。
https://www.tensorflow.org/tutorials/

```python3
import tensorflow as tf
mnist = tf.keras.datasets.mnist

(x_train, y_train),(x_test, y_test) = mnist.load_data()
x_train, x_test = x_train / 255.0, x_test / 255.0

model = tf.keras.models.Sequential([
  tf.keras.layers.Flatten(),
  tf.keras.layers.Dense(512, activation=tf.nn.relu),
  tf.keras.layers.Dropout(0.2),
  tf.keras.layers.Dense(10, activation=tf.nn.softmax)
])
model.compile(optimizer='adam',
              loss='sparse_categorical_crossentropy',
              metrics=['accuracy'])

model.fit(x_train, y_train, epochs=5)
model.evaluate(x_test, y_test)
```
以上です。