Изучение деталей реализации YOLO v3 - Часть 5 Потеря

искусственный интеллект глубокое обучение Python GitHub Keras алгоритм
Изучение деталей реализации YOLO v3 - Часть 5 Потеря

YOLO,СейчасYou Only Look OnceАббревиатура на основе сверточной нейронной сети (CNN).Алгоритмы обнаружения объектов. иYOLO v3это третья версия YOLO (т.е.YOLO,YOLO 9000,YOLO v3), эффект обнаружения более точен и сильнее.

Для получения более подробной информации о YOLO v3 вы можете обратиться к YOLO'sОфициальный сайт.

YOLO

YOLO — это американское высказывание You Only Live Once, жить можно только один раз, то есть жизнь слишком коротка, чтобы развлекаться.

Эта статья в основном делится,Как реализовать детали алгоритма YOLO v3, фреймворк Keras. Это пятая статья, функция потерь Loss, которая изысканно разработана, и значения потерь из 4 частей, включая центральную точку, ширину и высоту, достоверность поля и достоверность категории. И конечно же есть 6-я, энная, ведь это полная версия :).

GitHub для этой статьиисходный код:GitHub.com/спайк король/может…

обновлено:

Добро пожаловать, обратите внимание, публичный аккаунт WeChatглубокий алгоритм(ID: DeepAlgorithm), узнайте больше о глубоких технологиях!


1. Уровень потерь

В процессе обучения модели параметры в сети постоянно корректируются, а значение функции потерь loss оптимизируется до минимума, и обучение модели завершается. В YOLO v3 функция потерьyolo_lossВ слое потерь, который инкапсулирует пользовательскую лямбду, как последний слой модели, он участвует в обучении. Вход слоя потерь Lambda является выходом существующей модели.model_body.output и истинное значениеy_true, на выходе 1 значение, значение потерь.

Основная логика уровня потерь находится вyolo_lossсередина,yolo_lossВ дополнение к получению входных данных от слоя Lambdamodel_body.выход иy_true, который также получает анкорные ящики, анкоры, количество категорийnum_classesи порог фильтраignore_threshи так далее 3 параметра.

выполнить:

model_loss = Lambda(yolo_loss,
                    output_shape=(1,), name='yolo_loss',
                    arguments={'anchors': anchors,
                               'num_classes': num_classes,
                               'ignore_thresh': 0.5}
                    )(model_body.output + y_true)

в,model_body.output — прогнозируемое значение существующей модели,y_trueявляется реальным значением, оба имеют одинаковый формат, как показано ниже:

model_body: [(?, 13, 13, 18), (?, 26, 26, 18), (?, 52, 52, 18)]
y_true: [(?, 13, 13, 18), (?, 26, 26, 18), (?, 52, 52, 18)]

Далее, вyolo_lossметод, параметры:

  • args — входные данные слоя Lambda, т.е.model_body.выход иy_trueКомбинация;
  • Anchors — двумерный массив, структура (9, 2), то есть 9 якорей-якорей;
  • num_classes — количество классов;
  • ignore_thresh — порог фильтрации;
  • print_loss — переключатель для печати функции потерь;

который:

def yolo_loss(args, anchors, num_classes, ignore_thresh=.5, print_loss=True):

2. Параметры

