PyTorch デザインノート : Frequently Asked Questions (翻訳/解説)
翻訳 : (株)クラスキャット セールスインフォメーション
作成日時 : 05/27/2018 (0.4.0)
* 本ページは、PyTorch Doc Notes の – Frequently Asked Questions を動作確認・翻訳した上で
適宜、補足説明したものです:
* サンプルコードの動作確認はしておりますが、適宜、追加改変している場合もあります。
* ご自由にリンクを張って頂いてかまいませんが、sales-info@classcat.com までご一報いただけると嬉しいです。
私のモデルが “cuda runtime error(2): out of memory” をレポートします
エラーメッセージが提示しているように、GPU 上のメモリが不足しています。PyTorch で巨大なデータ総量をしばしば処理するので、小さな間違いが貴方のプログラムに GPU の総てを素早く使い切らせます ; 幸い、これらのケースの修正はしばしば単純です。ここに確認すべき幾つかの一般的なことがあります :
訓練ループに渡り履歴を蓄積しないでください。デフォルトでは、勾配を必要とする variable を伴う計算は履歴を保持します。これは計算時にそのような variable を回避するべきであることを意味します、それは訓練ループを越えて存続します, e.g., 統計を追跡しているとき。代わりに、variable を detach するかその基礎的なデータにアクセスするべきです。
時に、微分可能な variable が生じるときそれは自明ではないかもしれません。次の (ソース から要約した) 訓練ループを考えてください :
total_loss = 0 for i in range(10000): optimizer.zero_grad() output = model(input) loss = criterion(output) loss.backward() optimizer.step() total_loss += loss
ここで、total_loss は訓練ループに渡り履歴を累積しています、何故ならば loss は autograd 履歴を持つ微分可能な variable だからです。代わりに total_loss += float(loss) を書くことによりこれを修正できます。
この問題の他のインスタンス : 1
必要のない tensor と variable を保持し続けないでください。 Tensor や Variable を local に割り当てる場合、Python は local がスコープ外に出るまでは割当て解除しません。この参照を del x の使用で解放できます。同様に、Tensor か Variable をオブジェクトのメンバー変数に割り当てる場合、それはオブジェクトがスコープ外に出るまでは割り当て解除しません。必要のない一時的なものを保持し続けないのであればベストなメモリ使用を得るでしょう。
local のスコープは貴方が想定するよりも大きくなり得ます。例えば :
for i in range(5): intermediate = f(input[i]) result += g(intermediate) output = h(result) return output
ここで、intermediate は h が実行されているときでさえも存続しています、何故ならばそのスコープはループの最後を越えて突き出ているからです。それを早期に解放するためには、それを終えたときに intermediate を del するべきです。
巨大過ぎるシークエンス上で RNN を実行しないでください。 RNN を通して逆伝播するために必要なメモリ総量は RNN の長さに線形にスケールされます ; そのため、RNN に長すぎるシーケンスを供給しようとする場合メモリが不足します。
この現象のための技術用語は backpropagation through time, で、truncated BPTT をどのように実装するかのために word language model サンプルを含み、多くのリファレンスがあります ; truncation は このフォーラム投稿 で説明されているように、repackage 関数で処理されます。
巨大過ぎる線形層を使用しないでください。線形層 nn.Linear(m, n) は O(nm) メモリを使用します : つまり、重みのメモリ要求は特徴数により二次的にスケールされます。この方法でメモリを吹き飛ばすのは非常に簡単です (そして重みのサイズの少なくとも 2 倍は必要であることを覚えていてください、何故ならば勾配をストアする必要もまたあるからです)。
GPU メモリが適切に解放されません
PyTorch はメモリ割当てをスピードアップするためにキャッシュメモリ allocator を使用します。その結果、nvidia-smi で示される値は通常は真のメモリ使用量を反映しません。GPU メモリに関するより詳細については メモリ管理 を見てください。
もし Python を抜けた後でさえも GPU メモリが解放されないのであれば、ある Python サブプロセスが依然として活きている可能性が高いです。ps -elf | grep python でそれらを見つけることができるでしょう、そして kill -9 [pid] により手動でそれらを kill してください。
データローダ・ワーカーが同じランダム数を返します
貴方は多分、データセットでランダム数を生成するために他のライブラリを使用しています。例えば、NumPy の RNG はワーカー・サブプロセスが fork で開始されるとき複写されます。worker_init_fn によりワーカーでランダム・シードをどのように適切に設定するかについて torch.utils.data.DataLoader のドキュメントを見てください。
リカレント・ニューラルネットワークがデータ並列で動作しません
DataParallel あるいは data_parallel() を伴う Module における pack sequence -> recurrent network -> unpack sequence パターンの使用は微妙な点があります。各デバイス上の各 forward() への入力は全体の入力の一部だけです。unpack 演算 torch.nn.utils.rnn.pad_packed_sequence() はデフォルトではそれが見る最長の入力 i.e. 特定のデバイスの最長までパディングするだけですので、結果が一緒に集められるときにサイズのミスマッチが発生します。従って、forward() 呼び出しが同じ長さのシークエンスを返すことを確実にするために代わりに pad_packed_sequence() の total_length 引数を利用することができます。例えば、次のように書けます :
from torch.nn.utils.rnn import pack_padded_sequence, pad_packed_sequence class MyModule(nn.Module): # ... __init__, other methods, etc. # padding_input is of shape [B x T x *] (batch_first mode) and contains # the sequences sorted by lengths # B is the batch size # T is max sequence length def forward(self, padded_input, input_lengths): total_length = padded_input.size(1) # get the max sequence length packed_input = pack_padded_sequence(padded_input, input_lengths, batch_first=True) packed_output, _ = self.my_lstm(packed_input) output, _ = pad_packed_sequence(packed_output, batch_first=True, total_length=total_length) return output m = MyModule().cuda() dp_m = nn.DataParallel(m)
更に、データ並列を伴いバッチ次元が dim 1 であるとき (i.e., batch_first=False) 特別なケアが取られる必要があります。この場合、pack_padded_sequence の最初の引数 padding_input は shape [T x B x *] で dim 1 に沿って scatter されるべきですが、2 番目の引数 input_lengths は shape [B] で dim 0 に沿って scatter されるべきです。tensor shape を操作するための特別なコードが必要でしょう。
以上