2016-07-17 191 views
6

Ho utilizzato feed_dict per dirigere il feed a placeholder durante la pratica della codifica in piccoli problemi come MNIST. TensorFlow supporta anche l'alimentazione dei dati utilizzando queue e queue runner e richiede un certo sforzo per imparare.TensorFlow: alimentazione dati con la coda vs con alimentazione diretta con feed_dict

Qualcuno ha eseguito un confronto tra questi due metodi e ha misurato le prestazioni? Vale la pena dedicare del tempo a imparare a usare la coda per alimentare i dati?

Immagino di usare la coda non solo per le prestazioni, ma anche per un codice più pulito, qualunque cosa significhi. Forse il codice per un set di dati può essere facilmente utilizzato per un altro set di dati (una volta convertiti i dati in TFRecord)?

Tuttavia, this post sembra dire che la coda può essere più lenta del metodo feed_dict. È ancora vero ora? Perché dovrei usare la coda se è più lento e difficile da codificare?

Grazie per i vostri input.

+0

prima parte di quella esercitazione dà un po 'di indicazioni su quando utilizzare le code: https://indico.io/blog/tensorflow-data-inputs-part1-placeholders-protobufs-queues/ –

risposta

1

Ecco un punto di riferimento:

BasicRNNCell srotolati a 20 passi temporali con 200 unità nascoste. Ho avuto 250K esempi di addestramento e corsi 1 epoca con una dimensione del lotto di 20.

feed_dict: 597 secondi
coda: 591 secondi

questo era con TF v1.0, su un computer portatile i5 (quindi 4 CPU) con Ubuntu 16.04.

2

Il mio modello NMT ha 2 livelli, 512 unità nascoste. Mi alleno con una lunghezza massima della frase = 50, dimensione batch = 32 e vedo velocità simile tra feed_dict e queue, circa 2400-2500 parole target al secondo (io uso questa metrica per la velocità basata su questo paper).

Trovo feed_dict molto intuitivo e facile da usare. La coda è difficile. Utilizzando la coda, è necessario:

1/Converti i tuoi dati in tfrecords. Devo davvero google un po 'per capire come convertire i miei dati seq2seq in tfrecords perché i documenti non sono molto utili.

2/Decodifica i tuoi dati da tfrecords. Troverai le funzioni utilizzate per generare i tfrecords e decodificarli in modo non intuitivo. Per esempio, se ognuno dei miei esempi di allenamento ha 3 sequenze (solo 3 liste di interi) src_input, trg_input, trg_target e voglio registrare anche la lunghezza dello src_input (alcuni dei suoi elementi potrebbero essere IMBOTTITURE, quindi non contare), ecco come per generare tfrecord da ogni esempio:

def _make_example(src_input, src_seq_length, trg_input, trg_seq_length, trg_target, target_weight): 
    context = tf.train.Features(
     feature={ 
      'src_seq_length': int64_feature(src_seq_length) 
     }) 
    feature_lists = tf.train.FeatureLists(
     feature_list={ 
      'src_input': int64_featurelist(src_input), 
      'trg_input': int64_featurelist(trg_input), 
      'trg_target': int64_featurelist(trg_target) 
     }) 

    return tf.train.SequenceExample(context=context, feature_lists=feature_lists) 

Ed ecco come decodificarlo:

def _read_and_decode(filename_queue): 
    reader = tf.TFRecordReader(options=self.tfrecord_option) 
    _, serialized_ex = reader.read(filename_queue) 

    context_features = { 
     'src_seq_length': tf.FixedLenFeature([], dtype=tf.int64) 
    } 
    sequence_features = { 
     'src_input': tf.FixedLenSequenceFeature([], dtype=tf.int64), 
     'trg_input': tf.FixedLenSequenceFeature([], dtype=tf.int64), 
     'trg_target': tf.FixedLenSequenceFeature([], dtype=tf.int64) 
    } 
    context, sequences = tf.parse_single_sequence_example(
     serialized_ex, 
     context_features=context_features, 
     sequence_features=sequence_features) 

    src_seq_length = tf.cast(context['src_seq_length'], tf.int32) 
    src_input = tf.cast(sequences['src_input'], tf.int32) 
    trg_input = tf.cast(sequences['trg_input'], tf.int32) 
    trg_target = tf.cast(sequences['trg_target'], tf.int32) 

    return src_input, src_seq_length, trg_input, trg_target 

E per generare ogni caratteristica tfrecord/featurelist:

def int64_feature(value): 
    return tf.train.Feature(int64_list=tf.train.Int64List(value=[value])) 

def int64_featurelist(l): 
    feature = [tf.train.Feature(int64_list=tf.train.Int64List(value=[x])) for x in l] 
    return tf.train.FeatureList(feature=feature) 

http://gph.is/2cg7iKP

3/Train/dev setup. Credo che sia una pratica comune allenare periodicamente il tuo modello per un po 'di tempo, quindi valutare il set di sviluppo, quindi ripetere. Non so come farlo con le code. Con feed_dict, devi solo creare due grafici con parametri condivisi nella stessa sessione, uno per il treno e uno per lo sviluppo. Quando valutate il set di sviluppo, date semplicemente i dati di sviluppo al grafico dev, il gioco è fatto.Ma per la coda, l'output dalla coda è parte del grafico stesso. Per eseguire la coda, è necessario avviare il gestore della coda, creare un coordinatore, utilizzare questo coordinatore per gestire la coda. Quando è finito, la coda è vicina !!!. Al momento, non ho idea di come scrivere al meglio il mio codice per conformare il setup di treno/dev con le code, tranne aprire una nuova sessione, creare un nuovo grafico per dev ogni volta che valuto. Lo stesso problema è stato sollevato here, e puoi google per domande simili su Stackoverflow.

Tuttavia, un sacco di gente ha detto che la coda è più veloce di feed_dict. La mia ipotesi è che la coda è utile se ti alleni in modo distribuito. Ma per quanto mi riguarda, mi alleno spesso su solo 1 GPU e finora non sono affatto entusiasta della coda. Beh, solo la mia ipotesi.

2

penso che il beneficio si vedrà è fortemente dipendente sul vostro problema. Ho visto un 3x di accelerazione quando ho passato da feed_dict a una coda. C'erano almeno due ragioni ha dato un miglioramento drammatico nel mio caso:

  1. Il codice Python che è stato generando i vettori di alimentazione è stato piuttosto lento e non ottimizzata. Per ogni esempio di allenamento, ci sono stati molti passaggi intermedi (allocando alcuni array numpy, creando un dataframe di Pandas, chiamando un gruppo di funzioni per calcolare/trasformare le caratteristiche). Il 25% del tempo totale di formazione è stato speso per generare i dati del feed.

  2. Uno dei motivi per cui feed_dict può essere lento è che implica una memcpy dei dati alimentati da Python al runtime TF. I miei esempi erano molto grandi, quindi ho avuto un grande successo in questo. (Nel mio caso, perché i miei esempi erano seqeuences, e io ero a zero-padding ad una lunghezza massima grande prima di nutrirli).

Se pensi che uno di questi potrebbe essere applicato al tuo problema, vale la pena considerare l'uso di code.

Problemi correlati