バウンディングボックス回帰-失敗の冒険

Jan 20 2021

私はニューラルネットワークに関する多くの問題を解決しましたが、画像を扱うことはめったにありません。バウンディングボックス回帰ネットワークの作成には約18時間かかりますが、完全に失敗し続けています。一部の損失関数では、トレーニングと検証中に80%の精度が要求されますが(両方で非常に大きな損失があります)、予測をテストすると、特定の方向に1つまたは2つのピクセルしか移動せず、データを完全に無視しているように見えるバウンディングボックスが明らかになります。IoU損失の形式を実装しましたが、IoUがゼロに固定されていることがわかりました...これは、トレーニング後の出力に基づいて明らかに当てはまります。:)。誰かにこれを見て、次に進む方法についてアドバイスをもらいたいです。

私が持っているもの

それぞれにランダムに配置された1文字の200x100x3画像の40000の例を生成しています。同時に、トレーニングサンプルごとにグラウンドトゥルースバウンディングボックスを生成しています。これがすべて機能し、データが正しいことを徹底的に検証しました。

私はそれに何をしますか

次に、200x100x3の画像をグレースケールに変換して、200x100x1の画像を生成します。次に、画像が正規化され、境界ボックスが0から1の間に収まるようにスケーリングされます。簡略化された形式では、次のようになります。

x_train_normalized = (x_data - 127.5) / 127.5
y_train_scaled = boxes[:TRAIN]/[WIDTH,HEIGHT,WIDTH,HEIGHT]

私はこのデータを注意深く調べ、そこから画像やバウンディングボックスを再構成しました。これは間違いなく機能しています。

トレーニング

トレーニングするために、mse他の多くの試みを行った後、すべてが同じようにひどく失敗した後、単純なカスタムIOU損失関数を実装しました。実際にはを返します-ln(IoU)。損失は​​(奇妙なことに?)複数のエポックにわたってゼロに固定されていたので、私は紙に基づいてこの変更を行いました。

(損失関数:)

import tensorflow.keras.backend as kb
def iou_loss(y_actual,y_pred):
    b1 = y_actual
    b2 = y_pred
#    tf.print(b1)
#    tf.print(b2)
    zero = tf.convert_to_tensor(0.0, b1.dtype)
    b1_ymin, b1_xmin, b1_ymax, b1_xmax = tf.unstack(b1, 4, axis=-1)
    b2_ymin, b2_xmin, b2_ymax, b2_xmax = tf.unstack(b2, 4, axis=-1)
    b1_width = tf.maximum(zero, b1_xmax - b1_xmin)
    b1_height = tf.maximum(zero, b1_ymax - b1_ymin)
    b2_width = tf.maximum(zero, b2_xmax - b2_xmin)
    b2_height = tf.maximum(zero, b2_ymax - b2_ymin)
    b1_area = b1_width * b1_height
    b2_area = b2_width * b2_height

    intersect_ymin = tf.maximum(b1_ymin, b2_ymin)
    intersect_xmin = tf.maximum(b1_xmin, b2_xmin)
    intersect_ymax = tf.minimum(b1_ymax, b2_ymax)
    intersect_xmax = tf.minimum(b1_xmax, b2_xmax)
    intersect_width = tf.maximum(zero, intersect_xmax - intersect_xmin)
    intersect_height = tf.maximum(zero, intersect_ymax - intersect_ymin)
    intersect_area = intersect_width * intersect_height

    union_area = b1_area + b2_area - intersect_area
    iou = -1 * tf.math.log(tf.math.divide_no_nan(intersect_area, union_area))
    return iou

ネットワーク

これは、何度も何度も繰り返されてきました。私が言ったように、私はNNに関する他の多くの問題を解決しました...これは私を完全に立ち往生させた最初の問題です。この時点で、ネットワークは劇的に削除されますが、トレーニングにまったく失敗し続けます。

import tensorflow as tf
from tensorflow import keras
from tensorflow.keras import layers, optimizers

tf.keras.backend.set_floatx('float32') # Use Float32s for everything

input_shape = x_train_normalized.shape[-3:]
model = keras.Sequential()
model.add(layers.Conv2D(4, 16, activation = tf.keras.layers.LeakyReLU(alpha=0.2), input_shape=input_shape))
model.add(layers.MaxPooling2D(pool_size=(3, 3), strides=(2, 2)))
model.add(layers.Dropout(0.2))
model.add(layers.Flatten())
model.add(layers.Dense(200, activation = tf.keras.layers.LeakyReLU(alpha=0.2)))
model.add(layers.Dense(64, activation=tf.keras.layers.LeakyReLU(alpha=0.2)))
model.add(layers.Dense(4, activation="sigmoid"))

model.compile(loss = iou_loss, optimizer = "adadelta", metrics=['accuracy'])
history = model.fit(x_train_normalized, y_train_scaled, epochs=8, batch_size=100, validation_split=0.4)

すべてのポインタは大歓迎です!それまでの間、中心点損失関数を実装して、それがまったく役立つかどうかを確認しています。

回答

DavidHoelzer Jan 21 2021 at 07:35

結局、この問題は主に最急降下法が極小値に陥ることの問題であることが判明しました。

後世のために読んでいる人にとって、回避するのが難しいMLの問題の1つは、重み、バイアス、およびカーネル(CNN内)の妥当な初期値を直感的に選択できないことです。その結果、通常、ランダムに初期化することができます。これにはいくつかの課題があります。

最大の課題の1つは、ランダムな開始点から開始する場合、実験を完全に複製する方法を誰かに教えるのが難しいことです。トレーニング済みモデルから保存されたパラメーターを提供できるため、これは最終的にはそれほど重要ではありません。ただし、これにより、「悪い」ように見えるネットワークが実際には完全に正常である可能性もあります。

この場合、私は多くの時間を均一なイニシャライザー(上記のコードには存在しません)でCNNを初期化することに費やしていました。遺伝子検索ツールを使用してネットワークを改善できるように、ランダムシードやその他の関数を使用して初期値を生成することがあります。

さまざまなネットワーク反復とこの特定のデータを組み合わせた均一な初期化子は、絶対にひどいトレーニングパフォーマンスと非収束につながるようです。

上記のようにランダムな初期化と1つまたは2つの調整を行ってネットワークを実行すると、うまく収束しました。一部のトレーニング反復では、境界ボックスの側面の1つがエッジに固定され、一部は収束しませんが、テストセットの境界ボックスの96〜98%の精度範囲にあるいくつかを正常にトレーニングできました。 20000なので、すべて順調です。