В методе проигрыша yolo_loss необходимо задать несколько параметров:

  • num_layers: количество слоев, которое составляет 1/3 от количества анкоров;
  • yolo_outputsиy_true: отдельные аргументы, первые 3yolo_outputsПрогнозируемые значения, последние 3y_trueистинностное значение;
  • anchor_mask: массив индексов блока привязки, 3 группы по 1 в обратном порядке, 678 соответствует 13x13, 345 соответствует 26x26, 123 соответствует 52x52, а именно [[6, 7, 8], [3, 4, 5] , [0, 1, 2]];
  • input_shape:K.shape(yolo_outputs[0])[1:3], первая матрица предсказанияyolo_outputs[0]Первый-второй биты структуры (формы), то есть (13, 13) в (?, 13, 13, 18). Тогда x32 — входной размер сети YOLO, то есть (416, 416), потому что в сети 5 операций свертки с размером шага (2, 2), а размерность уменьшается на 32=5^ 2 раза;
  • grid_shapesinput_shapeпохожий,K.shape(yolo_outputs[l])[1:3], в виде списка выберите 3 измерения измерения карты предсказания, а именно [(13, 13), (26, 26), (52, 52)];
  • m: первый бит структуры первой карты предсказания, то естьK.shape(yolo_outputs[0])[0], общее количество изображений, вводимых в модель, то есть количество пакетов;
  • mf: плавающий тип m, т.е. K.cast(m, K.dtype(yolo_outputs[0]))
  • убыток: значение убытка равно 0;

который:

num_layers = len(anchors) // 3  # default setting
yolo_outputs = args[:num_layers]
y_true = args[num_layers:]
anchor_mask = [[6, 7, 8], [3, 4, 5], [0, 1, 2]] if num_layers == 3 else [[3, 4, 5], [1, 2, 3]]
# input_shape是输出的尺寸*32, 就是原始的输入尺寸,[1:3]是尺寸的位置,即416x416
input_shape = K.cast(K.shape(yolo_outputs[0])[1:3] * 32, K.dtype(y_true[0]))
# 每个网格的尺寸,组成列表
grid_shapes = [K.cast(K.shape(yolo_outputs[l])[1:3], K.dtype(y_true[0])) for l in range(num_layers)]

m = K.shape(yolo_outputs[0])[0]  # batch size, tensor
mf = K.cast(m, K.dtype(yolo_outputs[0]))

loss = 0

3. Прогнозные данные

существуетyolo_head, будет предсказывать графикyolo_outputs[l], разделить на начальную точку xy, ширину и высоту wh, достоверность и вероятность класса ограничивающей рамкиclass_probs. Входные параметры:

  • yolo_outputs[l] или feats: l-я карта предсказаний, например (?, 13, 13, 18);
  • привязки[anchor_mask[l]] или привязки: l-й блок привязки, например [(116, 90), (156,198), (373,326)];
  • num_classes: количество категорий, например 1;
  • input_shape: размер входного изображения, тензор, значение (416, 416);
  • calc_loss: Переключатель для расчета убытка, при расчете значения убытка,calc_lossоткрытый, истинный;

который:

grid, raw_pred, pred_xy, pred_wh = \
    yolo_head(yolo_outputs[l], anchors[anchor_mask[l]], num_classes, input_shape, calc_loss=True)
    
def yolo_head(feats, anchors, num_classes, input_shape, calc_loss=False):

Далее подсчитываем количество анкеровnum_anchors, то есть 3. Преобразование якорей в тензоры с той же размерностью, что и предсказанные особенности карты, т.е.anchors_tensorСтруктура (1, 1, 1, 3, 2), то есть:

num_anchors = len(anchors)
# Reshape to batch, height, width, num_anchors, box_params.
anchors_tensor = K.reshape(K.constant(anchors), [1, 1, 1, num_anchors, 2])

Далее создайте сетку:

  • Получите размер grid_shape сетки, который является 1-й ~ 2-й позицией подвигов графа предсказания, например 13x13;
  • grid_yиgrid_xИспользуется для создания сетки сетки с помощью комбинации аранжировки, изменения формы, плитки, создания комбинации 0 ~ 12 по оси Y.grid_y, а затем создайте комбинацию от 0 до 12 по оси x.grid_x, соединяя их в конкатенацию, которая представляет собой сетку;
  • сетка — это значение, которое проходит через комбинацию двоичных значений, и имеет структуру (13, 13, 1, 2);

который:

