Torchで複数のGPUを使用したディープラーニング


BigDLはtorchベースでApache Sparkクラスタ分散するライブラリだった。
BigDLはまだ発展途上で2017年3月時点では学習回数すら指定できないようになっている。

BigDL
http://qiita.com/miyamotok0105/items/bda5add77a2cc3bdfab8

pix2pixを動かしていたら勝手にgpuを全部使っていて、他の学習が回せなくて結構困った。
https://github.com/phillipi/pix2pix

ここを見るとsetDeviceで指定したら、指定した場所を使ってくれるはずだが


```
cutorch.setDevice(i) : 
If one has multiple-GPUs, you can switch the default GPU (to allocate CUDA tensors and do operations). The GPU IDs are 1-indexed, so having 4 GPUs means, you can setDevice(1), setDevice(2), setDevice(3), setDevice(4).
```

gpu指定してもダメだった。指定の仕方が間違ってるのかも。


```
DATA_ROOT=./datasets/facades name=facades_generation which_direction=BtoA gpu=1 th train.lua
```


分散の書き方を調べるためにもメモした。



<hr>

Torchマシンラーニングライブラリを使用して複数のGPUを使用して単一のニューラルネットワークをトレーニングする方法について説明します。 これは、複数のGPUと複数のノードを使用するディープニューラルネットワーク（DNN）のトレーニングワークロードを拡大する技術に関する一連の記事の中で初めての記事です。

このシリーズでは、単一のネットワークのトレーニングを並列化することに焦点を当てます。 複数のネットワークを効率的にトレーニングして構成パラメータを最適化するという厄介な問題については、このハイパーパラメータ最適化の記事を参照してください。
https://blog.rescale.com/deep-neural-network-hyper-parameter-optimization/

#トーチについて
Torchは、Luaプログラミング言語の上に構築された軽量で柔軟なテンソルライブラリです。 トーチは機械学習の研究者に人気がありますので、多くの新しいディープニューラルネットワークのアイデアがTorchで最初に実装され、オープンソースの拡張機能として利用できます。 したがって、トーチでは、深い学習の最先端技術が最初に利用可能になることがよくあります。

これの欠点は、Torchのドキュメントが実装よりも後ろにあることが多いため、あなたが何をしたいのかについてgithubの例を見つけない限り、どのTorchモジュールを使用すべきか、どのように使用するべきかを理解することは難しいことです。

これの一例は、Torchに複数のGPUを使用してニューラルネットワークをトレーニングさせる方法です。 インターネット上で「マルチプルトーチ」を検索することで、このギタップの問題が最上の結果の1つになります。 これから、トーチ環境から複数のGPUにアクセスできることはわかっていますが、この低レベルの構造を使って複雑なネットワークを育成するにはどうすればよいでしょうか？

#データ対モデル並列性
単一のニューラルネットワークを訓練するために作業を並列化する場合、モデルの並列性とデータ並列性の2つの選択肢があります。

