Bounding Box Regression - Ein Abenteuer im Scheitern
Ich habe viele Probleme mit neuronalen Netzen gelöst, arbeite aber selten mit Bildern. Ich habe ungefähr 18 Stunden Zeit, um ein Bounding-Box-Regressionsnetzwerk zu erstellen, und es schlägt weiterhin völlig fehl. Bei einigen Verlustfunktionen wird eine Genauigkeit von 80% während des Trainings und der Validierung behauptet (mit einem wirklich massiven Verlust bei beiden), aber das Testen der Vorhersagen zeigt einen Begrenzungsrahmen, der nur ein oder zwei Pixel in eine bestimmte Richtung bewegt und die Daten völlig zu ignorieren scheint. Ich habe jetzt eine Form des IoU-Verlusts implementiert, stelle jedoch fest, dass IoU auf Null festgelegt ist ... was offensichtlich auf der Grundlage der Ergebnisse nach dem Training zutrifft. :). Ich möchte, dass jemand dies überprüft und mir einige Ratschläge gibt, wie ich als nächstes vorgehen soll.
Was ich habe
Ich generiere 40000 Beispiele von 200x100x3-Bildern mit jeweils einem Buchstaben, der zufällig platziert wird. Gleichzeitig generiere ich für jedes Trainingsmuster die Grundwahrheitsbegrenzungsrahmen. Ich habe gründlich überprüft, dass dies alles funktioniert und die Daten korrekt sind.
Was ich damit mache
Ich wandle dann die 200x100x3-Bilder in Graustufen um, um ein 200x100x1-Bild zu erzeugen. Die Bilder werden dann normalisiert und die Begrenzungsrahmen werden so skaliert, dass sie zwischen 0 und 1 liegen. In vereinfachter Form geschieht dies:
x_train_normalized = (x_data - 127.5) / 127.5
y_train_scaled = boxes[:TRAIN]/[WIDTH,HEIGHT,WIDTH,HEIGHT]
Ich habe diese Daten sorgfältig durchgesehen und sogar Bilder und Begrenzungsrahmen daraus wiederhergestellt. Das funktioniert definitiv.
Ausbildung
Um zu trainieren, nachdem mse
ich es versucht habe und viele andere, die alle gleich schlecht scheitern, habe ich eine einfache benutzerdefinierte IOU-Verlustfunktion implementiert. Es kehrt tatsächlich zurück -ln(IoU)
. Ich habe diese Änderung auf der Grundlage eines Papiers vorgenommen, da der Verlust (seltsamerweise?) Über mehrere Epochen hinweg auf Null gesetzt wurde.
(Verlustfunktion:)
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
Das Netzwerk
Dies hat viele, viele Iterationen durchgemacht. Wie gesagt, ich habe viele andere Probleme mit NNs gelöst ... Dies ist das erste, bei dem ich völlig hängen geblieben bin. Zu diesem Zeitpunkt ist das Netzwerk dramatisch reduziert, trainiert aber weiterhin überhaupt nicht:
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)
Alle Hinweise sind willkommen! In der Zwischenzeit implementiere ich eine Mittelpunktverlustfunktion, um zu sehen, ob das überhaupt hilft.
Antworten
Am Ende stellte sich heraus, dass dieses Problem hauptsächlich auf den Gradientenabstieg zurückzuführen ist, der in lokale Minima fällt.
Für diejenigen, die für die Nachwelt lesen, ist eines der Probleme in ML, das schwer zu umgehen ist, dass wir nicht intuitiv vernünftige Anfangswerte für die Gewichte, Verzerrungen und Kernel (im CNN) auswählen können. Daher erlauben wir ihnen normalerweise, zufällig zu initialisieren. Dies kann einige Herausforderungen darstellen.
Eine der größten Herausforderungen besteht darin, dass es schwierig ist, jemandem zu sagen, wie er Ihre Experimente vollständig replizieren kann, wenn Sie von einem zufälligen Ausgangspunkt ausgehen. Dies ist am Ende nicht besonders wichtig, da Sie ihnen die gespeicherten Parameter Ihres trainierten Modells zur Verfügung stellen können. Dies kann jedoch auch zu Netzwerken führen, die als "schlecht" erscheinen und tatsächlich vollkommen in Ordnung sind.
In diesem Fall hatte ich viel Zeit damit verbracht, das CNN mit einem einheitlichen Initialisierer zu initialisieren (im obigen Code nicht vorhanden). Ich werde manchmal einen zufälligen Startwert oder eine andere Funktion verwenden, um Anfangswerte zu generieren, damit ich Netzwerke durch genetische Suchwerkzeuge besser verbessern kann.
Es scheint, dass die einheitlichen Initialisierer in Kombination mit den verschiedenen Netzwerkiterationen und diesen speziellen Daten zu einer absolut miserablen Trainingsleistung und Nichtkonvergenz führen.
Als ich das Netzwerk wie oben mit zufälligen Initialisierungen und ein oder zwei Optimierungen ausführte, konvergierte es gut. Einige Trainingsiterationen stecken eine der Seiten des Begrenzungsrahmens am Rand fest, andere konvergieren nie, aber ich habe es geschafft, mehrere erfolgreich zu trainieren, die im Genauigkeitsbereich von 96 bis 98% für die Begrenzungsrahmen in meinem Testsatz von liegen 20000, also ist alles gut!