web-dev-qa-db-ja.com

Kerasはtrain_on_batchやfitなどを呼び出すときにGPUメモリを使いすぎます

私はKerasをいじり続けてきましたが、これまでのところ気に入っています。かなり深いネットワークで作業しているときに抱えていた大きな問題が1つあります。model.train_on_batchやmodel.fitなどを呼び出すとき、Kerasはモデル自体が必要とするよりもかなり多くのGPUメモリを割り当てます。これは、いくつかの本当に大きな画像でトレーニングしようとすることによるものではなく、ネットワークモデル自体が大量のGPUメモリを必要とするようです。私はこのおもちゃの例を作成して、私が意味することを示しました。基本的には次のようになります。

まず、かなり深いネットワークを作成し、model.summary()を使用して、ネットワークに必要なパラメーターの総数を取得します(この場合、206538153、約826 MBに相当)。次に、nvidia-smiを使用して、Kerasが割り当てたGPUメモリの量を確認します。完全に理にかなっていることがわかります(849 MB)。

次に、ネットワークをコンパイルし、これによりGPUメモリ使用量が増加しないことを確認できます。この例でわかるように、この時点で1 GB近くのVRAMを使用できます。

次に、単純な16x16イメージと1x1のグラウンドトゥルースをネットワークに供給しようとすると、Kerasが再び多くのメモリを割り当て始めるため、すべてが爆発します。ネットワークのトレーニングについては、単にモデルを用意するよりも多くのメモリが必要と思われますが、これは私には意味がありません。私はこのGPUで他のフレームワークで非常に深いネットワークをトレーニングしているので、Kerasを間違って使用していると思います(または、セットアップやKerasに何か問題がありますが、もちろんそれを確実に知ることは困難です)。

コードは次のとおりです。

from scipy import misc
import numpy as np
from keras.models import Sequential
from keras.layers import Dense, Activation, Convolution2D, MaxPooling2D, Reshape, Flatten, ZeroPadding2D, Dropout
import os

model = Sequential()

model.add(Convolution2D(256, 3, 3, border_mode='same', input_shape=(16,16,1)))
model.add(MaxPooling2D(pool_size=(2,2), strides=(2,2)))
model.add(Convolution2D(512, 3, 3, border_mode='same'))
model.add(MaxPooling2D(pool_size=(2,2), strides=(2,2)))
model.add(Convolution2D(1024, 3, 3, border_mode='same'))
model.add(Convolution2D(1024, 3, 3, border_mode='same'))
model.add(Convolution2D(1024, 3, 3, border_mode='same'))
model.add(Convolution2D(1024, 3, 3, border_mode='same'))
model.add(Convolution2D(1024, 3, 3, border_mode='same'))
model.add(Convolution2D(1024, 3, 3, border_mode='same'))
model.add(Convolution2D(1024, 3, 3, border_mode='same'))
model.add(Convolution2D(1024, 3, 3, border_mode='same'))
model.add(Convolution2D(1024, 3, 3, border_mode='same'))
model.add(Convolution2D(1024, 3, 3, border_mode='same'))
model.add(Convolution2D(1024, 3, 3, border_mode='same'))
model.add(Convolution2D(1024, 3, 3, border_mode='same'))
model.add(Convolution2D(1024, 3, 3, border_mode='same'))
model.add(Convolution2D(1024, 3, 3, border_mode='same'))
model.add(Convolution2D(1024, 3, 3, border_mode='same'))
model.add(Convolution2D(1024, 3, 3, border_mode='same'))
model.add(Convolution2D(1024, 3, 3, border_mode='same'))
model.add(Convolution2D(1024, 3, 3, border_mode='same'))
model.add(Convolution2D(1024, 3, 3, border_mode='same'))
model.add(Convolution2D(1024, 3, 3, border_mode='same'))
model.add(Convolution2D(1024, 3, 3, border_mode='same'))
model.add(Convolution2D(1024, 3, 3, border_mode='same'))
model.add(MaxPooling2D(pool_size=(2,2), strides=(2,2)))
model.add(Convolution2D(256, 3, 3, border_mode='same'))
model.add(Convolution2D(32, 3, 3, border_mode='same'))
model.add(MaxPooling2D(pool_size=(2,2)))
model.add(Flatten())
model.add(Dense(4))
model.add(Dense(1))