![modelparallel.png](https://qiita-image-store.s3.amazonaws.com/0/64334/63da4b92-d1b6-bc06-12b4-d286cacb0482.png)

モデル並列処理では、各GPUは、ネットワーク内のノードのチャンクを実行して、特定のデータバッチを実行します。

![dataparallel.png](https://qiita-image-store.s3.amazonaws.com/0/64334/1c015438-ac06-5371-057d-080103917fae.png)

データ並列処理では、各GPUは異なるバッチのデータに対してネットワーク全体を実行します。

この違いについてはこのペーパーで詳しく説明していますが、GPU間でどのような同期が必要かについては、どちらを使用するかの選択に影響します。 データ並列処理ではモデルパラメータの同期が必要です。モデルの並列処理では、チャンク間で入力値と出力値を同期させる必要があります。
https://arxiv.org/pdf/1404.5997v2.pdf

シンプルなトーチの例
ここでは、トーチ自体の単体テストに基づいた畳み込みニューラルネットワークのトレーニングの簡単な例を見ていきます。 このネットワークには、2つの畳み込み層と2つのrectifier層があります。 私たちは、ネットワーク上で単純な順方向および逆方向パスを行います。 実際にトレーニングのための誤差勾配を計算するのではなく、それらをランダムなベクトルに設定して単純なものにしています。

```
require 'nn'
 
model = nn.Sequential()
model:add(nn.SpatialConvolution(3, 3, 3, 5))
model:add(nn.ReLU(true))
model:add(nn.SpatialConvolution(3, 3, 3, 5))
model:add(nn.ReLU(true))
 
input = torch.round(torch.Tensor(16, 3, 10, 10):uniform(0, 255))
output = model:forward(input)
fakeGradients = output:clone():uniform(-0.1, 0.1)
model:backward(input, fakeGradients)
```

次に、GPU上で実行するように変換してみましょう（この例は、CUDA互換のGPUを使用している場合にのみ実行されます）。

```
require 'cutorch'
require 'cunn'
 
cutorch.setDevice(1)
model = nn.Sequential()
model:add(nn.SpatialConvolution(3, 3, 3, 5))
model:add(nn.ReLU(true))
model:add(nn.SpatialConvolution(3, 3, 3, 5))
model:add(nn.ReLU(true))
model:cuda()
 
input = torch.round(torch.CudaTensor(16, 3, 10, 10):uniform(0, 255))
output = model:forward(input)
fakeGradients = output:clone():uniform(-0.1, 0.1)
model:backward(input, fakeGradients)
```

これをGPU上で実行するには、ネットワーク上でcuda（）を呼び出し、入力をCudaTensorにします。

次に、モデルを2つのGPU（モデルパラレルパラダイムの例として）に配布しましょう。 GPUのデバイスIDを繰り返し処理し、cutorch.withDeviceを使用して各レイヤーを特定のGPUに配置します。

```
require 'cutorch'
require 'cunn'
 
cutorch.setDevice(1)
model = nn.Sequential()
for i=1, math.min(2, cutorch.getDeviceCount()) do
    cutorch.withDevice(i, function() model:add(nn.SpatialConvolution(3, 3, 3, 5)) end)
    cutorch.withDevice(i, function() model:add(nn.ReLU(true)) end)
end
model:cuda()
 
input = torch.round(torch.CudaTensor(16, 3, 10, 10):uniform(0, 255))
output = model:forward(input)
fakeGradients = output:clone():uniform(-0.1, 0.1)
model:backward(input, fakeGradients)

```

これにより、各GPUに畳み込みレイヤーとReLUレイヤーが配置されます。 順方向および逆方向のパスは、GPU1とGPU2との間で出力を伝播しなければならない。

次に、nn.DataParallelTableを使用して、複数のGPUで実行されているネットワーク全体のコピーにデータのバッチを配布します。 DataParallelTableは、複数のコンテナをラップし、それらの間に入力を分配するTorchコンテナです。

![dataparalleltable.png](https://qiita-image-store.s3.amazonaws.com/0/64334/83ac1068-3828-d925-ab85-affb7885c129.png)


```
require 'cutorch'
require 'cunn'
 
cutorch.setDevice(1)
model = nn.Sequential()
model:add(nn.SpatialConvolution(3, 3, 3, 5))
model:add(nn.ReLU(true))
model:add(nn.SpatialConvolution(3, 3, 3, 5))
model:add(nn.ReLU(true))
model:cuda()
 
gpus = torch.range(1, cutorch.getDeviceCount()):totable()
dpt = nn.DataParallelTable(1):add(model, gpus):cuda()
 
input = torch.round(torch.CudaTensor(16, 3, 10, 10):uniform(0, 255))
output = dpt:forward(input)
fakeGradients = output:clone():uniform(-0.1, 0.1)
dpt:backward(input, fakeGradients)

```

したがって、元のシーケンシャルコンテナ上で順方向および逆方向パスを実行する代わりに、DataParallelTableコンテナで実行し、各GPU上のネットワークのコピーにデータを分散します。

ここでは、上記のすべてのコードを使用してクローンして実行することができるRescaleの仕事があります：
https://platform.rescale.com/tutorials/simple-torch-training/clone/

#より大きな例
実際のDNNをトレーニングするときのDataParallelTableの使用を見てみましょう。 私たちは、Sergey Zagoruykoのgithub上のCIFAR10でのWide Residual Networksの実装を使用します。

train.luaでは、基本ニューラルネットワークのすべての並列化がヘルパー関数によって適用されることがわかります。

```
model:add(utils.makeDataParallelTable(net, opt.nGPU))
```

makeDataParallelTableを調べると、上記の最後の例と同様の構造が
nn.DataParallelTable:add

```
function utils.makeDataParallelTable(model, nGPU)
    if nGPU > 1 then
        local gpus = torch.range(1, nGPU):totable()
        local fastest, benchmark = cudnn.fastest, cudnn.benchmark
 
        local dpt = nn.DataParallelTable(1, true, true)
             :add(model, gpus)
             :threads(function()
                 local cudnn = require 'cudnn'
                 cudnn.fastest, cudnn.benchmark = fastest, benchmark
             end)
        dpt.gradInput = nil
        model = dpt:cuda()
    end
    return model
end
```

これらのジョブをクローンし、自分でトレーニングを実行することができます。

CIFAR10 Wide ResNet、1 GPU：
https://platform.rescale.com/tutorials/torch-wide-resnet-cifar10-1gpu/clone/

CIFAR10 Wide ResNet、4 GPU：
https://platform.rescale.com/tutorials/torch-wide-resnet-cifar10-4gpu/clone/

10エポックのトレーニングを実行すると、4つのGPUジョブが単一のGPUジョブよりも約3.33倍速いことがわかります。 かなり良いスケールアップ！

この記事では、Torchを使用したモデルとデータの並列DNNトレーニングの実装例を示しました。 今後の記事では、マルチノードスケーリングだけでなく、他のニューラルネットワークライブラリを使用したマルチGPUトレーニングの使用方法についても説明します。

この記事はMark Whitneyによって書かれました。

Mark Whitneyさん。ありがとうございます。

#参考
https://blog.rescale.com/ja/deep-learning-with-multiple-gpus-on-rescale-torch/