YOLO — это сокращение от You Only Look Once, алгоритма обнаружения объектов, основанного на глубокой сверточной нейронной сети.YOLO v3Это третья версия YOLO, и алгоритм обнаружения стал быстрее и точнее.
Исходный код этой статьи: https://GitHub.com/spike king/ ras-yo-3-detection
Добро пожаловать в Следуй за мнойGitHub: https://GitHub.com/шип Кинг
YOLO v3 уже доступенCOCO(Общие объекты в контексте) Параметры модели для набора данных. Мы можем использовать параметры модели COCO в качестве параметров предварительной подготовки, а затем объединить существующие наборы данных для создания собственного алгоритма обнаружения.
В этом примере используетсяWIDER FACE
Данные о лицах, обучите высокоточную модель распознавания лиц.
WIDER
набор данных:WIDER Face
Дата основания: 19 ноября 2015 г.
WIDER FACEНабор данных представляет собой эталонный набор данных для обнаружения лиц, а изображения выбираются из набора данных WIDER (набор данных веб-изображений для распознавания событий). количество картинок32,203Чжан, количество лиц393,703Лицо сильно трансформируется в различных формах, таких как масштаб, поза, окклюзия и т. д. Набор данных WIDER FACE основан на 61 категории событий, и каждая категория событий выбирается случайным образом для обучения 40%, проверки 10% и тестирования 50%. Обучение и тестирование содержат истину о ограничивающей рамке, а проверка - нет.
Набор данных можно загрузить в открытом доступе на официальном сайте, среди которыхFace annotationsсередина,wider_face_train_bbx_gt.txt
является истинным значением границы, а формат данных следующий:
0--Parade/0_Parade_marchingband_1_849.jpg
1
449 330 122 149 0 0 0 0 0 0
данные показывают:
- Строка 1: расположение и название изображения;
- строка 2: количество границ;
- Строки 3~n: Границы и атрибуты каждой грани:
- Среди них 1~4 цифры
x1, y1, w, h
- размытие: размытие, 0 ясно, 1 обычное, 2 серьезное;
- выражение: выражение, 0 нормальный, 1 преувеличенный;
- освещение: экспозиция, 0 нормальная, 1 крайняя;
- окклюзия: окклюзия, 0 нет, 1 часть, 2 много;
- поза: поза, 0 нормальная, 1 нетипичная;
- Среди них 1~4 цифры
wider_face_val_bbx_gt.txt
Подобно этому.
Данные изображения имеют среднюю четкость и различаются по размеру, с размером 1024x и такой же шириной.
конверсия данных
Чтобы соответствовать требованиям обучения, формат кадра в более широком наборе данных необходимо преобразовать в формат кадра, необходимый для обучения.
то есть путь к файлу, границаxmin,ymin,xmax,ymax,label
:
data/WIDER_val/images/10--People_Marching/10_People_Marching_People_Marching_2_433.jpg 614,346,771,568,0 245,382,392,570,0 353,222,461,390,0 498,237,630,399,0
Конвертируйте исходный код. Пройдитесь по папке данных, проанализируйте данные в разных форматах построчно и запишите в файл. Уведомление:
- Кадр объекта, формат данных Wider — x, y, w, h, а формат данных обучения — xmin, ymin, xmax, ymax;
- Если обнаружена только одна категория лица, индекс категории равен 0;
конкретный эталонный проектwider_annotation.py
сценарий.
def generate_train_file(bbx_file, data_folder, out_file):
paths_list, names_list = traverse_dir_files(data_folder)
name_dict = dict()
for path, name in zip(paths_list, names_list):
name_dict[name] = path
data_lines = read_file(bbx_file)
sub_count = 0
item_count = 0
out_list = []
for data_line in data_lines:
item_count += 1
if item_count % 1000 == 0:
print('item_count: ' + str(item_count))
data_line = data_line.strip()
l_names = data_line.split('/')
if len(l_names) == 2:
if out_list:
out_line = ' '.join(out_list)
write_line(out_file, out_line)
out_list = []
name = l_names[-1]
img_path = name_dict[name]
sub_count = 1
out_list.append(img_path)
continue
if sub_count == 1:
sub_count += 1
continue
if sub_count >= 2:
n_list = data_line.split(' ')
x_min = n_list[0]
y_min = n_list[1]
x_max = str(int(n_list[0]) + int(n_list[2]))
y_max = str(int(n_list[1]) + int(n_list[3]))
p_list = ','.join([x_min, y_min, x_max, y_max, '0']) # 标签全部是0,人脸
out_list.append(p_list)
continue
файл классаwider_classes.txt
Есть только одна линия, которая является лицом.
тренироваться
Процесс обучения YOLO v3, параметры: данные метки, категория, путь хранения, модель предобучения, якоря, размер ввода.
annotation_path = 'dataset/WIDER_train.txt' # 数据
classes_path = 'configs/wider_classes.txt' # 类别
log_dir = 'logs/002/' # 日志文件夹
pretrained_path = 'model_data/yolo_weights.h5' # 预训练模型
anchors_path = 'configs/yolo_anchors.txt' # anchors
class_names = get_classes(classes_path) # 类别列表
num_classes = len(class_names) # 类别数
anchors = get_anchors(anchors_path) # anchors列表
input_shape = (416, 416) # 32的倍数,输入图像
Создайте модель:
-
input_shape
размер входного изображения; - Anchors — размер кадра обнаружения;
-
num_classes
количество категорий; -
freeze_body
, режим 1 — заморозить все, режим 2 — обучить последние три слоя; -
weights_path
, путь предварительно обученных весов; - логгирование — это обратный вызов TensorBoard, а контрольная точка — это обратный вызов сохранения весов;
model = create_model(input_shape, anchors, num_classes,
freeze_body=2,
weights_path=pretrained_path) # make sure you know what you freeze
logging = TensorBoard(log_dir=log_dir)
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,
Данные обучения и данные проверки:
val_split = 0.1 # 训练和验证的比例
with open(annotation_path) as f:
lines = f.readlines()
np.random.seed(10101)
np.random.shuffle(lines)
np.random.seed(None)
num_val = int(len(lines) * val_split) # 验证集数量
num_train = len(lines) - num_val # 训练集数量
Компиляция модели и подгонка данных:
- Функция потерь использует только
y_pred
результат прогноза; - Количество партий 32;
- И данные обучения, и данные проверки поступают из
data_generator_wrapper
; - Во время тренировочного процесса вес сохраняется через контрольную точку, и, наконец, сохраняется окончательный вес;
model.compile(optimizer=Adam(lr=1e-3), loss={
# use custom yolo_loss Lambda layer.
'yolo_loss': lambda y_true, y_pred: y_pred}) # 损失函数
batch_size = 32 # batch尺寸
print('Train on {} samples, val on {} samples, with batch size {}.'.format(num_train, num_val, batch_size))
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=200,
initial_epoch=0,
callbacks=[logging, checkpoint])
model.save_weights(log_dir + 'trained_weights_stage_1.h5') # 存储最终的参数,再训练过程中,通过回调存储
Создание модели:
- Создать модель для YOLO v3,
yolo_body
, ввод изображения параметров, количество якорей на шкалу, количество категорий; - Загрузите предварительно обученные веса, заморозьте параметры и сохраните последние три слоя;
- Custom Lambda, слой функции потерь модели;
- Входные данные — это вход модели YOLO и истинное значение, а выходные данные — функция потерь;
def create_model(input_shape, anchors, num_classes, load_pretrained=True, freeze_body=2,
weights_path='model_data/yolo_weights.h5'):
K.clear_session() # 清除session
image_input = Input(shape=(None, None, 3)) # 图片输入格式
h, w = input_shape # 尺寸
num_anchors = len(anchors) # anchor数量
# YOLO的三种尺度,每个尺度的anchor数,类别数+边框4个+置信度1
y_true = [Input(shape=(h // {0: 32, 1: 16, 2: 8}[l], w // {0: 32, 1: 16, 2: 8}[l],
num_anchors // 3, num_classes + 5)) for l in range(3)]
model_body = yolo_body(image_input, num_anchors // 3, num_classes) # model
print('Create YOLOv3 model with {} anchors and {} classes.'.format(num_anchors, num_classes))
if load_pretrained: # 加载预训练模型
model_body.load_weights(weights_path, by_name=True, skip_mismatch=True) # 加载参数,跳过错误
print('Load weights {}.'.format(weights_path))
if freeze_body in [1, 2]:
# Freeze darknet53 body or freeze all but 3 output layers.
num = (185, len(model_body.layers) - 3)[freeze_body - 1]
for i in range(num):
model_body.layers[i].trainable = False # 将其他层的训练关闭
print('Freeze the first {} layers of total {} layers.'.format(num, len(model_body.layers)))
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 = Model([model_body.input] + y_true, model_loss) # 模型,inputs和outputs
return model
Генератор данных:
-
data_generator_wrapper
для проверки состояния; - Рандомизировать строку входной метки;
- По количеству партий поместите изображения в
image_data
, поставь границу и параметры в землю правдаy_true
середина; - Выходное изображение и ограничивающая рамка, а также дополнение номером партии для сохранения уверенности.
def data_generator(annotation_lines, batch_size, input_shape, anchors, num_classes):
'''data generator for fit_generator'''
n = len(annotation_lines)
i = 0
while True:
image_data = []
box_data = []
for b in range(batch_size):
if i == 0:
np.random.shuffle(annotation_lines)
image, box = get_random_data(annotation_lines[i], input_shape, random=True) # 获取图片和盒子
image_data.append(image) # 添加图片
box_data.append(box) # 添加盒子
i = (i + 1) % n
image_data = np.array(image_data)
box_data = np.array(box_data)
y_true = preprocess_true_boxes(box_data, input_shape, anchors, num_classes) # 真值
yield [image_data] + y_true, np.zeros(batch_size)
def data_generator_wrapper(annotation_lines, batch_size, input_shape, anchors, num_classes):
"""
用于条件检查
"""
n = len(annotation_lines) # 标注图片的行数
if n == 0 or batch_size <= 0: return None
return data_generator(annotation_lines, batch_size, input_shape, anchors, num_classes)
Конкретная ссылка на исходный кодyolo3_train.py
, можно создать модель обнаружения лица.
проверять
существуетyolo3_predict.py
, замените параметры обученной модели:
Модель: ep108-loss44.018-val_loss43.270.h5
Обнаружение изображений:
Другие: добавление большего количества наборов данных в сочетании с определенными изображениями спроса может улучшить эффект обнаружения.
OK, that's all! Enjoy it!