Neden tek bir ReLU bir ReLU öğrenemez?


15

Bir takip gelince Benim sinir ağı bile Öklid mesafe öğrenemez Hatta daha ve tek relu için (rastgele ağırlığı) tek relu eğitmek çalıştı basitleştirilmiş. Bu, en basit ağdır ve yine de birleşemediği zamanın yarısıdır.

İlk tahmin hedefle aynı yönde ise, hızlı bir şekilde öğrenir ve doğru 1 ağırlığına yaklaşır:

ReLU'nun öğrenimi animasyonu ReLU

yakınsama noktalarını gösteren kayıp eğrisi

İlk tahmin "geriye" ise, sıfır ağırlıkta sıkışır ve asla daha düşük kayıp bölgesine geçmez:

ReLU'nun animasyonu ReLU'yu öğrenemiyor

ReLU'nun kayıp eğrisi ReLU'yu öğrenemiyor

0 kayıp eğrisi closeup

Nedenini anlamıyorum. Gradyan iniş, küresel minimadaki kayıp eğrisini kolayca takip etmemeli midir?

Örnek kod:

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

resim açıklamasını buraya girin

Yanlılık eklersem benzer şeyler olur: 2D kayıp işlevi pürüzsüz ve basittir, ancak relu baş aşağı başlarsa, daire etrafında döner ve sıkışır (kırmızı başlangıç ​​noktaları) ve degradeyi en aza indirmez (gibi) mavi başlangıç ​​noktaları için geçerlidir):

resim açıklamasını buraya girin

Çıkış ağırlığı ve yanlılığı da eklersem benzer şeyler olur. (Soldan sağa veya aşağıdan yukarıya, ancak ikisini birden çevirmeyecektir.)


3
@Sycorax Hayır, bu bir kopya değil, genel bir tavsiye değil, belirli bir sorun soruyor. Bunu Minimal, Complete ve Doğrulanabilir bir örneğe indirgeyerek önemli miktarda zaman harcadım. Lütfen sadece diğer geniş kapsamlı sorulara belirsiz bir şekilde benzediği için silmeyin. Bu soruya kabul edilen yanıttaki adımlardan biri "İlk olarak, tek bir gizli katmanla küçük bir ağ oluşturun ve doğru çalıştığını doğrulayın. Sonra aşamalı olarak ek model karmaşıklığı ekleyin ve her birinin de çalıştığını doğrulayın." Ben de tam olarak bunu yapıyorum ve çalışmıyor.
endolith

2
NN'de basit fonksiyonlara uygulanan bu "dizi" nin tadını çıkarıyorum: eats_popcorn_gif:
Cam.Davidson.Pilon 3:18 '

ReLU ideal bir doğrultucu, örneğin bir diyot gibi işlev görür. Tek yönlüdür. Yönün düzeltilmesini istiyorsanız, softplus kullanmayı, ardından eğitim pozitif olduğunda ReLU'ya geçmeyi veya ELU'lar gibi başka bir varyant kullanmayı düşünün.
Carl

x<0x<0

1
x

Yanıtlar:


14

ww=0w=0w=1w negatif olarak başlatılırsa, optimal olmayan bir çözüme yakınsama mümkündür.

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

ve bunu yapmak için birinci dereceden optimizasyon kullanıyorsunuz. Bu yaklaşımla ilgili bir problem, gradyanı olmasıdırf

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

