Изучение исходного кода YOLO v3 — Часть 1 Обучение

искусственный интеллект глубокое обучение Python Keras алгоритм Нейронные сети
Изучение исходного кода YOLO v3 — Часть 1 Обучение

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

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

官网

YOLO — это американское высказывание «Живешь только один раз, жизнь слишком коротка, чтобы веселиться».

В этой статье представлены детали реализации алгоритма YOLO v3, фреймворка Keras. Это часть 1, обучение. Конечно есть и 2-я, до энной, ведь это полная версия :)

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

обновлено:

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


1. Параметры

Параметры обучения модели, 5 параметров:

(1) Набор данных обрамленного изображения в следующем формате:

图片的位置 框的4个坐标和1个类别ID(xmin,ymin,xmax,ymax,label_id) ...
dataset/image.jpg 788,351,832,426,0 805,208,855,270,0

(2) Сводка категории поля аннотации, то есть список всех категорий объектов, отмеченных в наборе данных, выглядит следующим образом:

aeroplane
bicycle
bird
...

(3) Предобучающая модель, используемая для точной настройки (Fine Tune) в трансферном обучении (Transfer Learning), необязательные веса модели COCO, которые были обучены YOLO v3, а именно:

pretrained_path = 'model_data/yolo_weights.h5'

(4) Набор якорных полей Карты характеристик прогнозирования:

  • Карты характеристик 3-х масштабов, каждая карта характеристик имеет 3 поля привязки, всего 9 полей, расположенных от малого к большему;
  • 1–3 используются для крупномасштабных (52x52) карт объектов, 4–6 — среднего масштаба (26x26) и 7–9 — мелкомасштабных (13x13);
  • Крупномасштабные карты объектов обнаруживают мелкие объекты, а мелкомасштабные — крупные;
  • 9 якорей получены из кластеризации K-средних ограничительной рамки.

Среди них якоря COCO следующие:

10,13,  16,30,  33,23,  30,61,  62,45,  59,119,  116,90,  156,198,  373,326

(5) Размер входного изображения, по умолчанию 416x416.

  • Размер изображения соответствует32Кратно , в сети DarkNet существует 5 сверток понижающей дискретизации с шагом 2 (32=2^5). Реализация свертки с понижающей дискретизацией выглядит следующим образом:
x = DarknetConv2D_BN_Leaky(num_filters, (3, 3), strides=(2, 2))(x)
  • На нижнем слое размер карты объектов должен бытьнечетное число, например 13, чтобы центральная точка попадала в уникальное поле. Если оно четное, центральная точка попадает в 4 прямоугольника в центре, что приводит к неоднозначности.

2. Создайте модель

Чтобы создать сетевую модель для YOLOv3, введите:

  • input_shape: размер изображения;
  • анкеры: 9 анкерных ящиков;
  • num_classes: количество категорий;
  • freeze_body: режим заморозки, 1 — заморозить слои DarkNet53, 2 — заморозить все и сохранить только последние 3 слоя;
  • weights_path: веса предварительно обученной модели.

выполнить:

model = create_model(input_shape, anchors, num_classes,
                     freeze_body=2,
                     weights_path=pretrained_path)

Среди них последние 3 слоя сети:

Три сверточных слоя 1x1 (вместо полносвязных слоев) используются для преобразования карт объектов в трех масштабах в прогнозные значения в трех масштабах.

выполнить:

out_filters = num_anchors * (num_classes + 5)
// ...
DarknetConv2D(out_filters, (1, 1))

который:

conv2d_59 (Conv2D)      (None, 13, 13, 18)   18450       leaky_re_lu_58[0][0]    
conv2d_67 (Conv2D)      (None, 26, 26, 18)   9234        leaky_re_lu_65[0][0]    
conv2d_75 (Conv2D)      (None, 52, 52, 18)   4626        leaky_re_lu_72[0][0]    

3. Количество образцов

Образцы перемешиваются, а набор данных разбивается на 10 копий, 9 для обучения и 1 для проверки.

выполнить:

val_split = 0.1  # 训练和验证的比例
with open(annotation_path) as f:
    lines = f.readlines()
