Wie wende ich Gradienten-Clipping in TensorFlow an?

96

Betrachtet man den Beispielcode .

Ich würde gerne wissen, wie man Gradientenbeschneidungen in diesem Netzwerk auf dem RNN anwendet, wo die Möglichkeit besteht, dass Gradienten explodieren.

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

Dies ist ein Beispiel, das verwendet werden könnte, aber wo führe ich das ein? In der Def von 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)

Dies ist jedoch nicht sinnvoll, da der Tensor _X die Eingabe und nicht der Grad ist. Was soll abgeschnitten werden?

Muss ich dafür meinen eigenen Optimierer definieren oder gibt es eine einfachere Option?

Arsenal Fanatic
quelle

Antworten:

143

Das Abschneiden von Verläufen muss nach der Berechnung der Verläufe erfolgen, bevor sie angewendet werden, um die Parameter des Modells zu aktualisieren. In Ihrem Beispiel werden beide Dinge von der AdamOptimizer.minimize()Methode behandelt.

Um Ihre Verläufe zu beschneiden, müssen Sie sie explizit berechnen, beschneiden und anwenden, wie in diesem Abschnitt in der API-Dokumentation von TensorFlow beschrieben . Insbesondere müssen Sie den Aufruf der minimize()Methode durch Folgendes ersetzen :

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)
Styrke
quelle
4
Styrke, danke für den Beitrag. Wissen Sie, wie die nächsten Schritte aussehen, um eine Iteration des Optimierers auszuführen? In der Regel wird ein Optimierer als instanziiert optimizer = tf.train.AdamOptimizer(learning_rate=learning_rate).minimize(cost) und anschließend wird eine Iteration des Optimierers durchgeführt, optimizer.run()aber die Verwendung optimizer.run()scheint in diesem Fall nicht zu funktionieren.
Apfelwein
6
Ok, es optimizer.apply_gradients(capped_gvs)muss etwas zugewiesen werden, x = optimizer.apply_gradients(capped_gvs)dann können Sie innerhalb Ihrer Sitzung trainieren alsx.run(...)
Apfelwein
3
Wenden Sie sich an @ remi-cuingnet, um den netten Bearbeitungsvorschlag zu erhalten . (Was leider von hastigen Rezensenten abgelehnt wurde)
Styrke
Das gibt mir UserWarning: Converting sparse IndexedSlices to a dense Tensor with 148331760 elements. This may consume a large amount of memory.also irgendwie meine spärlichen Farbverläufe in dichte umgewandelt. Irgendeine Idee, wie man dieses Problem überwinden kann?
Pekka
8
Tatsächlich ist der richtige Weg, um Gradienten (gemäß Tensorflow-Dokumenten, Informatikern und Logik) zu beschneiden, mit tf.clip_by_global_norm, wie von @danijar
gdelab am
116

Trotz allem, was populär zu sein scheint, möchten Sie wahrscheinlich den gesamten Farbverlauf nach seiner globalen Norm abschneiden:

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

Das Abschneiden jeder Gradientenmatrix ändert einzeln ihre relative Skalierung, ist aber auch möglich:

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 berechnet ein Band die Farbverläufe, die Optimierer stammen von Keras, und wir müssen das Update nicht speichern, da es automatisch ausgeführt wird, ohne es an eine Sitzung zu übergeben:

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))
Danijar
quelle
10
Gutes Beispiel mit clip_by_global_norm()! Dies wird auch wie the correct way to perform gradient clippingin den Tensorflow-Dokumenten beschrieben: tensorflow.org/versions/r1.2/api_docs/python/tf/…
MZHm
9
@Escachator Es ist empirisch und hängt von Ihrem Modell und möglicherweise der Aufgabe ab. Was ich tue, ist, die Gradientennorm zu visualisieren tf.global_norm(gradients), um den üblichen Bereich zu sehen, und dann etwas darüber zu schneiden, um zu verhindern, dass Ausreißer das Training durcheinander bringen.
Danijar
1
Würden Sie immer noch danach anrufen opt.minimize()oder würden Sie etwas anderes anrufen, wie opt.run()es in einigen Kommentaren zu anderen Antworten vorgeschlagen wird?
reese0106
3
@ reese0106 Nein, optimizer.minimize(loss)ist nur eine Abkürzung zum Berechnen und Anwenden der Verläufe. Sie können das Beispiel in meiner Antwort mit ausführen sess.run(optimize).
Danijar
1
Wenn ich also tf.estimator.EstimatorSpec(mode, loss=loss, train_op=train_op)innerhalb einer Experimentfunktion verwenden optimizewürde, würde Ihre meine train_opkorrekte ersetzen ? Im Moment train_op = optimizer.minimize(loss, global_step=global_step))versuche ich sicherzustellen, dass ich mich entsprechend
anpasse
10

