Ich versuche, die folgende Variante der Bump-Funktion zu codieren , die komponentenweise angewendet wird:
wo σ trainierbar ist; aber es funktioniert nicht (Fehler unten gemeldet).
Mein Versuch:
Folgendes habe ich bisher codiert (wenn es hilft). Angenommen, ich habe zwei Funktionen (zum Beispiel):
def f_True(x):
# Compute Bump Function
bump_value = 1-tf.math.pow(x,2)
bump_value = -tf.math.pow(bump_value,-1)
bump_value = tf.math.exp(bump_value)
return(bump_value)
def f_False(x):
# Compute Bump Function
x_out = 0*x
return(x_out)
class trainable_bump_layer(tf.keras.layers.Layer):
def __init__(self, *args, **kwargs):
super(trainable_bump_layer, self).__init__(*args, **kwargs)
def build(self, input_shape):
self.threshold_level = self.add_weight(name='threshlevel',
shape=[1],
initializer='GlorotUniform',
trainable=True)
def call(self, input):
# Determine Thresholding Logic
The_Logic = tf.math.less(input,self.threshold_level)
# Apply Logic
output_step_3 = tf.cond(The_Logic,
lambda: f_True(input),
lambda: f_False(input))
return output_step_3
Fehlermeldung:
Train on 100 samples
Epoch 1/10
WARNING:tensorflow:Gradients do not exist for variables ['reconfiguration_unit_steps_3_3/threshlevel:0'] when minimizing the loss.
WARNING:tensorflow:Gradients do not exist for variables ['reconfiguration_unit_steps_3_3/threshlevel:0'] when minimizing the loss.
32/100 [========>.....................] - ETA: 3s
...
tensorflow:Gradients do not exist for variables
Darüber hinaus scheint es nicht komponentenweise angewendet zu werden (abgesehen von dem nicht trainierbaren Problem). Was könnte das Problem sein?
python
tensorflow
machine-learning
keras
tf.keras
FlabbyTheKatsu
quelle
quelle
input
? ist es ein Skalar?Antworten:
Ich bin ein bisschen überrascht, dass niemand den Hauptgrund (und den einzigen) für die gegebene Warnung erwähnt hat! Wie es scheint, soll dieser Code die verallgemeinerte Variante der Bump-Funktion implementieren; Schauen Sie sich jedoch die erneut implementierten Funktionen an:
Der Fehler ist offensichtlich: In diesen Funktionen wird das trainierbare Gewicht der Schicht nicht verwendet! Es ist also keine Überraschung, dass Sie die Meldung erhalten, dass dafür kein Farbverlauf vorhanden ist: Sie verwenden ihn überhaupt nicht, also keinen Farbverlauf, um ihn zu aktualisieren! Dies ist vielmehr genau die ursprüngliche Bump-Funktion (dh ohne trainierbares Gewicht).
Aber man könnte sagen: "Zumindest habe ich das trainierbare Gewicht im Zustand von verwendet
tf.cond
, also muss es einige Steigungen geben?!"; es ist jedoch nicht so und lassen Sie mich die Verwirrung beseitigen:Wie Sie ebenfalls bemerkt haben, sind wir zunächst an einer elementweisen Konditionierung interessiert. Also anstatt
tf.cond
Sie müssen verwendentf.where
.Das andere Missverständnis besteht darin, zu behaupten, dass da
tf.less
als Bedingung verwendet wird und da es nicht differenzierbar ist, dh keinen Gradienten in Bezug auf seine Eingaben hat (was wahr ist: Es gibt keinen definierten Gradienten für eine Funktion mit boolescher Ausgabe in Bezug auf ihre Real- Wert Eingaben!), dann ergibt sich die gegebene Warnung!relu(x) = 0 if x < 0 else x
. Wenn die Ableitung der Bedingung, dhx < 0
wird berücksichtigt / benötigt, was nicht existiert, dann könnten wir ReLU in unseren Modellen nicht verwenden und sie überhaupt mit gradientenbasierten Optimierungsmethoden trainieren!)(Hinweis: Ab hier würde ich den Schwellenwert wie in der Gleichung als Sigma bezeichnen und bezeichnen ).
Gut! Wir haben den Grund für den Fehler in der Implementierung gefunden. Könnten wir das beheben? Na sicher! Hier ist die aktualisierte Arbeitsimplementierung:
Einige Punkte zu dieser Implementierung:
Wir haben ersetzt
tf.cond
mit ,tf.where
um elementweise Anlage zu tun.Wie Sie sehen können, verwenden wir im Gegensatz zu Ihrer Implementierung, bei der nur eine Seite der Ungleichung überprüft wurde
tf.math.less
,tf.math.greater
und auchtf.logical_and
, um herauszufinden, ob die Eingabewerte Größen von weniger alssigma
(alternativ können wir dies mit nurtf.math.abs
undtf.math.less
ohne Unterschied tun) !). Und lassen Sie es uns wiederholen: Die Verwendung von Booleschen Ausgabefunktionen auf diese Weise verursacht keine Probleme und hat nichts mit Ableitungen / Verläufen zu tun.Wir verwenden auch eine Nicht-Negativitätsbeschränkung für den von der Schicht gelernten Sigma-Wert. Warum? Weil Sigma-Werte unter Null keinen Sinn ergeben (dh der Bereich
(-sigma, sigma)
ist schlecht definiert, wenn Sigma negativ ist).Und unter Berücksichtigung des vorherigen Punktes achten wir darauf, den Sigma-Wert richtig zu initialisieren (dh auf einen kleinen nicht negativen Wert).
Und bitte machen Sie auch keine Dinge wie
0.0 * inputs
! Es ist redundant (und ein bisschen komisch) und entspricht0.0
; und beide haben einen Gradienten von0.0
(wrtinputs
). Das Multiplizieren von Null mit einem Tensor fügt nichts hinzu oder löst kein bestehendes Problem, zumindest in diesem Fall nicht!Testen wir es jetzt, um zu sehen, wie es funktioniert. Wir schreiben einige Hilfsfunktionen, um Trainingsdaten basierend auf einem festen Sigma-Wert zu generieren und um ein Modell zu erstellen, das eine einzelne
BumpLayer
mit der Eingabeform von enthält(1,)
. Mal sehen, ob es den Sigma-Wert lernen kann, der zum Generieren von Trainingsdaten verwendet wird:Ja, es könnte den Wert von Sigma lernen, der zum Generieren von Daten verwendet wird! Aber ist garantiert, dass es tatsächlich für alle unterschiedlichen Werte von Trainingsdaten und Initialisierung von Sigma funktioniert? Die Antwort ist nein! Tatsächlich ist es möglich, dass Sie den obigen Code ausführen und
nan
nach dem Training den Wert von Sigma oderinf
den Verlustwert erhalten! Also, was ist das Problem? Warum könnten diesnan
oderinf
Werte erzeugt werden? Lassen Sie es uns unten diskutieren ...Umgang mit numerischer Stabilität
Eines der wichtigsten Dinge, die beim Erstellen eines maschinellen Lernmodells und beim Verwenden gradientenbasierter Optimierungsmethoden zum Trainieren berücksichtigt werden müssen, ist die numerische Stabilität von Operationen und Berechnungen in einem Modell. Wenn durch eine Operation oder ihren Gradienten extrem große oder kleine Werte erzeugt werden, würde dies mit ziemlicher Sicherheit den Trainingsprozess stören (dies ist beispielsweise einer der Gründe für die Normalisierung der Bildpixelwerte in CNNs, um dieses Problem zu vermeiden).
Schauen wir uns also diese verallgemeinerte Bump-Funktion an (und verwerfen wir vorerst die Schwellenwertbildung). Es ist offensichtlich, dass diese Funktion Singularitäten (dh Punkte, an denen entweder die Funktion oder ihr Gradient nicht definiert ist) bei
x^2 = sigma
(dh wannx = sqrt(sigma)
oderx=-sqrt(sigma)
) aufweist. Das animierte Diagramm unten zeigt die Höckerfunktion (die durchgezogene rote Linie), ihre Ableitung von Sigma (die gepunktete grüne Linie)x=sigma
undx=-sigma
Linien (zwei vertikale gestrichelte blaue Linien), wenn Sigma von Null beginnt und auf 5 erhöht wird:Wie Sie sehen können, verhält sich die Funktion in der Region der Singularitäten nicht für alle Sigma-Werte in dem Sinne gut, dass sowohl die Funktion als auch ihre Ableitung in diesen Regionen extrem große Werte annehmen. Bei einem Eingabewert in diesen Regionen für einen bestimmten Sigma-Wert würden also explodierende Ausgabe- und Gradientenwerte erzeugt, daher das Problem des
inf
Verlustwerts.Darüber hinaus gibt es ein problematisches Verhalten,
tf.where
das die Ausgabe vonnan
Werten für die Sigma-Variable in der Schicht verursacht: überraschenderweise, wenn der erzeugte Wert im inaktiven Zweig vontf.where
extrem groß ist oderinf
was mit der Bump-Funktion zu extrem großen oderinf
Gradientenwerten führt , dann von der Gradienttf.where
wärenan
, trotz der Tatsache , dass dieinf
in ist inaktiv Zweig und ist nicht einmal ausgewählt (siehe diese Github Ausgabe , die diese diskutiert genau) !!Gibt es also eine Problemumgehung für dieses Verhalten von
tf.where
? Ja, tatsächlich gibt es einen Trick, um dieses Problem irgendwie zu lösen, der in dieser Antwort erläutert wird : Grundsätzlich können wir einen zusätzlichen verwenden,tf.where
um zu verhindern, dass die Funktion auf diese Regionen angewendet wird. Mit anderen Worten, anstattself.bump_function
auf einen Eingabewert anzuwenden , filtern wir diejenigen Werte, die NICHT im Bereich liegen(-self.sigma, self.sigma)
(dh im tatsächlichen Bereich, in dem die Funktion angewendet werden soll), und speisen die Funktion stattdessen mit Null (was immer sichere Werte erzeugt, d. H. ist gleichexp(-1)
):Das Anwenden dieses Fixes würde das Problem der
nan
Werte für Sigma vollständig lösen . Lassen Sie es uns anhand von Trainingsdatenwerten bewerten, die mit verschiedenen Sigma-Werten generiert wurden, und sehen, wie es funktionieren würde:Es könnte alle Sigma-Werte richtig lernen! Das ist schön. Diese Problemumgehung hat funktioniert! Es gibt jedoch eine Einschränkung: Dies funktioniert garantiert ordnungsgemäß und lernt jeden Sigma-Wert, wenn die Eingabewerte für diese Ebene größer als -1 und kleiner als 1 sind (dh dies ist der Standardfall unserer
generate_data
Funktion). Andernfalls besteht immer noch das Problem desinf
Verlustwerts, der auftreten kann, wenn die Eingabewerte eine Größe von mehr als 1 haben (siehe Punkt 1 und 2 unten).Hier sind einige Denkanstöße für Neugierige und Interessierte:
Es wurde nur erwähnt, dass wenn die Eingabewerte für diese Ebene größer als 1 oder kleiner als -1 sind, dies Probleme verursachen kann. Können Sie argumentieren, warum dies der Fall ist? (Hinweis: Verwenden Sie das obige animierte Diagramm und berücksichtigen Sie Fälle, in denen
sigma > 1
der Eingabewert zwischensqrt(sigma)
undsigma
(oder zwischen-sigma
und) liegt-sqrt(sigma)
.)Können Sie eine Lösung für das Problem in Punkt 1 bereitstellen, dh, dass die Ebene für alle Eingabewerte funktionieren kann? (Hinweis: Überlegen Sie sich wie bei der Problemumgehung
tf.where
, wie Sie die unsicheren Werte, auf die die Bump-Funktion angewendet werden könnte, weiter herausfiltern und eine explodierende Ausgabe / einen explodierenden Gradienten erzeugen können.)Wenn Sie jedoch nicht daran interessiert sind, dieses Problem zu beheben, und diese Ebene in einem Modell wie bisher verwenden möchten, wie können Sie dann sicherstellen, dass die Eingabewerte für diese Ebene immer zwischen -1 und 1 liegen? (Hinweis: Als eine Lösung gibt es eine häufig verwendete Aktivierungsfunktion, die Werte genau in diesem Bereich erzeugt und möglicherweise als Aktivierungsfunktion der Schicht vor dieser Schicht verwendet werden kann.)
Wenn Sie sich das letzte Code-Snippet ansehen, werden Sie sehen, dass wir es verwendet haben
epochs=3 if s < 1 else (5 if s < 5 else 10)
. Warum ist das so? Warum brauchen große Sigma-Werte mehr Epochen, um gelernt zu werden? (Hinweis: Verwenden Sie erneut das animierte Diagramm und betrachten Sie die Ableitung der Funktion für Eingabewerte zwischen -1 und 1, wenn der Sigma-Wert zunimmt. Wie groß sind sie?)Sie müssen wir auch die erzeugten Trainingsdaten für jede überprüfen
nan
,inf
oder extrem große Werte vony
und filtern sie aus? (Hinweis: Ja, wennsigma > 1
und Wertebereich, dhmin_x
undmax_x
, außerhalb von(-1, 1)
; ansonsten nein, das ist nicht notwendig! Warum ist das so? Als Übung übrig!)quelle
Leider ist keine Operation zur Überprüfung, ob sie
x
innerhalb(-σ, σ)
liegt, differenzierbar, und daher kann σ nicht über eine Gradientenabstiegsmethode gelernt werden. Insbesondere ist es nicht möglich, die Gradienten in Bezug auf zu berechnen,self.threshold_level
datf.math.less
dies in Bezug auf die Bedingung nicht differenzierbar ist.In Bezug auf die elementweise Bedingung können Sie stattdessen tf.where verwenden , um Elemente aus
f_True(input)
oderf_False(input)
gemäß den komponentenweisen booleschen Werten der Bedingung auszuwählen . Zum Beispiel:HINWEIS: Ich habe anhand des angegebenen Codes geantwortet, in dem
self.threshold_level
weder verwendetf_True
noch verwendet wirdf_False
. Wennself.threshold_level
in diesen Funktionen wie in der bereitgestellten Formel verwendet wird, ist die Funktion natürlich in Bezug auf differenzierbarself.threshold_level
.Aktualisiert 19/04/2020: Vielen Dank an @today für die Klarstellung .
quelle
tf.math.less
in der Bedingung und der Tatsache zu tun , dass sie nicht differenzierbar ist. Die Bedingung muss nicht differenzierbar sein, damit dies funktioniert. Der Fehler liegt in der Tatsache, dass das trainierbare Gewicht überhaupt nicht verwendet wird, um die Ausgabe der Schicht zu erzeugen (dh es gibt keine Spur davon in der Ausgabe). Weitere Informationen hierzu finden Sie im ersten Teil meiner Antwort.Ich schlage vor, Sie versuchen eine Normalverteilung anstelle einer Beule. In meinen Tests hier verhält sich diese Bump-Funktion nicht gut (ich kann keinen Fehler finden, aber nicht verwerfen, aber meine Grafik zeigt zwei sehr scharfe Bumps, was für Netzwerke nicht gut ist).
Bei einer Normalverteilung erhalten Sie eine regelmäßige und differenzierbare Erhebung, deren Höhe, Breite und Mitte Sie steuern können.
Sie können diese Funktion also ausprobieren:
Probieren Sie es in einem Diagramm aus und sehen Sie, wie es sich verhält.
Dafür:
quelle