model.summary()

os.system("nvidia-smi")
raw_input("Press Enter to continue...")    

model.compile(optimizer='sgd',
              loss='mse', 
              metrics=['accuracy'])

os.system("nvidia-smi")              
raw_input("Compiled model. Press Enter to continue...")

n_batches = 1
batch_size = 1
for ibatch in range(n_batches):
    x = np.random.Rand(batch_size, 16,16,1)
    y = np.random.Rand(batch_size, 1)

    os.system("nvidia-smi")
    raw_input("About to train one iteration. Press Enter to continue...")

    model.train_on_batch(x, y)         
    print("Trained one iteration")

これにより、次の出力が得られます。

Using Theano backend.
Using gpu device 0: GeForce GTX 960 (CNMeM is disabled, cuDNN 5103)
/usr/local/lib/python2.7/dist-packages/theano/sandbox/cuda/__init__.py:600: UserWarning: Your cuDNN version is more recent than the one Theano officially supports. If you see any problems, try updating Theano or downgrading cuDNN to version 5.
  warnings.warn(warn)
____________________________________________________________________________________________________
Layer (type)                     Output Shape          Param #     Connected to                     
====================================================================================================
convolution2d_1 (Convolution2D)  (None, 16, 16, 256)   2560        convolution2d_input_1[0][0]      
____________________________________________________________________________________________________
maxpooling2d_1 (MaxPooling2D)    (None, 8, 8, 256)     0           convolution2d_1[0][0]            
____________________________________________________________________________________________________
convolution2d_2 (Convolution2D)  (None, 8, 8, 512)     1180160     maxpooling2d_1[0][0]             
____________________________________________________________________________________________________
maxpooling2d_2 (MaxPooling2D)    (None, 4, 4, 512)     0           convolution2d_2[0][0]            
____________________________________________________________________________________________________
convolution2d_3 (Convolution2D)  (None, 4, 4, 1024)    4719616     maxpooling2d_2[0][0]             
____________________________________________________________________________________________________
convolution2d_4 (Convolution2D)  (None, 4, 4, 1024)    9438208     convolution2d_3[0][0]            
____________________________________________________________________________________________________
convolution2d_5 (Convolution2D)  (None, 4, 4, 1024)    9438208     convolution2d_4[0][0]            
____________________________________________________________________________________________________
convolution2d_6 (Convolution2D)  (None, 4, 4, 1024)    9438208     convolution2d_5[0][0]            
____________________________________________________________________________________________________
convolution2d_7 (Convolution2D)  (None, 4, 4, 1024)    9438208     convolution2d_6[0][0]            
____________________________________________________________________________________________________
convolution2d_8 (Convolution2D)  (None, 4, 4, 1024)    9438208     convolution2d_7[0][0]            
____________________________________________________________________________________________________
convolution2d_9 (Convolution2D)  (None, 4, 4, 1024)    9438208     convolution2d_8[0][0]            
____________________________________________________________________________________________________
convolution2d_10 (Convolution2D) (None, 4, 4, 1024)    9438208     convolution2d_9[0][0]            
____________________________________________________________________________________________________
convolution2d_11 (Convolution2D) (None, 4, 4, 1024)    9438208     convolution2d_10[0][0]           
____________________________________________________________________________________________________
convolution2d_12 (Convolution2D) (None, 4, 4, 1024)    9438208     convolution2d_11[0][0]           
____________________________________________________________________________________________________
convolution2d_13 (Convolution2D) (None, 4, 4, 1024)    9438208     convolution2d_12[0][0]           
____________________________________________________________________________________________________
convolution2d_14 (Convolution2D) (None, 4, 4, 1024)    9438208     convolution2d_13[0][0]           
____________________________________________________________________________________________________
convolution2d_15 (Convolution2D) (None, 4, 4, 1024)    9438208     convolution2d_14[0][0]           
____________________________________________________________________________________________________
convolution2d_16 (Convolution2D) (None, 4, 4, 1024)    9438208     convolution2d_15[0][0]           
____________________________________________________________________________________________________
convolution2d_17 (Convolution2D) (None, 4, 4, 1024)    9438208     convolution2d_16[0][0]           
____________________________________________________________________________________________________
convolution2d_18 (Convolution2D) (None, 4, 4, 1024)    9438208     convolution2d_17[0][0]           
____________________________________________________________________________________________________
convolution2d_19 (Convolution2D) (None, 4, 4, 1024)    9438208     convolution2d_18[0][0]           
____________________________________________________________________________________________________
convolution2d_20 (Convolution2D) (None, 4, 4, 1024)    9438208     convolution2d_19[0][0]           
____________________________________________________________________________________________________
convolution2d_21 (Convolution2D) (None, 4, 4, 1024)    9438208     convolution2d_20[0][0]           
____________________________________________________________________________________________________
convolution2d_22 (Convolution2D) (None, 4, 4, 1024)    9438208     convolution2d_21[0][0]           
____________________________________________________________________________________________________
convolution2d_23 (Convolution2D) (None, 4, 4, 1024)    9438208     convolution2d_22[0][0]           
____________________________________________________________________________________________________
convolution2d_24 (Convolution2D) (None, 4, 4, 1024)    9438208     convolution2d_23[0][0]           
____________________________________________________________________________________________________
maxpooling2d_3 (MaxPooling2D)    (None, 2, 2, 1024)    0           convolution2d_24[0][0]           
____________________________________________________________________________________________________
convolution2d_25 (Convolution2D) (None, 2, 2, 256)     2359552     maxpooling2d_3[0][0]             
____________________________________________________________________________________________________
convolution2d_26 (Convolution2D) (None, 2, 2, 32)      73760       convolution2d_25[0][0]           
____________________________________________________________________________________________________
maxpooling2d_4 (MaxPooling2D)    (None, 1, 1, 32)      0           convolution2d_26[0][0]           
____________________________________________________________________________________________________
flatten_1 (Flatten)              (None, 32)            0           maxpooling2d_4[0][0]             
____________________________________________________________________________________________________
dense_1 (Dense)                  (None, 4)             132         flatten_1[0][0]                  
____________________________________________________________________________________________________
dense_2 (Dense)                  (None, 1)             5           dense_1[0][0]                    
====================================================================================================
Total params: 206538153
____________________________________________________________________________________________________
None
Thu Oct  6 09:05:42 2016       
+------------------------------------------------------+                       
| NVIDIA-SMI 352.63     Driver Version: 352.63         |                       
|-------------------------------+----------------------+----------------------+
| GPU  Name        Persistence-M| Bus-Id        Disp.A | Volatile Uncorr. ECC |
| Fan  Temp  Perf  Pwr:Usage/Cap|         Memory-Usage | GPU-Util  Compute M. |
|===============================+======================+======================|
|   0  GeForce GTX 960     Off  | 0000:01:00.0      On |                  N/A |
| 30%   37C    P2    28W / 120W |   1082MiB /  2044MiB |      9%      Default |
+-------------------------------+----------------------+----------------------+

