我正在开发一个多类分类模型,它有超过4000个类别,这意味着需要4000个文件夹来存放每个类别的图像,这些文件夹占用了大约30GB的空间。为了训练分类模型,我需要将图像复制粘贴到训练和验证文件夹中,每个类别都需要一个分类文件夹结构,这将占用另外30GB的空间,并且需要大量的时间来读取数据。我使用Keras的ImageDataGenerator API来加载数据并将其输入到模型中进行训练,如下所示:
train_generator = train_datagen.flow_from_directory(Training_DIR, batch_size= batch_size, class_mode='categorical',target_size=(img_height,img_width)
validation_generator = validation_datagen.flow_from_directory(VALIDATION_DIR, batch_size= batch_size, class_mode='categorical',target_size=(img_height,img_width)
然后将这些生成器传递给model.fit_generator函数,如下所示:
model.fit_generator(train,generator,validation_data=validation_generator)
有没有更好的方法可以高速直接从包含类别子文件夹的主文件夹中加载数据,而不需要创建新目录并将图像复制到其中,这将占用双倍的磁盘空间。我以前没有处理过这种大规模数据集,它占用了我所有驱动器的空间。
更新:我尝试了@<隐藏人名>提供的解决方案,但最终得到了如下错误:
第1/50个周期
2021-09-06 17:22:12.576079: Itensorflow/stream_executor/cuda/cuda_dnn.cc:369] 已加载cuDNN版本8101
2021-09-06 17:22:13.251294: Wtensorflow/core/common_runtime/bfc_allocator.cc:272] 分配器(GPU_0_bfc)尝试分配1.19GiB时内存不足,freed_by_count=0。调用者表示这不是失败,但可能意味着如果有更多内存可用,性能可能会有所提高。
2021-09-06 17:22:16.096112: Etensorflow/stream_executor/cuda/cuda_driver.cc:1010] 无法同步停止事件:CUDA_ERROR_LAUNCH_TIMEOUT:启动超时并被终止
2021-09-06 17:22:16.096299: Etensorflow/stream_executor/gpu/gpu_timer.cc:55] 内部错误:销毁CUDA事件时出错:CUDA_ERROR_LAUNCH_TIMEOUT:启动超时并被终止
2021-09-06 17:22:16.097126: Etensorflow/stream_executor/gpu/gpu_timer.cc:60] 内部错误:销毁CUDA事件时出错:CUDA_ERROR_LAUNCH_TIMEOUT:启动超时并被终止
2021-09-06 17:22:16.097682: Itensorflow/stream_executor/cuda/cuda_driver.cc:732] 无法从设备分配8B(8字节):CUDA_ERROR_LAUNCH_TIMEOUT:启动超时并被终止
2021-09-06 17:22:16.097935: Etensorflow/stream_executor/stream.cc:4508] 内部错误:无法排队异步memset操作:CUDA_ERROR_LAUNCH_TIMEOUT:启动超时并被终止
2021-09-06 17:22:16.098312: W tensorflow/core/kernels/gpu_utils.cc:69] 无法检查cudnn卷积的越界读取和写入,错误信息为:’无法加载内存中的CUBIN:CUDA_ERROR_LAUNCH_TIMEOUT:启动超时并被终止’;跳过此检查。这仅意味着我们不会检查cudnn的越界读取和写入。此消息仅打印一次。
2021-09-06 17:22:16.098676: Itensorflow/stream_executor/cuda/cuda_driver.cc:732] 无法从设备分配8B(8字节):CUDA_ERROR_LAUNCH_TIMEOUT:启动超时并被终止
2021-09-06 17:22:16.099006: Etensorflow/stream_executor/stream.cc:4508] 内部错误:无法排队异步memset操作:CUDA_ERROR_LAUNCH_TIMEOUT:启动超时并被终止
2021-09-06 17:22:16.099369: Ftensorflow/stream_executor/cuda/cuda_dnn.cc:216] 检查失败:status == CUDNN_STATUS_SUCCESS (7 vs. 0)无法设置cuDNN流。
回答:
尽管@隐藏人名的回答在我看来是正确的,并且回答了原问题所问的内容。这里还有另一个答案,受到评论中讨论的启发,试图在使用.flow_from_directory()
或.flow_from_dataframe()
时,通过减少训练过程中不必要的I/O操作瓶颈来提高效率。
免责声明:此解决方案仅在所有图像形状相同的情况下有效。
我建议使用ImageDataGenerator
的.flow()
方法与numpy.memmap
结合使用。你可以为每个数据子集(即train
、validation
和test
集)创建一个memmap。我创建了一个Google Colab笔记本,在其中我使用MNIST数据集比较了这些方法。以下是该笔记本中最重要的代码:
# 在驱动器上启动一个具有指定形状和数据类型的memmap
mmap = np.memmap('mnist.mmap', dtype='uint8', mode='w+', shape=(x_test.shape))
# 用数据填充memmap
# 如果硬盘空间是一个问题,我们可以在途中删除源图像文件
for i, fpath in enumerate(fpaths):
mmap[i][:] = np.expand_dims(imread(fpath)[:], -1)
# 如果需要,删除文件
# os.remove(fpath)
# 从磁盘加载memmap(不会将所有数据加载到RAM中),必须指定形状
mmap = np.memmap('mnist.mmap', dtype='uint8', mode='r', shape=(x_test.shape))
DataGen = ImageDataGenerator(rescale=1./255)
gen = DataGen.flow(mmap, y=None, batch_size=batch_size, shuffle=True, seed=10)
下面是测量从MNIST测试集(10k灰度图像28×28)生成2个周期所需时间的结果。
方法 | 来源 | 批次大小 | 时间* |
---|---|---|---|
.flow() |
np.array | 1 | 584 ms |
8 | 293 ms | ||
32 | 285 ms | ||
256 | 280 ms | ||
.flow() |
memmap | 1 | 574 ms |
8 | 296 ms | ||
32 | 278 ms | ||
256 | 274 ms | ||
flow_from_directory() |
文件 | 1 | 3.96 s |
8 | 3.50 s | ||
32 | 3.39 s | ||
256 | 3.41 s |
- 生成2个周期(即整个测试集的2倍)所需的时间