grid_shape = K.shape(feats)[1:3]
grid_shape = K.shape(feats)[1:3]  # height, width
grid_y = K.tile(K.reshape(K.arange(0, stop=grid_shape[0]), [-1, 1, 1, 1]),
                [1, grid_shape[1], 1, 1])
grid_x = K.tile(K.reshape(K.arange(0, stop=grid_shape[1]), [1, -1, 1, 1]),
                [grid_shape[0], 1, 1, 1])
grid = K.concatenate([grid_x, grid_y])
grid = K.cast(grid, K.dtype(feats))

Затем разверните последнее измерение подвигов, чтобы отделить якоря от других данных (количество категорий + 4 значения поля + уверенность поля)

feats = K.reshape(
    feats, [-1, grid_shape[0], grid_shape[1], num_anchors, num_classes + 5])

Затем вычислите начальную точку xy, ширину и высоту wh, достоверность коробки.box_confidenceи доверие классаbox_class_probs:

  • Начальная точка xy: значение xy в подвигах нормализовано сигмовидной, плюс соответствующая сетка с двумя кортежами, а затем разделена на длину стороны сетки, нормализована;
  • Ширина и высота wh: Значение wh в навыках положительно оценивается exp, а затем умножается наanchors_tensorБлок привязки, затем разделенный на ширину и высоту изображения, нормализуется;
  • коробка уверенностьbox_confidence: нормализовать значение уверенности в подвигах по сигмовидной;
  • доверие классаbox_class_probs: нормализовать значение class_probs в подвигах по сигмовидной;

который:

box_xy = (K.sigmoid(feats[..., :2]) + grid) / K.cast(grid_shape[::-1], K.dtype(feats))
box_wh = K.exp(feats[..., 2:4]) * anchors_tensor / K.cast(input_shape[::-1], K.dtype(feats))
box_confidence = K.sigmoid(feats[..., 4:5])
box_class_probs = K.sigmoid(feats[..., 5:])

Среди них формула расчета xywh, tx, ty, tw и th — значения умений, а bx, by, bw и bh — выходные значения, как показано ниже:

xywh

эти 4 значенияbox_xy, box_wh, box_confidence, box_class_probsДиапазон находится между 0 и 1.

Поскольку стоимость потерь рассчитывается,calc_lossистинно, возвращает:

  • Сетка: структура (13, 13, 1, 2), а значение представляет собой полный обход двойки от 0 до 12;
  • Характеристики предсказанного значения: после преобразования формы 18-мерные данные разделяются на 3-мерные привязки, а структура имеет вид (?, 13, 13, 3, 6)
  • box_xyиbox_whНормализованная начальная точка xy, ширина и высота wh, структура xy (?, 13, 13, 3, 2) и структура wh (?, 13, 13, 3, 2);box_xyДиапазон (0~1),box_whДиапазон равен (0~1), то есть после завершения вычисления bx, by, bw и bh выполняется нормализация.

который:

if calc_loss == True:
    return grid, feats, box_xy, box_wh

4. Функция потерь

При расчете значения потерь значение потерь каждого слоя вычисляется в цикле и накапливается вместе, то есть

for l in range(num_layers):
        // ... 
        loss += xy_loss + wh_loss + confidence_loss + class_loss

В теле каждого цикла:

  • Получите доверие к объектуobject_mask, четвертая позиция последнего измерения, позиция 0~3 — это кадр, а четвертая позиция — достоверность объекта;
  • доверие классаtrue_class_probs, 5-я позиция последнего измерения;

который:

object_mask = y_true[l][..., 4:5]
true_class_probs = y_true[l][..., 5:]

Затем вызовите yolo_head, чтобы восстановить карту предсказания и вывести:

  • Сетка: структура (13, 13, 1, 2), а значение представляет собой полный обход двойки от 0 до 12;
  • Прогнозируемое значение raw_pred: после преобразования формы якоря разделяются, и структура (?, 13, 13, 3, 6)
  • pred_xyиpred_whНормализованная начальная точка xy, ширина и высота wh, структура xy (?, 13, 13, 3, 2) и структура wh (?, 13, 13, 3, 2);