+-----------------------------------------------------------------------------+
| Processes:                                                       GPU Memory |
|  GPU       PID  Type  Process name                               Usage      |
|=============================================================================|
|    0      1796    G   /usr/bin/X                                     155MiB |
|    0      2597    G   compiz                                          65MiB |
|    0      5966    C   python                                         849MiB |
+-----------------------------------------------------------------------------+
Press Enter to continue...
Thu Oct  6 09:05:44 2016       
+------------------------------------------------------+                       
| NVIDIA-SMI 352.63     Driver Version: 352.63         |                       
|-------------------------------+----------------------+----------------------+
| GPU  Name        Persistence-M| Bus-Id        Disp.A | Volatile Uncorr. ECC |
| Fan  Temp  Perf  Pwr:Usage/Cap|         Memory-Usage | GPU-Util  Compute M. |
|===============================+======================+======================|
|   0  GeForce GTX 960     Off  | 0000:01:00.0      On |                  N/A |
| 30%   38C    P2    28W / 120W |   1082MiB /  2044MiB |      0%      Default |
+-------------------------------+----------------------+----------------------+

+-----------------------------------------------------------------------------+
| Processes:                                                       GPU Memory |
|  GPU       PID  Type  Process name                               Usage      |
|=============================================================================|
|    0      1796    G   /usr/bin/X                                     155MiB |
|    0      2597    G   compiz                                          65MiB |
|    0      5966    C   python                                         849MiB |
+-----------------------------------------------------------------------------+
Compiled model. Press Enter to continue...
Thu Oct  6 09:05:44 2016       
+------------------------------------------------------+                       
| NVIDIA-SMI 352.63     Driver Version: 352.63         |                       
|-------------------------------+----------------------+----------------------+
| GPU  Name        Persistence-M| Bus-Id        Disp.A | Volatile Uncorr. ECC |
| Fan  Temp  Perf  Pwr:Usage/Cap|         Memory-Usage | GPU-Util  Compute M. |
|===============================+======================+======================|
|   0  GeForce GTX 960     Off  | 0000:01:00.0      On |                  N/A |
| 30%   38C    P2    28W / 120W |   1082MiB /  2044MiB |      0%      Default |
+-------------------------------+----------------------+----------------------+

