【问题标题】:HDF5 adding numpy arrays slowHDF5添加numpy数组很慢
【发布时间】:2017-06-05 22:26:07
【问题描述】:

第一次使用 hdf5,你能帮我找出问题所在,为什么添加 3d numpy 数组很慢。 预处理需要 3s,添加 3d numpy 数组 (100x512x512) 30s 并随着每个样本上升

首先我使用以下命令创建 hdf:

def create_h5(fname_):
  """
  Run only once
  to create h5 file for dicom images
  """
  f = h5py.File(fname_, 'w', libver='latest') 

  dtype_ = h5py.special_dtype(vlen=bytes)


  num_samples_train = 1397
  num_samples_test = 1595 - 1397
  num_slices = 100

  f.create_dataset('X_train', (num_samples_train, num_slices, 512, 512), 
    dtype=np.int16, maxshape=(None, None, 512, 512), 
    chunks=True, compression="gzip", compression_opts=4)
  f.create_dataset('y_train', (num_samples_train,), dtype=np.int16, 
    maxshape=(None, ), chunks=True, compression="gzip", compression_opts=4)
  f.create_dataset('i_train', (num_samples_train,), dtype=dtype_, 
    maxshape=(None, ), chunks=True, compression="gzip", compression_opts=4)          
  f.create_dataset('X_test', (num_samples_test, num_slices, 512, 512), 
    dtype=np.int16, maxshape=(None, None, 512, 512), chunks=True, 
    compression="gzip", compression_opts=4)
  f.create_dataset('y_test', (num_samples_test,), dtype=np.int16, maxshape=(None, ), chunks=True, 
    compression="gzip", compression_opts=4)
  f.create_dataset('i_test', (num_samples_test,), dtype=dtype_, 
    maxshape=(None, ), 
    chunks=True, compression="gzip", compression_opts=4)

  f.flush()
  f.close()
  print('HDF5 file created')

然后我运行代码更新 hdf 文件:

num_samples_train = 1397
num_samples_test = 1595 - 1397

lbl = pd.read_csv(lbl_fldr + 'stage1_labels.csv')

patients = os.listdir(dicom_fldr)
patients.sort()

f = h5py.File(h5_fname, 'a') #r+ tried

train_counter = -1
test_counter = -1

for sample in range(0, len(patients)):    

    sw_start = time.time()

    pat_id = patients[sample]
    print('id: %s sample: %d \t train_counter: %d test_counter: %d' %(pat_id, sample, train_counter+1, test_counter+1), flush=True)

    sw_1 = time.time()
    patient = load_scan(dicom_fldr + patients[sample])        
    patient_pixels = get_pixels_hu(patient)       
    patient_pixels = select_slices(patient_pixels)

    if patient_pixels.shape[0] != 100:
        raise ValueError('Slices != 100: ', patient_pixels.shape[0])



    row = lbl.loc[lbl['id'] == pat_id]

    if row.shape[0] > 1:
        raise ValueError('Found duplicate ids: ', row.shape[0])

    print('Time preprocessing: %0.2f' %(time.time() - sw_1), flush=True)



    sw_2 = time.time()
    #found test patient
    if row.shape[0] == 0:
        test_counter += 1

        f['X_test'][test_counter] = patient_pixels
        f['i_test'][test_counter] = pat_id
        f['y_test'][test_counter] = -1


    #found train
    else: 
        train_counter += 1

        f['X_train'][train_counter] = patient_pixels
        f['i_train'][train_counter] = pat_id
        f['y_train'][train_counter] = row.cancer

    print('Time saving: %0.2f' %(time.time() - sw_2), flush=True)

    sw_el = time.time() - sw_start
    sw_rem = sw_el* (len(patients) - sample)
    print('Elapsed: %0.2fs \t rem: %0.2fm %0.2fh ' %(sw_el, sw_rem/60, sw_rem/3600), flush=True)


f.flush()
f.close()

【问题讨论】:

  • 因此,您将获取 1500 个患者文件,并将它们收集到一个 HDF5 文件中,并在此过程中使用分块和压缩。我将从这些文件的一个子集开始,探索各种 HDF5 设置的效果(关于块、压缩)。每个 3d 数组是 52MB,对吧?将它们放在单独的数据集中而不是 4d 数组中会有什么不同吗?

