HDF5添加numpy数组缓慢

时间:2017-01-20 20:38:41

标签: python numpy hdf5 h5py

第一次使用hdf5所以你可以帮我弄清楚什么是错的,为什么添加3d numpy数组很慢。 预处理需要3秒,添加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()

2 个答案:

答案 0 :(得分:2)

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

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

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

  

Chunking具有性能影响。建议将块的总大小保持在10 KiB和1 MiB之间,对于较大的数据集,建议大一些。 另请注意,当访问块中的任何元素时,将从磁盘读取整个块。

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

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

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

最后,我会在shuffle=True来电中设置create_dataset。这可能会让您获得更好的压缩比。但它不应该影响性能。

答案 1 :(得分:0)

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

您可以多次向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