Come applicare il ritaglio del gradiente in TensorFlow?


96

Considerando il codice di esempio .

Vorrei sapere come applicare il gradiente di clipping su questa rete sulla RNN dove c'è la possibilità di esplodere i gradienti.

tf.clip_by_value(t, clip_value_min, clip_value_max, name=None)

Questo è un esempio che potrebbe essere utilizzato, ma dove lo presento? Nella definizione di RNN

    lstm_cell = rnn_cell.BasicLSTMCell(n_hidden, forget_bias=1.0)
    # Split data because rnn cell needs a list of inputs for the RNN inner loop
    _X = tf.split(0, n_steps, _X) # n_steps
tf.clip_by_value(_X, -1, 1, name=None)

Ma questo non ha senso in quanto il tensore _X è l'input e non il grad cosa deve essere ritagliato?

Devo definire il mio ottimizzatore per questo o esiste un'opzione più semplice?

Risposte:


143

Il ritaglio del gradiente deve avvenire dopo aver calcolato i gradienti, ma prima di applicarli per aggiornare i parametri del modello. Nel tuo esempio, entrambe queste cose sono gestite dal AdamOptimizer.minimize()metodo.

Per ritagliare i tuoi gradienti dovrai calcolarli, ritagliarli e applicarli esplicitamente come descritto in questa sezione nella documentazione dell'API di TensorFlow . In particolare, dovrai sostituire la chiamata al minimize()metodo con qualcosa di simile al seguente:

optimizer = tf.train.AdamOptimizer(learning_rate=learning_rate)
gvs = optimizer.compute_gradients(cost)
capped_gvs = [(tf.clip_by_value(grad, -1., 1.), var) for grad, var in gvs]
train_op = optimizer.apply_gradients(capped_gvs)

4
Styrke, grazie per il post. Sai quali sono i passaggi successivi per eseguire effettivamente un'iterazione dell'ottimizzatore? In genere, viene creata un'istanza di un ottimizzatore optimizer = tf.train.AdamOptimizer(learning_rate=learning_rate).minimize(cost) e quindi viene eseguita un'iterazione dell'ottimizzatore, optimizer.run()ma l'utilizzo optimizer.run()non sembra funzionare in questo caso?
applecider

6
Ok capito optimizer.apply_gradients(capped_gvs)che deve essere assegnato a qualcosa, x = optimizer.apply_gradients(capped_gvs)quindi all'interno della tua sessione puoi allenarti comex.run(...)
applecider

3
Shout-out a @ remi-cuingnet per il bel suggerimento di modifica . (Che purtroppo è stato rifiutato da critici frettolosi)
Styrke

Questo mi dà UserWarning: Converting sparse IndexedSlices to a dense Tensor with 148331760 elements. This may consume a large amount of memory.Quindi in qualche modo i miei gradienti sparsi vengono convertiti in densi. Qualche idea su come superare questo problema?
Pekka

8
In realtà il modo giusto per ritagliare i gradienti (secondo i documenti di tensorflow, gli scienziati informatici e la logica) è con tf.clip_by_global_norm, come suggerito da @danijar
gdelab

116

Nonostante ciò che sembra essere popolare, probabilmente vorrai ritagliare l'intero gradiente secondo la sua norma globale:

optimizer = tf.train.AdamOptimizer(1e-3)
gradients, variables = zip(*optimizer.compute_gradients(loss))
gradients, _ = tf.clip_by_global_norm(gradients, 5.0)
optimize = optimizer.apply_gradients(zip(gradients, variables))

Il ritaglio di ciascuna matrice gradiente cambia individualmente la loro scala relativa, ma è anche possibile:

optimizer = tf.train.AdamOptimizer(1e-3)
gradients, variables = zip(*optimizer.compute_gradients(loss))
gradients = [
    None if gradient is None else tf.clip_by_norm(gradient, 5.0)
    for gradient in gradients]
optimize = optimizer.apply_gradients(zip(gradients, variables))

In TensorFlow 2, un nastro calcola i gradienti, gli ottimizzatori provengono da Keras e non abbiamo bisogno di memorizzare l'operazione di aggiornamento perché viene eseguita automaticamente senza passarla a una sessione:

optimizer = tf.keras.optimizers.Adam(1e-3)
# ...
with tf.GradientTape() as tape:
  loss = ...
variables = ...
gradients = tape.gradient(loss, variables)
gradients, _ = tf.clip_by_global_norm(gradients, 5.0)
optimizer.apply_gradients(zip(gradients, variables))

10
Buon esempio con clip_by_global_norm()! Questo è anche descritto come the correct way to perform gradient clippingnei documenti di tensorflow
MZHm

9
@Escachator È empirico e dipenderà dal tuo modello e possibilmente dal compito. Quello che faccio è visualizzare la norma del gradiente tf.global_norm(gradients)per vedere il suo intervallo normale e quindi ritagliare un po 'sopra quello per evitare che i valori anomali rovinino l'allenamento.
danijar

