HDF5 添加 numpy 数组很慢
HDF5 adding numpy arrays slow
第一次使用 hdf5,你能帮我找出问题所在吗,为什么添加 3d numpy 数组很慢。
预处理需要 3 秒,添加 3d numpy 数组 (100x512x512) 30 秒并随每个样本上升
首先,我创建了 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()
速度缓慢几乎可以肯定是由于压缩和分块造成的。很难做到这一点。在我过去的项目中,我经常不得不关闭压缩,因为它太慢了,尽管我并没有放弃一般的 HDF5 压缩的想法。
首先您应该尝试确认压缩和分块是导致性能问题的原因。关闭分块和压缩(即省略 chunks=True, compression="gzip", compression_opts=4
参数)并重试。我怀疑它会快很多。
如果您想使用压缩,您必须了解分块的工作原理,因为 HDF 会压缩数据 chunk-by-chunk。 Google吧,不过至少看了section on chunking from the h5py docs。以下引述至关重要:
Chunking has performance implications. It’s recommended to keep the total size of your chunks between 10 KiB and 1 MiB, larger for larger datasets. Also keep in mind that when any element in a chunk is accessed, the entire chunk is read from disk.
通过设置 chunks=True
,您可以让 h5py 自动为您确定块大小(打印数据集的 chunks
属性 以查看它们是什么)。假设第一个维度(您的 sample
维度)中的块大小为 5 。这意味着当您添加一个样本时,底层 HDF 库将从磁盘读取包含该样本的所有块(因此总共它将完全读取 5 个样本)。对于每个块,HDF 都会读取它、解压缩它、添加新数据、压缩它并将它写回磁盘。不用说,这很慢。 HDF 具有块缓存这一事实减轻了这种情况,因此未压缩的块可以驻留在内存中。然而,块缓存似乎相当小(参见 here),所以我认为在您的 for-loop 的每次迭代中,所有块都被换入和换出缓存。我在 h5py 中找不到任何设置来改变块缓存大小。
您可以通过将元组分配给 chunks
关键字参数来显式设置块大小。考虑到所有这些,您可以尝试不同的块大小。我的第一个实验是将第一个(样本)维度中的块大小设置为 1,这样就可以在不将其他样本读入缓存的情况下访问单个样本。让我知道这是否有帮助,我很想知道。
即使您找到适合写入数据的块大小,读取时它可能仍然很慢,具体取决于您读取的切片。选择块大小时,请记住您的应用程序通常如何读取数据。您可能需要调整 file-creation 例程以适应这些块大小(例如逐块填充数据集)。或者您可以认为这样做根本不值得,并创建未压缩的 HDF5 文件。
最后,我会在 create_dataset
调用中设置 shuffle=True
。这可能会让您获得更好的压缩率。但是,它不应该影响性能。
您必须设置适当的块缓存大小。
例如:
您多次将数据添加到 HDF5 数据集,这可能会导致对块的多次写入访问。如果 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
第一次使用 hdf5,你能帮我找出问题所在吗,为什么添加 3d numpy 数组很慢。 预处理需要 3 秒,添加 3d numpy 数组 (100x512x512) 30 秒并随每个样本上升
首先,我创建了 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()
速度缓慢几乎可以肯定是由于压缩和分块造成的。很难做到这一点。在我过去的项目中,我经常不得不关闭压缩,因为它太慢了,尽管我并没有放弃一般的 HDF5 压缩的想法。
首先您应该尝试确认压缩和分块是导致性能问题的原因。关闭分块和压缩(即省略 chunks=True, compression="gzip", compression_opts=4
参数)并重试。我怀疑它会快很多。
如果您想使用压缩,您必须了解分块的工作原理,因为 HDF 会压缩数据 chunk-by-chunk。 Google吧,不过至少看了section on chunking from the h5py docs。以下引述至关重要:
Chunking has performance implications. It’s recommended to keep the total size of your chunks between 10 KiB and 1 MiB, larger for larger datasets. Also keep in mind that when any element in a chunk is accessed, the entire chunk is read from disk.
通过设置 chunks=True
,您可以让 h5py 自动为您确定块大小(打印数据集的 chunks
属性 以查看它们是什么)。假设第一个维度(您的 sample
维度)中的块大小为 5 。这意味着当您添加一个样本时,底层 HDF 库将从磁盘读取包含该样本的所有块(因此总共它将完全读取 5 个样本)。对于每个块,HDF 都会读取它、解压缩它、添加新数据、压缩它并将它写回磁盘。不用说,这很慢。 HDF 具有块缓存这一事实减轻了这种情况,因此未压缩的块可以驻留在内存中。然而,块缓存似乎相当小(参见 here),所以我认为在您的 for-loop 的每次迭代中,所有块都被换入和换出缓存。我在 h5py 中找不到任何设置来改变块缓存大小。
您可以通过将元组分配给 chunks
关键字参数来显式设置块大小。考虑到所有这些,您可以尝试不同的块大小。我的第一个实验是将第一个(样本)维度中的块大小设置为 1,这样就可以在不将其他样本读入缓存的情况下访问单个样本。让我知道这是否有帮助,我很想知道。
即使您找到适合写入数据的块大小,读取时它可能仍然很慢,具体取决于您读取的切片。选择块大小时,请记住您的应用程序通常如何读取数据。您可能需要调整 file-creation 例程以适应这些块大小(例如逐块填充数据集)。或者您可以认为这样做根本不值得,并创建未压缩的 HDF5 文件。
最后,我会在 create_dataset
调用中设置 shuffle=True
。这可能会让您获得更好的压缩率。但是,它不应该影响性能。
您必须设置适当的块缓存大小。 例如:
您多次将数据添加到 HDF5 数据集,这可能会导致对块的多次写入访问。如果 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