假设我可以在一台机器上访问多个GPU(为了便于讨论,假设在一台机器上有一定数量的RAM和磁盘,每个最大内存为8GB的8GPU)。我想在一个脚本中运行,在一台机器上运行一个程序,在TensorFlow中评估多个模型(比如50或200),每个模型都有不同的超参数设置(比如步长、衰减率、批处理大小、时间段/迭代次数等)。在训练结束时,假设我们只是记录它的准确性并去掉模型(如果你想假设模型经常被检查,那么扔掉模型并从头开始训练就可以了。您还可以假设其他一些数据可能会被记录,例如特定的超参数、训练、验证、训练错误会在我们训练时记录等)。
目前我有一个(伪)脚本,如下所示:
def train_multiple_modles_in_one_script_with_gpu(arg):
'''
trains multiple NN models in one session using GPUs correctly.
arg = some obj/struct with the params for trianing each of the models.
'''
#### try mutliple models
for mdl_id in range(100):
#### define/create graph
graph = tf.Graph()
with graph.as_default():
### get mdl
x = tf.placeholder(float_type, get_x_shape(arg), name='x-input')
y_ = tf.placeholder(float_type, get_y_shape(arg))
y = get_mdl(arg,x)
### get loss and accuracy
loss, accuracy = get_accuracy_loss(arg,x,y,y_)
### get optimizer variables
opt = get_optimizer(arg)
train_step = opt.minimize(loss, global_step=global_step)
#### run session
with tf.Session(graph=graph) as sess:
# train
for i in range(nb_iterations):
batch_xs, batch_ys = get_batch_feed(X_train, Y_train, batch_size)
sess.run(fetches=train_step, feed_dict={x: batch_xs, y_: batch_ys})
# check_point mdl
if i % report_error_freq == 0:
sess.run(step.assign(i))
#
train_error = sess.run(fetches=loss, feed_dict={x: X_train, y_: Y_train})
test_error = sess.run(fetches=loss, feed_dict={x: X_test, y_: Y_test})
print( 'step %d, train error: %s test_error %s'%(i,train_error,test_error) )
本质上,它在一次运行中尝试了很多模型,但它在一个单独的图中构建每个模型,并在一个单独的会话中运行每个模型。
我想我主要担心的是,我不清楚tensorflow在hood下是如何分配资源给gpu使用的。例如,它是否只在运行会话时加载(部分)数据集?当我创建一个图和一个模型时,它是立即引入GPU还是何时插入GPU?每次GPU尝试新型号时,我需要清除/释放它吗?实际上,我并不太在意这些模型是否在多个GPU中并行运行(这可能是一个很好的附加功能),但我希望它首先以串行方式运行所有东西,而不会崩溃。我有什么特别的事要做吗?
目前,我收到一个错误,开始如下:
I tensorflow/core/common_runtime/bfc_allocator.cc:702] Stats:
Limit: 340000768
InUse: 336114944
MaxInUse: 339954944
NumAllocs: 78
MaxAllocSize: 335665152
W tensorflow/core/common_runtime/bfc_allocator.cc:274] ***************************************************xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
W tensorflow/core/common_runtime/bfc_allocator.cc:275] Ran out of memory trying to allocate 160.22MiB. See logs for memory state.
W tensorflow/core/framework/op_kernel.cc:975] Resource exhausted: OOM when allocating tensor with shape[60000,700]
再往下说就是:
ResourceExhaustedError (see above for traceback): OOM when allocating tensor with shape[60000,700]
[[Node: standardNN/NNLayer1/Z1/add = Add[T=DT_FLOAT, _device="/job:localhost/replica:0/task:0/gpu:0"](standardNN/NNLayer1/Z1/MatMul, b1/read)]]
I tensorflow/core/common_runtime/gpu/gpu_device.cc:975] Creating TensorFlow device (/gpu:0) -> (device: 0, name: Tesla P100-SXM2-16GB, pci bus id: 0000:06:00.0)
不管输出文件在哪里打印,它似乎可以打印出应该在训练过程中显示的错误/消息。这是否意味着它没有耗尽资源?或者它真的能使用GPU吗?如果它能够使用CPU而不是CPU,那么为什么只有在即将使用GPU时才会发生这种错误?
奇怪的是,数据集并没有那么大(所有的60K点都是2450万),当我在自己的计算机上本地运行一个模型时,这个过程使用的数据量似乎不到5GB。GPU至少有8GB,计算机有足够的RAM和磁盘(至少16GB)。因此,tensorflow给我的错误是相当令人费解的。它想做什么?为什么会发生?有什么想法吗?
在阅读了建议使用多处理库的答案后,我想出了以下脚本:
def train_mdl(args):
train(mdl,args)
if __name__ == '__main__':
for mdl_id in range(100):
# train one model with some specific hyperparms (assume they are chosen randomly inside the funciton bellow or read from a config file or they could just be passed or something)
p = Process(target=train_mdl, args=(args,))
p.start()
p.join()
print('Done training all models!')
老实说,我不知道为什么他的答案建议使用pool,或者为什么会有奇怪的元组括号,但这对我来说是有意义的。每次在上述循环中创建新进程时,是否会重新分配tensorflow的资源?
据我所知,首先,tensorflow构造了一个符号图,并根据链式规则推导出导数。然后为所有(必要的)张量分配内存,包括一些层的输入和输出以提高效率。运行会话时,数据将加载到图表中,但通常情况下,内存使用不会再更改。
我想,您遇到的错误可能是在一个GPU中构造多个模型造成的。
根据@user2476373的建议,将训练/评估代码与超参数隔离是一个不错的选择。但是我直接使用bash脚本,而不是任务假脱机程序(可能更方便),例如
或者可以在bash脚本中编写“for”循环,而不一定是在python脚本中。注意到我在脚本的开头使用了
CUDA_VISIBLE_DEVICES=0
(如果一台计算机中有8个gpu,那么索引可以是7)。因为根据我的经验,我发现tensorflow在一台机器上使用所有的GPU,如果我没有指定操作,那么在这样的代码中使用哪个GPU如果您想尝试多GPU实现,有一些example。
希望这对你有帮助。
你可能不想这么做。
如果你在你的数据上运行成千上万个模型,并选择一个评价最好的模型,你就不是在进行机器学习;相反,你是在记忆你的数据集,并且不能保证你选择的模型会在数据集之外执行。
换言之,这种方法类似于拥有一个拥有数千自由度的单一模型。拥有一个如此复杂的模型是有问题的,因为它能够比实际保证的更好地拟合您的数据;这样的模型能够令人恼火地记住您的训练数据中的任何噪声(离群值、测量误差等),这导致模型在噪声甚至稍有不同的情况下表现不佳。
(很抱歉将此作为回复发布,网站不允许我添加评论。)
我认为从长远来看,在一个脚本中运行所有模型可能是不好的做法(请参阅下面的建议以获得更好的替代方案)。但是,如果您想这样做,这里有一个解决方案:您可以使用
multiprocessing
模块将TF会话封装到进程中,这将确保TF在进程完成后释放会话内存。下面是一段代码:来自OP的注意:如果您选择使用随机数生成器种子,则它不会随多处理库一起自动重置。详情如下:Using python multiprocessing with different random seed for each process
关于TF资源分配:通常TF分配的资源比它需要的多得多。很多时候,您可以限制每个进程使用总GPU内存的一小部分,并通过尝试和错误发现脚本所需的一小部分。
你可以用下面的代码片段
请注意,有时TF会增加内存使用量以加快执行速度。因此,减少内存使用可能会使模型运行速度变慢。
编辑/评论中新问题的答案:
是的,Tensorflow将在每次创建新进程时重新分配,并在进程结束时清除。
编辑中的for循环也应该完成这项工作。我建议改用Pool,因为它可以让您在一个GPU上同时运行多个模型。请参阅我关于设置
gpu_memory_fraction
和“选择最大进程数”的说明。还要注意:(1)池映射为您运行循环,因此一旦使用它,就不需要外部for循环。(2) 在您的示例中,在调用train()之前应该有类似mdl=get_model(args)
的内容奇怪的元组括号:池只接受一个参数,因此我们使用元组传递多个参数。有关详细信息,请参见multiprocessing.pool.map and function with two arguments。正如在一个答案中所建议的,您可以使用
正如@Seven所建议的,您可以使用CUDA U VISIBLE U DEVICES环境变量来选择要用于进程的GPU。您可以在您的python脚本中使用以下流程函数的开头(
train_mdl
)来完成它。执行实验的更好做法是将训练/评估代码与超参数/模型搜索代码分离。 E、 g.有一个名为
train.py
的脚本,它接受超参数和对数据的引用作为参数的特定组合,并对单个模型执行训练。然后,要遍历所有可能的参数组合,可以使用一个简单的任务(作业)队列,并将所有可能的超参数组合提交为单独的作业。任务队列将一次为您的计算机提供一个作业。通常,您还可以将队列设置为同时执行进程数(请参阅下面的详细信息)。
具体来说,我使用task spooler,这是非常容易安装和少量(不需要管理特权,详细信息如下)。
基本用法是(请参阅下面关于任务后台处理程序用法的说明):
实际上,我有一个单独的python脚本来管理我的实验,为每个特定的实验设置所有参数,并将作业发送到
ts
队列。以下是来自我的实验管理器的一些相关python代码片段:
run_bash
执行bash命令下一个代码段设置要运行的并发进程数(请参见下面有关选择最大进程数的说明):
下一个代码片段将遍历超参数/模型参数的所有组合的列表。列表的每个元素都是一个字典,其中的键是
train.py
脚本的命令行参数关于选择最大进程数的说明:
如果你缺少GPU,你可以使用找到的
gpu_memory_fraction
,将进程数设置为max_job_num_per_gpu=int(1/gpu_memory_fraction)
关于任务假脱机程序的说明(
ts
):您可以使用以下命令设置要运行的并发进程数(“slot”):
ts -S <number-of-slots>
安装
ts
不需要管理员权限。您可以用一个简单的make
从源代码下载并编译它,将其添加到您的路径中,就完成了。您可以设置多个队列(我将其用于多个GPU),使用
TS_SOCKET=<path_to_queue_name> ts <your-command>
例如
TS_SOCKET=/tmp/socket-ts.gpu_queue_1 ts <your-command>
TS_SOCKET=/tmp/socket-ts.gpu_queue_2 ts <your-command>
有关进一步的用法示例,请参见here
关于自动设置路径名和文件名的说明: 一旦您将主代码与实验管理器分离,您将需要一种有效的方法来生成文件名和目录名,给定超参数。我通常将重要的超参数保存在字典中,并使用以下函数从字典键值对生成单链字符串。 以下是我使用的函数:
相关问题 更多 >
编程相关推荐