2017-01-20 1 views
2

Das erste Mal mit hdf5 so können Sie mir helfen, herauszufinden, was falsch ist, warum das Hinzufügen von 3d numpy Arrays ist langsam. Preprocessing nimmt 3s, 3d numpy array (100x512x512) 30s Addieren und mit jeder Probe steigendeHDF5 Hinzufügen von numpy Arrays langsam

Zuerst I erstellen HDF- mit:

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') 

Dann laufen I-Code der Aktualisierung HDF- Datei:

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

So Sie 1500 Patientenakten nehmen und sie in eine HDF5 Datei zu sammeln, mit Chunking und Kompression auf dem Weg . Ich würde mit einer Teilmenge dieser Dateien beginnen und die Auswirkungen verschiedener HDF5-Einstellungen (in Bezug auf Chunks, Komprimierung) untersuchen. Jedes 3D-Array ist 52MB, oder? Ist es sinnvoll, sie in separate Datensätze anstatt in ein 4-D-Array zu setzen? – hpaulj

Antwort

2

Die Langsamkeit ist fast sicher auf Kompression und Chunking zurückzuführen. Es ist schwer, das richtig zu machen. In meinen früheren Projekten musste ich oft die Kompression abschalten, weil es zu langsam war, obwohl ich die Idee der Komprimierung in HDF5 im Allgemeinen nicht aufgegeben habe.

Zuerst sollten Sie versuchen zu bestätigen, dass Komprimierung und Chunking die Ursache der Leistungsprobleme sind. Deaktivieren Sie Chunking und Komprimierung (d. H. Lassen Sie die Parameter chunks=True, compression="gzip", compression_opts=4 aus) und versuchen Sie es erneut. Ich vermute, dass es viel schneller sein wird.

Wenn Sie Komprimierung verwenden möchten, müssen Sie verstehen, wie Chunking funktioniert, weil HDF die Daten Chunk-by-Chunk komprimiert. Google es, aber lesen Sie mindestens die section on chunking from the h5py docs. Das folgende Zitat ist entscheidend:

Chunking hat Auswirkungen auf die Leistung. Es wird empfohlen, die Gesamtgröße Ihrer Stücke zwischen 10 KiB und 1 MiB zu halten, größer für größere Datensätze. Beachten Sie auch, dass beim Zugriff auf ein beliebiges Element in einem Chunk der gesamte Chunk von der Festplatte gelesen wird.

von chunks=True Einstellung h5py bestimmen lassen Sie den Brocken für Sie Größen automatisch (die chunks Eigenschaft des Datensatzes drucken, um zu sehen, was sie sind). Nehmen wir an, die Chunk-Größe in der ersten Dimension (Ihre sample Dimension) ist 5. Das würde bedeuten, dass die zugrunde liegende HDF-Bibliothek alle Blöcke, die dieses Beispiel enthalten, von der Festplatte liest, wenn Sie ein Beispiel hinzufügen (also insgesamt die 5 Beispiele vollständig lesen). Für jeden Chunk liest HDF es, dekomprimiert es, fügt die neuen Daten hinzu, komprimiert sie und schreibt sie zurück auf die Festplatte. Es ist unnötig zu sagen, dass dies langsam ist. Dies wird durch die Tatsache gemildert, dass HDF einen Chunk-Cache hat, so dass unkomprimierte Chunks im Speicher verbleiben können. Allerdings scheint der Chunk-Cache eher klein zu sein (siehe here), so dass ich denke, dass alle Chunks in jeder Iteration Ihrer For-Schleife in den Cache-Speicher hinein und wieder heraus getauscht werden. Ich konnte in h5py keine Einstellung finden, um die Chunk-Cachegröße zu ändern.

Sie können die Chunk-Größe explizit festlegen, indem Sie dem Schlüsselwort chunks ein Tupel zuweisen. In diesem Sinne können Sie mit verschiedenen Chunk-Größen experimentieren. Mein erstes Experiment würde sein, die Chunk-Größe in der ersten (Abtast-) Dimension auf 1 zu setzen, so dass auf einzelne Samples zugegriffen werden kann, ohne andere Samples in den Cache zu lesen. Lass mich wissen, wenn das hilft, bin ich neugierig zu wissen.

Auch wenn Sie eine Chunk-Größe finden, die sich gut zum Schreiben der Daten eignet, kann sie beim Lesen immer noch langsam sein, je nachdem, welche Slices Sie gelesen haben. Berücksichtigen Sie bei der Auswahl der Chunk-Größe, wie Ihre Anwendung die Daten liest. Möglicherweise müssen Sie Ihre Dateierstellungsroutinen an diese Chunk-Größen anpassen (füllen Sie beispielsweise Ihre Datensätze Chunk für Chunk). Oder Sie können entscheiden, dass es sich nicht lohnt, unkomprimierte HDF5-Dateien zu erstellen.

Schließlich würde ich shuffle=True in die create_dataset Anrufe setzen. Dadurch erhalten Sie möglicherweise eine bessere Komprimierungsrate. Es sollte die Performance jedoch nicht beeinflussen.

+0

Danke titusjan. Nach einigen Versuchen und Tests setze ich Chunks = (1, 100, 512, 512) und Komprimierung gzip - das ergibt insgesamt 1,5 bis 3 Stunden (inkl. Vorverarbeitung), gleiche Chunk-Größe und keine Komprimierung dauert 40-60 Minuten . Der Teil, wenn ich die Datei lese, ist immer noch vor mir, also weiß ich noch nicht, ob ich diesen Ansatz nicht ändere, besonders, dass ich etwas Verarbeitung basierend auf Samples, aber auf Spalten/Features machen muss. – klubow

0

Sie müssen eine richtige Chunk Chache-Größe einrichten. Zum Beispiel:

Sie fügen Daten mehrmals zu einem HDF5-Dataset hinzu, was wahrscheinlich zu mehreren Schreibzugriffen auf Chunks führen wird. Wenn der Klotz-chache zu niedrig ist, funktioniert es wie folgt aus:

Lese-> decompression-> hinzufügen Daten-> Kompressions-> Schreiben

Deshalb empfehle ich Ihnen einen richtigen Brocken-Chache Größe einrichten (default ist nur 1 MB). Dies kann mit der Low-Level-API oder h5py-chache https://pypi.python.org/pypi/h5py-cache/1.0

getan werden Nur die Zeile, wo Sie die HDF5-Datei öffnen müssen geändert werden.

Auch die Anzahl der Dimensionen aus dem numpy-Array, die dem Dataset hinzugefügt werden sollten, sollte konsistent sein.

Dies ist

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

7 mal auf meinem Notebook schneller als die

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