Warum kann eine einzelne ReLU keine ReLU lernen?

15

Als Folge dessen, dass mein neuronales Netzwerk nicht einmal die euklidische Distanz lernen kann, vereinfachte ich noch mehr und versuchte, eine einzelne ReLU (mit zufälliger Gewichtung) zu einer einzelnen ReLU zu trainieren. Dies ist das einfachste Netzwerk, das es gibt, und dennoch scheitert die Konvergenz in der Hälfte der Zeit.

Wenn die anfängliche Vermutung mit der Ausrichtung des Ziels übereinstimmt, lernt es schnell und nähert sich dem korrekten Gewicht von 1 an:

Animation von ReLU lernen ReLU

Verlustkurve mit Konvergenzpunkten

Wenn die anfängliche Vermutung "rückwärts" ist, bleibt sie bei einer Gewichtung von Null stecken und gelangt niemals in den Bereich mit geringerem Verlust:

Animation von ReLU, die ReLU nicht lernen kann

Verlustkurve von ReLU, die ReLU nicht lernt

Nahaufnahme der Verlustkurve bei 0

Ich verstehe nicht warum. Sollte der Gefälleverlauf nicht einfach der Verlustkurve bis zu den globalen Minima folgen?

Beispielcode:

from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense, ReLU
from tensorflow import keras
import numpy as np
import matplotlib.pyplot as plt

batch = 1000


def tests():
    while True:
        test = np.random.randn(batch)

        # Generate ReLU test case
        X = test
        Y = test.copy()
        Y[Y < 0] = 0

        yield X, Y


model = Sequential([Dense(1, input_dim=1, activation=None, use_bias=False)])
model.add(ReLU())
model.set_weights([[[-10]]])

model.compile(loss='mean_squared_error', optimizer='sgd')


class LossHistory(keras.callbacks.Callback):
    def on_train_begin(self, logs={}):
        self.losses = []
        self.weights = []
        self.n = 0
        self.n += 1

    def on_epoch_end(self, batch, logs={}):
        self.losses.append(logs.get('loss'))
        w = model.get_weights()
        self.weights.append([x.flatten()[0] for x in w])
        self.n += 1


history = LossHistory()

model.fit_generator(tests(), steps_per_epoch=100, epochs=20,
                    callbacks=[history])

fig, (ax1, ax2) = plt.subplots(2, 1, True, num='Learning')

ax1.set_title('ReLU learning ReLU')
ax1.semilogy(history.losses)
ax1.set_ylabel('Loss')
ax1.grid(True, which="both")
ax1.margins(0, 0.05)

ax2.plot(history.weights)
ax2.set_ylabel('Weight')
ax2.set_xlabel('Epoch')
ax2.grid(True, which="both")
ax2.margins(0, 0.05)

plt.tight_layout()
plt.show()

Bildbeschreibung hier eingeben

Ähnliche Dinge passieren, wenn ich die Verzerrung hinzufüge: Die 2D-Verlustfunktion ist glatt und einfach, aber wenn das Relu verkehrt herum startet, dreht es sich und bleibt hängen (rote Startpunkte) und folgt dem Gefälle nicht bis zum Minimum (wie es ist) gilt für blaue Startpunkte):

Bildbeschreibung hier eingeben

Ähnliches passiert, wenn ich Output Weight und Bias hinzufüge. (Es wird von links nach rechts oder von unten nach oben gewendet, aber nicht von beiden.)

Endolith
quelle
3
@Sycorax Nein, dies ist kein Duplikat, sondern fragt nach einem bestimmten Problem und nicht nach allgemeinen Ratschlägen. Ich habe viel Zeit darauf verwendet, dies auf ein minimales, vollständiges und überprüfbares Beispiel zu reduzieren. Bitte löschen Sie es nicht, nur weil es einer anderen, zu weit gefassten Frage vage ähnelt. Einer der Schritte in der akzeptierten Antwort auf diese Frage lautet: "Bauen Sie zunächst ein kleines Netzwerk mit einer einzelnen verborgenen Ebene auf, und überprüfen Sie, ob es ordnungsgemäß funktioniert. Fügen Sie dann schrittweise zusätzliche Modellkomplexität hinzu, und überprüfen Sie, ob jede dieser Komponenten ebenfalls funktioniert." Genau das mache ich und es funktioniert nicht.
Endolith
2
Ich genieße diese "Serie" auf NN, angewendet auf einfache Funktionen: eats_popcorn_gif:
Cam.Davidson.Pilon
ReLU funktioniert wie ein idealer Gleichrichter, z. B. eine Diode. Es ist unidirektional. Wenn Sie die Richtung korrigieren möchten, ziehen Sie die Verwendung von Softplus in Betracht und wechseln Sie dann zu ReLU, wenn das Training positiv ist, oder verwenden Sie eine andere Variante wie ELUs.
Carl
x<0x<0
1
x