Dies wird in der Dokumentation tatsächlich richtig erklärt. ::

Durch Aufrufen von minim () werden sowohl die Gradienten berechnet als auch auf die Variablen angewendet. Wenn Sie die Farbverläufe vor dem Anwenden verarbeiten möchten, können Sie den Optimierer stattdessen in drei Schritten verwenden:

  • Berechnen Sie die Farbverläufe mit compute_gradients ().
  • Verarbeiten Sie die Farbverläufe nach Ihren Wünschen.
  • Wenden Sie die verarbeiteten Verläufe mit apply_gradients () an.

In dem von ihnen bereitgestellten Beispiel verwenden sie diese drei Schritte:

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

Hier MyCapperist jede Funktion, die Ihren Farbverlauf begrenzt. Die Liste der nützlichen Funktionen (außer tf.clip_by_value()) finden Sie hier .

Salvador Dali
quelle
Würden Sie immer noch danach anrufen opt.minimize()oder würden Sie etwas anderes anrufen, wie opt.run()es in einigen Kommentaren zu anderen Antworten vorgeschlagen wird?
reese0106
@ reese0106 Nein, Sie müssen das opt.apply_gradients(...)einer Variablen wie train_stepzum Beispiel zuweisen (genau wie Sie es für opt.minimize()sess.run([train_step, ...], feed_dict)
tun
Beachten Sie, dass der Gradient als der Vektor der Ableitungen des Verlusts für alle Parameter im Modell definiert ist. TensorFlow stellt es als Python-Liste dar, die ein Tupel für jede Variable und ihren Verlauf enthält. Dies bedeutet, dass Sie zum Schneiden der Gradientennorm nicht jeden Tensor einzeln abschneiden können. Sie müssen die Liste sofort berücksichtigen (z tf.clip_by_global_norm(list_of_tensors). B. mit ).
Danijar
8

Für diejenigen, die die Idee des Gradientenschneidens (nach Norm) verstehen möchten:

Immer wenn die Gradientennorm größer als ein bestimmter Schwellenwert ist, schneiden wir die Gradientennorm so ab, dass sie innerhalb des Schwellenwerts bleibt. Dieser Schwellenwert wird manchmal auf gesetzt 5.

Der Gradient sei g und der max_norm_threshold sei j .

Nun, wenn || g || > j , wir machen:

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

Dies ist die Implementierung in tf.clip_by_norm

kmario23
quelle
Wenn ich den Schwellenwert von Hand auswählen muss, gibt es eine gängige Methode, um dies zu tun?
Ningyuwhut
Dies ist eine Art schwarze Magie, die in einigen Zeitungen vorgeschlagen wird. Ansonsten müssen Sie viele Experimente durchführen und herausfinden, welches besser funktioniert.
kmario23
4

IMO ist die beste Lösung, Ihren Optimierer mit dem Schätzer-Dekorator von TF zu versehen 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)

Auf diese Weise müssen Sie dies nur einmal definieren und nicht nach jeder Gradientenberechnung ausführen.

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

Ido Cohn
quelle
2

Gradient Clipping hilft im Grunde bei explodierenden oder verschwindenden Gradienten. Sagen Sie, Ihr Verlust ist zu hoch, was dazu führt, dass exponentielle Gradienten durch das Netzwerk fließen, was zu Nan-Werten führen kann. Um dies zu überwinden, schneiden wir Farbverläufe innerhalb eines bestimmten Bereichs (-1 bis 1 oder eines beliebigen Bereichs gemäß der Bedingung).

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

Dabei sind Absolventen _und_vars die Gradientenpaare (die Sie über tf.compute_gradients berechnen) und ihre Variablen, auf die sie angewendet werden.

Nach dem Abschneiden wenden wir den Wert einfach mit einem Optimierer an. optimizer.apply_gradients(clipped_value)

Raj
quelle