TensorFlow を macOS 10.12 + CUDA 8.0 向けにセットアップする

概要
====

基本的な指針は[TensorflowでOSXのGPUが対応されたよ](http://qiita.com/tawago/items/15160c6aa0ebd1c61715)に従う。

2016年12月5日現在で、 Brew で CUDA を入れると、そのバージョンは 8.0 になるようだった。公式の[Download and Setup](https://www.tensorflow.org/versions/r0.12/get_started/os_setup.html)の手順ではうまくいかなかったので、[この gist のこのコメント](https://gist.github.com/Mistobaan/dd32287eeb6859c6668d#gistcomment-1935235)を参考にする。

セットアップ
====

Python 3.5 のインストール
----

Python は最近使っていなかったので、今回は 3.5 をインストールすることにする。 Mac ではデフォルトで Python 2.x が入っているので、 pyenv を使うのが良いらしい[^1]。

```bash
$ brew install pyenv
$ pyenv install 3.5.2
Downloading Python-3.5.2.tar.xz...
-> https://www.python.org/ftp/python/3.5.2/Python-3.5.2.tar.xz
Installing Python-3.5.2...

BUILD FAILED (OS X 10.12 using python-build 20160602)

Inspect or clean up the working tree at /var/folders/__/37mkgf2x4t70wt679szztc5m0000gn/T/python-build.20161205163930.24297
Results logged to /var/folders/__/37mkgf2x4t70wt679szztc5m0000gn/T/python-build.20161205163930.24297.log

Last 10 log lines:
  File "/private/var/folders/__/37mkgf2x4t70wt679szztc5m0000gn/T/python-build.20161205163930.24297/Python-3.5.2/Lib/ensurepip/__main__.py", line 4, in <module>
    ensurepip._main()
  File "/private/var/folders/__/37mkgf2x4t70wt679szztc5m0000gn/T/python-build.20161205163930.24297/Python-3.5.2/Lib/ensurepip/__init__.py", line 209, in _main
    default_pip=args.default_pip,
  File "/private/var/folders/__/37mkgf2x4t70wt679szztc5m0000gn/T/python-build.20161205163930.24297/Python-3.5.2/Lib/ensurepip/__init__.py", line 116, in bootstrap
    _run_pip(args + [p[0] for p in _PROJECTS], additional_paths)
  File "/private/var/folders/__/37mkgf2x4t70wt679szztc5m0000gn/T/python-build.20161205163930.24297/Python-3.5.2/Lib/ensurepip/__init__.py", line 40, in _run_pip
    import pip
zipimport.ZipImportError: can't decompress data; zlib not available
make: *** [install] Error 1
```

zlib がないと言われたので、 `xcode-select` する[^2]。

```bash
$ xcode-select --install
$ pyenv install 3.5.2
$ pyenv global 3.5.2
$ python --version
Python 3.5.2
```

Python 3 は昔、 Pytho 2.x 向けに書かれたコードと互換性がないことを理由に触らないでいて、そのまま触れる機会がないままでいたけど、改めて調べてみたらそれほど大きな変更はなさそうだ[^3]。

CUDA, Bazel, cuDNN のインストール
----

CUDA と cuDNN は TensorFlow が直接必要としている。 Bazel は TensorFlow のビルドに必要になる。

```bash
$ brew cask install cuda
$ brew install bazel
```

cuDNN は NVIDIA Developer に登録しないとダウンロードできない。

[NVIDIA cuDNN](https://developer.nvidia.com/cudnn) から "Download" をクリックしてアニメでも見ながら頭を使わずに NVIDIA の指示に従っていれば、気づいたときには利用規約に同意した上で `cudnn-8.0-osx-x64-v5.1.tgz` がダウンロードされているはずだ。

macOS 向けかつCUDA 8.0 向けのものをダウンロードすることに注意。困ったら、詳しい方法は[TensorflowでOSXのGPUが対応されたよ](http://qiita.com/tawago/items/15160c6aa0ebd1c61715)で紹介されている。

このアーカイブにはバイナリが入っているので、適切なディレクトリに移動しておく。

```bash
$ tar xvf cudnn-8.0-osx-x64-v5.1.tgz
$ sudo mv -v cuda/lib/libcudnn* /usr/local/cuda/lib
$ sudo mv -v cuda/include/cudnn.h /usr/local/cuda/include
$ sudo ln -s /usr/local/cuda/lib/libcudnn.5.dylib /usr/local/cuda/lib/libcudnn.5
$ cd /usr/local/lib
$ sudo ln -s libcuda.dylib libcuda.1.dylib
$ sudo ln -s ../cuda/lib/libcudart.8.0.dylib .
$ sudo ln -s /usr/local/cuda/lib/* /usr/local/lib
```

最後の3つの symlink は、もしかしたら今の TensorFlow のビルドシステムに問題があって必要なだけかもしれない。

1つ目(`libcudnn.5`)は、張らないと `configure` できない。

2つ目(`libcuda.1.dylib`)は、 Bazel が長時間いい感じに進んだあとで、最後にビルドエラーになる。

3つ目の symlink は張らなくてもビルドはできるが、 TensorFlow をインポートしたときに segmentation fault を得ることになる。そのときに張っても良い。

4つ目の symlink も張らなくてもビルドはできる。 [El Capitan 以降、 `DYLD_LIBRARY_PATH` は System Itegrity Protection の加護により使えなくなった](https://developer.apple.com/library/content/documentation/Security/Conceptual/System_Integrity_Protection_Guide/RuntimeProtections/RuntimeProtections.html)ので、 CUDA 関係のライブラリはこの環境変数を使う代わりに、ロードパスにある場所から symlink しておく。

TensorFlow のインストール
----

ビルド対象のコードを得る。

```bash
$ git clone --recurse-submodules https://github.com/tensorflow/tensorflow
$ git pull --tags
$ git checkout 0.12.0-rc0
```

configure する。いくつか入力を求められるが、基本的にはデフォルトのままで良い。「GPU support を有効化するか？」という確認もあったはずだけど、この環境変数を渡した場合は、そこは自明だということなのか、入力は求められなかった。

自分の場合は「Python library path が複数見つかったがどれを使う？」という確認があったので、 pyenv を使って入れた 3.5 のパスを指定した。それ以外はデフォルトにした。

```bash
$ CUDA_TOOLKIT_PATH="/usr/local/cuda" CUDNN_INSTALL_PATH="/usr/local/cuda" TF_UNOFFICIAL_SETTING=1 TF_NEED_CUDA=1 TF_CUDA_COMPUTE_CAPABILITIES="3.0" TF_CUDNN_VERSION="5" TF_CUDA_VERSION="8.0" TF_CUDA_VERSION_TOOLKIT=8.0 ./configure
```

configure できたら、 bazel で pip パッケージをビルドして、 pip でインストールする。pyenv で入れても pip は sudo じゃないと permission error が出た。ここは rbenv と思想の違うところなのだろうか。

```bash
$ bazel build -c opt --config=cuda //tensorflow/tools/pip_package:build_pip_package
$ bazel-bin/tensorflow/tools/pip_package/build_pip_package /tmp/tensorflow_pkg
$ pip3 install --upgrade /tmp/tensorflow_pkg/tensorflow-0.12.0rc0-cp35-cp35m-macosx_10_6_x86_64.whl
```
直接指定しても良いと思う。

いくつか実行時に必要なパスがあるので、 `.bash_profile` に書いておく。

```bash
export LD_LIBRARY_PATH="$LD_LIBRARY_PATH:/usr/local/cuda/lib64:/usr/local/cuda/extras/CUPTI/lib64"
export CUDA_HOME=/usr/local/cuda
```

これで準備が整った。動作確認する。

```python
$ python
Python 3.5.2 (default, Dec  5 2016, 16:46:59)
[GCC 4.2.1 Compatible Apple LLVM 8.0.0 (clang-800.0.42.1)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import tensorflow
I tensorflow/stream_executor/dso_loader.cc:128] successfully opened CUDA library libcublas.8.0.dylib locally
I tensorflow/stream_executor/dso_loader.cc:128] successfully opened CUDA library libcudnn.5.dylib locally
I tensorflow/stream_executor/dso_loader.cc:128] successfully opened CUDA library libcufft.8.0.dylib locally
I tensorflow/stream_executor/dso_loader.cc:128] successfully opened CUDA library libcuda.1.dylib locally
I tensorflow/stream_executor/dso_loader.cc:128] successfully opened CUDA library libcurand.8.0.dylib locally
```

CUDA 関係のライブラリがロードされている。とりあえずセットアップは完了したみたいだ。

動作確認
====

Hello World
----

[公式の手順](https://www.tensorflow.org/versions/r0.12/get_started/os_setup.html#run-tensorflow-from-the-command-line)に従って動作確認する。

```python
$ python
Python 3.5.2 (default, Dec  5 2016, 16:46:59)
[GCC 4.2.1 Compatible Apple LLVM 8.0.0 (clang-800.0.42.1)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import tensorflow as tf
I tensorflow/stream_executor/dso_loader.cc:128] successfully opened CUDA library libcublas.8.0.dylib locally
I tensorflow/stream_executor/dso_loader.cc:128] successfully opened CUDA library libcudnn.5.dylib locally
I tensorflow/stream_executor/dso_loader.cc:128] successfully opened CUDA library libcufft.8.0.dylib locally
I tensorflow/stream_executor/dso_loader.cc:128] successfully opened CUDA library libcuda.1.dylib locally
I tensorflow/stream_executor/dso_loader.cc:128] successfully opened CUDA library libcurand.8.0.dylib locally
>>> hello = tf.constant('Hello, TensorFlow!')
>>> sess = tf.Session()
E tensorflow/stream_executor/cuda/cuda_driver.cc:509] failed call to cuInit: CUDA_ERROR_NOT_INITIALIZED
I tensorflow/stream_executor/cuda/cuda_diagnostics.cc:158] retrieving CUDA diagnostic information for host: ingrid.local
I tensorflow/stream_executor/cuda/cuda_diagnostics.cc:165] hostname: ingrid.local
I tensorflow/stream_executor/cuda/cuda_diagnostics.cc:189] libcuda reported version is: Not found: was unable to find libcuda.so DSO loaded into this program
I tensorflow/stream_executor/cuda/cuda_diagnostics.cc:193] kernel reported version is: Invalid argument: expected %d.%d or %d.%d.%d form for driver version; got ""
>>> print(sess.run(hello))
b'Hello, TensorFlow!'
>>> a = tf.constant(10)
>>> b = tf.constant(32)
>>> print(sess.run(a + b))
42
>>>
```

GPU を活用して[生命、宇宙、そして万物についての究極の疑問の答え](https://ja.wikipedia.org/wiki/生命、宇宙、そして万物についての究極の疑問の答え)を計算することができた。

MNIST
----

`tensorflow.models.image.mnist.convolutional` を使って動作検証する。

```bash
$ python -m tensorflow.models.image.mnist.convolutional
(snip)
Minibatch loss: 1.598, learning rate: 0.006302
Minibatch error: 0.0%
Validation error: 0.9%
Test error: 0.8%
```

以前 CPU-only で試したときより早い…？

おまけ
====

`import tensorflow` すると、 `ImportError: cannot import name 'pywrap_tensorflow'` と言われる
----

TensorFlow のリポジトリがあるディレクトリで `tensorflow` をロードするとこういうエラーが出る。別のディレクトリに言って試せば良い[^4]

`import tensorflow` すると、 `segmentation fault 11` が起きる
----

`libcuda.1.dylib` という名前でロードしようとするので、そのための symlink を作っておく[^5]。

CPU-only TensorFlow のインストール
----

こっちはとても簡単。

```bash
$ pip3 install --upgrade https://storage.googleapis.com/tensorflow/mac/cpu/tensorflow-0.12.0rc0-py3-none-any.whl
```



[^1]: [Mac環境へのPython3系インストール](http://qiita.com/spyc/items/73d1295f8b3dde3b49ca)
[^2]: [MacでPython 3.5.0インストールに失敗したら](http://qiita.com/maosanhioro/items/bf93540515d4ea75b222)
[^3]: [Python 2.7.x と 3.x の決定的な違いを例とともに](http://postd.cc/the-key-differences-between-python-2-7-x-and-python-3-x-with-examples/)
[^4]: [ImportError: cannot import name pywrap_tensorflow](https://github.com/tensorflow/tensorflow/issues/3217)
      > You're running the script in the same directory as tensorflow. Move it out and you should be fine.
[^5]: [Segmentation fault, import tensorflow, tensorflow 0.9, mac osx](https://github.com/tensorflow/tensorflow/issues/3263)
      > Having the same problem, It appears that it fails when loading libcuda.dylib. Unfortunately, since #2878 it's now looking for libcuda.1.dylib instead.
      > 
      > Creating a symlink or changing the name of libcuda.dylib to libcuda.1.dylib in /usr/local/cuda/lib seems to do the job.