2016-06-21 2 views
0

Ich habe ein 2-Schicht nicht-konvolutionelles Netzwerk in Tensorflow, mit tanh als Aktivierungsfunktion. Ich verstehe, dass Gewichte sollten durch sqrt(nInputs) zB geteilt mit einer abgeschnittenen Normalverteilung initialisiert werden:Warum verbessert das Unterdrücken von Gewichten die Leistung des Tensorflow-Neuralnetzes?

weightsLayer1 = tf.Variable(tf.div(tf.truncated_normal([nInputUnits, nUnitsHiddenLayer1),math.sqrt(nInputUnits)))) 

Sein ein bisschen eines unbeholfenen Neulings in NN und Tensorflow, ich dies fälschlicherweise implementiert als 2 Zeilen nur, um es besser lesbar:

Ich weiß jetzt, dass dies falsch ist und dass die zweite Zeile bewirkt, dass die Gewichte bei jedem Lernschritt neu berechnet werden. Zu meiner Überraschung führt die "inkorrekte" Implementierung jedoch zu einer besseren Leistung, sowohl in den Zug- als auch in den Test-/Evaluierungsdatensätzen. Ich dachte, dass die falsche 2-Linien-Implementierung ein Zugunglück sein sollte, da es Gewichte auf andere Werte als die vom Optimierer gewählten zurückrechnet (was ich erwarten würde, würde im Optimierungsprozess Chaos anrichten, aber es verbessert es tatsächlich) . Hat jemand dafür eine Erklärung? Ich benutze den Tensorflow adam optimizer.

-Update 2016.06.22 - der zweiten Codeblock oben aktualisiert. Diese

+0

Zunächst einmal: In der Zwei-Zeilen-Implementierung machen Sie die Division zweimal durch '' sqrt (nInputUnits) '', effektiv dividiert durch '' nInputUnits''. Ist das beabsichtigt? Zweitens: Ich verstehe deine Frage nicht genau? Haben Sie Bedenken, dass die Variable '' weightsLayer1' 'jedes Mal neu initialisiert wird, wenn Sie einen Trainingsschritt ausführen? Wenn dem so ist, ist das nicht der Fall. Ich kann das näher ausführen, aber ich bin mir nicht ganz sicher, ob Sie das wirklich fragen. – lballes

+0