np.random.seed(47)
np.random.shuffle(lines)
np.random.seed(None)
num_val = int(len(lines) * val_split)  # 验证集数量
num_train = len(lines) - num_val  # 训练集数量

4. Обучение 1 этапу

На этапе 1 часть сети замораживается и обучаются только веса нижнего слоя.

  • Оптимизатор использует обычного Адама;
  • Функция потерь, используемая напрямую, выход моделиy_pred, игнорируя истинное значениеy_true;

выполнить:

model.compile(optimizer=Adam(lr=1e-3), loss={
    # 使用定制的 yolo_loss Lambda层
    'yolo_loss': lambda y_true, y_pred: y_pred})  # 损失函数

Среди них функция потерьyolo_loss,а такжеy_trueиy_pred:

Пучокy_trueВ качестве входных данных формируется многовходовая модель, а потери записываются в виде слоя (лямбда-слоя) в качестве конечного вывода. Таким образом, при построении модели вам нужно только определить выходные данные модели как потери. При компиляции (компиляции) установите потери прямо вy_pred, потому что выход модели — это потери, т. е.y_predэто потеря, так что игнорируйтеy_true. При тренировке просто добавьте фигуру, подходящую по формеy_trueМассив подойдет.

О лямбда-выражениях Python:

f = lambda y_true, y_pred: y_pred
print(f(1, 2))  # 输出2

данные подгонки модели, используя оболочку генерации данных (data_generator_wrapper) для создания пакетов данных для обучения и проверки. Наконец, модель модели хранит веса. Реализация выглядит следующим образом:

