2016-01-19 19 views
26

Ho visto tf.identity utilizzato in alcuni punti, come il tutorial ufficiale CIFAR-10 e l'implementazione di normalizzazione del batch su StackOverflow, ma non vedo perché sia ​​necessario.In TensorFlow, a cosa serve tf.identity?

A cosa serve? Qualcuno può dare un caso d'uso o due?

Una risposta proposta è che può essere utilizzato per il trasferimento tra CPU e GPU. Questo non è chiaro per me. Estensione alla domanda, basata su this: loss = tower_loss(scope) è sotto il blocco GPU, il che mi suggerisce che tutti gli operatori definiti in tower_loss sono mappati alla GPU. Quindi, alla fine di tower_loss, vediamo total_loss = tf.identity(total_loss) prima che venga restituito. Perché? Quale sarebbe il difetto di non utilizzare tf.identity qui?

+0

un buon uso [qui] (https://stackoverflow.com/questions/43839431/tensorflow-how-to-replace-or-modify-gradient) per tf.identity e gradiente calcoli –

risposta

29

Dopo un po 'di inciampi penso di aver notato un caso monouso che si adatta a tutti gli esempi che ho visto. Se ci sono altri casi d'uso, si prega di elaborare con un esempio.

Caso d'uso:

Supponiamo che desideri eseguire un operatore ogni volta che una particolare variabile viene valutato. Ad esempio, supponiamo di voler aggiungere uno a x ogni volta che viene valutata la variabile . Potrebbe sembrare questo funzionerà:

x = tf.Variable(0.0) 
x_plus_1 = tf.assign_add(x, 1) 

with tf.control_dependencies([x_plus_1]): 
    y = x 
init = tf.initialize_all_variables() 

with tf.Session() as session: 
    init.run() 
    for i in xrange(5): 
     print(y.eval()) 

Non è così: è lo stamperemo 0, 0, 0, 0, 0, invece, sembra che abbiamo bisogno di aggiungere un nuovo nodo al grafico all'interno il blocco control_dependencies.Quindi usiamo questo trucco:

x = tf.Variable(0.0) 
x_plus_1 = tf.assign_add(x, 1) 

with tf.control_dependencies([x_plus_1]): 
    y = tf.identity(x) 
init = tf.initialize_all_variables() 

with tf.Session() as session: 
    init.run() 
    for i in xrange(5): 
     print(y.eval()) 

Questo funziona: la stampa 1, 2, 3, 4, 5.

Se nel CIFAR-10 Tutorial abbiamo abbassato tf.identity, quindi loss_averages_op sarebbe mai eseguito.

16

tf.identity è utile quando si desidera trasportare in modo esplicito il tensore tra i dispositivi (ad esempio, dalla GPU alla CPU). L'operazione aggiunge nodi di invio/ricezione al grafico, che eseguono una copia quando i dispositivi dell'ingresso e dell'uscita sono diversi.

Un comportamento predefinito è che i nodi di invio/recv vengono aggiunti implicitamente quando l'operazione avviene su un dispositivo diverso ma è possibile immaginare alcune situazioni (specialmente in un ambiente multi-thread/distribuito) quando potrebbe essere utile recuperare il valore della variabile più volte all'interno di una singola esecuzione di session.run. tf.identity consente un maggiore controllo in merito a quando il valore deve essere letto dal dispositivo sorgente. Forse un nome più appropriato per questa operazione sarebbe read.

Inoltre, nell'implementazione di tf.Variablelink, la funzione di identità viene aggiunta nel costruttore, che assicura che tutti gli accessi alla variabile copino i dati dall'origine solo una volta. Le copie multiple possono essere costose nei casi in cui la variabile risiede su una GPU ma viene letta da più CPU op (o viceversa). Gli utenti possono modificare il comportamento con più chiamate a tf.identity quando lo si desidera.

MODIFICA: risposta aggiornata dopo la modifica della domanda.

Inoltre, tf.identity può essere utilizzato come nodo fittizio per aggiornare un riferimento al tensore. Questo è utile con varie opzioni di controllo del flusso. Nel caso CIFAR vogliamo imporre che ExponentialMovingAverageOp aggiorni le variabili rilevanti prima di recuperare il valore della perdita. Questo può essere implementato come:

with tf.control_dependencies([loss_averages_op]): 
    total_loss = tf.identity(total_loss) 

Qui, il tf.identity non fa nulla di utile da parte di segnare il total_loss tensore ad essere eseguito dopo aver valutato loss_averages_op.

+0

Se possibile, puoi commentare il mio aggiornamento sopra? In particolare, cosa potrebbe andare storto in https://tensorflow.googlesource.com/tensorflow/+/master/tensorflow/models/image/cifar10/cifar10_multi_gpu_train.py se omettiamo la riga 'total_loss = tf.identity (total_loss)'? – rd11

+0

Aggiornamento della risposta. Spero che sia più chiaro ora. –

4

Mi sono imbattuto in un altro caso di utilizzo che non è completamente coperto dalle altre risposte.

def conv_layer(input_tensor, kernel_shape, output_dim, layer_name, decay=None, act=tf.nn.relu): 
    """Reusable code for making a simple convolutional layer. 
    """ 
    # Adding a name scope ensures logical grouping of the layers in the graph. 
    with tf.name_scope(layer_name): 
     # This Variable will hold the state of the weights for the layer 
     with tf.name_scope('weights'): 
      weights = weight_variable(kernel_shape, decay) 
      variable_summaries(weights, layer_name + '/weights') 
     with tf.name_scope('biases'): 
      biases = bias_variable([output_dim]) 
      variable_summaries(biases, layer_name + '/biases') 
     with tf.name_scope('convolution'): 
      preactivate = tf.nn.conv2d(input_tensor, weights, strides=[1, 1, 1, 1], padding='SAME') 
      biased = tf.nn.bias_add(preactivate, biases) 
      tf.histogram_summary(layer_name + '/pre_activations', biased) 
     activations = act(biased, 'activation') 
     tf.histogram_summary(layer_name + '/activations', activations) 
     return activations 

maggior parte del tempo quando si costruisce uno strato convoluzionale, si vuole solo le attivazioni restituiti in modo da poter alimentare quelli nello strato successivo. A volte, tuttavia, ad esempio quando si costruisce un auto-encoder, si vogliono i valori di pre-attivazione.

In questa situazione una soluzione elegante è quella di passare tf.identity come funzione di attivazione, non attivando efficacemente il livello.

+0

Se l'output è un elenco di tensori, è possibile utilizzare direttamente l'identità o utilizzare il primo stack e quindi l'identità? Qual è il modo migliore. –

2

Ho trovato un'altra applicazione di t.identity in Tensorboard. Se si utilizza tf.shuffle_batch, esso restituisce più tensori in una sola volta, in modo da vedere la foto disordinato quando visualizzare il grafico, non è possibile dividere tensore creazione gasdotto da tensori ingresso actiual: messy

Ma con tf.identity si può creare nodi duplicati, che non influenzano il flusso di calcolo: nice

+0

Se l'output è un elenco di tensori, è possibile utilizzare direttamente l'identità o utilizzare il primo stack e quindi l'identità? Qual è il modo migliore. –

+0

Supponiamo di avere un grafico che richiede 2 parametri: immagine ed etichetta. Hai implementato 2 modi per alimentare il valore: con i segnaposti e con '' 'tf.shuffle_batch'''. Vuoi visualizzare bene il tuo grafico. Dividiamo il sottografo di addestramento e il sottografo della generazione di batch. Con i segnaposto è facile. Ma batch non lo è: se si posiziona l'operazione tf.shuffle_batch in scope 'batch_generation' e altre operazioni in scope' model', quindi non esiste alcun nodo INPUT all'interno di 'model'. Suggerisco di applicare tf.identity direttamente a ciascun vettore generato da tf.shuffle_batch e spostarli nello scope 'model' – grihabor

+0

Hi grihabor, ti dispiacerebbe condividere lo snippet di codice? – user288609

2

In aggiunta a quanto sopra, lo uso semplicemente quando devo assegnare un nome alle operazioni che non hanno un argomento nome, proprio come quando si inizializza uno stato in RNN:

rnn_cell = tf.contrib.rnn.MultiRNNCell([cells]) 
# no name arg 
initial_state = rnn_cell.zero_state(batch_size,tf.float32) 
# give it a name with tf.identity() 
initial_state = tf.identity(input=initial_state,name="initial_state") 
0

In formazione di distribuzione , Dovremmo usare tf.identity o gli operai appendere ad aspettare per l'inizializzazione del capo operaio:

vec = tf.identity(tf.nn.embedding_lookup(embedding_tbl, id)) * mask 
with tf.variable_scope("BiRNN", reuse=None): 
    out, _ = tf.nn.bidirectional_dynamic_rnn(fw, bw, vec, sequence_length=id_sz, dtype=tf.float32) 

Per i dettagli, senza identità, il capo lavoratore avrebbe il trattamento di alcune variabili come variabili locali in modo inadeguato e aspettare gli altri lavoratori per un'operazione di inizializzazione che non può terminare

0

Quando i dati di input sono serializzati in byte e vogliamo estrarre le funzionalità da questo set di dati. Possiamo farlo in formato chiave-valore e quindi ottenere un segnaposto per questo. I suoi benefici sono più realizzati quando ci sono più funzionalità e ogni funzione deve essere letta in un formato diverso.

#read the entire file in this placeholder  
    serialized_tf_example = tf.placeholder(tf.string, name='tf_example') 

    #Create a pattern in which data is to be extracted from input files 
    feature_configs = {'image': tf.FixedLenFeature(shape=[256], dtype=tf.float32),/ 
        'text': tf.FixedLenFeature(shape=[128], dtype=tf.string),/ 
        'label': tf.FixedLenFeature(shape=[128], dtype=tf.string),} 

    #parse the example in key: tensor dictionary 
    tf_example = tf.parse_example(serialized_tf_example, feature_configs) 

    #Create seperate placeholders operation and tensor for each feature 
    image = tf.identity(tf_example['image'], name='image') 
    text = tf.identity(tf_example['text'], name='text') 
    label = tf.identity(tf_example['text'], name='label') 
+0

Questa risposta voleva solo mostrare un altro caso d'uso di tf.identity(). –