标签: python numpy hdf5 h5py


【解决方案1】:

速度缓慢几乎可以肯定是由于压缩和分块。很难做到这一点。在我过去的项目中,我经常不得不关闭压缩,因为它太慢了,尽管我并没有放弃 HDF5 中压缩的想法。

首先,您应该尝试确认压缩和分块是性能问题的原因。关闭分块和压缩(即省略 chunks=True, compression="gzip", compression_opts=4 参数)并重试。我怀疑它会快很多。

如果您想使用压缩,您必须了解分块的工作原理,因为 HDF 会逐块压缩数据。谷歌它,但至少阅读section on chunking from the h5py docs。以下引用至关重要:

分块会影响性能。建议将块的总大小保持在 10 KiB 和 1 MiB 之间,对于较大的数据集更大。 还要记住,当访问块中的任何元素时,会从磁盘读取整个块。

通过设置chunks=True,您可以让 h5py 自动为您确定块大小(打印数据集的 chunks 属性以查看它们是什么)。假设第一个维度(您的 sample 维度)中的块大小为 5 。这意味着当您添加一个样本时,底层 HDF 库将从磁盘读取包含该样本的所有块(因此总共它将完全读取 5 个样本)。对于每个块,HDF 都会读取它、解压缩它、添加新数据、压缩它并将其写回磁盘。不用说,这很慢。 HDF 有一个块缓存这一事实缓解了这种情况,因此未压缩的块可以驻留在内存中。然而,块缓存似乎相当小(请参阅here),所以我认为在 for 循环的每次迭代中,所有块都被换入和换出缓存。我在 h5py 中找不到任何设置来更改块缓存大小。

您可以通过将元组分配给chunks 关键字参数来显式设置块大小。考虑到所有这些,您可以尝试不同的块大小。我的第一个实验是将第一个(样本)维度中的块大小设置为 1,以便可以访问单个样本而无需将其他样本读取到缓存中。如果这有帮助,请告诉我,我很想知道。

即使您找到适合写入数据的块大小,读取时它可能仍然很慢,具体取决于您读取的切片。选择块大小时,请记住您的应用程序通常如何读取数据。您可能必须使文件创建例程适应这些块大小(例如,逐块填充数据集)。或者,您可以认为根本不值得付出努力并创建未压缩的 HDF5 文件。

最后,我将在create_dataset 调用中设置shuffle=True。这可能会为您带来更好的压缩比。但是它不应该影响性能。

【讨论】:

  • 谢谢titusjan。经过一些尝试和测试,我设置了块 = (1, 100, 512, 512) 和压缩 gzip - 总时间完成 1,5 - 3 小时(包括预处理),相同的块大小和不压缩需要 40-60 分钟.我读取文件的部分仍然在我前面,所以我不知道我是否不改变这种方法,特别是我将不得不根据列/特征而不是样本进行一些处理。
【解决方案2】:

你必须设置一个合适的块大小。 例如:

您多次向 HDF5 数据集添加数据,这可能会导致对块的多次写入访问。如果 chunk-chache 太低,它的工作方式如下:

读取->解压->添加数据->压缩->写入

因此我建议你设置一个合适的 chunk-chache 大小(默认只有 1 MB)。这可以通过低级 API 或 h5py-chache 来完成 https://pypi.python.org/pypi/h5py-cache/1.0

只需更改打开 HDF5 文件的那一行。

此外,应该添加到数据集的 numpy 数组的维数应该是一致的。

这是

A=np.random.rand(1000,1000)
for i in xrange(0,200):
    dset[:,:,i:i+1]=A[:,:,np.newaxis]

在我的笔记本上比那快 7 倍

A=np.random.rand(1000,1000)
for i in xrange(0,200):
    dset[:,:,i]=A

【讨论】:

    猜你喜欢
    • 2014-04-09
    • 1970-01-01
    • 2016-03-11
    • 1970-01-01
    • 2021-12-24
    • 2022-01-24
    • 2018-01-10
    • 2018-03-25
    • 2021-06-17
    相关资源
    最近更新 更多