batch_size = 32  # batch
model.fit_generator(data_generator_wrapper(lines[:num_train], batch_size, input_shape, anchors, num_classes),
                    steps_per_epoch=max(1, num_train // batch_size),
                    validation_data=data_generator_wrapper(
                        lines[num_train:], batch_size, input_shape, anchors, num_classes),
                    validation_steps=max(1, num_val // batch_size),
                    epochs=50,
                    initial_epoch=0,
                    callbacks=[logging, checkpoint])
# 存储最终的去权重,再训练过程中,也通过回调存储
model.save_weights(log_dir + 'trained_weights_stage_1.h5')  

В процессе обучения также сохраняются заполненные эпохой веса моделей, среди которых только веса (save_weights_only), сохраняется только лучший результат (save_best_only), сохраняется каждые 3 эпохи (period),который:

checkpoint = ModelCheckpoint(log_dir + 'ep{epoch:03d}-loss{loss:.3f}-val_loss{val_loss:.3f}.h5',
                             monitor='val_loss', save_weights_only=True,
                             save_best_only=True, period=3)  # 只存储weights权重

5. Обучение 2 этапу

Этап 2, используя веса сети, обученной на этапе 1, продолжаем обучение:

  • Установите все веса для обучения, и первый этап — заморозить некоторые веса;
  • Оптимизатор по-прежнему Адам, но скорость обучения (lr) уменьшилась с 1e-3 до 1e-4, а оптимальные веса изучаются деликатно;
  • функция потерь, просто используйтеy_pred,пренебрегатьy_true.

выполнить:

for i in range(len(model.layers)):
    model.layers[i].trainable = True

model.compile(optimizer=Adam(lr=1e-4),
              loss={'yolo_loss': lambda y_true, y_pred: y_pred})

Данные подгонки модели второго этапа аналогичны первому этапу, начиная с 50-й эпохи и обучения до 100-й эпохи, а условие триггера прекращается досрочно. Добавлены два дополнительных обратных вызоваreduce_lrиearly_stopping, который управляет завершением выборки обучения:

  • reduce_lr: когда индекс оценки не улучшается, уменьшайте скорость обучения на 10% каждый раз (фактор) и прекращайте обучение, когда потеря проверки не уменьшается (терпение) в три раза.
  • early_stopping: Точность набора проверки, непрерывное увеличение меньше 0 (min_delta) за 10 эпох (patience), обучение прекращается.

выполнить:

reduce_lr = ReduceLROnPlateau(monitor='val_loss', factor=0.1, patience=3, verbose=1)  # 当评价指标不在提升时,减少学习率
early_stopping = EarlyStopping(monitor='val_loss', min_delta=0, patience=10, verbose=1)  # 验证集准确率,下降前终止

batch_size = 32
model.fit_generator(data_generator_wrapper(lines[:num_train], batch_size, input_shape, anchors, num_classes),
                    steps_per_epoch=max(1, num_train // batch_size),
                    validation_data=data_generator_wrapper(lines[num_train:], batch_size, input_shape, anchors,
                                                           num_classes),
                    validation_steps=max(1, num_val // batch_size),
                    epochs=100,
                    initial_epoch=50,
                    callbacks=[logging, checkpoint, reduce_lr, early_stopping])
model.save_weights(log_dir + 'trained_weights_final.h5')

Пока что после завершения второго этапа обучения вес выходной сети является окончательным весом модели.


Приложение 1. K-средние

K-MeansАлгоритм представляет собой алгоритм кластеризации, который делит набор данных на несколько групп, каждая из которых содержит центр.

YOLOv3 получает все поля привязки в наборе данных и использует алгоритм K-средних для группировки этих полей в 9 категорий, получения 9 центров кластеров и упорядочения областей от малых до больших в виде 9 полей привязки.

Смоделируйте алгоритм K-средних:

  1. Создайте контрольную точку, X — данные, y — метка, например X:(300,2), y:(300,);
  2. Кластеризовать данные в 9 категорий;
  3. Входные данные X, поезд;
  4. Предсказать класс X, какy_kmeans;
  5. Используйте Scatter, чтобы нарисовать диаграмму рассеяния, цветовой диапазонviridis;
  6. Получить кластерные центрыcluster_centers_, представлен черными точками;

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

import matplotlib.pyplot as plt
import seaborn as sns
sns.set()  # for plot styling
from sklearn.cluster import KMeans
from sklearn.datasets.samples_generator import make_blobs


def test_of_k_means():
    # 创建测试点,X是数据,y是标签,X:(300,2), y:(300,)
    X, y_true = make_blobs(n_samples=300, centers=9, cluster_std=0.60, random_state=0)
    kmeans = KMeans(n_clusters=9)  # 将数据聚类
    kmeans.fit(X)  # 数据X
    y_kmeans = kmeans.predict(X)  # 预测

    # 颜色范围viridis: https://matplotlib.org/examples/color/colormaps_reference.html
    plt.scatter(X[:, 0], X[:, 1], c=y_kmeans, s=20, cmap='viridis')  # c是颜色,s是大小

    centers = kmeans.cluster_centers_  # 聚类的中心
    plt.scatter(centers[:, 0], centers[:, 1], c='black', s=40, alpha=0.5)  # 中心点为黑色

    plt.show()  # 展示


if __name__ == '__main__':
    test_of_k_means()

вывод:

K-Means


Дополнение 2. Ранняя остановка

EarlyStopping — это подкласс Callback (класс обратного вызова), который используется для указания действий, которые должны выполняться в начале и в конце каждого этапа. В обратном вызове уже реализованы несколько простых подклассов, напримерacc,val_acc,lossиval_lossи т. д., а также некоторые сложные подклассы, такие какModelCheckpoint(для хранения веса модели) иTensorBoard(для рисования) и др.

Интерфейс обратного вызова Callback выглядит следующим образом:

def on_epoch_begin(self, epoch, logs=None):
def on_epoch_end(self, epoch, logs=None):
def on_batch_begin(self, batch, logs=None):
def on_batch_end(self, batch, logs=None):
def on_train_begin(self, logs=None):
def on_train_end(self, logs=None):

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

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

callbacks=[logging, checkpoint, reduce_lr, early_stopping]

Параметры ранней остановки:

  • монитор: тип данных мониторинга, поддерживает акк,val_acc, потеря,val_lossЖдать;
  • min_delta: Порог остановки в сочетании с параметром режима, порог, который меньше всего увеличивается или уменьшается;
  • режим: min — минимум, max — максимум, auto — автоматический, аmin_deltaСотрудничать;
  • терпение: после достижения порога количество эпох, которые можно выдержать, чтобы избежать остановки в дрожании;
  • verbose: сложность журнала. Чем больше значение, тем больше информации выводится.

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

Пример:

early_stopping = EarlyStopping(monitor='val_loss', min_delta=0, patience=10, verbose=1)

OK, that's all! Enjoy it!

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