※初投稿なのでご了承ください
「よーし完璧なプログラムが完成した!!」
「実行するぞーー!!!」
OutOfMemoryError
「うぎゃっぱー!」(憤死)
はじめに
あなたが、画像や音楽を対象にしたデータを元に機械学習をしているならば、
時に大量のデータを使うこともあるでしょう。
使うのがCPUであれば、多少ビックなデータを使っていたって問題はありませんが、
それでは学習時間も非常に長くなってしまうことでしょう。
今回は、Chainerを用いて、莫大なデータを用いていてもGPUメモリーエラーになり難くなる秘策について説明いたします。
※私はそこまで強くないので、強い人たちの役には立ちません。
使用している環境
Chainer 5.0.0
Cupy 5.0.0
考え方
どの様にすれば、メモリーエラーにならないのでしょうか。
例えば、以下の様に、大量のデータを全て一度Cupyに変換すると、
メモリーエラーになりやすいのは自明です。
# dataをどっかから取ってき、説明変数Xと目的変数yに分ける。
# numpyの配列に変換する。
X_np = np.array(X,dtype='float32').reshape(-1,1,28,28)
y_np = np.array(y,dtype='int32').reshape(-1,1)
# cupyの配列に変換する
X_cp = cp.array(X,dtype='float32').reshape(-1,1,28,28)
y_cp = cp.array(y,dtype='int32').reshape(-1,1)
dataset=datasets.TupleDataset(X_cp, y_cp)
iterater = iterators.SerialIterator(dataset, batchsize)
classifier = L.Classifier(model)
# 最適化手法の選択
optimizer = optimizers.SGD(lr=0.01).setup(classifier)
# UpdaterにIteratorとOptimizerを渡す
updater = training.StandardUpdater(iterater, optimizer, device=-1)
max_epoch = 10
# TrainerにUpdaterを渡す
trainer = training.Trainer(
updater, (max_epoch, 'epoch'), out='mnist_result')
trainer.run()
解決の糸口としては、一度に全てcupyに変換してから用いず、
処理中に部分的にcupyに変換させることです。
しかし、trainerを用いながらそれを行う方法はあるのでしょうか?
Updaterの力を使う方法
Updaterの引数を調節すれば、バッチサイズごとにnumpyをcupyに変換するのはとても簡単にできます。
Updaterの内部では、
chainer.dataset.concat_example(batch, device=None, padding=None)
が呼ばれますが,ここで、device >= 0とすればnumpyがGPUに送られ、自動的にcupyへ変換されます。
Updaterの引数の device とUpdater内で呼ばれる device には同じ値が入るので、
numpyでデータセットを作り
Updaterで device によりGPUを指定する
これをすればバッチサイズごとにcupyへ変換することができます。
# dataをどっかから取ってき、説明変数Xと目的変数yに分ける。
# numpyの配列に変換する。
X_np = np.array(X,dtype='float32').reshape(-1,1,28,28)
y_np = np.array(y,dtype='int32').reshape(-1,1)
'''#cupyの配列に変換しない'''
dataset=datasets.TupleDataset(X_np, y_np)
iterater = iterators.SerialIterator(dataset, batchsize)
classifier = L.Classifier(model)
# 最適化手法の選択
optimizer = optimizers.SGD(lr=0.01).setup(classifier)
# UpdaterにIteratorとOptimizerを渡す
''' diviceに使用するGPUを入れる。 ↓ ( ≠ -1) '''
updater = training.StandardUpdater(iterater, optimizer, device = 1)
max_epoch = 10
# TrainerにUpdaterを渡す
trainer = training.Trainer(
updater, (max_epoch, 'epoch'), out='mnist_result')
trainer.run()