2017-01-20 2 views
2

Première fois en utilisant hdf5 alors pourriez-vous m'aider à comprendre ce qui ne va pas, pourquoi ajouter des tableaux 3d chiffrés est lent. Prétraitement prend 3 secondes, ajouter 3d tableau de numpy (100x512x512) 30 et la hausse avec chaque échantillonHDF5 ajouter des tableaux chiffrés lents

D'abord, je crée hdf avec:

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

J'exécuter du code mise à jour de fichier 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

Alors vous prenez 1500 dossiers des patients, et la collecte dans un seul fichier HDF5, en utilisant Chunking et compression le long du chemin . Je commencerais par un sous-ensemble de ces fichiers, et j'explorerais l'effet de divers paramètres HDF5 (concernant les morceaux, la compression). Chaque tableau 3d est de 52 Mo, non? Est-ce que les mettre dans des ensembles de données distincts au lieu d'un tableau 4d fait la différence? – hpaulj

Répondre

2

La lenteur est presque certainement due à la compression et au découpage. C'est difficile de bien faire les choses. Dans mes projets passés, j'ai souvent dû désactiver la compression car elle était trop lente, même si je n'ai pas renoncé à l'idée de compression en HDF5 en général.

D'abord, vous devriez essayer de confirmer que la compression et le découpage sont la cause des problèmes de performance. Désactivez la segmentation et la compression (c'est-à-dire, ignorez les paramètres chunks=True, compression="gzip", compression_opts=4) et réessayez. Je pense que ce sera beaucoup plus rapide. Si vous souhaitez utiliser la compression, vous devez comprendre le fonctionnement du découpage, car HDF compresse le fragment de données par bloc. Google, mais au moins lire le section on chunking from the h5py docs. La citation suivante est cruciale:

La segmentation a des implications sur les performances. Il est recommandé de garder la taille totale de vos blocs entre 10 KiB et 1 Mio, plus grande pour les jeux de données plus volumineux. Gardez également à l'esprit que lorsqu'un élément d'un tronçon est accédé, le tronçon entier est lu sur le disque.

En définissant chunks=True vous permettent de déterminer h5py le morceau tailles pour vous automatiquement (imprimer la propriété chunks de l'ensemble de données pour voir ce qu'ils sont). Disons que la taille du segment dans la première dimension (votre dimension sample) est 5. Cela signifierait que lorsque vous ajoutez un échantillon, la bibliothèque HDF sous-jacente lira tous les morceaux qui contiennent cet échantillon du disque (donc au total il lira complètement les 5 échantillons). Pour chaque segment, HDF le lira, le décompressera, ajoutera les nouvelles données, les compressera et les réécrira sur le disque. Inutile de dire que c'est lent. Ceci est atténué par le fait que HDF a un cache de morceaux, de sorte que les morceaux non compressés peuvent résider dans la mémoire. Cependant, le cache de blocs semble être plutôt petit (voir here), donc je pense que tous les morceaux sont échangés dans et hors du cache à chaque itération de votre boucle for. Je n'ai trouvé aucun paramètre dans h5py pour modifier la taille du cache de blocs.

Vous pouvez définir explicitement la taille de segment en affectant un tuple au paramètre de mot-clé chunks. Avec tout cela en tête, vous pouvez expérimenter avec différentes tailles de morceaux. Ma première expérience consisterait à définir la taille du segment dans la première dimension (échantillon) à 1, de sorte que des échantillons individuels puissent être consultés sans lire d'autres échantillons dans le cache. Faites-moi savoir si cela a aidé, je suis curieux de savoir.

Même si vous trouvez une taille de bloc qui fonctionne bien pour l'écriture des données, elle peut être lente lors de la lecture, en fonction des tranches que vous avez lues. Lorsque vous choisissez la taille de bloc, gardez à l'esprit la manière dont votre application lit généralement les données. Vous devrez peut-être adapter vos routines de création de fichiers à ces tailles de blocs (par exemple, remplir votre bloc de données par morceau). Ou vous pouvez décider que cela ne vaut tout simplement pas la peine et créer des fichiers HDF5 non compressés.

Enfin, je définirais shuffle=True dans les create_dataset appels. Cela peut vous donner un meilleur taux de compression. Cela ne devrait toutefois pas influencer la performance.

+0

Merci titusjan. Après quelques essais et tests, j'ai réglé les morceaux = (1, 100, 512, 512) et la compression gzip - qui donne le temps total pour finir 1,5 - 3 heures (y compris le prétraitement), la même taille de bloc et aucune compression prend 40-60 minutes . La partie quand je lis le fichier est toujours en avance sur moi, donc je ne sais pas encore si je ne change pas cette approche, surtout que je devrai faire un traitement basé non sur des samples mais sur des colonnes/fonctionnalités. – klubow

0

Vous devez définir une taille de chache de tronçon appropriée. Par exemple:

Vous ajoutez plusieurs fois des données à un jeu de données HDF5, ce qui entraîne probablement plusieurs accès en écriture aux blocs. Si le morceau-chache est faible, il fonctionne comme ceci:

lecture-> decompression-> ajouter data-> compression-> écriture

je vous recommande ainsi tous de mettre en place une taille correcte morceau-chache (par défaut est seulement 1 Mo). Cela peut être fait avec l'API de bas niveau ou h5py-chache https://pypi.python.org/pypi/h5py-cache/1.0

Seule la ligne où vous ouvrez le fichier HDF5 doit être modifiée.

De même, le nombre de dimensions du tableau numpy qui doit être ajouté à l'ensemble de données doit être cohérent.

C'est

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

7 fois plus rapide sur mon ordinateur portable que celui

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