Tensorflow2.0:GPU 在超参数调优循环期间内存不足



我正在尝试对用 GPU 扩展的 Tensorflow 2.0 编写的卷积神经网络执行一些超参数调整。

我的系统设置是:

  • 视窗 10 64 位
  • GeForce RTX2070, 8GB
  • 张量流 2.0-beta
  • CUDA 10.0 正确安装(我希望,设备查询.exe和带宽测试.exe通过积极)

我的神经网络有 75.572.574 个参数,我正在 3777 个样本上训练它。在一次运行中,我在训练 CNN 方面没有问题。

下一步,我想调整CNN的两个超参数。为此,我创建了一个 for 循环(迭代 20 个步骤),每次构建和编译新模型时,我都会在其中构建和编译,并在每次循环迭代时更改超参数。 代码的要点(这不是MWE)如下

import tensorflow as tf
from tensorflow import keras
def build_model(input_shape, output_shape, lr=0.01, dropout=0, summary=True):
model = keras.models.Sequential(name="CNN")
model.add(keras.layers.Conv2D(32, (7, 7), activation='relu', input_shape=input_shape, padding="same"))
model.add(keras.layers.BatchNormalization())
model.add(keras.layers.MaxPooling2D((2, 2)))
model.add(keras.layers.Dropout(dropout))
model.add(keras.layers.Conv2D(128, (3, 3), activation='relu', padding="same"))
model.add(keras.layers.BatchNormalization())
model.add(keras.layers.MaxPooling2D((2, 2)))
model.add(keras.layers.Dropout(dropout))
model.add(keras.layers.Flatten())
model.add(keras.layers.Dense(1024, activation='relu'))
model.add(keras.layers.BatchNormalization())
model.add(keras.layers.Dense(output_shape, activation='linear'))
model.build()
model.compile(optimizer=keras.optimizers.Adam(learning_rate=lr),
loss="mse",
metrics=[RMSE])
if summary:
print(model.summary())
return model
...
for run_id in range(25):
lr = learning_rate.max_value + (learning_rate.min_value - learning_rate.max_value) * np.random.rand(1)
dropout = dropout.min_value + (dropout.max_value -
dropout.min_value) * np.random.rand(1)
print("%=== Run #{0}".format(run_id))
run_dir = hparamdir + "\run{0}".format(run_id)
model0 = build_model(IMG_SHAPE, Ytrain.shape[1], lr=lr, dropout=dropout)
model0_history = model0.fit(Xtrain,
Ytrain,
validation_split=0.3,
epochs=2,
verbose=2)

我遇到的问题是,在几 (6) 次循环后,程序停止返回错误

tensorflow.python.framework.errors_impl.ResourceExhaustedError: OOM when allocating tensor with shape[73728,1024] and type float on /job:localhost/replica:0/task:0/device:GPU:0 by allocator GPU_0_bfc [Op:Add] name: dense_12/kernel/Initializer/random_uniform/
Process finished with exit code 1.

我认为问题是 GPU 在 for 循环的每次迭代之间不会释放内存,一段时间后它会饱和并崩溃。

我已经挖掘并尝试了类似帖子中建议的不同解决方案(post1,post2)

  1. 尝试在 for 循环的每次迭代结束时使用 Keras 后端释放内存
from keras import backend as K
K.clear_session()
  1. 尝试使用 Numba 和 CUDA 清除 GPU
from numba import cuda
cuda.select_device(0)
cuda.close()
  1. 我尝试使用del model0删除图表,但这也不起作用。

  2. 我无法尝试使用tf.reset_default_graph(),因为 TF2.0 的编程风格不再有默认图形 (AFAIK),因此我没有找到在运行时杀死/删除图形的方法。

解决方案1. 和 3. 返回相同的内存不足错误,而解决方案 2. 在 for 循环的第二次迭代期间返回以下错误,同时在build_model()调用中构建模型:

2019-07-24 19:51:31.909377: F .tensorflow/core/kernels/random_op_gpu.h:227] Non-OK-status: GpuLaunchKernel(FillPhiloxRandomKernelLaunch<Distribution>, num_blocks, block_size, 0, d.stream(), gen, data, size, dist) status: Internal: invalid resource handle
Process finished with exit code -1073740791 (0xC0000409)

我试图环顾四周,但我真的不明白最后一个错误,我猜 GPU 没有正确关闭/被占用/无法再被 Python 看到。

无论如何,我找不到这个问题的任何解决方案,除了为每个要测试的超参数手动运行训练。

有人知道如何解决这个问题吗? 还是超参数优化的解决方法? 我是否应该在TF2.0 Github问题跟踪器中打开一个问题(这本身似乎不是TensorFlow问题,因为他们声明他们不想释放GPU以避免分段问题)?

这是由于TF处理内存的方式。

如果在迭代训练 TF 模型时监视系统,则会观察到内存消耗呈线性增加。此外,如果您watch -n 0.1 nvidia-smi,您会注意到该过程的 PID 在迭代时保持不变。TF 不会完全释放已用内存,直到控制内存的 PID 被杀死。此外,Numba 文档指出,如果您想重置 GPU,cuda.close()没有用(尽管当我发现它时,我确实花了一段时间试图让它工作!

最简单的解决方案是使用 Ray python 包和如下所示的内容进行迭代:

import ray
@ray.remote(
num_gpus=1 # or however many you want to use (e.g., 0.5, 1, 2)
)
class RayNetWrapper:
def __init__(self, net):
self.net = net
def train(self):
return self.net.train()
ray.init()
actors = [RayNetWrapper.remote(model) for _ in range(25)]
results = ray.get([actor.train.remote() for actor in actors]

然后,您应该注意到 GPU 进程每次都会使用新的 PID 循环打开/关闭,并且您的系统内存将不再增加。或者,您可以将模型训练代码放入新的 python 脚本中,并使用 python 的subprocess模块迭代调用。现在,当模型关闭和新模型启动时,您还会注意到一些延迟,但这是意料之中的,因为 GPU 正在重新启动。 Ray 还有一个实验性的异步框架,我已经取得了一些成功,并且支持 GPU 的部分共享(模型大小允许)。

您可以在代码顶部找到这两行。

from tensorflow.python.framework.config import set_memory_growth
tf.compat.v1.disable_v2_behavior()
gpus = tf.config.experimental.list_physical_devices('GPU')
if gpus:
try:
for gpu in gpus:
tf.config.experimental.set_memory_growth(gpu, True)
except RuntimeError as e:
print(e)

这对我有用。

最新更新