Изучение деталей реализации YOLO v3 — сеть, часть 3

искусственный интеллект глубокое обучение Python Keras алгоритм
Изучение деталей реализации YOLO v3 — сеть, часть 3

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. Это третья статья, Сеть, основанная на DarkNet. Конечно есть и 4-я, до энной, ведь это полная версия :) Эта немного длиннее.

GitHub для этой статьиисходный код:GitHub.com/spike king/can…

обновлено:

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


1. Сеть

В модели, передав входной слойimage_input, количество анкеров на слойnum_anchors//3и количество категорийnum_classes,перечислитьyolo_body()метод построения сети для YOLO v3model_body. в,image_inputСтруктура (?, 416, 416, 3).

model_body = yolo_body(image_input, num_anchors // 3, num_classes)  # model

существуетmodel_body, окончательный вводimage_input, окончательный вывод представляет собой список из 3 матриц:

[(?, 13, 13, 18), (?, 26, 26, 18), (?, 52, 52, 18)]

Базовая сеть YOLO v3Сеть Даркнет, матрица признаков нижнего слоя и среднего слоя в сети DarkNet, посредством операции свертки и операции объединения нескольких матриц создают 3 выхода масштаба, а именно[y1, y2, y3].

def yolo_body(inputs, num_anchors, num_classes):
    darknet = Model(inputs, darknet_body(inputs))
    
    x, y1 = make_last_layers(darknet.output, 512, num_anchors * (num_classes + 5))

    x = compose(
        DarknetConv2D_BN_Leaky(256, (1, 1)),
        UpSampling2D(2))(x)
    x = Concatenate()([x, darknet.layers[152].output])
    x, y2 = make_last_layers(x, 256, num_anchors * (num_classes + 5))

    x = compose(
        DarknetConv2D_BN_Leaky(128, (1, 1)),
        UpSampling2D(2))(x)
    x = Concatenate()([x, darknet.layers[92].output])
    x, y3 = make_last_layers(x, 128, num_anchors * (num_classes + 5))

    return Model(inputs, [y1, y2, y3])

2. Darknet

Вход сети Даркнет — это входы набора данных изображений, т. е. (?, 416, 416, 3), а выход —darknet_body()вывод метода. Инкапсулируйте основную логику сети вdarknet_body()метод. который:

darknet = Model(inputs, darknet_body(inputs))

в,darknet_bodyВыходной формат (?, 13, 13, 1024).

даркнетУпрощенная схема сети,следующее:

网络简化图

Версия Darknet, используемая YOLO v3,Darknet53. Итак, почему Darknet53? Поскольку Darknet53 представляет собой комбинацию 53 сверточных слоев и слоев пула, однозначное соответствие с упрощенной диаграммой Darknet, а именно:

53 = 2 + 1*2 + 1 + 2*2 + 1 + 8*2 + 1 + 8*2 + 1 + 4*2 + 1

существуетdarknet_body(), сеть Даркнет содержит 5 наборов повторяющихсяresblock_body()единица, то есть:

def darknet_body(x):
    '''Darknent body having 52 Convolution2D layers'''
    x = DarknetConv2D_BN_Leaky(32, (3, 3))(x)
    x = resblock_body(x, num_filters=64, num_blocks=1)
    x = resblock_body(x, num_filters=128, num_blocks=2)
    x = resblock_body(x, num_filters=256, num_blocks=8)
    x = resblock_body(x, num_filters=512, num_blocks=8)
    x = resblock_body(x, num_filters=1024, num_blocks=4)
    return x

В первой операции сверткиDarknetConv2D_BN_Leaky(), представляет собой комбинацию 3-х операций, а именно:

  • 1 Darknet 2D сверточный слой Conv2D, т.е.DarknetConv2D();
  • 1 уровень пакетной нормализации (BN), а именно BatchNormalization();
  • 1 слой LeakyReLU, наклон 0,1, LeakyReLU — преобразование ReLU;

который:

def DarknetConv2D_BN_Leaky(*args, **kwargs):
    """Darknet Convolution2D followed by BatchNormalization and LeakyReLU."""
    no_bias_kwargs = {'use_bias': False}
    no_bias_kwargs.update(kwargs)
    return compose(
        DarknetConv2D(*args, **no_bias_kwargs),
        BatchNormalization(),
        LeakyReLU(alpha=0.1))

Среди них функция активации LeakyReLU выглядит следующим образом:

LeakyReLU

Среди них двумерная свертка Даркнета DarknetConv2D, конкретные операции заключаются в следующем:

  • Регуляризируйте матрицу весов ядра, используйте регуляризацию L2 и параметр 5e-4, то есть используйте параметр w;
  • Отступы, как правило, используют тот же режим, только если размер шага равен (2,2), используйте допустимый режим. Избегайте введения бесполезной информации о границах при понижении дискретизации;
  • Остальные параметры остаются неизменными, что согласуется с операцией двумерной свертки Conv2D();

kernel_regularizerзаключается в регуляризации параметра веса ядра w, а BatchNormalization — в регуляризации входных данных x.

выполнить:

@wraps(Conv2D)
def DarknetConv2D(*args, **kwargs):
    """Wrapper to set Darknet parameters for Convolution2D."""
    darknet_conv_kwargs = {'kernel_regularizer': l2(5e-4)}
    darknet_conv_kwargs['padding'] = 'valid' if kwargs.get('strides') == (2, 2) else 'same'
    darknet_conv_kwargs.update(kwargs)
    return Conv2D(*args, **darknet_conv_kwargs)

Далее первая остаточная структураresblock_body(), входные данные x равны (?, 416, 416, 32), фильтры каналов равны 64, количество повторенийnum_blocksЭто 1 раз. Первая остаточная структура является частью 1 упрощенной схемы сети.

x = resblock_body(x, num_filters=64, num_blocks=1)

существуетresblock_bodyсодержит следующую логику:

  • ZeroPadding2D(): дополняет x 0 границами, преобразованными из (?, 416, 416, 32) в (?, 417, 417, 32). Поскольку размер шага следующей операции свертки равен 2, длина стороны графа должна быть нечетной;
  • DarknetConv2D_BN_Leaky()это операция 2D-свертки DarkNet, ядро ​​​​(3,3) и шаг (2,2).Обратите внимание, что это приведет к меньшему размеру объекта, который преобразуется из (?, 417, 417, 32 ) до (?, 208, 208, 64). так какnum_filtersравно 64, поэтому создается 64 канала.
  • compose(): выходная карта предсказания y, функция представляет собой комбинированную функцию, сначала выполните операцию свертки 1x1, а затем выполните операцию свертки 3x3, фильтр сначала уменьшается в 2 раза, а затем восстанавливается, и, наконец, такой же, как вход , оба 64;
  • x = Add()([x, y])это Остаточная операция, которая добавляет значение x к значению y. Остаточная работа может избежать проблемы дисперсии градиента (проблема исчезающего градиента), возникающей при глубокой сети.

выполнить:

def resblock_body(x, num_filters, num_blocks):
    '''A series of resblocks starting with a downsampling Convolution2D'''
    # Darknet uses left and top padding instead of 'same' mode
    x = ZeroPadding2D(((1, 0), (1, 0)))(x)
    x = DarknetConv2D_BN_Leaky(num_filters, (3, 3), strides=(2, 2))(x)
    for i in range(num_blocks):
        y = compose(
            DarknetConv2D_BN_Leaky(num_filters // 2, (1, 1)),
            DarknetConv2D_BN_Leaky(num_filters, (3, 3)))(x)
        x = Add()([x, y])
    return x

Остаточный рабочий процесс, как показано на рисунке:

Residual

Точно так же вdarknet_body(), выполнить 5 подходовresblock_body()Остаточный блок, повторенный [1, 2, 8, 8, 4] раз, операции двойной свертки (1x1 и 3x3), каждая группа содержит операцию свертки с шагом 2, поэтому всего 5 раз 32 раза уменьшения размерности, то есть 32=2^5, тогда размерность выходной карты объектов равна 13, то есть 13=416/32. Количество фильтров в последнем слое равно 1024, поэтому итоговая структура вывода будет (?, 13, 13, 1024), а именно:

Tensor("add_23/add:0", shape=(?, 13, 13, 1024), dtype=float32)

Пока вход модели даркнета (?, 416, 416, 3), а выход (?, 13, 13, 1024).


3. Карта характеристик

В сети YOLO v3 выводятся 3 карты обнаружения разного масштаба для обнаружения объектов разного размера. звонили 3 разаmake_last_layers(), что приводит к 3 картам обнаружения, а именно y1, y2 и y3.

инспекционная карта 13x13

Для первой части выходной размер составляет 13x13. существуетmake_last_layers()метод, входные параметры следующие:

  • darknet.output: вывод сети DarkNet, т.е. (?, 13, 13, 1024);
  • num_filters: количество каналов равно 512, которое используется для генерации промежуточного значения x, и x будет передано на вторую карту обнаружения;
  • out_filters: количество каналов первого вывода y1, значение равно количеству полей привязки * (количество категорий + 4 значения поля + достоверность поля);

который:

x, y1 = make_last_layers(darknet.output, 512, num_anchors * (num_classes + 5))

существуетmake_last_layers()метод, выполните 2 шага:

  • На первом этапе x выполняет несколько наборов операций свертки 1 x 1 и операций свертки 3 x 3. Фильтр сначала расширяется, а затем восстанавливается и, наконец, остается неизменным по сравнению с входным фильтром, который по-прежнему равен 512, тогда x определяется (?, 13 , 13, 1024) в (?, 13, 13, 512);
  • Шаг 2, x сначала выполняет операцию свертки 3x3, а затем выполняетНет BN и утечкиОперация свертки 1x1, аналогичная операции полного соединения, генерирует матрицу предсказания y;

выполнить:

def make_last_layers(x, num_filters, out_filters):
    '''6 Conv2D_BN_Leaky layers followed by a Conv2D_linear layer'''
    x = compose(
        DarknetConv2D_BN_Leaky(num_filters, (1, 1)),
        DarknetConv2D_BN_Leaky(num_filters * 2, (3, 3)),
        DarknetConv2D_BN_Leaky(num_filters, (1, 1)),
        DarknetConv2D_BN_Leaky(num_filters * 2, (3, 3)),
        DarknetConv2D_BN_Leaky(num_filters, (1, 1)))(x)
    y = compose(
        DarknetConv2D_BN_Leaky(num_filters * 2, (3, 3)),
        DarknetConv2D(out_filters, (1, 1)))(x)
    return x, y

Наконец, 1-йmake_last_layers()метод, выход x равен (?, 13, 13, 512), а выход y равен (?, 13, 13, 18). Поскольку модель имеет только 1 категорию обнаружения, четвертое измерение y равно 18, т. е.3*(1+5)=18.

Карта обнаружения 26x26

Вторая часть, выходной размер 26x26, содержит следующие шаги:

  1. пройти черезDarknetConv2D_BN_LeakyСвертка, преобразование x из числа каналов 512 в число каналов 256;
  2. повышение частоты дискретизации в 2 разаUpSampling2D, преобразовать x из структуры 13x13 в структуру 26x26;
  3. Объединить x со слоем 152 DarkNetConcatenate, как второйmake_last_layers()Входные данные используются для генерации второй карты предсказания y2;

Среди них структура input x и darknet.layers[152].output имеет размер 26x26, а именно:

x: shape=(?, 26, 26, 256)
darknet.layers[152].output: (?, 26, 26, 512)

После конкатенации выходной формат x будет (?, 26, 26, 768).

Цель этого: после нескольких преобразований самая продвинутая абстрактная информация Даркнета, darknet.output, не только выводится в первую часть обнаружения, но и используется для второй части обнаружения.После повышения частоты дискретизации она соединяется с магистралью. Darknet. , конкатенация данных предпоследнего уменьшения размерности используется в качестве входных данных второй части обнаружения. Абстрактные признаки низкого уровня содержат глобальную информацию, а абстрактные признаки среднего уровня содержат локальную информацию Таким образом, сплайсинг с учетом обоих используется для обнаружения более мелких объектов.

Наконец, все еще звонит то же самоеmake_last_layers(), выведите второй слой обнаружения y2 и временные данные x.

выполнить:

x = compose(
    DarknetConv2D_BN_Leaky(256, (1, 1)),
    UpSampling2D(2))(x)
x = Concatenate()([x, darknet.layers[152].output])
x, y2 = make_last_layers(x, 256, num_anchors * (num_classes + 5))

Наконец, 2-йmake_last_layers()метод, выход x равен (?, 26, 26, 256), а выход y равен (?, 26, 26, 18).

Карта осмотра 52x52

Третья часть, выходной размер 52x52, аналогична второй части и содержит следующие шаги:

x = compose(
    DarknetConv2D_BN_Leaky(128, (1, 1)),
    UpSampling2D(2))(x)
x = Concatenate()([x, darknet.layers[92].output])
_, y3 = make_last_layers(x, 128, num_anchors * (num_classes + 5))

Логика следующая:

  • x свертывается 128 фильтрами, затем выполняется повышающая дискретизация, и на выходе получается (?, 52, 52, 128);
  • darknet.layers[92].output, аналогичный 152 слоям, структура (?, 52, 52, 256);
  • После объединения двух x равно (?, 52, 52, 384);
  • Последний ввод вmake_last_layers(), сгенерированный y3 равен (?, 52, 52, 18), игнорируя вывод x;

Наконец, модель строится на основе ввода и вывода всей логики. Входные данные остаются неизменными, т. е. (?, 416, 416, 3), а выходные данные преобразуются в слой предсказания с 3 шкалами, т. е. [y1, y2, y3].

return Model(inputs, [y1, y2, y3])

Структура [y1, y2, y3] следующая:

Tensor("conv2d_59/BiasAdd:0", shape=(?, 13, 13, 18), dtype=float32)
Tensor("conv2d_67/BiasAdd:0", shape=(?, 26, 26, 18), dtype=float32)
Tensor("conv2d_75/BiasAdd:0", shape=(?, 52, 52, 18), dtype=float32)

Наконец, вyolo_body, построение всей сети YOLO v3 завершено, а базовой сетью является DarkNet.

model_body = yolo_body(image_input, num_anchors // 3, num_classes)

Схематическая диаграмма сети с немного другими порядковыми номерами слоев:

YOLO v3


Дополнение 1. Заполнение свертки

В операции свертки есть две операции для краевых данных: одна — отбрасывать действительные, а другая — заполнять их.

как:

数据:1 2 3 4 5 6 7 8 9 10 11 12 13
输入数据 = 13
过滤器宽度 = 6
步长 = 5

Первый тип, допустимая операция, ширина 6, размер шага 5 и данные выполнения:

1 2 3 4 5 6
6 7 8 9 10 11
11 12 13(不足,舍弃)

Второй тип, та же операция, выполняет данные:

1 2 3 4 5 6(前两步相同)
6 7 8 9 10 11
11 12 13 0 0(不足,填充)

Среди них коэффициент использования данных выше в одном и том же режиме, а недопустимые граничные данные избегаются в действительном режиме.Эти два режима имеют свои преимущества и недостатки.


Дополнение 2. функция составления

Функция compose(), используя лямбда-выражение Python, последовательно выполняет список функций, а выходные данные предыдущей функции являются входными данными следующей функции. Функция compose() подходит для соединения двух слоев в нейронной сети.

Например:

def compose(*funcs):
    if funcs:
        return reduce(lambda f, g: lambda *a, **kw: g(f(*a, **kw)), funcs)
    else:
        raise ValueError('Composition of empty sequence not supported.')
def func_x(x):
    return x * 10
def func_y(y):
    return y - 6
z = compose(func_x, func_y)  # 先执行x函数,再执行y函数
print(z(10))  # 10*10-6=94

Приложение 3. UpSampling2D Апсемплинг

Операция апсэмплинга UpSampling2D расширяет матрицу признаков кратно.Ядро заключается в использовании метода изменения размера, а алгоритм интерполяции ближайшего соседа (Nearest Neighbor) используется по умолчанию.data_formatрежим данных, значение по умолчаниюchannels_last, то есть канал находится в конце, например(128,128,3).

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

def call(self, inputs):
    return K.resize_images(inputs, self.size[0], self.size[1],
                           self.data_format)
// ...
x = tf.image.resize_nearest_neighbor(x, new_shape)                           

Например: данные (?, 13, 13, 256) после двукратной операции повышения дискретизации, а именно UpSampling2D(2), генерирует карту признаков (?, 26, 26, 256).


Дополнение 4. Операция свертки 1x1 и полное соединение

Как сверточный слой 1x1, так и полносвязный слой могут использоваться в качестве выходных данных предсказания последнего слоя с небольшими различиями между ними.

Пункт 1:

  • Слой свертки 1x1 может выводить матрицу признаков с фиксированным количеством каналов независимо от количества входных каналов;
  • Полносвязный слой (плотный), вход и выход фиксированы, и фиксированное значение нельзя изменить при проектировании сети;

Таким образом, сверточный слой 1x1 является более гибким, чем полносвязный слой;

Пункт 2:

Например: ввод (13,13,1024), вывод (13,13,18), затем две операции:

  • сверточный слой 1x1, меньше параметров, только параметры, соответствующие выходному каналу, например параметры 1x1x1024x18;
  • Полносвязный слой имеет множество параметров и требует параметров, которые соответствуют как входным, так и выходным данным, например параметры 13x13x1024x18;

OK, that's all! Enjoy it!

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