Почему один ReLU не может выучить ReLU?


15

В качестве продолжения Моей нейронной сети не могу даже изучить евклидово расстояние, я упростил еще больше и попытался обучить один ReLU (со случайным весом) одному ReLU. Это самая простая сеть, которая существует, и все же половину времени она не может сходиться.

Если исходное предположение имеет ту же ориентацию, что и цель, оно быстро учится и сходится к правильному весу 1:

анимация обучения ReLU ReLU

кривая потерь, показывающая точки схождения

Если первоначальное предположение «назад», оно застревает с нулевым весом и никогда не попадает в область с меньшими потерями:

анимация ReLU не в состоянии изучить ReLU

Кривая потерь ReLU не в состоянии изучить ReLU

крупный план кривой потерь на 0

Я не понимаю почему. Разве градиентный спуск не должен легко следовать кривой потерь к глобальным минимумам?

Пример кода:

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

введите описание изображения здесь

Подобные вещи случаются, если я добавляю смещение: функция двухмерных потерь является плавной и простой, но если откат начинается вверх ногами, он вращается вокруг и застревает (красные начальные точки), и не следует градиенту до минимума (как это делает для синих стартовых точек):

введите описание изображения здесь

Подобные вещи случаются, если я добавлю выходной вес и смещение тоже. (Он будет переворачиваться слева направо или снизу вверх, но не одновременно.)


3
@ Sycorax Нет, это не дубликат, он спрашивает о конкретной проблеме, а не общие советы. Я потратил значительное количество времени, сводя это к минимальному, полному и проверяемому примеру. Пожалуйста, не удаляйте его только потому, что он смутно похож на какой-то другой слишком широкий вопрос. Один из шагов в принятом ответе на этот вопрос: «Сначала создайте небольшую сеть с одним скрытым слоем и убедитесь, что она работает правильно. Затем постепенно добавьте дополнительную сложность модели и убедитесь, что каждый из них работает». Это именно то, что я делаю, и это не работает.
Эндолит

2
Я действительно наслаждаюсь этой "серией" на NN, примененной к простым функциям: eats_popcorn_gif:
Cam.Davidson.Pilon

ReLU функционирует как идеальный выпрямитель, например, диод. Это однонаправлено. Если вы хотите, чтобы направление изменилось, рассмотрите возможность использования softplus, затем переключитесь на ReLU, когда обучение будет положительным, или используйте какой-либо другой вариант, например ELU.
Карл

x<0x<0

1
Икс

Ответы:


14

ww=0w=0w=1вес инициализируется как отрицательный, можно сходиться к неоптимальному решению.

минвес,б| |е(Икс)-Y| |22е(Икс)знак равноМаксимум(0,весИкс+б)

е

е'(Икс)знак равно{вес,если Икс>00,если Икс<0

вес<00весзнак равно1|вес|

вес(0)<0вес(я)знак равно0

Это связано с феноменом умирающего отлива; см. обсуждение « Моя сеть ReLU не запускается»

Подход, который мог бы быть более успешным, заключался бы в использовании другой нелинейности, такой как утечка, которая не имеет так называемой проблемы «исчезающего градиента». Функция протекающего реле

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 решает проблему

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

Соответствующий код ниже; использовать opt_sgdили 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)

Я видел ту же проблему с LeakyReLU, ELU, SELU, когда у меня был выходной вес и смещение, но я не уверен, пробовал ли я их без выхода. Я проверю
эндолит

1
(Да, вы правы, что LeakyReLU и ELU отлично работают в этом примере)
эндолит

2
Я понял. Это будет делать градиентный спуск функции потерь, это просто , что функция потерь становится плоским (0 градиента) в 0 при приближении с отрицательной стороны, так что градиентные застревает там. Теперь это кажется очевидным. : D
эндолит

2
ww=0

2
w(i)
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.