Google Colaboratoryで利用していたノートブックをGoogle AI Platformで実行し、さらにコマンドラインからも実行する方法

Google Colaboratoryは、[シャットダウンまでの残時間の90分ルールと12時間ルール](https://qiita.com/tomo_makes/items/b3c60b10f7b25a0a5935#2-%E3%82%B7%E3%83%A3%E3%83%83%E3%83%88%E3%83%80%E3%82%A6%E3%83%B3%E3%81%BE%E3%81%A7%E3%81%AE%E6%AE%8B%E6%99%82%E9%96%93%E3%81%AF90%E5%88%86%E3%83%AB%E3%83%BC%E3%83%AB%E3%81%A812%E6%99%82%E9%96%93%E3%83%AB%E3%83%BC%E3%83%AB)というのがあります。

ディープラーニングのモデルのトレーニングなどは実行時間がかかるため、課金はされるけれどもGoogle AI PlatformなどのGPUのインスタンスマシンを用意して、実行を継続したくなることがあります。

また、GPUインスタンス上でノートブックを長時間実行していると、画面のセッションが切れて実行状況が画面から確認できなくなることがあるので、最後はコマンドラインで実行したくなります。

ということで、Google Colaboratoryで作ったノートブックをGoogle AI Platformで動作させ、さらにコマンドラインから実行する方法を説明します。

あわせて次のこともハマりどころなので、説明します。

・Google AI Platform上でTensorFlowを実行したがGPUが使われていない
・コマンドラインから実行するとTensorFlowが`NotFoundError:  No algorithm worked!`というエラーを出す

#### Google Colaboratory→Google AI Platform に移して実行する方法

手順を説明していきます。

**1. Google Colabからipynbファイルをダウンロードする**

Google Colab上でファイル > .ipynbをダウンロードで、ipynbファイルをダウンロードすることができます。ダウンロードしたipynbはGoogle AI PlatformのNotebook上でそのまま動作させることができます。

<img width="500px" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/116010/25bf7619-14ad-d030-5d53-00a777d0b46e.png">

**2. Google Cloud PlatformでGPUの割当を申請する**

Google Cloud PlatformでGPUを利用するには事前に申請が必要です。
こちらの記事を参考に申請しておきます。

[TensorFlowのGPU環境をGCPでさくっと作る方法](https://qiita.com/kazunori279/items/b17d16a2bfbec5e72984)

※記事ではDatalabインスタンスの作成の説明が載っていますが、それはこちらの記事にも書くので参照しなくても良いです

**3. AI Platformで新しいノートブックを作成する**

ノートブックを作成します。ノートブックを作成するとGPUのドライバやTensorFlow(GPU版）なども合わせてインストールしてくれます。また、このノートブックにSSHで入ってコマンドラインから実行することもできるので、GPUインスタンスを手っ取り早く用意することができます。

作り方は、Google Cloud Platform > AI Platform > ノートブック より「新しいインスタンス」を押して、インスタンスの種類を選択します。ここでは例としてインスタンスの種類に「TensorFlow 2.3」を選択し、GPUに「NVIDIA Tesla T4 GPU」を選択してみます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/116010/29111f01-77a8-3742-90f3-194d029cb299.png)

インスタンスの作成画面が出るので、GPUの割当を申請したリージョンを設定します。「NVIDIA GPUドライバを自動的にインストールする」を選択するとドライバもインストールしてくれます。「作成」を押すとインスタンスの作成が始まります。

<img width="300px" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/116010/a4cb67d1-0dd8-f5ad-30cb-9aba94bffcb0.png">

これでGPUインスタンスが用意されました。

**4. AI Platformのノートブックでipynbを実行する**

インスタンスが作成されて5分ぐらい待つと、「JUPYTER LABを開く」が押せるようになるので押します。

Jupyter Labが開いたら、Google Colabからダウンロードしたipynbを左ペインにDrag & Dropしてアップロードします。アップロードしたらダブルクリックすると開くことができます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/116010/153464f7-5ed4-befb-b906-59e2a3e9d2b0.png)

開いたノートブックはそのまま実行することができます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/116010/da9bce7e-bf4f-1f9d-d452-e6d96b152469.png)

