如何使用tf.data.Dataset实现内存高效加载,解决实例分割内存占用过高问题
实例分割tf.data.Dataset内存溢出优化方案
问题原因
核心原因是tf.data并行map和预取机制默认会缓存大量已处理的样本,实例分割的二值掩码单样本内存开销远大于普通图像分类任务,多批次缓存后会快速占满主内存。
你预想的按批次处理、用完即清理的思路是完全可行的,以下是对应的落地优化方案:
可直接落地的优化方案
限制map节点的缓冲大小
每个map操作默认会预加载足够多的样本保证并行效率,但无限制的缓冲会导致大量已生成的掩码滞留在内存中,给所有map操作新增max_buffer_size参数,限制单节点缓冲样本数为2~3倍批次大小即可。压缩掩码存储格式
二值分割掩码无需用float32格式存储,直接存为uint8格式,内存占用直接降为原来的1/4;如果单图实例数较多,可改用稀疏张量存储掩码,仅保存非零区域坐标,训练前再转为稠密张量。调整预取策略
无需使用AUTOTUNE作为预取大小,仅预取1~2个待训练批次即可掩盖IO延迟,避免预取十几个甚至几十个批次占满内存。合并处理逻辑减少中间变量
检查prepare_for_batch函数中是否存在未释放的无用中间张量,比如生成全分辨率掩码后,resize完成可手动删除原全分辨率掩码;如果有可批量执行的操作,尽量后移到batch之后用向量化逻辑实现,降低单样本处理的内存开销。
优化后代码示例
train_dataset, train_examples = dataset.load_train_datasets() # 单map节点最大缓冲样本数,设置为2倍批次大小足够保证并行效率 MAP_BUFFER_SIZE = 2 * args.batch_size ds = ( train_dataset.shuffle(min(100, train_examples), reshuffle_each_iteration=True) .map( dataset.decode, num_parallel_calls=args.num_parallel_calls, max_buffer_size=MAP_BUFFER_SIZE ) .map( # 调整prepare_for_batch输出掩码为uint8格式,内部自动清理无用中间变量 lambda x: train_processing.prepare_for_batch(x, mask_dtype=tf.uint8), num_parallel_calls=args.num_parallel_calls, max_buffer_size=MAP_BUFFER_SIZE ) .batch(args.batch_size) .map( train_processing.preprocess_batch, # 批量处理不需要太高并行度,避免缓冲过多批次 num_parallel_calls=min(2, args.num_parallel_calls), max_buffer_size=2 ) # 仅预取1个待训练批次 .prefetch(1) )
内容的提问来源于stack exchange,提问作者Cospel
相关产品推荐
相关产品推荐

