Лучшая стратегия для уменьшения ложных срабатываний: новый API обнаружения объектов Google на спутниковых снимках

Я настраиваю новый API Tensorflow Object Detection, чтобы находить небольшие объекты на больших площадях спутниковых снимков. Он работает довольно хорошо - он находит все 10 объектов, которые я хочу, но я также получаю 50-100 ложных срабатываний [вещи, которые немного похожи на целевой объект, но не похожи].

Я использую пример конфигурации из учебника "домашние животные", чтобы настроить faster_rcnn_resnet101_coco модель, которую они предлагают. Я начал с малого, только с 100 примерами обучения моих предметов (всего 1 класс). 50 примеров в моем наборе валидации. Каждый пример представляет собой изображение размером 200x200 пикселей с помеченным объектом (~40x40) в центре. Я тренируюсь до тех пор, пока у меня не выйдет плато точности и кривых потерь.

Я относительно новичок в использовании глубокого обучения для обнаружения объектов. Какова лучшая стратегия для повышения моей точности? например, хард-негативный майнинг? Увеличить размер моего тренировочного набора? Я еще не попробовал самую точную модель, которую они предлагают faster_rcnn_inception_resnet_v2_atrous_coco как я хотел бы сохранить некоторую скорость, но сделаю это при необходимости.

Сложно-негативный майнинг кажется логичным шагом. Если вы согласны, как мне это реализовать при настройке файла tfrecord для моего учебного набора данных? Допустим, я делаю 200х200 изображений для каждого из 50-100 ложных срабатываний:

  • Я создаю xml-файлы "аннотаций" для каждого без элемента "object"?
  • ... или я маркирую эти жесткие негативы как второй класс?
  • Если в моем тренировочном наборе будет от 100 негативов до 100 позитивов - это здоровое соотношение? Сколько негативов я могу включить?

2 ответа

Я недавно вернулся к этой теме в своей работе и подумал, что буду обновлять свои текущие знания для тех, кто посещает в будущем.

Эта тема появилась на трекере репозитория моделей Tensorflow. SSD позволяет установить соотношение количества отрицательных: положительных примеров к моему (max_negatives_per_positive: 3), но вы также можете установить минимальное количество для изображений без позитива (min_negatives_per_image: 3). Оба они определены в разделе конфигурации model-ssd-loss.

Тем не менее, я не вижу такой же опции в конфигурации модели Faster-RCNN. Это упоминается в проблеме, models/research/object_detection/core/balanced_positive_negative_sampler.py содержит код, используемый для Faster-RCNN.

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

Наконец, я наткнулся на эту статью о сетях усилителей фильтров (FAN), которая может быть полезна для вашей работы над аэрофотоснимками.

================================================== =================

В следующей статье описывается сложный негативный майнинг для той же цели, что и вы: Тренировка детекторов объектов на основе регионов с использованием интеллектуального интеллектуального анализа в режиме онлайн.

В разделе 3.1 они описывают использование класса переднего плана и фона:

Фоновая информация. Регион помечается фоновым (bg), если его максимальная IoU с истинностью земли находится в интервале [bg lo, 0,5). Нижний порог bg lo = 0,1 используется как FRCN, так и SPPnet, и он предположен в [14] для грубой аппроксимации жесткого отрицательного майнинга; Предполагается, что регионы с некоторой частотой совпадения с основной правдой, скорее всего, будут запутанными или трудными. В разделе 5.4 мы показываем, что, хотя эта эвристика помогает сходимости и точности обнаружения, она неоптимальна, поскольку игнорирует некоторые нечастые, но важные, сложные фоновые области. Наш метод удаляет порог bg lo.

На самом деле на эту статью ссылаются, и ее идеи используются в коде Tensorflow для обнаружения объектов loss.py для жесткого майнинга:

class HardExampleMiner(object):
"""Hard example mining for regions in a list of images.
Implements hard example mining to select a subset of regions to be
back-propagated. For each image, selects the regions with highest losses,
subject to the condition that a newly selected region cannot have
an IOU > iou_threshold with any of the previously selected regions.
This can be achieved by re-using a greedy non-maximum suppression algorithm.
A constraint on the number of negatives mined per positive region can also be
enforced.
Reference papers: "Training Region-based Object Detectors with Online
Hard Example Mining" (CVPR 2016) by Srivastava et al., and
"SSD: Single Shot MultiBox Detector" (ECCV 2016) by Liu et al.
"""

Основываясь на файле конфигурации вашей модели, HardMinerObject возвращается lost_builder.py в следующем фрагменте кода:

def build_hard_example_miner(config,
                            classification_weight,
                            localization_weight):
"""Builds hard example miner based on the config.
Args:
    config: A losses_pb2.HardExampleMiner object.
    classification_weight: Classification loss weight.
    localization_weight: Localization loss weight.
Returns:
    Hard example miner.
"""
loss_type = None
if config.loss_type == losses_pb2.HardExampleMiner.BOTH:
    loss_type = 'both'
if config.loss_type == losses_pb2.HardExampleMiner.CLASSIFICATION:
    loss_type = 'cls'
if config.loss_type == losses_pb2.HardExampleMiner.LOCALIZATION:
    loss_type = 'loc'

max_negatives_per_positive = None
num_hard_examples = None
if config.max_negatives_per_positive > 0:
    max_negatives_per_positive = config.max_negatives_per_positive