**5. ノートブック上でGPUが利用できる状態にあるかを確認する(TensorFlowのみ）**

TensorFlowでGPUが利用できる状態にあるかを確認するにはノートブック上で次のコードを書きます。

```python:NoteBook
import tensorflow as tf
print("GPU Available: ", tf.config.list_physical_devices('GPU'))
  
# 出力結果
# GPU Available:  [PhysicalDevice(name='/physical_device:GPU:0', device_type='GPU')]
```

GPUの情報が表示されていれば利用できる状態にあります。

**6. ノートブック上でGPUを実際に利用しているかどうかを確認する**

GPUが利用できる状態にあっても、実際に使っていないことがあります。例えば利用しているライブラリがCPUしか使わない場合などです。

この状態を識別するために、実際の利用状況を確認します。

まず、ノートブックを実行しているインスタンスにSSHで入ります。

GCP > Compute Engine > VMインスタンス > ノートブックのインスタンス名 から「SSH」を押します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/116010/28550901-5c78-97f0-0e2a-07cc238a79d9.png)

ターミナルが起動したら次のように打ちます。

```bash:コマンドライン
$ nvidia-smi
```

すると次のようにGPUの利用状況を確認することができます。

<img width="500px" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/116010/e3a97911-bb47-ea34-1bd2-92eb0af2d97a.png">

`nvidia-smi -l 1`と打つと1秒ごとに実行してくれるので便利です。

#### Google AI Platformのノートブックをコマンドラインから実行する方法

ノートブックをコマンドラインから実行する方法を説明します。

**1. ノートブックを実行する**

ノートブックのVMインスタンスへのSSHログインは上で説明しているので、ログインした後からの説明になります。

Jupyter Labのディレクトリに移動します。

```bash:コマンドライン
$ cd /home/jupyter
```

`ls`を実行すると、ノートブック上で実行しているipynbファイルが確認できると思います。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/116010/8b2537bc-8cd7-5229-67dd-14ad255075f3.png)

ここから次のように実行すると、ipynbファイルを直接実行することができます。
実行はバックグラウンドプロセスとして動き、ターミナルをExitしたあとも動作し続けます。

```bash:コマンドライン
$ sudo -u jupyter nohup sh -c "/opt/conda/bin/jupyter nbconvert --to notebook --execute my_notebook.ipynb --output my_notebook_output.ipynb --ExecutePreprocessor.timeout=2678400 --allow-errors --debug 1> stdout.log 2> stderr.log" &
```
※「my_notebook.ipynb」の部分は実行したいipynbを指定する。
※「my_notebook_output.ipynb」の部分は出力結果を保存する場所なので、任意の名前を指定する。

解説：
`sudo -u jupyter` ・・・ jupyterユーザーで実行しています。Notebookはこのユーザーで動作しているので同じユーザーを使わないと適切にリソースにアクセスできません。
`nohup` ・・・　ターミナルを抜けても実行を続けたい場合は指定します。
`sh -c "・・・1> stdout.log 2> stderr.log"`　・・・　標準出力や標準エラー出力を、stdout.log, stderr.logにリダイレクトするために指定しています。
`/opt/conda/bin/jupyter nbconvert --to notebook --execute my_notebook.ipynb --output my_notebook_output.ipynb --ExecutePreprocessor.timeout=2678400 --allow-errors --debug`　・・・　ipynbを実行している部分です。「my_notebook.ipynb」を実行し、出力先に「my_notebook_output.ipynb」を指定しています。`--ExecutePreprocessor.timeout=2678400`はセルあたりのタイムアウト時間で、デフォルトが30秒なので、2678400秒=31日にしています。