Mein Fehler, der obige 2-Zeilen-Fall wurde hier falsch eingegeben, ich habe ihn oben korrigiert. Ich dividiere nur einmal mit 'sqrt (nInputUnits)'. Im 2-Zeilen-Fall weiß ich, dass 'weightsLayer1' nur einmal in der 'tf.Variable'-Zeile initialisiert wird. Meine Fragen: a) Stimmt es, dass die zweite Zeile ('weightsLayer1 = tf.div (weightsLayer1, math.sqrt (nInputUnits))' ') zur Laufzeit ausgeführt wird? b) Wenn ja, verändert es den Wert des 'WeightsLayer1', der vom Optimierer berechnet wurde und daher den Optimierungsprozess beeinflusst?c) Wenn ja, wie kann dies besser sein als die vom Optimizer berechneten "WeightsLayer1" -Werte? –

+0

Okay, ich denke ich habe es jetzt bekommen. Siehe meine Antwort unten. – lballes

Antwort

4

Sie haben Recht, dass weightsLayer1 = tf.div(weightsLayer1, math.sqrt(nInputUnits)) bei jedem Schritt ausgeführt wird. Aber das bedeutet NICHT, dass die Werte in der Gewichtsvariablen in jedem Schritt um sqrt(nInputUnits) herunterskaliert werden. Diese Zeile ist keine direkte Operation, die sich auf die in der Variablen gespeicherten Werte auswirkt. Er berechnet einen neuen Tensor, der die Werte in der Variablen geteilt durch sqrt(nInputUnits) enthält, und diesen Tensor nehme ich an, dann geht er in den Rest Ihres Berechnungsgraphen. Dies stört den Optimierer nicht. Sie definieren immer noch einen gültigen Berechnungsgraphen, nur mit einer etwas willkürlichen Skalierung der Gewichte. Der Optimierer kann immer noch die Gradienten in Bezug auf diese Variable berechnen (er wird sich durch Ihre Divisionsoperation zurück übertragen) und die entsprechenden Aktualisierungsoperationen erstellen.

In Bezug auf das Modell, das Sie definieren, sind die beiden Versionen völlig gleichwertig. Für jeden Wertebereich von weightsLayer1 im Originalmodell (wo Sie die Division nicht machen) können Sie sie einfach um sqrt(nInputUnits) skalieren und Sie erhalten die identischen Ergebnisse mit Ihrem zweiten Modell. Die beiden repräsentieren genau die gleiche Modellklasse, wenn Sie so wollen.

Warum funktioniert einer besser als der andere? Deine Vermutung ist genauso gut wie meine. Wenn Sie für alle Ihre Variablen die gleiche Aufteilung vorgenommen haben, haben Sie Ihre Lernrate effektiv auf sqrt(nInputUnits) aufgeteilt. Diese kleinere Lernrate könnte für das vorliegende Problem vorteilhaft gewesen sein.

Edit: Ich denke, dass die Tatsache, dass Sie der Variablen und dem neu erstellten Tensor denselben Namen geben, Verwirrung stiftet. Wenn Sie das tun

A = tf.Variable(1.0) 
A = tf.mul(A, 2.0) 
# Do something with A 

erzeugt dann die zweite Zeile einen neuen Tensor (wie oben beschrieben) und Sie erneut binden den Namen (und es ist nur ein Name) A zu diesem neuen Tensor. Für den zu definierenden Graph ist die Benennung absolut irrelevant. Der folgende Code definiert das gleiche Diagramm:

A = tf.Variable(1.0) 
B = tf.mul(A, 2.0) 
# Do something with B 

Vielleicht wird deutlich, wenn Sie den folgenden Code ausführen:

A = tf.Variable(1.0) 
print A 
B = A 
A = tf.mul(A, 2.0) 
print A 
print B 

Der Ausgang ist

<tensorflow.python.ops.variables.Variable object at 0x7ff025c02bd0> 
Tensor("Mul:0", shape=(), dtype=float32) 
<tensorflow.python.ops.variables.Variable object at 0x7ff025c02bd0> 

Das erste Mal, wenn Sie print A es sagt Sie, dass A ist ein variables Objekt. Nach der Ausführung von A = tf.mul(A, 2.0) und dem erneuten Drucken von A können Sie sehen, dass der Name A jetzt an ein Objekt tf.Tensor gebunden ist. Die Variable existiert jedoch immer noch, wie aus dem Blick auf das Objekt hinter dem Namen B ersichtlich ist.

+0

Danke. Wenn ich also 'A = tf.Variable (1.0)' und dann 'A = tf.mul (A, 2.0)' tue, verändert die zweite Zeile A nicht, sondern erzeugt einen anderen Tensor, auch A genannt, den Tensorflow zu benutzen weiß das zweite A und nicht das erste, wenn A anderswo im Berechnungsgraphen verwendet wird? Ich nehme an, das ist funktional äquivalent zum Ändern des Wertes in der Variablen A, ja? –

+0

Siehe aktualisierte Antwort. – lballes

1

ist, was die einzige Zeile Code tut:

t = tf.truncated_normal([ nInputUnits, nUnitsHiddenLayer1 ]) 

Erstellt einen Tensor Form mit [nInputUnits, nUnitsHiddenLayer1], initialisiert mit 1,0 als die Standardabweichung der abgestumpften Normalverteilung. (1.0 ist Standard stddev Wert)

t1 = tf.div(t, math.sqrt(nInputUnits)) 

divide alle Werte in t mit math.sqrt (nInputUnits)

Ihre zwei Zeilen Code zu tun genau das Gleiche. In der ersten Zeile und der zweiten Zeile werden alle Werte durch math.sqrt (nInputUnits) geteilt.

Was Ihre Aussage:

Ich weiß jetzt, dass dies falsch ist und dass die zweite Zeile bewirkt, dass die Gewichte bei jedem Lernschritt neu berechnet werden.

EDIT mein Fehler

der Tat, Sie haben Recht, sie durch math.sqrt unterteilt sind (nInputUnits) bei jedem execuction, aber nicht neu initialisiert! Der Punkt von Bedeutung ist, wo Sie tf.variable()

hier beide Linien setzen, werden nur einmal initialisiert:

weightsLayer1 = tf.truncated_normal([ nInputUnits, nUnitsHiddenLayer1 ]) 
weightsLayer1 = tf.Variable(tf.div(weightsLayer1, math.sqrt(nInputUnits))) 

und hier die zweite Zeile bei jedem Schritt vorgeformt wird:

weightsLayer1 = tf.Variable(tf.truncated_normal([ nInputUnits, nUnitsHiddenLayer1 ]) 
weightsLayer1 = tf.div(weightsLayer1, math.sqrt(nInputUnits)) 

Warum funktioniert die zweite bessere Ergebnisse liefern? es sieht für mich wie eine Art Normalisierung aus, aber jemand, der besser informiert ist, sollte das überprüfen.

Ps. Sie können schreiben, es besser lesbar wie folgt aus:

weightsLayer1 = tf.Variable(tf.truncated_normal([ nInputUnits, nUnitsHiddenLayer1 ] , stddev = 1./math.sqrt(nInputUnits)) 
+0

Mein Verständnis ist, dass z.B. 'Y = tf.Variable (2.0)' ist eine Initialisierung, die nur einmal ausgeführt wird, während 'Y = tf.div (Y, X) 'bei jedem Ausführungsschritt des Graphen abläuft. Ist das korrekt? Wenn ja, ja, ich initialisiere die Dinge einmal, aber es scheint, dass die zweite Zeile bei jedem Schritt ausgeführt würde und die Gewichtungswerte auf andere als die vom Optimierer berechneten Werte ändern würde. –