ile başladığınızda , doğru cevaba yaklaşmak için diğer tarafına geçmeniz gerekir ( . Bunu yapmak zordur, çünküçok, çok küçük, gradyan da aynı şekilde kaybolacaktır. Dahası, soldan 0'a ne kadar yaklaşırsanız ilerlemeniz o kadar yavaş olacaktır!w<00w=1|w|

Bu nedenle, negatif olan başlatmalar için çizimlerinizde , yörüngeleriniz yakınında durur . Bu aynı zamanda ikinci animasyonunuzun gösterdiği şeydir.w(0)<0w(i)=0

Bu ölmekte olan relu fenomeni ile ilgilidir; bazı tartışmalar için bkz. ReLU ağım başlatılamıyor

Daha başarılı olabilecek bir yaklaşım, "yok olan gradyan" sorunu olmayan sızdıran relu gibi farklı bir doğrusal olmayanlık kullanmak olacaktır. Sızdıran relu işlevi

g(x)={x,if x>0cx,otherwise
; burada sabittir, böyleceküçük ve pozitif. Bunun işe yaramasının nedeni "solda" 0 değil.c|c|

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

ayarı sıradan relu. Çoğu insan veya gibi bir şey olarak seçer . kullanıldığını görmedim , ancak bu tür ağlar üzerinde ne gibi bir etkisinin olduğunu görmek isterim. ( bunun kimlik işlevine azaldığını unutmayın; , bu tür birçok katmanın bileşimleri, gradyanlar birbirini izleyen katmanlarda daha büyük hale geldiklerinden patlayan gradyanlara neden olabilir.)c=0c0.10.3c<0c=1,|c|>1

OP'nin kodunu biraz değiştirmek, sorunun aktivasyon işlevinin seçimiyle ilgili olduğunu gösterir. Bu kod başlatır olarak negatif ve kullanan sıradan yerine . Kayıp hızla küçük bir değere düşer ve ağırlık doğru bir şekilde , bu da en uygunudur.wLeakyReLUReLUw=1

LeakyReLU sorunu düzeltir

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

Bir başka karmaşıklık katmanı, sonsuz düzeyde hareket etmememizden, bunun yerine sonlu olarak birçok "sıçramada" ortaya çıkması ve bu sıçramaların bizi bir yinelemeden diğerine götürmesidir. Bazı koşullar mevcut olduğu bu araçlar burada negatif başlangıç vales olmaz takılıp; bu durumlar, ve kaybolan gradyanın üzerinden "atlayacak" kadar büyük gradyan iniş adımı boyutlarının özel kombinasyonları için ortaya çıkar .w w(0)

Bazı bu kod ile oynadım ve başlatmayı bırakarak ve optimizatörü SGD'den Adam, Adam + AMSGrad veya SGD + momentumuna değiştirmenin yardım etmek için hiçbir şey yapmadığını buldum. Dahası, SGD'den Adem'e geçmek, bu sorundaki kaybolan gradyanın üstesinden gelmemeye yardımcı olmanın yanı sıra ilerlemeyi yavaşlatıyor .w(0)=10

Diğer yandan, için başlatma değiştirmek ise ve Adam (adım büyüklüğü 0.01) en iyi duruma değiştirin ve aslında ufuk gradyanı üstesinden gelebilir. Ayrıca, ve SGD'yi momentumla (adım boyutu 0.01) kullanırsanız da çalışır. Vanilya SGD (adım boyutu 0.01) ve kullanıyorsanız bile çalışır .w(0)=1 w(0)=1w(0)=1

İlgili kod aşağıdadır; opt_sgdveya kullanın 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)

Ben çıkış ağırlığı ve önyargı vardı LeakyReLU, ELU, SELU ile aynı sorunu gördüm, ama çıktı olmadan bunları denedim emin değilim. Ben kontrol edecek
Endolit

1
(Evet haklısın bu örneğin LeakyReLU ve ELU çalışma ince)
Endolit

2
Ah, anladım. Bu bir işlev kaybı gradyan yaparken, olumsuz yan yaklaşırken gradyan iniş sıkışmış bulunmaktadır alır böylece kayıp fonksiyonu, 0 ° C'de, düz (0 gradyanı) olur sadece. Şimdi belli görünüyor. : D
endolit

2
Kesinlikle. vs kayıp arazilerinizin 0 yakınında bir "bükülme" ne sahip olduğuna dikkat edin : 0'ın solunda , kaybın gradyanı 0'a kaybolur (ancak, bu düşük bir çözümdür, çünkü kayıp orada olduğundan daha yüksektir) için ). Ayrıca, bu grafik, kayıp fonksiyonunun dışbükey olmadığını gösterir (3 veya daha fazla konumda kayıp eğrisini geçen bir çizgi çizebilirsiniz), böylece SGD gibi yerel optimize edicileri kullanırken dikkatli olmamız gerektiğini gösterir. ww=0
Sycorax, Reinstate Monica

2
Relu aktivasyonunu kullanırken, adım boyutu nin belirli değeri için yeterince büyükse, momentum olmadan SGD bile dudağın üzerinden geçebilir . w(i)
Sycorax, Reinstate Monica'nın
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.