Azure Batch AI - Chainer MNをInfiniband / GPU付きノードで高速並列実行する！

Azure Batch AI - AzureでGPU/マルチノードでディープラーニング計算を簡単に行う！（Chainer MN編）
https://qiita.com/YoshiakiOi/items/2f143e62bcf5d7daa8e9 に続いて、Batch AIを使っていきます。
今回はChainer MNのジョブを、AzureのInfiniband付き（もちろんGPUも！）ノードで並列実行していきます！

# Batch AIとは？

前項参照。
https://qiita.com/YoshiakiOi/items/2f143e62bcf5d7daa8e9
ディープラーニングの学習を、オンデマンドでたくさんGPU付きノードを並べたクラスターを作成して、簡単に並列実行できるようにする、Microsoft Azureの1製品。

今回はInfinibandを利用して、より高速に実行できる環境を作っていきます。

# Infinibandとは？

![qiitainfiniband.png](https://qiita-image-store.s3.amazonaws.com/0/276225/3a4321ca-c82b-6fe7-c451-dbf245c8ae7d.png)

主にHPC (High Performance Computing、大量の計算資源を利用して、大量・高速なコンピューティングを行う分野）で利用される、低待機時間・広帯域な通信規格。
MPIをInfiniband経由で実行すると、ノード間通信が高速になり、ノードを並列に並べたときにスケールするようになります（もちろんコードによりますが）。
MicrosoftのAzureでは、クラウドから一部の仮想マシンでInfinibandを利用することができます。

### 注意点

AzureでMPIからInfinibandを利用するには、2018年現在、Intel MPI、Platform MPI、MS MPIを利用する必要があります。本稿では、Intel MPIを利用して、Chainer MNを実行します。

#### （参考）
[Azure Antenna] HPCだけじゃないDeep Learningでも使える ハイパフォーマンスAzureインフラ ～ HPC and Deep Learning on Azure ～
https://www.slideshare.net/ShuichiGojuki/hpc-on-azure21

### Chainer MN on Azure GPU/Infiniband Nodes

Batch AI上ではありませんが、Chainer MNをAzureのGPU/Infinibandノードで実行すると、ノード数を増やしてもスケールする（ノードを増やした分だけ線形的に実行時間が短くなる）ことが、発表されています。

####（参考）

de:code 2018 Online AI05 AI ChainerMN on Microsoft Azure で大規模分散深層学習
https://info.microsoft.com/JA-CNTNT-eBook-decode18Online-MGC0002650_01Registration-ForminBody.html

Batch AIは、Azureの上記のような高性能なインフラをなるべく簡単にディープラーニングに利用するためのPaaS型のコンピューティングソリューションです。

# Batch AIでInfinibandを利用して、Chainer MNのMNISTジョブを実行する

ここからは、以下のマイクロソフト社のBatch AI開発チームが作成しているGithubレポジトリを参考に、ジョブを実行していきます。前稿と同じく、AzureポータルのCloud Shellから実行します。

https://github.com/Azure/BatchAI/tree/master/recipes/Chainer/Chainer-GPU-Distributed-Infiniband

途中までは前回と同じ作業になります。

## リソースグループとワークスペースの作成

```bash
az group create -n batchai.recipes -l eastus
az batchai workspace create -g batchai.recipes -n recipe_workspace -l eastus
```
＊各コマンド（az batchai ***）の詳細なオプションの説明
https://docs.microsoft.com/en-us/cli/azure/batchai?view=azure-cli-latest

## ストレージの準備

＊データやアウトプット先をAzure Filesにしてしまうと、せっかくInfinibandで高速化してもストレージがネックになってしまうので、BlobまたはNFSなど別のオプションにしましょう。

こちらも前回と同じように準備します。ただし、今回は後から使うjobprep.shというスクリプトをtrain_mnist.pyと一緒にダウンロード、アップロードしておきます。

```bash
az storage account create -n <storage account name> --sku Standard_LRS -g batchai.recipes -l eastus

az storage container create -n scripts --account-name <storage account name>
az storage container create -n result --account-name <storage account name>
az storage share create -n logs --account-name <storage account name>

wget https://raw.githubusercontent.com/chainer/chainermn/v1.3.0/examples/mnist/train_mnist.py
wget https://raw.githubusercontent.com/Azure/BatchAI/master/recipes/Chainer/Chainer-GPU-Distributed-Infiniband/jobprep.sh
az storage blob upload -f train_mnist.py -n chainer/train_mnist.py --account-name <storage account name> --container scripts
az storage blob upload -f jobprep.sh -n chainer/jobprep.sh --account-name <storage account name> --container scripts
```

## クラスターの作成とジョブの投入

クラスターの作成も流れは前回と同じですが、今回はInfinibandが付いているNC24Rシリーズを選択します。（Azureでは、仮想マシンのシリーズ名の最後にRが付いているものがInfiniband付きになります。今回はTesla K80が4枚載った、Infiniband付きシリーズのNC24Rにしていますが、例えばP100/V100のNC24R v2/v3）を利用することでより高速に実行できます。

### 注意点
Batch AIのデフォルトのクオータ（リソース制限）ではNCシリーズの仮想コアの上限値は24コアのため、NC24Rを2台（48仮想コア）並べようとするとエラーになります。サポートリクエストを上げて、クオータを上げて貰う必要があります。

クラスターを作成します。

```bash
az batchai cluster create -n nc24r -g batchai.recipes -w recipe_workspace -s Standard_NC24r -t 2 --generate-ssh-keys 
```

前回と同じく、ジョブ管理の箱であるExperimentを作成します。

```bash
az batchai experiment create -g batchai.recipes -w recipe_workspace -n chainer_experiment
```

次にカレントディレクトリにjob.jsonを以下のように作成します。いくつか前回（OpenMPIを利用）とは異なる箇所があります。

1. コンテナー：今回は開発チームが作成したInfiniband用のIntel MPIを使う準備をしているコンテナーを利用します。
2. --comnunicator：Intel MPIはcuda awareでないため、non_cuda_awareオプションを利用します。
3. jobPreparationオプション：jobPraparationはコンテナー内で、コード（ここではtrain_mnist.py）を実行する前に流すスクリプトを設定するオプションです。ここでは、jobprep.shを流し、Intel MPIをインストールしています。

```<  >```は適宜変更してください。

```json
{
    "$schema": "https://raw.githubusercontent.com/Azure/BatchAI/master/schemas/2018-05-01/job.json",
    "properties": {
        "nodeCount": 2,
        "chainerSettings": {
            "pythonScriptFilePath": "$AZ_BATCHAI_JOB_MOUNT_ROOT/scripts/chainer/train_mnist.py",
            "commandLineArgs": "-g --communicator non_cuda_aware -o $AZ_BATCHAI_OUTPUT_MODEL",
             "processCount": 8
        },
        "jobPreparation": {
            "commandLine": "bash $AZ_BATCHAI_JOB_MOUNT_ROOT/scripts/chainer/jobprep.sh"
        },
        "stdOutErrPathPrefix": "$AZ_BATCHAI_JOB_MOUNT_ROOT/logs",
        "mountVolumes": {
            "azureFileShares": [
                {
                    "azureFileUrl": "https://<AZURE_BATCHAI_STORAGE_ACCOUNT>.file.core.windows.net/logs",
                    "relativeMountPath": "logs"
                }
            ],
            "azureBlobFileSystems" :[
                {
                    "accountName": "<Storage Account Name>",
                    "containerName": "scripts",
                    "credentials": {
                        "accountKey": "<Storage Account Key>"
                    },
                    "relativeMountPath": "scripts"
                },
                {
                    "accountName": "<Storage Account Name>",
                    "containerName":"result",
                    "credentials": {
                        "accountKey": "<Storage Account Key>"
                    },
                    "relativeMountPath": "result"
                }
              ]
        },
        "outputDirectories": [{
            "id": "MODEL",
            "pathPrefix": "$AZ_BATCHAI_JOB_MOUNT_ROOT/result"
        }],
        "containerSettings": {
            "imageSourceRegistry": {
                "image": "batchaitraining/chainermn:IntelMPI"
            }
        }
    }
}
```

あとは、前回と同じくジョブを実行します。

```bash
az batchai job create -n distributed_chainer_ib -c nc24r -g batchai.recipes -w recipe_workspace -e chainer_experiment -f job.json --storage-account-name <storage account name>
```

結果をポータルから確認します。無事ジョブが終了しました！

![image.png](https://qiita-image-store.s3.amazonaws.com/0/276225/abd4a498-96fe-2cea-1869-b0c9e7e0da0c.png)

## リソースのお片付けも忘れずに！

前項と同じですが。。。

#### クラスターのノード関係

・クラスターのノード数を減らす（0ノードにする）

```bash
az batchai cluster resize -n nc6 -g batchai.recipes -w recipe_workspace -t 0
```

・クラスターを削除する

```bash
az batchai cluster delete -n nc6 -g batchai.recipes -w recipe_workspace
```

・クラスターを自動スケールする（最小0ノード、最大10ノードの例）

```bash
az batchai cluster auto-scale -n nc6 -g batchai.recipes -w recipe_workspace --min 0 --max 10
```


####リソースグループごと削除する

すべて削除する場合は、リソースグループごと削除します。

```bash
az group delete -n batchai.recipes -y
```

# （おまけ）Intel MPIのベンチマークをノードに入って実行してみます！

実際にBatch AIのノードに入って、コンテナー上からIntel MPIのベンチマークを実行してみます。

### 補足：Batch AIのネットワークについて
Azureの仮想マシンは、すべて仮想ネットワークという閉じたネットワークに属すようデプロイされます。Batch AIは仮想マシン（仮想マシンスケールセット）がデプロイされる部分が隠蔽されていますが、このことには変わりはありません。ユーザーが作成した仮想ネットワーク（正確にはサブネット）にBatch AIのクラスターをデプロイすることもできますが、通常は10.0.0.0/*の仮想ネットワーク（サブネット）にデプロイされ、10.0.0.4から順番にプライベートIPが割り振られます。そしてDockerは--net=hostで起動されているため、ホストのネットワークインターフェースを共有します。
また、外部からアクセスする際は、パブリックIPを持ったロードバランサー経由（ポート転送）でSSHでアクセスすることになります。

SSHで外部からアクセスするために、ロードバランサーのIPアドレスとポート転送しているポートを調べます。

```bash
az batchai cluster node list -c nc24r -g batchai.recipes -w recipe_workspace
```

![qiitaport.png](https://qiita-image-store.s3.amazonaws.com/0/276225/d4c01559-2018-d3d6-e3ea-c7bab5192b8b.png)

そしてそのIPとポート番号でSSHします。

![qiitassh.png](https://qiita-image-store.s3.amazonaws.com/0/276225/1e0cdc0a-bb2a-2107-e5f3-27c2f90d9b76.png)


Dockerが動いていることを確認し、中に入ります。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/276225/04e0ac2a-9c50-0e7a-97c8-bdfdaf0480c9.png)

そして、Intel MPIのベンチマーク、pingpongをInfiniband上で流してみます。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/276225/382a8f44-5998-d9ef-9201-23f4776f9878.png)

ちなみに、Infinibandを使わないで同じベンチマークを流すと以下のようになります。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/276225/32151960-d37a-9b08-9dfa-c723e4a46bb2.png)

レイテンシーは数十倍、バンド幅も10倍以上変わってくるのが見えますね。
今回はMNISTでかつ2ノードなので、Open MPI版と大きく結果が変わらないかもしれませんが、ノード数が増え、かつ通信量が大きくなるようなジョブの場合、速度が全く変わってきます。

以上、今回はChainer MNをBatch AIを使って、簡単にInfinibandとGPUを使って高速・並列に実行してみました。

# 参考資料

（公式）Azure Document
https://docs.microsoft.com/ja-jp/azure/batch-ai/

（公式）Github Batch AI
https://github.com/Azure/BatchAI
*ドキュメントよりもGithubのほうが内容が充実しており、各フレームワークのレシピ（例）が揃っています。

（手前味噌ですが）Deep Learning Lab MeetUp (2018年6月27日） 学習編資料
https://www.slideshare.net/ssuser147cbc/deep-learning-lab-meetup-azurebatch-ai


＊本稿は、個人の見解に基づいた内容であり、所属する会社の公式見解ではありません。また、いかなる保証を与えるものでもありません。正式な情報は、各製品の販売元にご確認ください。