if config.num_hard_examples > 0:
    num_hard_examples = config.num_hard_examples
hard_example_miner = losses.HardExampleMiner(
    num_hard_examples=num_hard_examples,
    iou_threshold=config.iou_threshold,
    loss_type=loss_type,
    cls_loss_weight=classification_weight,
    loc_loss_weight=localization_weight,
    max_negatives_per_positive=max_negatives_per_positive,
    min_negatives_per_image=config.min_negatives_per_image)
return hard_example_miner

который возвращается model_builder.py и вызывается train.py. В общем, мне кажется, что простой генерации ваших истинно положительных меток (с помощью инструмента, такого как LabelImg или RectLabel) должно быть достаточно для алгоритма поезда, чтобы найти жесткие негативы на тех же изображениях. Соответствующий вопрос дает отличное прохождение.

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

Я думаю, что проходил через тот же или близкий сценарий, и им стоит поделиться с вами.

Мне удалось решить эту проблему, передав в трейнер изображения без аннотаций.

В моем сценарии я создаю проект для обнаружения сбоев сборки продуктов моего клиента в режиме реального времени. Я успешно добился очень надежных результатов (для производственной среды), используя обнаружение + классификацию для компонентов, которые имеют явно отрицательный образец (например, винт, у которого есть винт вкл / выкл (только отверстие)), и обнаружение только тех вещей, у которых нет негативы (например, лента, которую можно разместить где угодно).

В системе обязательно, чтобы пользователь записал 2 видео, одно из которых содержит положительный сценарий, а другое - отрицательный (или n видеороликов, содержащих n шаблонов положительного и отрицательного, чтобы алгоритм мог обобщить).

После некоторого тестирования я обнаружил, что если я регистрируюсь для обнаружения только ленты, детектор давал очень уверенные (0,999) ложные срабатывания обнаружения ленты. Он изучал шаблон, в который вставлялась лента, а не сама лента. Когда у меня был другой компонент (например, винт на его отрицательном формате), я передавал отрицательный образец ленты, явно не осознавая этого, поэтому FP не происходило.

Итак, я обнаружил, что в этом сценарии мне обязательно нужно передавать изображения без ленты, чтобы можно было отличить ленту от ленты без ленты.

Я рассмотрел две альтернативы эксперименту и попытался решить эту проблему:

  1. Тренируйтесь, передавая значительное количество изображений без аннотации (10% всех моих негативных образцов) вместе со всеми изображениями, у которых есть настоящие аннотации.
  2. На изображениях, у которых нет аннотации, я создаю фиктивную аннотацию с фиктивной меткой, чтобы я мог заставить детектор тренироваться с этим изображением (таким образом изучая шаблон без ленты). Позже, когда получите фиктивные прогнозы, просто игнорируйте их.

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

Чтобы первая альтернатива работала, мне пришлось внести две небольшие модификации:

  1. Все изображения, не имевшие аннотации, я передал фиктивную аннотацию (class=None, xmin/ymin/xmax/ymax=-1)
  2. При создании файлов tfrecord я использую эту информацию (в данном случае xmin == -1), чтобы добавить пустой список для образца:
      def create_tf_example(group, path, label_map):
    with tf.gfile.GFile(os.path.join(path, '{}'.format(group.filename)), 'rb') as fid:
        encoded_jpg = fid.read()
    encoded_jpg_io = io.BytesIO(encoded_jpg)
    image = Image.open(encoded_jpg_io)
    width, height = image.size

    filename = group.filename.encode('utf8')
    image_format = b'jpg'

    xmins = []
    xmaxs = []
    ymins = []
    ymaxs = []
    classes_text = []
    classes = []

    for index, row in group.object.iterrows():
        if not pd.isnull(row.xmin):
            if not row.xmin == -1:
                xmins.append(row['xmin'] / width)
                xmaxs.append(row['xmax'] / width)
                ymins.append(row['ymin'] / height)
                ymaxs.append(row['ymax'] / height)
                classes_text.append(row['class'].encode('utf8'))
                classes.append(label_map[row['class']])

    tf_example = tf.train.Example(features=tf.train.Features(feature={
        'image/height': dataset_util.int64_feature(height),
        'image/width': dataset_util.int64_feature(width),
        'image/filename': dataset_util.bytes_feature(filename),
        'image/source_id': dataset_util.bytes_feature(filename),
        'image/encoded': dataset_util.bytes_feature(encoded_jpg),
        'image/format': dataset_util.bytes_feature(image_format),
        'image/object/bbox/xmin': dataset_util.float_list_feature(xmins),
        'image/object/bbox/xmax': dataset_util.float_list_feature(xmaxs),
        'image/object/bbox/ymin': dataset_util.float_list_feature(ymins),
        'image/object/bbox/ymax': dataset_util.float_list_feature(ymaxs),
        'image/object/class/text': dataset_util.bytes_list_feature(classes_text),
        'image/object/class/label': dataset_util.int64_list_feature(classes),
    }))
    return tf_example

Часть прогресса обучения:

введите описание изображения здесь

В настоящее время я использую обнаружение объектов tensorflow вместе с tensorflow==1.15, используя fasten_rcnn_resnet101_coco.config.

Надеюсь, это решит чью-то проблему, поскольку я не нашел решения в Интернете. Я читал множество людей, говорящих о том, что fast_rcnn не адаптирован для отрицательного обучения сокращению FP, но мои тесты доказали обратное.

Другие вопросы по тегам