単一の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()

ここに画像の説明を入力してください

バイアスを追加すると同様のことが起こります:2D損失関数は滑らかでシンプルですが、回転が逆さまに開始すると、回転してスタックし(赤色の開始点)、勾配が最小にならなくなります(そのように)青の開始点に対して)

ここに画像の説明を入力してください

出力の重みとバイアスも追加すると、同様のことが起こります。(左から右、または下から上に反転しますが、両方ではありません。)


3
@Sycoraxいいえ、これは重複ではありません。一般的なアドバイスではなく、特定の問題について尋ねます。これを最小限の、完全な、検証可能な例に減らすのにかなりの時間を費やしました。他の一般的な質問と漠然と似ているからといって、削除しないでください。その質問に対する受け入れられた答えの手順の1つは、「最初に、1つの隠れ層で小さなネットワークを構築し、それが正しく機能することを確認します。次に、モデルの複雑さを徐々に追加し、それらのそれぞれが機能することを確認します」それはまさに私がやっていることであり、それは動作していません。
エンドリス

2
単純な関数に適用されるNNのこの「シリーズ」を本当に楽しんでいます:eats_popcorn_gif:
Cam.Davidson.Pilon

ReLUは、ダイオードなどの理想的な整流器のように機能します。一方向です。方向を修正したい場合は、softplusの使用を検討してから、トレーニングが肯定的であればReLUに切り替えるか、ELUなどの他のバリアントを使用することを検討してください。
カール

これに別の方法を言うために、ReLUは役に立たないことが予想され、のための学習を見て ; それは平坦であり、学習しません。x<0バツ<0
カール

1
がゼロ未満の場合、勾配はゼロになる傾向があります。失速します。x
カール

回答:


14

ww=0w=0w=1w 負に初期化されると、次善の解に収束する可能性があります。

wbfバツy22fバツ=最大0wバツ+b

そして、あなたはそうするために一次最適化を使用しています。このアプローチの問題は、に勾配があることです。f

fバツ={wもし バツ>00もし バツ<0

で開始すると、の反対側に移動して正解(近づく必要があります。がある場合、これは困難です非常に、非常に小さく、勾配も同様に、非常に小さくなります。さらに、左から0に近づくほど、進行が遅くなります!w<00w=1|w|

これが、負のである初期化のプロットで、軌跡がすべて近くで失速する理由です。これは、2番目のアニメーションが示しているものでもあります。w0<0w=0

これは、死にかけているrelu現象に関連しています。議論については、My ReLUネットワークが起動に失敗するをご覧ください。

より成功する可能性のあるアプローチは、いわゆる「消失勾配」の問題を持たない漏れるreluなどの異なる非線形性を使用することです。漏れやすいrelu関数は

gバツ={バツもし バツ>0cバツさもないと
ここで、はとなる定数小さくてポジティブです。これが機能する理由は、導関数が「左側」に0でないことです。c|c|

gバツ={1もし バツ>0cもし バツ<0

設定するのが通常の解決策です。ほとんどの人はをやように選択します。使用されているのを見たことはありませんが、そのようなネットワークにどのような影響があるのか​​、もしあれば、どのような影響があるのか​​興味があります。(これは恒等関数になります。場合、多くのこのようなレイヤーの構成は、連続するレイヤーで勾配が大きくなるため、爆発勾配を引き起こす可能性があります。)c=0c0.10.3c<0c=1|c|>1

OPのコードを少し変更すると、アクティベーション機能の選択に問題があることを示すデモが提供されます。このコードはを負の値に初期化し、通常のの代わりにを使用します。損失は​​すぐに小さな値に減少し、重みは最適な正しく移動します。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 w0

このコードをいじってみたところ、初期化をにして、オプティマイザーをSGDからAdam、Adam + AMSGradまたはSGD + momentumに変更しても何の助けにもならないことがわかりました。さらに、SGDからAdamに変更すると、実際には、この問題の消失勾配を克服するのに役立たないことに加えて、進行が遅くなります。w0=10

一方、初期化を変更し、オプティマイザーをAdam(ステップサイズ0.01)に変更すると、実際に消失勾配を克服できます。およびSGDを運動量(ステップサイズ0.01)で使用する場合にも機能します。バニラSGD(ステップサイズ0.01)とを使用する場合でも機能します。w0=1 w 0 = 1 w 0 = 1w0=1w0=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作業罰金)
endolith

2
わかりました。それされて損失関数の勾配降下をやって、それが負の側から接近するとき勾配降下が立ち往生そこに着くように、損失関数は、0でフラット(0勾配)になるだけのことです。今では明らかです。:D
エンドリス

2
丁度。損失対プロットが0の近くに「キンク」を持っていることに注意してください。0の左側では、損失の勾配が0に消えているためです(ただし、損失はそれよりも高いため、これは準最適なソリューションです用)。さらに、このプロットは、損失関数が非凸型であることを示しています(3つ以上の場所で損失曲線を横切る線を引くことができます)。そのため、SGDなどのローカルオプティマイザーを使用する場合は注意が必要です。ww=0
Sycoraxは、Reinstate Monicaを言う

2
reluアクティベーションを使用する場合、ステップサイズがの特定の値に十分な大きさであれば、勢いのない SGDでも唇を乗り越えることができます。w
Sycoraxによると、Reinstate Monica
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.