GCP の Deep Learning VM Image を使って Deep Learning の勉強をする

# Google Cloud Platform の Deep Learning VM Image とは？
こんにちは、chlochanです。いつの日か仕事に生かせる事を目指して、休日とかにDeep Learning の勉強をしています。教科書は、以下の２つを使っています。

[PythonとKerasによるディープラーニング](https://book.mynavi.jp/ec/products/detail/id=90124)

内容は同じで、上記のR版
[RとKerasによるディープラーニング](https://www.oreilly.co.jp/books/9784873118574/)

上記の本のサンプルコードを実行するのに、今回は Google Cloud Platform(以降GCPと表記) を使う事にします。GCPの場合、コア数制限の解除依頼(Azureの場合)とか、GPUインスタンスのデプロイ数の制限解除依頼(AWSの場合)とかめんどくさい事をする必要なく、いきなり Tesla V100 まで使用できるのが良い点かと思います。また料金はAWSよりはあきらかに安いです。いろんなサイトの情報みると、Azureとも同等か、GCPの方が安い様です。Azure の Data Science Virtual Machines と同じ様に、GCP のマーケットプレイスにも最近 Deep Learning 用のVMイメージが用意されました（ただし、まだベータバージョンです。）Youtubeでは以下で解説されています。
https://youtu.be/kyNbYCHFCSw
まだ2018年10月にリリースされたばかりという事もあるのか、あまりネットを検索しても日本語の情報がでてきません。なので、自分の備忘録をかねて、Deep Learning の勉強に必要な一通りのツールが使えるところまで記載してみます。使用するのは、これです。↓
![image.png](https://qiita-image-store.s3.amazonaws.com/0/307576/8efa49d2-bb70-fa4f-ee53-3f1876c8fc56.png)
これを使うと、GPUが使えるVMインスタンスを簡単に作成し、Deep Learningの勉強を始める事ができます。
このVMは、NVIDIA GPUライブラリ(CUDA,CuDNN,NCCL)、インテルのライブラリ(MKL-DNN/MKL)、scikit-learn、pandas、nltk など、データを扱うためのパッケージもインストールされた python も含まれています。jupyter も入っています。ただし、Rで勉強したい場合は、R や RStudio は入っていませんので、これらは自分でインストールする必要があります。


# どのGPUを選択するか？
VMデプロイ後にGPUは変更できません。なのでGPUの選択は慎重に行う必要があります。GCPで使えるGPUは以下に載っています。
https://cloud.google.com/compute/docs/gpus/
上記によるとGCPで提供しているdeep learning用途向きのGPUは現在4種類あります。まず、現時点(2018/11月)では東京リージョンはGPUはどれも使えません。Googleはネットワーク早いので東京に無くても問題ないかもしれませんが、ちょっと寂しいです。他のリージョンから選ぶ必要がありますが、P4は利用可能なゾーンが一番多いので、現時点では一番良さそうです。なおCPUコア数とCPUメモリ容量はデプロイ後でも停止中であれば変更できます。表にして比較してみましたので参考にしてください。
[GCPのGPUを比較してみた(T4が東京リージョンにくる!)](https://qiita.com/chlochan/items/ac788965d8e84c5e2898)

# Deep Learning VM をデプロイして起動する
デプロイして起動するのは、GCP のGUIでポチポチ操作していけば簡単に起動できてしまい、それほど難しくはありません。自分が気がついた注意点としては...

- 永続ディスク（Persistent Disk）はVMを起動していないときも料金が発生します。料金はロサンゼルスリージョンで標準が100GBあたり\$4/月、SSDだと100GBあたり\$17/月です。容量に比例して高くなりますので必要最小限にしたほうがよいと思います。
- しばらく使用しない時は、スナップショットを取り、VMを削除してしまうのも費用を抑える手かと思います。スナップショットにすると実際に使用している大きさになります。たとえば200GBのディスクのうち30GBしか使っていなければ30GBになります。またGBあたりの料金も永続ディスクより安いです。しかもスナップショットから直接インスタンスを作成できます。
- 外部IPアドレスは起動の度に変わります。静的外部IPアドレスを割り当てる事もできますが、VMを起動していないときも料金が発生するのでご注意ください。約$7.2/月になります。
- Tesla V100のリソースは不足気味みたいで、どこのリージョンでもなかなかデプロイできないです。またデプロイ出来ても、使おうと思った時に起動できない事がありました。(2018/11月時点)
- 今年の8月にGCPにTesla P4が導入されたばかりなのに、上記の表の通りすでに使えるゾーンが一番多くなっています。驚異的なスピード展開です。やはりファンレスなのが効いているんでしょうね。料金もCPUの数とメモリを同条件で比較するとV100の半額以下なので、コスト重視の時はこれをつかうのが良いと思います。

上記のYoutubeでの解説では元気のよいアジア系のお兄さんがgcloudコマンドをつかった方法を紹介しています。gcloudコマンドを使いこなした方が、いろいろ楽な場面がありそうです。なので、以下ではgcloudコマンド を使ってVMを作成します。

# gcloudコマンド を使って起動する
gcloudコマンドを使うには、画面右上ある以下の部分をポチッと押します。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/307576/99b02380-7141-9de6-a992-b33d5aa36388.png)
すると画面下側に gcloud shell が開きます。ちなみに gcloud shell は超小さいlinuxのVMで実現しています。なので、bashが動いているし、vim とかlinuxのコマンドは一通り使えます。ドキュメントを見て、gcloudコマンドのオプションを確認してコマンド作成します。VMの名称は、hogehogeにしています。GPUはp4にしています。ゾーンもp4が使えるゾーンにします。マシンタイプを指定しないとCPUコア数とメモリ容量が最小構成になる様です。またディスク容量も指定しないと30GBになる様です。よってマシンタイプは n1-highmem-16 にしました。またディスク容量は200GB以上にしないとI/Oのパフォーマンスが落ちる、というワーニングが出ましたので200GBにしました。なおimage-familyをtf-latest-gpuにすると自動的にCUDAが最新のバージョンの VM Image を持ってくるようですが、現時点(2018/11/5)のCUDAの最新バージョンは10です。しかし以下を見ると、最新の TensorFlow でも、対応する CUDAは、9 の様です。 
https://www.tensorflow.org/install/source#common_installation_problems
![image.png](https://qiita-image-store.s3.amazonaws.com/0/307576/31f4213e-815c-78e4-4c5c-a336488e4a90.png)
そこで、CUDA 9.0 をインストールしてある TensorFlow の Deep Learning VM Image を探して見ましたが、ないみたいです。おそらくテストしてないだけで CUDA10 でも動くので用意してないのだと思います。またネットの情報をみると、最新バージョンつかった方が学習が早いようです。なのでここは最新のCUDAのバージョンを使っている tf-latest-gpu にする事にします。（もしCUDAのバージョンが原因で動かない場合がある時は、common-cu90 という base のイメージでVM作って、自分で TensorFlow と keras をインストールする事にします。）
以上を全部合わせたgcloudコマンドは以下になります。

~~~bash
export IMAGE_FAMILY="tf-latest-gpu"
export ZONE="us-west2-c"
export INSTANCE_NAME="hogehoge"
export BOOT_DISK_SIZE="200GB"
export MACHINE_TYPE="n1-highmem-16"
gcloud compute instances create $INSTANCE_NAME \
 --zone=$ZONE \
 --image-family=$IMAGE_FAMILY \
 --image-project=deeplearning-platform-release \
 --maintenance-policy=TERMINATE \
 --accelerator='type=nvidia-tesla-p4,count=1' \
 --metadata='install-nvidia-driver=True' \
 --boot-disk-size=$BOOT_DISK_SIZE \
 --machine-type=$MACHINE_TYPE \
 --tags=http-server
~~~
VMがデプロイできたらCUDAのバーションを確認してみます。

~~~
$ nvcc -V
nvcc: NVIDIA (R) Cuda compiler driver
Copyright (c) 2005-2018 NVIDIA Corporation
Built on Sat_Aug_25_21:08:01_CDT_2018
Cuda compilation tools, release 10.0, V10.0.130
~~~
10.0になっていますね。
次に環境変数にCUDAへのパス設定がされているか確認します。以下の様にLD_LIBRARY_PATHが
/usr/local/cuda/lib64  に通っている、PATHが、/usr/local/cuda/bin  に通っている必要があります。通っていなければ、~/.bashrc か ~/.profile に追加します。

~~~bash
LD_LIBRARY_PATH=/usr/local/cuda/lib64:/usr/local/nccl2/lib:/usr/local/cuda/extras/CUPTI/lib64
PATH=/usr/local/cuda/bin:/usr/local/bin:/usr/bin:/bin:/usr/local/games:/usr/games
~~~

# 必要なライブラリをインストールする

サンプルコードを実行する時に必要なライブラリをインストールします。

~~~bash
$ sudo apt-get update
$ sudo apt-get upgrade -y
$ sudo apt-get install python3-pip python3-dev -y
$ sudo apt-get install build-essential cmake git unzip pkg-config -y
$ sudo apt-get install libopenblas-dev liblapack-dev -y
$ sudo apt-get install libhdf5-serial-dev python-h5py -y
$ sudo apt-get install python3-numpy -y
$ sudo apt-get install python3-scipy -y
$ sudo apt-get install python3-matplotlib -y
$ sudo apt-get install ipython3 -y
$ sudo apt-get install graphviz -y
$ sudo apt-get install python-opencv -y
$ sudo pip3 install opencv-python
$ sudo pip3 install pydot-ng
~~~

# baseのイメージを選んだ場合は tensorflow と keras をインストールする
tf-latest-gpuではなく、common-cu90 などのbaseのイメージを選んだ場合は tensorflow-gpu と keras がインストールされていないので、自分でインストールします。3系のみインストールする場合は以下のコマンドになります。

~~~bash
$ sudo pip3 install tensorflow-gpu
$ sudo pip3 install keras
~~~

# jupyter lab を使ってサンプルコードを実行する
次にjupyter labが使うポートをフォワーディングします。jupyter notebook / jupyter lab は、ポート番号はデフォルトで8888となっていますので、それをフォワーディングします。gcloud shell で以下を実行してVMにログインします。

~~~bash
export INSTANCE_NAME="hogehoge"
gcloud compute ssh $INSTANCE_NAME --zone us-west2-c -- -L 8888:127.0.0.1:8888
~~~
VMにログインして、jupyter lab を起動すると以下の様な表示がでます。

~~~bash
Copy/paste this URL into your browser when you connect for the first time,
to login with a token:
    http://localhost:8888/?token=...数字とアルファベットの羅列...
~~~
ローカル側では、上記の http://.... の部分をポチッとクリックして jupyter lab に接続できます。
VMを起動する度に変わってしまう外部IPアドレスを入力しなくても良いのは楽です。初回はパスワードを聞かれると思うので、上記の token= 以下の数字とアルファベットの羅列部分のみをコピペして入力します。jupyter labが動く様になったら、Kerasのバージョンを確認してみます。以下を実行します。

~~~py
import keras
keras.__version__
~~~

2018/11月時点では、以下が表示されました。

~~~py
Using TensorFlow backend.
'2.2.4'
~~~
なお、fp16で実行する場合は以下を有効にします。

~~~py
import keras.backend as K
K.set_floatx('float16')
~~~

ここまでくれば python と jupyter lab で Deep Learning の勉強を開始できる状態になっています。
R/RStudio を使わない場合の教科書は以下になります。

PythonとKerasによるディープラーニング
https://book.mynavi.jp/ec/products/detail/id=90124

GitHub から上記の教科書のサンプルプログラムをダウンロードして実行してみます。

```bash
$ git clone https://github.com/fchollet/deep-learning-with-python-notebooks.git
```
現在、サンプルコードを順に実行してみていますが、1時間ぐらい時間がかかるものもあります。以下のコマンドで GPU の稼働率を調べる事ができます。

```bash
$ nvidia-smi -l
```
以下、5章までで、これからサンプルコードを実行する方へのお役立ち情報を記載します。

- サンプルコードを実行していると、Blas GEMM launch failed とか、GPU sync failed とか出る時がありました。原因がわからないまま、とりあえず jupyter lab を再起動すると発生しなくなるので、それで対応していました。しかしネットで検索したところ、Keras のバックエンドに TensorFlow を使う場合、デフォルトでは一つのプロセスが GPU のメモリを全て使ってしまうらしい、ということがわかりました。つまりpythonのカーネルが複数動作していると上記の現象が起きる可能性がある、という事です。なので対策としては、実行するサンプルコード用以外のpythonのカーネルが動いていたらshutdownしておけばよいと思われます。これを実行する様になってからは同じ現象は発生していません。
- 3章のサンプルコードで、acc を binary_accuracy、val_acc を val_binary_accuracy に変更しないとエラーが出る箇所がありました。本のリスト3-9と3-10に相当するコードです。
- 4章のサンプルコードの以下の部分で、layer_outputが定義されていない、というエラーがでます。RStudioの方でも同じ箇所でエラーがでます。本を読むとわかるのですが、この部分は文章で概念を説明しているコードであって、実行してみるコードではありません。なので実行する必要はありません。同じ様な箇所が4箇所あります。

```python
# At training time:
layer_output *= np.randint(0, high=2, size=layer_output.shape)
# Note that we are scaling *up* rather scaling *down* in this case
layer_output /= 0.5
```
- 5章のリスト5-23、VGG16を実行したところ、P4でだいたい27分ぐらいかかりました。その際、GPUは下記のように最高86℃に上昇しました。最初は54℃だったので、フルに動作すると30℃ぐらい上昇するようです。ファンがないせいか温度上昇が結構ありますね。まあ、実体は太平洋を越えて海の向こうにあるし、温度がどうなろうとそれで問題なければ関係はないんですが...

```
+-----------------------------------------------------------------------------+
| NVIDIA-SMI 390.46                 Driver Version: 390.46                    |
|-------------------------------+----------------------+----------------------+
| GPU  Name        Persistence-M| Bus-Id        Disp.A | Volatile Uncorr. ECC |
| Fan  Temp  Perf  Pwr:Usage/Cap|         Memory-Usage | GPU-Util  Compute M. |
|===============================+======================+======================|
|   0  Tesla P4            Off  | 00000000:00:04.0 Off |                    0 |
| N/A   86C    P0    69W /  75W |   7401MiB /  7611MiB |     97%      Default |
+-------------------------------+----------------------+----------------------+
```

- P100でも実行してみました。だいたい20分ぐらいかかりました。GPUは最高でも57℃程度でした。P100はP4より40%ぐらいはやいですね。
- k80でも実行してみました。だいたい38分ぐらいかかりました。GPUは最高でも50℃程度でした。P4はk80より41%ぐらいはやいですね。
- V100でも実行してみました。だいたい22分ぐらいかかりました。GPUは最高でも48℃程度でした。P100より遅い結果となりました。GPU-Util は15%～40%程度で推移していました。P100はFP16が早いので、もしかしたらそれが効いているのかな。
- 5章のサンプルコードを実行するためには、kaggleからデータをダウンロードする必要があります。kaggleのアカウントを作って必要なデータをダウンロードし、そのデータをgoogle Cloud Storage に置いておきます。以下のコマンドでVMのカレントディレクトリにそのデータを持ってくる事ができます。以下は自分が作ったgoogle Cloud Storage の名称がファイル名がgcs-usw2-1で、ファイル名が all.zip の場合です。解凍してサンプルコードが読み出せる様に指定のディレクトリにデータを移動します。

```bash
$ gsutil cp gs://gcs-usw2-1/all.zip .
$ unzip all.zip
$ unzip train.zip
$ unzip test1.zip
$ mkdir ./Downloads
$ mkdir ./Downloads/kaggle_original_data
$ mv ./train/* ./Downloads/kaggle_original_data
```
- サンプルコード中のディレクトリは /User/fcholet/Downloads/kaggle_original_data となっていますが、これは /home/chlochan/Downloads/kaggle_original_data といった感じでコードを自分の環境のディレクトリに書き換える必要があります。
- 5.4章の象さんのjpgファイル "creative_commons_elephant.jpg" は、以下のコマンドでダウンロードできます。

```bash
$ wget https://s3.amazonaws.com/book.keras.io/img/ch5/creative_commons_elephant.jpg
```
- 6章のサンプルコードを実行するために、同じ様に3箇所からデータを持ってきて、サンプルコードから読み出せる様に置きます。最初の２つは5章のデータと同じ様にもってきます。３つ目のデータは以下のコマンドでもってこれます。

```bash
$ wget https://s3.amazonaws.com/keras-datasets/jena_climate_2009_2016.csv.zip
$ unzip jena_climate_2009_2016.csv.zip
```
- 6章のサンプルコードの以下の部分で、predsが定義されていない、というエラーがでます。本を読むとわかるのですが、この部分は文章で概念を説明しているコードであって、実行してみるコードではありません。なので実行する必要はありません。

```python
np.mean(np.abs(preds - targets))
```
- 6章のリスト6-37を実行したところ、P4でだいたい54分ぐらいかかりました。
- 6章のリスト6-40を実行したところ、P4でだいたい115分ぐらいかかりました。
- 6章のリスト6-41を実行したところ、P4でだいたい241分ぐらいかかりました。P100でも実行してみたところ、253分ぐらいかかりました。P4より遅い結果となってしまいました。理由が不明です。
- 8章のリストをすべて実行しましたが、この章のリストはあまり時間はかかりません。ただ一番興味深い結果が得られる章だと思います。

# すべてのサンプルコードを実行した感想
前提として、Deep Learning 以前に、自分はまだPythonもRを勉強しはじめたばかりです。
- この本は素晴らしいと思います。Deep Learning の基礎を、体験を通して一通り学べます。
- 7章のサンプルコードは GitHub に無いのですがなぜなんだろう。自分で入力してみろという事かな。
- α運用はしているようですが、東京リージョンにはやくT4を入れてほしいです。
- RStudioはすばらしいです。昔、自分は MATLAB 大好きでよく使ってましたが、そのときの感覚を思い出しました。無料でこの様な環境が使えるなんてよい時代です。
- Jupyter labで、RStudioの様に変数も別ウインドウで確認できるとありがたいです。
- 言語仕様はPythonもRもいまいち奇妙に感じるところがあります。
たとえばPythonには if～else～ の最後に予約語がないところ、end とかがあったほうがわかりやすいと思うんだけどな。Rはそもそも "<-" が納得できないです。素直に "=" にすればいいじゃないですか、一文字減らせるし。

# 教科書を読み終えた感想
じつはこの本で一番良いのは9章かもしれません。