Затем объедините xy и wh в поле предсказания pred_box со структурой (?, 13, 13, 3, 4).

grid, raw_pred, pred_xy, pred_wh = \
    yolo_head(yolo_outputs[l], anchors[anchor_mask[l]], 
              num_classes, input_shape, calc_loss=True)
pred_box = K.concatenate([pred_xy, pred_wh])

Затем сгенерируйте данные наземной истины:

  • raw_true_xy: центральная точка xy в сетке, данные смещения, диапазон значений 0~1, 0-й и 1-й биты y_true являются относительными положениями центральной точки xy, диапазон 0~1;
  • raw_true_wh: wh в сети основывается на соотношении якорей, а затем преобразуется в логарифмическую форму, диапазон положительный или отрицательный; второй и третий биты y_true являются относительными позициями ширины и высоты wh, а диапазон равен 0~1;
  • box_loss_scale: Рассчитать вес белого, диапазон значений (1~2);

выполнить:

# Darknet raw box to calculate loss.
raw_true_xy = y_true[l][..., :2] * grid_shapes[l][::-1] - grid
raw_true_wh = K.log(y_true[l][..., 2:4] / anchors[anchor_mask[l]] * input_shape[::-1])  # 1
raw_true_wh = K.switch(object_mask, raw_true_wh, K.zeros_like(raw_true_wh))  # avoid log(0)=-inf
box_loss_scale = 2 - y_true[l][..., 2:3] * y_true[l][..., 3:4]  # 2-w*h

Затем сгенерируйте в соответствии с порогом игнорирования IoU.ignore_mask, будет предсказывать коробкуpred_boxи правдаtrue_boxРассчитайте IoU для подавления значения нежелательного поля привязки, то есть поля привязки, IoU которого меньше максимального порога.ignore_maskФорма (?, ?, ?, 3, 1), 0-й бит — это номер пакета, а 1-й ~ 2-й бит — размер карты объектов.

выполнить:

ignore_mask = tf.TensorArray(K.dtype(y_true[0]), size=1, dynamic_size=True)
object_mask_bool = K.cast(object_mask, 'bool')

def loop_body(b, ignore_mask):
    true_box = tf.boolean_mask(y_true[l][b, ..., 0:4], object_mask_bool[b, ..., 0])
    iou = box_iou(pred_box[b], true_box)
    best_iou = K.max(iou, axis=-1)
    ignore_mask = ignore_mask.write(b, K.cast(best_iou < ignore_thresh, K.dtype(true_box)))
    return b + 1, ignore_mask

_, ignore_mask = K.control_flow_ops.while_loop(lambda b, *args: b < m, loop_body, [0, ignore_mask])
ignore_mask = ignore_mask.stack()
ignore_mask = K.expand_dims(ignore_mask, -1)

Функция потерь:

  • xy_loss: Значение потерь центральной точки.object_maskдаy_trueЧетвертый бит , то есть содержит ли он объект, равен 1, если содержит, и 0, если нет.box_loss_scaleЗначение , связано с размером рамки объекта, 2 минус относительная площадь, диапазон значений составляет (1~2).binary_crossentropyбинарная кросс-энтропия.
  • wh_loss: Значение потери ширины и высоты. Кроме того, дополнительно умножьте на коэффициент 0,5 квадрат K.square().
  • confidence_loss: Значение потери коробки. Он состоит из двух частей, первая часть — это значение потери присутствия объекта, а вторая часть — значение потери отсутствия объекта, которое умножается на маску игнорирования.ignore_mask, игнорируя блоки, IoU которых меньше порогового значения в блоке предсказания.
  • class_loss: Значение потери категории.
  • Сумма значений потерь каждой части делится на среднее значение и накапливается как окончательное значение потерь изображения.