+-----------------------------------------------------------------------------+
| Processes:                                                       GPU Memory |
|  GPU       PID  Type  Process name                               Usage      |
|=============================================================================|
|    0      1796    G   /usr/bin/X                                     155MiB |
|    0      2597    G   compiz                                          65MiB |
|    0      5966    C   python                                         849MiB |
+-----------------------------------------------------------------------------+
About to train one iteration. Press Enter to continue...
Error allocating 37748736 bytes of device memory (out of memory). Driver report 34205696 bytes free and 2144010240 bytes total 
Traceback (most recent call last):
  File "memtest.py", line 65, in <module>
    model.train_on_batch(x, y)         
  File "/usr/local/lib/python2.7/dist-packages/keras/models.py", line 712, in train_on_batch
    class_weight=class_weight)
  File "/usr/local/lib/python2.7/dist-packages/keras/engine/training.py", line 1221, in train_on_batch
    outputs = self.train_function(ins)
  File "/usr/local/lib/python2.7/dist-packages/keras/backend/theano_backend.py", line 717, in __call__
    return self.function(*inputs)
  File "/usr/local/lib/python2.7/dist-packages/theano/compile/function_module.py", line 871, in __call__
    storage_map=getattr(self.fn, 'storage_map', None))
  File "/usr/local/lib/python2.7/dist-packages/theano/gof/link.py", line 314, in raise_with_op
    reraise(exc_type, exc_value, exc_trace)
  File "/usr/local/lib/python2.7/dist-packages/theano/compile/function_module.py", line 859, in __call__
    outputs = self.fn()
MemoryError: Error allocating 37748736 bytes of device memory (out of memory).
Apply node that caused the error: GpuContiguous(GpuDimShuffle{3,2,0,1}.0)
Toposort index: 338
Inputs types: [CudaNdarrayType(float32, 4D)]
Inputs shapes: [(1024, 1024, 3, 3)]
Inputs strides: [(1, 1024, 3145728, 1048576)]
Inputs values: ['not shown']
Outputs clients: [[GpuDnnConv{algo='small', inplace=True}(GpuContiguous.0, GpuContiguous.0, GpuAllocEmpty.0, GpuDnnConvDesc{border_mode='half', subsample=(1, 1), conv_mode='conv', precision='float32'}.0, Constant{1.0}, Constant{0.0}), GpuDnnConvGradI{algo='none', inplace=True}(GpuContiguous.0, GpuContiguous.0, GpuAllocEmpty.0, GpuDnnConvDesc{border_mode='half', subsample=(1, 1), conv_mode='conv', precision='float32'}.0, Constant{1.0}, Constant{0.0})]]

HINT: Re-running with most Theano optimization disabled could give you a back-trace of when this node was created. This can be done with by setting the Theano flag 'optimizer=fast_compile'. If that does not work, Theano optimizations can be disabled with 'optimizer=None'.
HINT: Use the Theano flag 'exception_verbosity=high' for a debugprint and storage map footprint of this apply node.

