web-dev-qa-db-ja.com

事前にトレーニングされた畳み込みベースを使用した、kerasモデルの損失関数の奇妙な動作

写真から数値予測を行うために、Kerasでモデルを作成しようとしています。私のモデルにはdensenet121畳み込みベースがあり、その上にいくつかの追加レイヤーがあります。最後の2つのレイヤーを除くすべてのレイヤーは_layer.trainable = False_に設定されます。これは回帰タスクであるため、私の損失は平均二乗誤差です。トレーニング中に_loss: ~3_を取得しますが、まったく同じバッチのデータを評価すると_loss: ~30_が得られます。

_model.fit(x=dat[0],y=dat[1],batch_size=32)
_

エポック1/132/32 [==============================]-0秒11ms /ステップ-損失:2.5571

_model.evaluate(x=dat[0],y=dat[1])
_

32/32 [==============================]-2秒59ms /ステップ29.276123046875

トレーニングと評価の間、まったく同じ32枚の写真をフィードします。また、y_pred=model.predict(dat[0])からの予測値を使用して損失を計算し、numpyを使用して平均二乗誤差を作成しました。結果は、私が評価から得たものと同じでした(つまり、29.276123 ...)。

この動作は、畳み込みベースのBatchNormalizationレイヤーが原因である可能性があるという提案がありました( githubでの議論 )。もちろん、私のモデルのすべてのBatchNormalizationレイヤーも_layer.trainable=False_に設定されています。たぶん誰かがこの問題に遭遇し、解決策を見つけましたか?

6

解決策を見つけたようです。私が提案したように、問題はBatchNormalizationレイヤーにあります。それらはツリーを作成します1)平均を減算してstdで正規化します2)移動平均を使用して平均とstdの統計を収集します3)2つの追加パラメーター(ノードごとに2つ)をトレーニングします。トレーニング可能をFalseに設定すると、これら2つのパラメーターがフリーズし、レイヤーも平均と標準の統計の収集を停止します。しかし、レイヤーはトレーニング時間中にまだ正規化を実行しているようですトレーニングバッチを使用。おそらくそれはkerasのバグであるか、何らかの理由で故意にそれを行ったのかもしれません。その結果、トレーニング時間中の順伝播の計算は、予測時間と比較して異なりますトレーニング可能な属性がFalseに設定されている場合でも

私が考えることができる2つの可能な解決策があります:

  1. すべてのBatchNormalizationレイヤーをトレーニング可能に設定します。この場合、これらのレイヤーは、事前にトレーニングされたレイヤーを使用する代わりに、データセットから統計を収集します(これは大幅に異なる可能性があります!)。この場合、トレーニング中にすべてのBatchNormレイヤーをカスタムデータセットに調整します。
  2. モデルを2つの部分に分割します_model=model_base+model_top_。その後、_model_base_を使用してmodel_base.predict()で特徴を抽出し、これらの特徴を_model_top_にフィードして、_model_top_のみをトレーニングします。

最初の解決策を試しましたが、機能しているようです。

_model.fit(x=dat[0],y=dat[1],batch_size=32)

Epoch 1/1
32/32 [==============================] - 1s 28ms/step - loss: **3.1053**

model.evaluate(x=dat[0],y=dat[1])

32/32 [==============================] - 0s 10ms/step
**2.487905502319336**
_

これはいくつかのトレーニングの後でした-平均と標準に関する十分な統計が収集されるまで待つ必要があります。

まだ試したことがない2番目の解決策ですが、トレーニングと予測中の順伝播は同じであるため、うまくいくと確信しています。

- 更新。この問題が詳細に議論されている素晴らしいブログ投稿を見つけました。それをチェックしてください ここ

9

ただし、ドロップアウトレイヤーは通常、逆の効果を生み出し、トレーニング中の損失よりも評価の損失を少なくします。

必ずしも!ドロップアウト層では一部のニューロンがドロップされますが、ドロップアウト率に応じて出力が縮小されることに注意してください。推論時間(つまりテスト時間)では、ドロップアウトが完全に削除され、モデルを1つのエポックのみでトレーニングしたことを考慮すると、見た動作が発生する可能性があります。モデルを1つのエポックのみでトレーニングしているため、ニューロンの一部のみがドロップアウトレイヤーにドロップされていますが、それらはすべて推論時に存在することを忘れないでください。

より多くのエポックでモデルのトレーニングを続けると、トレーニングの損失とテストの損失(同じデータ上)がほぼ同じになると予想される場合があります。

自分で試してみてください。ドロップアウトレイヤーのtrainableパラメーターをFalseに設定し、これが発生するかどうかを確認してください。


トレーニングの1つのエポックの後、トレーニングの損失が同じデータのバッチでの評価の損失と等しくないことを見て、(私がそうであったように)混乱するかもしれません。また、これはDropoutまたはBatchNormalizationレイヤーを持つモデルに固有のものではありません。この例を考えてみましょう。

from keras import layers, models
import numpy as np

model = models.Sequential()
model.add(layers.Dense(1000, activation='relu', input_dim=100))
model.add(layers.Dense(1))

model.compile(loss='mse', optimizer='adam')
x = np.random.Rand(32, 100)
y = np.random.Rand(32, 1)

print("Training:")
model.fit(x, y, batch_size=32, epochs=1)

print("\nEvaluation:")
loss = model.evaluate(x, y)
print(loss)

出力:

Training:
Epoch 1/1
32/32 [==============================] - 0s 7ms/step - loss: 0.1520

Evaluation:
32/32 [==============================] - 0s 2ms/step
0.7577340602874756

つまり、同じデータに対して計算された場合、損失が異なるのはなぜですか。つまり、0.1520 != 0.7577

あなたがこれを尋ねるなら、それはあなたが私のように十分な注意を払っていないからです:それは0.1520は、モデルのパラメーターを更新する前(つまり、バックワードパスまたはバックプロパゲーションを実行する前)の損失です。そして0.7577は、モデルの重みが更新された後の損失です。使用されるデータが同じであっても、それらの損失値を計算するときのモデルの状態は同じではありません(別の質問:バックプロパゲーション後に損失が増加したのはなぜですか?それは、1つのエポックに対してのみトレーニングしたためです。したがって、重みの更新はまだ十分に安定していません)。

これを確認するために、検証データと同じデータバッチを使用することもできます。

model.fit(x, y, batch_size=32, epochs=1, validation_data=(x,y))

上記の変更された行を使用して上記のコードを実行すると、次のような出力が得られます(明らかに、正確な値は異なる場合があります)。

Training:
Train on 32 samples, validate on 32 samples
Epoch 1/1
32/32 [==============================] - 0s 15ms/step - loss: 0.1273 - val_loss: 0.5344

Evaluation:
32/32 [==============================] - 0s 89us/step
0.5344240665435791

検証の損失と評価の損失はまったく同じであることがわかります。これは、検証がエポックの最後に実行されるためです(つまり、モデルの重みがすでに更新されている場合)。

1
today