Регрессия ограничивающих рамок - приключение в неудаче
Решил много задач с нейросетями, но с изображениями работаю редко. У меня есть около 18 часов на создание сети регрессии ограничивающей рамки, и она по-прежнему терпит неудачу. С некоторыми функциями потерь он будет требовать 80% точности во время обучения и проверки (с действительно огромными потерями для обоих), но тестирование прогнозов обнаруживает ограничивающую рамку, которая перемещает только один или два пикселя в любом заданном направлении и, кажется, полностью игнорирует данные. Теперь я реализовал форму потери IoU, но обнаружил, что IoU закреплен на нуле ... что, очевидно, верно на основе выходных данных после обучения. :). Я бы хотел, чтобы кто-нибудь просмотрел это и дал мне несколько советов, как действовать дальше.
Что у меня есть
Я генерирую 40000 примеров изображений размером 200x100x3, в каждом из которых случайным образом помещается одна буква. Одновременно я генерирую ограничивающие рамки истинности для каждой обучающей выборки. Я полностью подтвердил, что все это работает и данные верны.
Что я с этим делаю
Затем я преобразовываю изображения 200x100x3 в оттенки серого, чтобы получить изображение 200x100x1. Затем изображения нормализуются, а ограничивающие рамки масштабируются в диапазоне от 0 до 1. В упрощенном виде это происходит:
x_train_normalized = (x_data - 127.5) / 127.5
y_train_scaled = boxes[:TRAIN]/[WIDTH,HEIGHT,WIDTH,HEIGHT]
Я внимательно просмотрел эти данные, даже воссоздав из них изображения и ограничивающие рамки. Это определенно работает.
Обучение
Чтобы тренироваться, после попыток mse
и многих других, каждый из которых терпит неудачу, я реализовал простую настраиваемую функцию потерь долговых расписок. Он действительно возвращается -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
Сеть
Это было сделано через много-много итераций. Как я уже сказал, я решил много других проблем с сетевыми узлами ... Это первая проблема, которая меня полностью застряла. На данный момент сеть резко урезана, но по-прежнему не обучается вообще:
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)
Все указатели приветствуются! А пока я реализую функцию потери центральной точки, чтобы посмотреть, помогает ли это вообще.
Ответы
В конце концов, эта проблема оказалась во многом вопросом попадания градиентного спуска в локальные минимумы.
Для тех, кто читает для потомков, одна из проблем машинного обучения, которую трудно обойти, заключается в том, что мы не можем интуитивно выбрать разумные начальные значения для весов, смещений и ядер (в CNN). В результате мы обычно позволяем им инициализироваться случайным образом. Это может вызвать некоторые проблемы.
Одна из самых больших проблем заключается в том, что когда вы начинаете со случайной отправной точки, трудно сказать кому-то, как полностью воспроизвести ваши эксперименты. В конце концов, это не так уж важно, поскольку вы можете предоставить им сохраненные параметры из вашей обученной модели. Однако это также может привести к появлению сетей, которые кажутся «плохими», но на самом деле вполне нормальными.
В этом случае я потратил большую часть времени на инициализацию CNN с помощью универсального инициализатора (которого нет в приведенном выше коде). Иногда я буду использовать случайное начальное число или какую-либо другую функцию для генерации начальных значений, чтобы я мог лучше улучшать сети с помощью инструментов генетического поиска.
Кажется, что унифицированные инициализаторы в сочетании с различными сетевыми итерациями и этими конкретными данными приводят к абсолютно ужасной производительности обучения и несходимости.
Когда я запускал сеть, как указано выше, со случайными инициализациями и одной или двумя настройками, она хорошо сходилась. Некоторые итерации обучения будут прикреплять одну из сторон ограничивающей рамки к краю, некоторые никогда не сойдутся, но мне удалось успешно обучить несколько, которые находятся в диапазоне точности 96-98% для ограничивающих прямоугольников в моем тестовом наборе 20000, так что все хорошо!