[参考：Jupyter notebook をコマンドラインで実行する](https://qiita.com/ikeyasu/items/f6216565bcfee5af96c9)

コマンドラインで実行したら、'nvidia-smi'を実行してGPUが利用されているか確認します。ノートブック上ではGPUを利用していたのに、コマンドラインだと利用しないなんてこともあるので確認しておきましょう。

**2. ノートブックを手動で停止する**

ノートブックは実行が終われば停止しますが、処理中に手動で停止したい場合もあると思います。

このような場合に備えて、ノートブック上では途中停止されたら自動保存するようなコードを書いておくと良いでしょう。

例えば、TensorFlowのトレーニングであれば、`try`〜`except`を使って中断されたらモデルを保存するようにしておきます。

```python:ノートブック
try:
    model.fit(x_train, y_train_reshape, epochs=600, batch_size=32, validation_split=0.2, callbacks=[lr_callback, tb_cb])
except KeyboardInterrupt:
    model.save('saved_model/my_model')
    print('Output saved')
```

次に、手動で停止をする方法を説明します。

ターミナルに入ったままノートブックをバックグラウンドで実行していた場合は`fg`コマンドを打った後に`CTRL+C`で止めることができます。

ターミナルを一回抜けてしまった場合は`ps`でプロセスを確認し、`/opt/conda/bin/python -m ipykernel_launcher`にSIGINTを送ります。ブラウザ側でノートブックを実行している場合は、`ipykernel_launcher`が複数現れるので、コマンドラインから実行したほうのプロセスを選ぶ必要があります。

```bash:コマンドライン
$ ps aux | grep python
〜略〜
jupyter   4145  102  3.5 12695156 2899356 ?    Rsl  21:55   0:23 /opt/conda/bin/python -m ipykernel_launcher -f /tm
p/tmp8d9j2ld7.json --HistoryManager.hist_file=:memory:
〜略〜
$ sudo kill -SIGINT 4145
```

これでノートブックが停止しました。

#### 補足： ノートブックをコマンドラインから実行したら`NotFoundError:  No algorithm worked!`とでた場合の対処

これについて正確な理由は分かりませんが、[公式：GPUを使用する - GPUメモリの増加を制限する](https://www.tensorflow.org/guide/gpu#limiting_gpu_memory_growth)をノートブック上で指定しておくと回避することができました。

```python:ノートブック
gpus = tf.config.experimental.list_physical_devices('GPU')
if gpus:
  # Restrict TensorFlow to only use the first GPU
  try:
    tf.config.experimental.set_visible_devices(gpus[0], 'GPU')
    logical_gpus = tf.config.experimental.list_logical_devices('GPU')
    print(len(gpus), "Physical GPUs,", len(logical_gpus), "Logical GPU")
  except RuntimeError as e:
    # Visible devices must be set before GPUs have been initialized
    print(e)
```

この現象は、TensorFlowのIssueにもされています。

[参考：tensorflow/Issue/NotFoundError: No algorithm worked! when using Conv2D #43174](https://github.com/tensorflow/tensorflow/issues/43174)

ただ、ここに書かれている次のような方法は、私の環境(TensorFlow 2.3.1）の場合はうまくいきませんでした。一応、紹介しておきます。

```python:ノートブック
from tensorflow.compat.v1 import ConfigProto
from tensorflow.compat.v1 import InteractiveSession

config = ConfigProto()
config.gpu_options.allow_growth = True
session = InteractiveSession(config=config)
```

[参考：TensorflowでConv2Dを使うとNo algorithm worked!になってしまう時の対処法](https://zenn.dev/aquahika/articles/61da9970f532cd)

### 最後に

NoteではiOS開発、とくにCoreML、ARKit、Metalなどについて定期的に発信しています。
https://note.com/tokyoyoshida

Twitterでも発信しています。
https://twitter.com/jugemjugemjugem