Детали реализации:

object_mask = y_true[l][..., 4:5]  # 物体掩码
box_loss_scale = 2 - y_true[l][..., 2:3] * y_true[l][..., 3:4]  # 框损失比例
z * -log(sigmoid(x)) + (1 - z) * -log(1 - sigmoid(x))  # 二值交叉熵函数
iou = box_iou(pred_box[b], true_box)  # 预测框与真正框的IoU

Реализация функции потерь:

xy_loss = object_mask * box_loss_scale * K.binary_crossentropy(raw_true_xy, raw_pred[..., 0:2],
                                                               from_logits=True)
wh_loss = object_mask * box_loss_scale * 0.5 * K.square(raw_true_wh - raw_pred[..., 2:4])
confidence_loss = object_mask * K.binary_crossentropy(object_mask, raw_pred[..., 4:5], from_logits=True) + \
                  (1 - object_mask) * K.binary_crossentropy(object_mask, raw_pred[..., 4:5],
                                                            from_logits=True) * ignore_mask
class_loss = object_mask * K.binary_crossentropy(true_class_probs, raw_pred[..., 5:], from_logits=True)

xy_loss = K.sum(xy_loss) / mf
wh_loss = K.sum(wh_loss) / mf
confidence_loss = K.sum(confidence_loss) / mf
class_loss = K.sum(class_loss) / mf
loss += xy_loss + wh_loss + confidence_loss + class_loss

Формула функции потерь YOLO v1 немного отличается от v3, для справки:

Loss


Пополнить

1. Оператор "..."

В Python оператор "..." (многоточие) означает, что другие измерения остаются неизменными, и работает только первое или последнее измерение;

import numpy as np

x = np.array([[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12]])
"""
[[ 1  2  3  4]
 [ 5  6  7  8]
 [ 9 10 11 12]]
"""
print(x.shape)  # (3, 4)
y = x[1:2, ...]
"""
[[5 6 7 8]]
"""
print(y)

2. Перебрать комбинацию значений

В YOLO v3 при расчете значения сетки его необходимо преобразовать из относительного положения в абсолютное положение, которое является относительным значением плюс значение верхнего левого угла сетки, например относительное значение (0,2, 0.3) в первом (1, 1) Абсолютное значение в сетке равно (1.2, 1.3). При преобразовании значений координат добавьте соответствующее начальное значение в соответствии с положением точки координат. Таким образом, необходимо пройти пары числовых комбинаций, например, создать матрицу сетки от 0 до 12.

Благодаря комбинации arange -> reshape -> tile -> concatenate это можно сделать быстро.

Исходный код:

from keras import backend as K

grid_y = K.tile(K.reshape(K.arange(0, stop=3), [-1, 1, 1]), [1, 3, 1])
grid_x = K.tile(K.reshape(K.arange(0, stop=3), [1, -1, 1]), [3, 1, 1])

sess = K.get_session()
print(grid_x.shape)  # (3, 3, 1)
print(grid_y.shape)  # (3, 3, 1)
z = K.concatenate([grid_x, grid_y])
print(z.shape)  # (3, 3, 2)
print(sess.run(z))
"""
创建3x3的二维矩阵,遍历全部数组0~2
"""

3. ::-1

"::-1" - значение инвертированного массива, например:

import numpy as np

a = np.array([1, 2, 3, 4, 5])
print a[::-1]
"""
[5 4 3 2 1]
"""

4. Session

В Keras используйте тест сеанса для проверки данных, реализуйте:

from keras import backend as K

sess = K.get_session()
a = K.constant([2, 4])
b = K.constant([3, 2])
c = K.square(a - b)
print(sess.run(c))

OK, that's all! Enjoy it!

Добро пожаловать, обратите внимание, публичный аккаунт WeChatглубокий алгоритм(ID: DeepAlgorithm), узнайте больше о глубоких технологиях!