Antworten:

14

ww=0w=0w=1w Negativ initialisiert ist, ist es möglich, zu einer suboptimalen Lösung zu konvergieren.

minw,bf(x)y22f(x)=max(0,wx+b)

f

f(x)={w,if x>00,if x<0

w<00w=1|w|

w(0)<0w(i)=0

Dies hängt mit dem sterbenden Relu-Phänomen zusammen. für eine Diskussion sieheWeitere Informationen finden Mein ReLU-Netzwerk kann nicht gestartet werden

Ein Ansatz, der erfolgreicher sein könnte, wäre die Verwendung einer anderen Nichtlinearität, wie z. B. des Leaky Relu, bei dem das sogenannte "Vanishing Gradient" -Problem nicht vorliegt. Die Leaky Relu Funktion ist

g(x)={x,if x>0cx,otherwise
c|c|

g(x)={1,if x>0c,if x<0

c=0c0.10.3c<0c=1,|c|>1

wLeakyReLUReLUw=1

LeakyReLU behebt das Problem

from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense, ReLU
from tensorflow import keras
import numpy as np
import matplotlib.pyplot as plt

batch = 1000


def tests():
    while True:
        test = np.random.randn(batch)

        # Generate ReLU test case
        X = test
        Y = test.copy()
        Y[Y < 0] = 0

        yield X, Y


model = Sequential(
    [Dense(1, 
           input_dim=1, 
           activation=None, 
           use_bias=False)
    ])
model.add(keras.layers.LeakyReLU(alpha=0.3))
model.set_weights([[[-10]]])

model.compile(loss='mean_squared_error', optimizer='sgd')


class LossHistory(keras.callbacks.Callback):
    def on_train_begin(self, logs={}):
        self.losses = []
        self.weights = []
        self.n = 0
        self.n += 1

    def on_epoch_end(self, batch, logs={}):
        self.losses.append(logs.get('loss'))
        w = model.get_weights()
        self.weights.append([x.flatten()[0] for x in w])
        self.n += 1


history = LossHistory()

model.fit_generator(tests(), steps_per_epoch=100, epochs=20,
                    callbacks=[history])

fig, (ax1, ax2) = plt.subplots(2, 1, True, num='Learning')

ax1.set_title('LeakyReLU learning ReLU')
ax1.semilogy(history.losses)
ax1.set_ylabel('Loss')
ax1.grid(True, which="both")
ax1.margins(0, 0.05)

ax2.plot(history.weights)
ax2.set_ylabel('Weight')
ax2.set_xlabel('Epoch')
ax2.grid(True, which="both")
ax2.margins(0, 0.05)

plt.tight_layout()
plt.show()

w w(0)

w(0)=10

w(0)=1 w(0)=1w(0)=1

Der relevante Code ist unten; benutze opt_sgdoder opt_adam.

opt_sgd = keras.optimizers.SGD(lr=1e-2, momentum=0.9)
opt_adam = keras.optimizers.Adam(lr=1e-2, amsgrad=True)
model.compile(loss='mean_squared_error', optimizer=opt_sgd)
Sycorax sagt Reinstate Monica
quelle
Ich habe das gleiche Problem mit LeakyReLU, ELU, SELU gesehen, als ich ein Ausgangsgewicht und eine Vorspannung hatte, aber ich bin nicht sicher, ob ich diese ohne Ausgang ausprobiert habe. Ich werde überprüfen
Endolith
1
(Ja, Sie haben Recht, dass LeakyReLU und ELU für dieses Beispiel gut funktionieren)
Endolith
2
Oh ich verstehe. Es wird ein Gradientenabstieg der Verlustfunktion durchgeführt. Die Verlustfunktion wird nur flach (0 Gradient), wenn sie sich von der negativen Seite nähert, und der Gradientenabstieg bleibt dort hängen. Jetzt scheint es offensichtlich. : D
Endolith
2
ww=0
2
w(i)