1
chiameresti ancora opt.minimize()dopo o chiameresti qualcosa di diverso come opt.run()suggerito in alcuni commenti su altre risposte?
reese0106

3
@ reese0106 No, optimizer.minimize(loss)è solo una scorciatoia per calcolare e applicare i gradienti. Puoi eseguire l'esempio nella mia risposta con sess.run(optimize).
danijar

1
Quindi, se usassi tf.estimator.EstimatorSpec(mode, loss=loss, train_op=train_op)all'interno di una funzione di esperimento, il tuo optimizesostituirà il mio train_opcorretto? In questo momento, train_op = optimizer.minimize(loss, global_step=global_step))quindi sto cercando di assicurarmi di adattarmi di conseguenza ...
reese0106

10

Questo è effettivamente spiegato correttamente nella documentazione. :

La chiamata a minimizzare () si occupa sia di calcolare i gradienti sia di applicarli alle variabili. Se desideri elaborare i gradienti prima di applicarli, puoi invece utilizzare l'ottimizzatore in tre passaggi:

  • Calcola i gradienti con compute_gradients ().
  • Elabora i gradienti come desideri.
  • Applica i gradienti elaborati con apply_gradients ().

E nell'esempio che forniscono usano questi 3 passaggi:

# Create an optimizer.
opt = GradientDescentOptimizer(learning_rate=0.1)

# Compute the gradients for a list of variables.
grads_and_vars = opt.compute_gradients(loss, <list of variables>)

# grads_and_vars is a list of tuples (gradient, variable).  Do whatever you
# need to the 'gradient' part, for example cap them, etc.
capped_grads_and_vars = [(MyCapper(gv[0]), gv[1]) for gv in grads_and_vars]

# Ask the optimizer to apply the capped gradients.
opt.apply_gradients(capped_grads_and_vars)

Ecco MyCapperuna qualsiasi funzione che chiude il tuo gradiente. L'elenco delle funzioni utili (diverse da tf.clip_by_value()) è qui .


chiameresti ancora opt.minimize()dopo o chiameresti qualcosa di diverso come opt.run()suggerito in alcuni commenti su altre risposte?
reese0106

@ reese0106 No, devi assegnare il opt.apply_gradients(...)a una variabile come train_stepper esempio (proprio come faresti per opt.minimize(). E nel tuo ciclo principale lo chiami come al solito per allenartisess.run([train_step, ...], feed_dict)
dsalaj

Tieni presente che il gradiente è definito come il vettore delle derivate della perdita rispetto a tutti i parametri del modello. TensorFlow lo rappresenta come un elenco Python che contiene una tupla per ogni variabile e il suo gradiente. Ciò significa ritagliare la norma del gradiente, non è possibile ritagliare ogni tensore individualmente, è necessario considerare l'elenco in una volta (ad esempio utilizzando tf.clip_by_global_norm(list_of_tensors)).
danijar

8

Per coloro che desiderano comprendere l'idea del gradiente di ritaglio (per norma):

Ogni volta che la norma del gradiente è maggiore di una determinata soglia, ritagliamo la norma del gradiente in modo che rimanga all'interno della soglia. Questa soglia a volte è impostata su 5.

Sia g il gradiente e j max_norm_threshold .

Ora, se || g || > j , facciamo:

g = ( j * g ) / || g ||

Questa è l'implementazione eseguita in tf.clip_by_norm


se devo selezionare manualmente la soglia, esiste un metodo comune per farlo?
ningyuwhut

Questa è una sorta di magia nera suggerita in alcuni giornali. Altrimenti, devi fare molti esperimenti e scoprire quale funziona meglio.
kmario23

4

IMO la soluzione migliore è avvolgere il tuo ottimizzatore con il decoratore estimatore di TF tf.contrib.estimator.clip_gradients_by_norm:

original_optimizer = tf.train.AdamOptimizer(learning_rate=learning_rate)
optimizer = tf.contrib.estimator.clip_gradients_by_norm(original_optimizer, clip_norm=5.0)
train_op = optimizer.minimize(loss)

In questo modo devi definirlo solo una volta e non eseguirlo dopo ogni calcolo dei gradienti.

Documentazione: https://www.tensorflow.org/api_docs/python/tf/contrib/estimator/clip_gradients_by_norm


2

Il gradiente di ritaglio aiuta fondamentalmente in caso di gradienti che esplodono o che scompaiono. Dì che la tua perdita è troppo alta, il che si tradurrà in gradienti esponenziali che fluiranno attraverso la rete che potrebbero portare a valori Nan. Per ovviare a ciò, ritagliamo i gradienti all'interno di un intervallo specifico (da -1 a 1 o qualsiasi intervallo secondo la condizione).

clipped_value=tf.clip_by_value(grad, -range, +range), var) for grad, var in grads_and_vars

dove grads _and_vars sono le coppie di gradienti (che calcoli tramite tf.compute_gradients) e le loro variabili a cui verranno applicati.

Dopo il ritaglio, applichiamo semplicemente il suo valore utilizzando un ottimizzatore. optimizer.apply_gradients(clipped_value)

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.