注意すべきいくつかの点:

  • TheanoとTensorFlowの両方のバックエンドを試しました。どちらにも同じ問題があり、同じ行でメモリが不足します。 TensorFlowでは、Kerasは大量のメモリ(約1.5 GB)を事前に割り当てているため、nvidia-smiはそこで起こっていることを追跡するのに役立ちませんが、同じメモリ不足の例外が発生します。繰り返しになりますが、これはKeras(私の使用法)のエラーを示しています(そのようなことを確認するのは難しいのですが、私の設定に問題がある可能性があります)。
  • TanoFlowのように動作するTheanoでCNMEMを使用してみました:大量のメモリ(約1.5 GB)を事前に割り当てますが、同じ場所でクラッシュします。
  • CudNNバージョンについていくつかの警告があります。 TheanoバックエンドをCUDAで実行しようとしましたが、CudNNでは実行しませんでしたが、同じエラーが発生したため、問題の原因ではありません。
  • 独自のGPUでこれをテストする場合は、テストするGPUメモリの量に応じて、ネットワークをより深く/より浅くすることができます。
  • 私の構成は次のとおりです:Ubuntu 14.04、GeForce GTX 960、CUDA 7.5.18、CudNN 5.1.3、Python 2.7、Keras 1.1.0(pip経由でインストール)
  • 異なるオプティマイザーと損失を使用するようにモデルのコンパイルを変更しようとしましたが、それは何も変わらないようです。
  • 代わりにfitを使用するようにtrain_on_batch関数を変更しようとしましたが、同じ問題があります。
  • StackOverflowで同様の質問が1つありました- なぜこのKerasモデルは6GBを超えるメモリを必要とするのですか? -しかし、私が知る限り、設定にこれらの問題はありません。 CUDAの複数のバージョンがインストールされたことは一度もありません。また、PATH、LD_LIBRARY_PATH、CUDA_ROOT変数を数え切れないほど繰り返しチェックしました。
  • Juliusは、アクティベーションパラメータ自体がGPUメモリを占有することを提案しました。これが本当なら、誰かがそれをもう少し明確に説明できますか?私の知る限り、畳み込み層の活性化関数を、学習可能なパラメーターなしで明確にハードコードされた関数に変更しようとしましたが、それは何も変更しません。また、これらのパラメータがネットワーク自体の他の部分とほぼ同じ量のメモリを占有する可能性は低いようです。
  • 徹底的なテストの後、トレーニングできる最大のネットワークは、約2 GBのGPU RAMのうち、約453 MBのパラメーターです。これは正常ですか?
  • GPUに適合する小さなCNNでKerasをテストした後、GPU RAM使用率。約100 MBのパラメーターでネットワークを実行している場合、99トレーニング中の時間の割合は200 MB未満のGPU RAMを使用しますが、時々、メモリ使用量が約1.3 GBに急増します。これらの急増が問題の原因であると想定するのは安全なようです。他のフレームワークでこれらのスパイクを見たことはありませんが、正当な理由でそこにある可能性がありますか?誰かがそれらの原因を知っていて、それらを避ける方法があれば、チャイムしてください!
35
Martin

アクティベーションとグラディエントもパラメータだけでなくvramを使用し、メモリ使用量がかなり増加することを忘れるのは非常によくある間違いです。バックプローブの計算自体により、トレーニングフェーズでは、ニューラルネットのフォワード/推論使用のVRAMのほぼ2倍の時間がかかります。

したがって、ネットワークが作成される最初の段階では、パラメータのみが割り当てられます。ただし、トレーニングが開始されると、バックプロップ計算と同様にアクティベーション(各ミニバッチの回数)が割り当てられ、メモリ使用量が増加します。

18
Jules G.M.

TheanoとTensorflowは両方とも、作成されるシンボリックグラフを拡張しますが、両方とも異なります。

メモリ消費がどのように起こっているかを分析するには、小さなモデルから始めて、それを成長させて、対応するメモリの成長を確認します。同様に、_batch_size_を大きくして、対応するメモリの増加を確認できます。

以下は、初期コードに基づいて_batch_size_を増やすためのコードスニペットです。

_from scipy import misc
import numpy as np
from keras.models import Sequential
from keras.layers import Dense, Activation, Convolution2D, MaxPooling2D, Reshape, Flatten, ZeroPadding2D, Dropout
import os
import matplotlib.pyplot as plt


