Bounding Box Regression - Un'avventura fallimentare

Jan 20 2021

Ho risolto molti problemi con le reti neurali, ma raramente lavoro con le immagini. Ho circa 18 ore per creare una rete di regressione del riquadro di delimitazione e continua a fallire completamente. Con alcune funzioni di perdita, rivendicherà una precisione dell'80% durante l'addestramento e la convalida (con una perdita davvero massiccia su entrambi), ma testare le previsioni rivela un riquadro di delimitazione che sposta solo uno o due pixel in una determinata direzione e sembra ignorare completamente i dati. Ora ho implementato una forma di perdita di IoU, ma ho scoperto che IoU è bloccato a zero ... il che è ovviamente vero in base agli output dopo l'addestramento. :). Vorrei che qualcuno esaminasse questo aspetto e mi desse qualche consiglio su come procedere successivamente.

Ciò che ho

Sto generando 40000 esempi di immagini 200x100x3 con una singola lettera posizionata casualmente in ciascuna. Contemporaneamente sto generando i riquadri di delimitazione della verità di base per ogni campione di addestramento. Ho verificato a fondo che tutto funzioni e che i dati siano corretti.

Cosa ci faccio

Sto quindi trasformando le immagini 200x100x3 in scala di grigi per produrre un'immagine 200x100x1. Le immagini vengono quindi normalizzate e le caselle di delimitazione vengono ridimensionate in modo che rientrino tra 0 e 1. In forma semplificata, questo accade:

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

Ho esaminato attentamente questi dati, anche ricostituendo immagini e riquadri di delimitazione da essi. Funziona decisamente.

Formazione

Per allenarmi, dopo aver provato msee molti altri, che falliscono ugualmente male, ho implementato una semplice funzione di perdita di IOU personalizzata. In realtà ritorna -ln(IoU). Ho apportato questa modifica sulla base di un documento poiché la perdita è stata (stranamente?) Bloccata a zero in più epoche.

(Funzione di perdita :)

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

Il network

Questo è stato attraversato da molte, molte iterazioni. Come ho detto, ho risolto molti altri problemi con gli NN ... Questo è il primo che mi ha bloccato completamente. A questo punto, la rete viene drasticamente ridotta ma continua a non riuscire affatto ad addestrarsi:

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)

Tutti i suggerimenti sono i benvenuti! Nel frattempo sto implementando una funzione di perdita del punto centrale per vedere se questo aiuta.

Risposte

DavidHoelzer Jan 21 2021 at 07:35

Alla fine, questo problema si è rivelato essere in gran parte una questione di discesa del gradiente che cade nei minimi locali.

Per coloro che leggono per i posteri, uno dei problemi in ML che è difficile aggirare è che non possiamo scegliere intuitivamente valori iniziali ragionevoli per pesi, pregiudizi e kernel (nella CNN). Di conseguenza, in genere consentiamo loro di inizializzarsi in modo casuale. Questo può presentare alcune sfide.

Una delle sfide più grandi è che quando parti da un punto di partenza casuale, è difficile dire a qualcuno come replicare completamente i tuoi esperimenti. Questo non è molto importante alla fine poiché puoi fornire loro i parametri salvati dal tuo modello addestrato. Tuttavia, questo può anche portare a reti che sembrano "cattive" che in realtà sono perfettamente a posto.

In questo caso, avevo passato gran parte del tempo a inizializzare la CNN con un inizializzatore uniforme (non presente nel codice sopra). A volte userò un seme casuale o qualche altra funzione per generare valori iniziali in modo da poter migliorare meglio le reti attraverso strumenti di ricerca genetica.

Sembra che gli inizializzatori uniformi combinati con le varie iterazioni di rete e questi dati particolari conducano a prestazioni di addestramento assolutamente pessime e non convergenti.

Quando ho eseguito la rete come sopra con inizializzazioni casuali e uno o due ritocchi, convergeva bene. Alcune iterazioni di allenamento fisseranno uno dei lati del riquadro di delimitazione sul bordo, altre non convergeranno mai, ma sono riuscito ad addestrarne con successo diversi che si trovano nell'intervallo di precisione del 96-98% per i riquadri di delimitazione nel mio set di test di 20000, quindi va tutto bene!