NVidia Optimus＋Ubuntu14.04のノートPC環境でTensorflow 0.7.1を導入してみた

お待たせしました。

前回の記事ではNVidia Optimus＋Ubuntu14.04のノートPC環境、CUDA 7.0まで導入しましたが、Tensorflow 0.7.1まで無事導入できたので、その導入手順を紹介したいとおもいます！

以前の記事での環境前提で進めていきますので、（この手順通り一から進めたい場合は）前回やってきたつぎの記事を参照してから進めてください。

[NVidia Optimusを搭載したノートPCでUbuntu14.04＋CUDA7.0を動かしてみた](http://qiita.com/FaithnhMaster/items/b129268ef4718e61dd9a)

# 若干のお詫び

やり方を紹介する前にお詫びをしなければいけないことがありました。

以前の記事ではCUDA 7.0が必要という話をしましたが、tensorflow 0.7.1からCUDA 7.0以降がサポートされました。なので、CUDA 7.5でも動くようになります。申し訳ありません。

ですが、CUDA7.5を入れなおすのは、ドライバ関係もあり正直大変ですので、今回はCUDA 7.0環境のままで導入手順を進めていきたいと思います。

しかし、導入までの流れはほとんど一緒なので、CUDA7.5でもこの手順でなんとかなるとは思います。

# 導入手順

## CuDDN v2を導入

CuDDNはディープラーニングに特化したCUDAライブラリですが、tensorflowでは必要なので、インストールしておきます。

次のサイトでCuDDN v2をダウンロードしていきます（ちなみに、CUDA 7.5の人は記述時点で最新であるCuDDN v4を落とすといいと思いますが、今回はv2を入れています）

なお、CuDDNはNVIDIAの会員登録（無料）が必要ですので、登録していない方はここで登録しておきましょう！

https://developer.nvidia.com/rdp/cudnn-download

ダウンロードできたら、CUDAに組み込んでいきます。

```bash
# CUDAのインストール場所が /usr/local/cuda という前提で進めています
cd ${CuDDNをダウンロードした場所}

tar -zxf cudnn-6.5-linux-x64-v2.tgz 
cd cudnn-6.5-linux-x64-v2/
sudo cp lib* /usr/local/cuda/lib64/
sudo cp cudnn.h /usr/local/cuda/include/
```

## Tensorflowの導入について

ここでおまちかねのTensorflowを導入していきます。Tensorflowは、次の方法で導入できます。

* Dockerでの導入
* pipモジュールでの導入
* ソースからビルド

上の２つは簡単に導入できますが、今回は一番厄介なソースからビルドを選択します。これに選択した理由は以下のとおりです。

* （特にDockerでの導入は）bumblebeeで適宜nvidiaデバイスを動かしているため、これに関する工夫が必要なため
* （これがソースからのビルドに踏み込んだ決定的な理由ですが）ソースからのビルド以外は、CUDA・CuDDNが最新版（執筆時点ではCUDA 7.5, CuDDN v4）とGPUのCompute Capabilityが3.5以上が前提になってしまうため

Compute Capabilityとは、余り理解はしてませんがGPUのアーキテクチャのバージョンだそうです。

ちなみに、Compute Capability 3.5以上とはこのページによると、デスクトップなら GeForce GTX 780 以上、ノートPCなら GeForce 910M だそうです。

https://developer.nvidia.com/cuda-gpus

というわけで、結構新しいGPUが要求されます。若干古いマシンだと上記の２つの方法では動かせません。

今回使用するノートPC（Geforce 610M）は Compute Capability 2.1 です。このままだとアウトですが、ソースからのビルドであれば、かろうじて動かす方法は見つかりました。

というわけで、ソースのビルドを選択しています。

### virtualenvを導入

[tensorflowの公式インストールマニュアル](https://www.tensorflow.org/versions/r0.7/get_started/os_setup.html#installing-from-sources)では、システムpythonに入れていますが、ここはあえて virtualenv を使って入れたいと思います。

というのも tensorflow は枯れていない技術なので、バージョンアップが頻繁に行われることが想定されます。 virtualenv で導入しておけば、pythonの環境を分離できますので、バージョンアップ時に元のバージョンが動かないといったリスクも避けられます。

まずは virtualenv および必要なライブラリもインストールしていきます。

```bash
sudo apt-get install python-pip python-dev python-virtualenv
```

つぎに tensorflow の環境を作ります。(あとの手順でtensorflowと被りそうだったので、ここではあえて tensorflow_moduleとしていますが、名前はなんでもいいです。）

```bash
virtualenv --system-site-packages ~/tensorflow_module
```

最後に virtualenv の環境を適用します。実行後はターミナルを再起動しないかぎりはこの環境でpipをインストールできます。

ただし、ターミナル再起動した場合は、下記コマンドを再実行しないとシステムpythonのほうを使ってしまうので、注意しましょう！

~/.bashrcに入れればこの問題は回避できますが、今やると後で問題が出るので、後の手順で入れていきます。

```bash
source ~/tensorflow_module/bin/activate
```

### tensorflowビルドに必要なpipモジュール・ライブラリをインストール

つぎはtensorflowビルドに必要なpipモジュールを入れます。(公式ではapt-getでインストールしていますが、環境分離したいのでpipでインストールしてます。）

```bash
pip install numpy
```

そして、ライブラリswigも入れます。

```bash
sudo apt-get install swig
```

### Bazelを導入

tensorflowのソースビルドはmakeではなく、Bazelというので行います。BazelとはGoogle製のビルドツールです。（今後Androidのビルドはこれに置き換わるとは言われているようです）

BazelはJDK 7以上が必要なので、まず入れていきます。

```bash
sudo add-apt-repository ppa:webupd8team/java
sudo apt-get update
sudo apt-get install oracle-java7-installer
```

その後、bazelをダウンロードし、インストールしていきます。

```bash
wget https://github.com/bazelbuild/bazel/releases/download/0.2.1/bazel-0.2.1-jdk7-installer-linux-x86_64.sh
chmod +x bazel-0.2.1-jdk7-installer-linux-x86_64.sh 
./bazel-0.2.1-jdk7-installer-linux-x86_64.sh --user
```

インストール後、こんな感じに指示が出ます。

```
## Build informations
   - [Build log](http://ci.bazel.io/job/Bazel/JAVA_VERSION=1.7,PLATFORM_NAME=linux-x86_64/428/)
   - [Commit](https://github.com/bazelbuild/bazel/commit/447f7f3)
Uncompressing......Extracting Bazel installation...
.

Bazel is now installed!

Make sure you have "/home/faithnh/bin" in your path. You can also activate bash
completion by adding the following line to your ~/.bashrc:
  source /home/faithnh/.bazel/bin/bazel-complete.bash
```

ここで~/.bashrcに反映しろということなので、反映していきましょう。

以前入れたCUDAやvirtualenv環境の適用を含めてこんな感じに変更します。

```bash:~/.bashrc
# activate
source $HOME/tensorflow_module/bin/activate

# for CUDA
export PATH=$PATH:/usr/local/cuda-7.0/bin
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/cuda-7.0/lib64
export CUDA_HOME=/usr/local/cuda-7.0

# for Bazel
source $HOME/.bazel/bin/bazel-complete.bash
export PATH=$PATH:$HOME/bin
```

virtualenvの適用をCUDAやBazelの設定より前に持ってきているのがミソです。

というのも、これを逆にするとvirtualenvのactivate実行時、CUDAやBazelのPATHが書き換わって消えてしまうからです（ここでBazelが動かず、散々ハマりました）。

virtualenvをこのタイミングで定義したのも、こういったミスを防ぐためにしただけです（逆に入れ方さえ気をつければ大丈夫です）。

最後に、~/.bashrcを適用しましょう


```bash
source ~/.bashrc
```

### Tensorflowのダウンロード・ビルド設定

Tensorflowのプロジェクトをgitでクローンしていきます。

```bash
cd ~
git clone --recurse-submodules https://github.com/tensorflow/tensorflow
```

ビルド設定を行っていきましょう！

```bash
cd tensorflow
TF_UNOFFICIAL_SETTING=1 ./configure
```

ここでTF_UNOFFICIAL_SETTINGを有効にしているのは、Tensorflowで動かすGPUのCompute Compability 2.1に対応するよう設定するためにやっています

つまり、ここの設定でGeforce 610M環境でも動かすようにします（非公式と書いてあることから、いつこの設定が消えるのかがわからないですが。。。執筆時点のバージョンでは使えます）

このあと幾つか尋ねられるので、それにしたがって設定していきます。今回はこのような設定をしています。

Compute Capabilityは搭載しているGPUによってバージョンが異なるので、つぎのサイトで自分のバージョンが何か必ず確認してから設定しましょう！！

https://developer.nvidia.com/cuda-gpus

```bash
# virtualenvの場所になっているか確認します。なっていればそのままENTER
Please specify the location of python. [Default is /home/faithnh/tensorflow_module/bin/python]:

# CUDAを使いたいのでy
Do you wish to build TensorFlow with GPU support? [y/N] y
GPU support will be enabled for TensorFlow

# GCCのパスを確認している。変更する必要がないのでそのままENTER
Please specify which gcc nvcc should use as the host compiler. [Default is /usr/bin/gcc]:

# CUDAのバージョンを聞かれている、未入力の場合システムデフォルト（つまり前回いれた7.0)が指定されるのでそのままENTER
Please specify the Cuda SDK version you want to use, e.g. 7.0. [Leave empty to use system default]: 

# CUDAの場所を聞かれているが、デフォルトに問題なければそのままENTER 
Please specify the location where CUDA  toolkit is installed. Refer to README.md for more details. [Default is /usr/local/cuda]: 

# cuDNNのバージョンを聞かれているが、前回v2しか入れていないのでそのままENTER
Please specify the Cudnn version you want to use. [Leave empty to use system default]: 

# CuDNNの場所を聞かれているが、デフォルトに入れたのでそのままENTER（ここでCuDDNの入れる場所がエラーになるので注意！） 
Please specify the location where cuDNN  library is installed. Refer to README.md for more details. [Default is /usr/local/cuda]: 

# (TF_UNOFFICIAL_SETTINGを有効にしている場合）Compute Capabilityのバージョン指定ができる。今回は2.1以降対応したいので、"2.1,3.0,3.5,5.2"にしている。3.5以降のマシンならこの設定はしなくてもいい。
Please specify a list of comma-separated Cuda compute capabilities you want to build with.
You can find the compute capability of your device at: https://developer.nvidia.com/cuda-gpus.
Please note that each additional compute capability significantly increases your build time and binary size.
[Default is: "3.5,5.2"]: 2.1,3.0,3.5,5.2

# ここで問題がなかったらビルド設定完了！
Setting up Cuda include
Setting up Cuda lib64
Setting up Cuda bin
Setting up Cuda nvvm
Configuration finished
```

### Bazelを使ってTensorflowのサンプルアプリの動作確認

ここでbazelを使ってtensorflowのサンプルアプリをビルドし、ビルド設定が有効か確認していきます。

ちなみに、公式ではこうなっていましたが、

```bash
# ~/tensorflow 内で実行
bazel build -c opt --config=cuda //tensorflow/cc:tutorials_example_trainer
```

上記のコマンドをそのまま使用すると、ビルドにメモリを大量に消費してしまいます。ちなみに私の環境（CPUコア4 メモリ4GB）では、ビルド途中でメモリ不足に陥り、そのままスワップが発生したため、PCが固まってしまいました。

ここで--local_resourcesオプションを設定して使用することで、ビルド時の使用するリソースには制限をかけておきましょう！

```bash
# ~/tensorflow 内で実行(--local_resourcesオプションの設定は必ず環境に合わせて設定してください！！！）
bazel build -c opt --config=cuda //tensorflow/cc:tutorials_example_trainer --local_resources=1000,4.0,1.0
```

--local_resourcesオプションは[このページ](http://bazel.io/docs/bazel-user-manual.html)を参照すると、右から順にavailableRAM,availableCPU,availableIOと数値を指定できます。

availableRAMは使用するメモリ容量(MB)が入ります。今回はメモリ全体の25%でとどめておきたいので、1000にしています。

availableCPUは使用するCPUコア数が入ります。1.0の場合はCPUコア１個分です。今回使用しているPCは4コアのCPUであり、CPUはフルに使いたいので 4.0を指定しています（ここの数値が低いほどビルドが遅くなるので要注意）。

availableIOはよくわかりません。。。が、1.0が標準っぽいので、1.0にしています。

あとは非常に時間がかかりますがビルドが終わるまで待ちます（私の環境では１時間は軽くかかりました）。

あと、zlibが必要なので、もし怒られた場合はここでインストールしましょう！

```bash
sudo apt-get install zlib1g-dev
```

ビルドに成功したら、動かしてみましょう！

前回の記事で入れたBumblebeeは、optirunコマンドを実行するタイミングでGPUを動かしています。

そこで、（無理やりですが）別窓で次のコマンドを叩いてGPUを動かせる状態にします。

```bash
sudo optirun top
```

上記コマンドが別窓で動かしている間に、サンプルを動かしてみます。

```bash
# ~/tensorflowで実行
bin/x86_64/linux/release/mergeSort
```

ここでエラーがでなければ、無事tensorflowのビルドに成功したことが確認できます。

### Bazelを使ってTensorflowのpipモジュールを作成

上記の手順でtensorflowのビルドに成功したら、pythonにtensorflowモジュールを入れていきます！

まずはBazelでpipモジュールを作っていきます。

```bash
bazel build -c opt --config=cuda //tensorflow/tools/pip_package:build_pip_package --local_resources=1000,4.0,1.0
bazel-bin/tensorflow/tools/pip_package/build_pip_package ~/.
```

### Tensorflowのpipをインストール

作成したpipモジュールをインストールしていきます（ビルドしたtensorflowのバージョンで名前が変わるため注意）

```bash
pip install ~/tensorflow-0.7.1-cp27-none-linux_x86_64.whl
```

エラーなくインストールできれば、tensorflowが無事入りました。

## 追記(2016-04-24)

実はこの手順ではGPUが動いてませんでしたので、追記します。

tensorflowのインポートには成功するものの、実行時はこういったエラーが出てきます。

```
failed call to cuInit: CUDA_ERROR_UNKNOWN
```

このエラーが出た場合はCPUモードで動いてしまうようです。これは完全に見落としてました。。。

このエラーの回避方法を探したところ、[githubのissue](https://github.com/tensorflow/tensorflow/issues/394)より、nvidia-modprobeを入れれば治せると書いてあったので入れてみました。

```shell
sudo apt-get install nvidia-modprobe
```

これでうまく認識したと思ったら、今度はこのようなエラーが出ました。


```
E tensorflow/stream_executor/cuda/cuda_dnn.cc:266] could not create cudnn handle: CUDNN_STATUS_ARCH_MISMATCH
```

調べたところ、CUDNNはCuda Compute Capability 3.0以上じゃないことが原因だったようです。今回使用したGPUは2.1だったので、CUDNNは使用できません。これも完全に見落としです。すいません。

しかし、CUDNNを使わずtensorflowを動かすという回避策は見つけました。

tensorflowのコードを調べていったところ、下記のソースからTF_USE_CUDNNという環境変数が設定できることがわかり、どうやらこの値を0にするとCUDNNなしでtensorflowを動かせる模様です。

https://github.com/tensorflow/tensorflow/blob/master/tensorflow/core/util/use_cudnn.cc

つまり、実行する際は

```shell
TF_USE_CUDNN=0 python ...
```

と書けばいいです。しかし、CUDNNを無効にした場合、何に影響を受けるのかについてはまだ調査はしていません

さらに、このオプションの説明は公式には書かれていません（執筆時点ではGoogle検索にも引っかかなかったので）。。。ので非公式オプションかもしれません。が、これでGPUありで動いたので、追記しておきます。


## Tensorflowのチュートリアルを動かす

実際に動かしてみます。公式マニュアルで紹介されたコマンドを動かしてみます。

例のごとく、別窓で次のコマンドを叩いてGPUを有効にします

```bash
sudo optirun top
```

そして、pythonを起動し、

```bash
# CUDA Compute Capability 3.0未満はTF_USE_CUDNN=0を入れる(2016-04-24追記)
TF_USE_CUDNN=0 python
```

公式マニュアルで紹介されたコマンドを叩いていきます。

```python
import tensorflow as tf
hello = tf.constant('Hello, TensorFlow!')
sess = tf.Session()
print(sess.run(hello))
a = tf.constant(10)
b = tf.constant(32)
print(sess.run(a + b))
```

エラーなく無事に動けばOKです。

これでtensorflowが無事に動く環境ができました。

お疲れ様でした。

# おわりに
ここまでたどるのに先週の土日をフルに使いましたｗ

かなり大変でしたが、勉強にはなりました！

今後はこの環境使って、tensorflowの理解を深めていきたいです！