def gpu_memory():
    out = os.popen("nvidia-smi").read()
    ret = '0MiB'
    for item in out.split("\n"):
        if str(os.getpid()) in item and 'python' in item:
            ret = item.strip().split(' ')[-2]
    return float(ret[:-3])

gpu_mem = []
gpu_mem.append(gpu_memory())

model = Sequential()
model.add(Convolution2D(100, 3, 3, border_mode='same', input_shape=(16,16,1)))
model.add(Convolution2D(256, 3, 3, border_mode='same'))
model.add(Convolution2D(32, 3, 3, border_mode='same'))
model.add(MaxPooling2D(pool_size=(2,2)))
model.add(Flatten())
model.add(Dense(4))
model.add(Dense(1))

model.summary()
gpu_mem.append(gpu_memory())

model.compile(optimizer='sgd',
              loss='mse', 
              metrics=['accuracy'])
gpu_mem.append(gpu_memory())


batches = []
n_batches = 20
batch_size = 1
for ibatch in range(n_batches):
    batch_size = (ibatch+1)*10
    batches.append(batch_size)
    x = np.random.Rand(batch_size, 16,16,1)
    y = np.random.Rand(batch_size, 1)
    print y.shape

    model.train_on_batch(x, y)         
    print("Trained one iteration")

    gpu_mem.append(gpu_memory())

fig = plt.figure()
plt.plot([-100, -50, 0]+batches, gpu_mem)
plt.show()
_

また、速度のために、Tensorflowは利用可能なすべてのGPUメモリを占有します。それを止めるには、get_session()に_config.gpu_options.allow_growth = True_を追加する必要があります

_# keras/backend/tensorflow_backend.py
def get_session():
    global _SESSION
    if tf.get_default_session() is not None:
        session = tf.get_default_session()
    else:
        if _SESSION is None:
            if not os.environ.get('OMP_NUM_THREADS'):
                config = tf.ConfigProto(allow_soft_placement=True,
                    )
            else:
                nb_thread = int(os.environ.get('OMP_NUM_THREADS'))
                config = tf.ConfigProto(intra_op_parallelism_threads=nb_thread,
                                        allow_soft_placement=True)
            config.gpu_options.allow_growth = True
            _SESSION = tf.Session(config=config)
        session = _SESSION
    if not _MANUAL_VAR_INIT:
        _initialize_variables()
    return session
_

前のスニペットを実行すると、次のようなプロットが得られます:

テアノ: Theano テンソルフロー: Tenserflow

Theano:model.compile()後、必要なメモリが何であれ、トレーニングの開始時に、それはほぼ2倍になります。これは、Theanoが逆伝播を行うためにシンボリックグラフを増強し、各テンソルが勾配の逆流を実現するために対応するテンソルを必要とするためです。メモリニーズは_batch_size_で増加するようには見えません。CPU-> GPUからのデータインフローに対応するためにプレースホルダーのサイズが増加するため、これは私には予想外です。

Tensorflow:Kerasはmodel.compile()を実際に呼び出すまでget_session()を呼び出さないため、_initialize_variables()の後でもGPUメモリは割り当てられません。 Tensorflowは、速度のためにメモリを大量に占有するように見えるため、_batch_size_でメモリが線形に増加することはありません。

Tensorflowはメモリを大量に消費するように見えますが、大きなグラフの場合は非常に高速です。一方、Theanoはgpuメモリ効率が非常に高くなりますが、トレーニングの開始時にグラフを初期化するにはかなりの時間がかかります。その後も非常に高速です。

13
indraforyou

2 Gb GPUの200Mパラメーターは大きすぎます。また、アーキテクチャが効率的ではないため、ローカルボトルネックを使用する方が効率的です。また、小さなモデルから大きなモデルに移行する必要があります。逆ではなく、入力16x16があります。このアーキテクチャでは、最終的にネットワークのほとんどが「ゼロパディング」され、入力機能に基づいていません。モデルレイヤーは入力に依存するため、任意の数のレイヤーとサイズを設定することはできません。各レイヤーに渡されるデータの量をカウントする必要があります。その理由を理解してください。この無料コースをご覧になることをお勧めします http://cs231n.github.io

0