Régression de la boîte englobante - Une aventure en échec

Jan 20 2021

J'ai résolu de nombreux problèmes avec les réseaux de neurones, mais je travaille rarement avec des images. J'ai environ 18 heures pour créer un réseau de régression par boîte englobante et il continue à échouer complètement. Avec certaines fonctions de perte, il revendiquera une précision de 80% pendant l'entraînement et la validation (avec une perte vraiment massive sur les deux), mais le test des prédictions révèle une boîte englobante qui ne déplace qu'un ou deux pixels dans une direction donnée et semble ignorer totalement les données. J'ai maintenant implémenté une forme de perte d'IoU, mais je trouve que l'IoU est épinglé à zéro ... ce qui est évidemment vrai en fonction des sorties après l'entraînement. :). J'aimerais que quelqu'un examine ceci et me donne quelques conseils sur la façon de procéder ensuite.

Ce que j'ai

Je génère 40000 exemples d'images 200x100x3 avec une seule lettre placée au hasard dans chacune. En même temps, je génère les boîtes de délimitation de la vérité terrain pour chaque échantillon d'apprentissage. J'ai complètement validé que tout cela fonctionne et que les données sont correctes.

Ce que j'y fais

Je transforme ensuite les images 200x100x3 en niveaux de gris pour produire une image 200x100x1. Les images sont ensuite normalisées et les cadres de délimitation sont mis à l'échelle pour tomber entre 0 et 1. Dans une forme simplifiée, cela se produit:

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

J'ai parcouru attentivement ces données, reconstituant même des images et des cadres de délimitation. Cela fonctionne vraiment.

Formation

Pour m'entraîner, après avoir essayé mseet bien d'autres, qui échouent tous aussi mal, j'ai implémenté une simple fonction de perte d'IOU personnalisée. Il revient en fait -ln(IoU). J'ai fait ce changement sur la base d'un article puisque la perte a été (curieusement?) Épinglée à zéro sur plusieurs époques.

(Fonction de perte :)

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

Le réseau

Cela a été à travers de très nombreuses itérations. Comme je l'ai dit, j'ai résolu de nombreux autres problèmes avec les NN ... C'est le premier à me bloquer complètement. À ce stade, le réseau est considérablement réduit mais ne parvient toujours pas à s'entraîner:

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)

Tous les pointeurs sont les bienvenus! En attendant, j'implémente une fonction de perte de point central pour voir si cela aide du tout.

Réponses

DavidHoelzer Jan 21 2021 at 07:35

En fin de compte, ce problème s'est avéré être en grande partie une question de descente de gradient tombant dans les minima locaux.

Pour ceux qui lisent pour la postérité, l'un des problèmes de ML qui est difficile à contourner est que nous ne pouvons pas choisir intuitivement des valeurs initiales raisonnables pour les poids, les biais et les noyaux (dans CNN). En conséquence, nous leur permettons généralement de s'initialiser de manière aléatoire. Cela peut présenter certains défis.

L'un des plus grands défis est que lorsque vous partez d'un point de départ aléatoire, il est difficile de dire à quelqu'un comment reproduire complètement vos expériences. Ce n'est pas très important à la fin puisque vous pouvez leur fournir les paramètres enregistrés à partir de votre modèle entraîné. Cependant, cela peut également conduire à des réseaux qui semblent «mauvais» qui sont en fait parfaitement bien.

Dans ce cas, j'avais passé une grande partie du temps à initialiser le CNN avec un initialiseur uniforme (non présent dans le code ci-dessus). J'utiliserai parfois une graine aléatoire ou une autre fonction pour générer des valeurs initiales afin de pouvoir mieux améliorer les réseaux grâce à des outils de recherche génétique.

Il semble que les initialiseurs uniformes combinés aux différentes itérations du réseau et à ces données particulières conduisent à des performances d'apprentissage absolument épouvantables et à une non-convergence.

Lorsque j'ai exécuté le réseau comme ci-dessus avec des initialisations aléatoires et un ou deux ajustements, il a bien convergé. Certaines itérations d'entraînement épingleront l'un des côtés de la boîte englobante au bord, certaines ne convergeront jamais, mais j'ai réussi à en entraîner plusieurs qui sont dans la plage de précision de 96 à 98% pour les boîtes englobantes dans mon ensemble de test de 20000, donc tout va bien!