Pythonのforループでメモリリークした時の対策


自分のトラブルシューティングのメモ。

起こった現象

KerasでモデリングするPythonコードを書いたのだが、その中でfor文でデータセットの組を変えながら動かしていくと、3ループ目でメモリリークでエラーが出る。
Keras: ResourceExhaustedError (see above for traceback): OOM when allocating tensor with shape
1周目を見ているとメモリは50%程度の使用率なのだが、2週目には95%を超え、3週目で落ちてしまう。
実行時間を見ると、1週目は40分くらいで計算が終わっているが、2週目は2時間以上かかっている。キャッシュが発生しまくっている模様。
(なお、OSはWindows8.1, PythonのバージョンはPython 3.6.0[Anaconda])

対策前のコード

main.py (概要だけ)

main.py
if __name__ == '__main__':
    for i in range(0, 30):
        mykeras(i)

mykeras()の引数に0, 1, 2, ...と入れて実行していく。
これをコマンドプロンプトから、python main.pyで実行していた。
i=2でエラーになる。i=2が問題なのかと思ったが、range(4, 10)とかにしても同じエラーで落ちる。やはりループ回数の問題らしい。

想定される原因

forループの時にGCが働いていないっぽい。前のループでのオブジェクトがメモリを専有したままになっているのではないか。
Pythonではこの辺の挙動は遮蔽されているので、本当の原因はよくわからないが……

取った対策

Pythonでfor文回すのをやめる。
Pythonで1周動かすなら問題ないようなので、ループ処理をバッチファイルで制御する。

対策後のコード

exec.bat

exec.bat
cd /d %~dp0
for /l %%n in (0,1,29) do (
  python main.py %%n
)

main.py (概要だけ)

main.py
import sys
args = sys.argv
i = int(args[1])
if __name__ == '__main__':
    mykeras(i)

これで、myfunc()の引数のを0, 1, 2, ...と代入しながら、実行できる。
main.pyの処理が1回完了するごとにPythonのプロセスを落とすので、最初に挙げたようなメモリリークの問題は解消された。

感想

Pythonでも手動でGCする方法はあるようなので、調べてみたほうが良いだろうか (あまり使わないほうが良いとも聞く)。
以前に、Rでメモリリークで悩んだことがあった。その時には思い至らなかったが、実は同じ現象かもしれない。
いずれにせよ、RやPythonの中で解決する方法=メモリを効率的に使う方法があるならば、それも知っておきたい。