2017-01-20 10 views
2

Первый раз, используя hdf5, не могли бы вы помочь мне выяснить, что не так, почему добавление 3D-массивов numpy происходит медленно. Препроцессирование занимает 3s, добавив 3d массива NumPy (100x512x512) 30-х годов и рост с каждым образцомHDF5 с добавлением массивов numpy slow

Сначала я создать 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() 
+0

Таким образом, вы принимаете 1500 файлов пациентов, и собирать их в один HDF5 файл, используя комков и сжатие вдоль пути , Я бы начал с подмножества этих файлов и исследовал влияние различных настроек HDF5 (в отношении кусков, сжатия). Каждый трехмерный массив равен 52 МБ, не так ли? Помещает ли их в отдельные наборы данных вместо четырехмерного массива? – hpaulj

ответ

2

Медленность почти наверняка обусловлена ​​сжатием и разделением. Трудно это понять. В моих прошлых проектах мне часто приходилось отключать сжатие, потому что оно было слишком медленным, хотя я вообще не отказался от идеи сжатия в HDF5 в целом.

Сначала вы должны попытаться подтвердить, что сжатие и блокировка являются причиной проблем с производительностью. Отключите блокировку и сжатие (т. Е. Оставьте параметры chunks=True, compression="gzip", compression_opts=4) и повторите попытку. Я подозреваю, что это будет намного быстрее.

Если вы хотите использовать компрессию, вы должны понимать, как работает chunking, потому что HDF сжимает кусок данных за куском. Google, но, по крайней мере, прочитайте 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. Это может повысить коэффициент сжатия. Однако это не должно влиять на производительность.

+0

Спасибо, titusjan. После некоторых попыток и тестирования я устанавливаю chunks = (1, 100, 512, 512) и сжатие gzip, что дает общее время для завершения 1,5 - 3 часа (включая предварительную обработку), тот же размер блока и без сжатия занимает 40-60 минут , Часть, когда я читаю файл, все еще опережает меня, поэтому я еще не знаю, не изменил ли я этот подход, тем более что мне придется делать некоторую обработку, основанную не на образцах, а на столбцах/функциях. – klubow

0

Вы должны установить правильный размер chatch chache. Например:

Вы добавляете данные несколько раз в набор данных HDF5, который, вероятно, приведет к множественному доступу на запись к кускам. Если кусок-chache является низкой она работает так:

читальное> decompression-> добавить данных-> сжатие> написание

Therefor я рекомендую вам установить размер собственно куска chache (по умолчанию составляет всего 1 МБ). Это можно сделать с помощью низкоуровневого 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