【2022年最新】ABCIでJupyter notebookを利用し，tensorflowでGPUを使う方法

# はじめに
Google colaboratoryからABCLに環境を変えたが，ABCLの環境構築とJupyter notebook上でGPUを使う方法が分かりにくかったのでここにメモしておく

こんな人に見てほしい :
ABCLを使う際に
・ABCLの開始方法がわからない/環境構築の方法がわからない

Jupyter notebookでTensorFlowを使う際に， 
・GPUを使って学習をしたい
・GPUが認識されない
・GPUを使ってるはずなのに遅い
・GPUを使った際に毎回学習結果が異なる

# 目次
1. ABCLの利用方法
2. ABCIでJupyter Notebookを使用する
3. GPUの再現性を確保する
4. GPUを複数使って並列処理をする


# 1. ABCLの利用方法
ABCI利⽤ポータルへの初回ログインとパスワード登録, 公開鍵の登録は[ABCIのマニュアル】(https://abci.ai/ja/how_to_use/data/ABCI_handson_2020.pdf)を見ればできるので飛ばす.

利用グループ:gccXXX
ABCIのアカウント:aXXX
ABCIのパスワード:pass
とする．

#### ①インタラクティブノード(es)へのshhログイン
```
$ ssh -L 10022:es:22 -l aXXX as.abci.ai
```
10022がlocalhostのポート番号で， 22がremotehostのポート番号．
入力したらセッションを切らずに放置しておく．

##### <初回時>
"Are you sure you want to continue connecting (yes/no)?"と聞かれるので， yesと入力．
"Enter passphrase for key '/home/username/.ssh/id_rsa':"でパスワードを入力．
##### <2回目以降>
"Enter passphrase for key '/home/username/.ssh/id_rsa':"で初回時に入力したパスワードを入力

#### ②別ターミナルを開き， インタラクティブノード(es)にポートフォーワーディング
```
$ ssh -p 10022 -l aXXX localhost
```

#### ③インタラクティブノードから計算ノードへログイン
```
[aXXX@es1 ~]$ qrsh -g gccXXX -l rt_F=1  h_rt=1:00:00
```

$[実行サービス] -g gccXXX -l [計算ノードのタイプ] [ジョブの実行時間]

qrshでインタラクティブジョブの実行サービスを利用し，rt_Fでノード占有.h_rtは指定しなくてもデフォルトで1:00:00になる．詳しくは[こちら](https://docs.abci.ai/ja/job-execution/)．

# 2. ABCIでJupyter Notebookを使用する
#### ①Pythonモジュールのロード
##### モジュールをロードする前に(初回時or定期的に確認)
使用できるモジュールのバージョンを確認し，　使用するtensorflow(③でインストールする)に合わせて，cudaとcudnnのバージョンを合わせる必要がある. (しっかりバージョンがあってないとGPUが使われない)

tensorflowとcuda, cudnnのバージョンの統合性の確認は[こちら](https://www.tensorflow.org/install/source_windows#configure_the_build)を参照. 2022年最新のTensorFlow 2.9.1 は[こちら](https://www.kkaneko.jp/tools/win/tensorflow2.html)に載ってる. とりあえず, 最新のcudaとcudnnを使用すればGPUの処理は速くなる．

モジュールのバージョン確認は，下記のコマンドでできるため，初回時に確認しておく．
```
[aXXX@gXXX ~]$ module avail gcc
[aXXX@gXXX ~]$ module avail python
[aXXX@gXXX ~]$ module avail cuda
[aXXX@gXXX ~]$ module avail cudnn
```

##### モジュールのロードは以下のコマンド
```
[aXXX@gXXX ~]$ module load gcc/9.3.0 python/3.8/3.8.13 cuda/11.7/11.7.0 cudnn/8.4/8.4.1
```

#### ②Python仮想環境を作成 / 仮想環境に入る
##### 初回時

(1) 仮想環境を作成し，仮想環境に入る．
```
[aXXX@gXXX ~]$ python3 -m venv ~/jupyter_env
[aXXX@gXXX ~]$ source ~/jupyter_env/bin/activate
```
※ちなみに出る場合は， deactivateと入力

(2)ライブラリのインストール
```
[aXXX@gXXX ~]$ pip3 install --upgrade pip setuptools
[aXXX@gXXX ~]$ pip3 install jupyter numpy tensorflow==2.9.1
[aXXX@gXXX ~]$ pip3 install tensorflow-gpu tensorflow-determinism #GPU使う人用
[aXXX@gXXX ~]$ pip3 install ipython-sql #sqlを使う人用
[aXXX@gXXX ~]$ pip3 install jupyter-resource-usage #メモリの使用量を確認したい人用
```
pip3 listでインストールしたライブラリを確認できる.

##### ２回目以降
```
[aXXX@gXXX ~]$ source ~/jupyter_env/bin/activate
```
のみでOK

#### ③ジュピターノートブックをport8888で起動して，トークンを確認
```
[aXXX@gXXX ~]$ jupyter notebook --ip=`hostname`  --port=8888 --no-browser
```

#### ④別ターミナル(3つ目のターミナル)で， ローカルPCの8888番ポートを計算ノードの8888番ポートに転送するSSHトンネルを作成
※１つ目と２つ目のターミナルは開いたままにしておく
8888:gXXX:8888のXXXは,２つ目のターミナルの[aXXX@gXXX ~]$の部分を確認.

```
$ ssh -L 8888:gXXX:8888 -l aXXX -p 10022 localhost
```

#### ④Jupyter Notebookへの接続
ブラウザで
http://127.0.0.1:8888/
を開く．開いたらpasswordかtokenを求められるので，２つ目のターミナルに表示された'http\://localhost:8888/?token='の後の文字列を入力


Jupyter Notebookの終了は3つ目のターミナルにquitと入力することでできる

# 3.GPUの再現性を確保する
tensorflowでGPUを使った際に学習結果が毎回異なる人へ
#### (1) 2. (2) でtensorflow-determinismをインストール
```
[aXXX@gXXX ~]$ pip3 install tensorflow-determinism 
```
#### (2)ノートブック上に記入
```
os.environ['TF_DETERMINISTIC_OPS'] = '1' # GPU内の計算順を固定するかどうかのフラグを指定(1'または'True'で固定)
os.environ['TF_XLA_FLAGS'] = '--tf_xla_enable_xla_devices'

# グローバルシードの指定
seed=1234
np.random.seed(seed)
tf.random.set_seed(seed)
os.environ["PYTHONHASHSEED"] = str(seed)
```

# 4. GPUを複数使って並列処理をする
訓練データをX_train, 訓練ラベルをy_train, 検証データをX_val, 検証ラベルをy_valとする
```
import tensorflow as tf

strategy = tf.distribute.MirroredStrategy()
with strategy.scope():
    <scopeの中でモデルを構築，　コンパイルする>

#データを変形
train_data=tf.data.Dataset.from_tensor_slices((X_train, y_train)).batch(BATCH_SIZE)
val_data=tf.data.Dataset.from_tensor_slices((X_val, y_val)).batch(BATCH_SIZE)

options = tf.data.Options()
options.experimental_distribute.auto_shard_policy = tf.data.experimental.AutoShardPolicy.OFF
train_data, val_data =train_data.with_options(options), val_data.with_options(options)

#通常通りにモデルをfit
history =model.fit(tr_data, validation_data = (val_data))  
```

# 参考文献
[ABCI 2.0 User Guide Jupyter Notebookの利用](https://docs.abci.ai/ja/tips/jupyter-notebook/)
[ABCI 2.0 User Guide Python](https://docs.abci.ai/ja/python/)
[ABCI 2.0 User Guide TensorFlow-Keras](https://docs.abci.ai/ja/apps/tensorflow-keras/
)
[Tensorflowによる再現性の確保について（GPUを使用する場合）](https://book-read-yoshi.hatenablog.com/entry/2021/03/21/TensorFlow_determinism)
[GPUで並列処理をする際の入力データの形式について](https://stackoverflow.com/questions/65322700/tensorflow-keras-consider-either-turning-off-auto-sharding-or-switching-the-a)