Как оптимизировать модели обнаружения объектов для конкретных областей

Оптимизация моделей обнаружения объектов для конкретных областей советы и рекомендации

Разработайте лучшие и более быстрые модели, чтобы решить вашу конкретную проблему

Фото Paolo Candelo на Unsplash

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

В качестве примера можно привести набор данных Common Objects in Context (COCO), который обычно используется в качестве базового для исследований в этой области и влияет на гиперпараметры и архитектурные детали моделей. В нем содержится 90 отдельных классов с различными условиями освещения, фонами и размерами. Однако иногда возникает необходимость в решении более простой задачи. Возможно, вам нужно обнаружить всего несколько конкретных объектов без большого разнообразия сцен и размеров. В этом случае, если вы обучите модель, используя общий набор гиперпараметров, вы скорее всего получите модель, которая порождает ненужные вычислительные затраты.

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

Предоставление некоторого контекста

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

Примеры из HFSL набора данных, созданных в этой работе. Изображение от автора.

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

Архитектуры и настройка обнаружения объектов

В рамках этого проекта были протестированы следующие архитектуры обнаружения объектов: EfficientDetD0, Faster-RCNN, SDD320, SDD640 и YoloV7. Однако представленные здесь концепции могут быть применены для адаптации других архитектур.

Для разработки моделей я в основном использовал Python 3.8 и фреймворк TensorFlow, за исключением YoloV7, где использовался PyTorch. В то время как большинство примеров относятся к TensorFlow, вы можете применить эти принципы к вашему предпочтительному фреймворку.

В плане аппаратного обеспечения тестирование проводилось с использованием GPU RTX 3060 и процессора Intel Core i5-10400. Весь исходный код и модели доступны на GitHub.

Настройка обнаружителей объектов

При использовании TensorFlow для обнаружения объектов важно понимать, что все гиперпараметры хранятся в файле с именем “pipeline.config”. Этот файл protobuf содержит конфигурации, используемые для обучения и оценки модели, и вы найдете его в любой предварительно обученной модели, загруженной из TF Model Zoo, например. В этом контексте я опишу изменения, которые я внес в файлы конвейера, чтобы оптимизировать обнаружители объектов.

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

Общие упрощения

Первое изменение, которое может быть применено ко всем моделям – это сокращение максимального количества предсказаний для каждого класса и количества создаваемых ограничивающих рамок с 100 до 2 и 4 соответственно. Вы можете сделать это, изменив свойство “max_number_of_boxes” внутри объекта “train_config”:

...train_config {  batch_size: 128  sync_replicas: true  optimizer { ... }  fine_tune_checkpoint: "PATH_TO_BE_CONFIGURED"  num_steps: 50000  startup_delay_steps: 0.0  replicas_to_aggregate: 8  max_number_of_boxes: 4 # <------------------ измените эту строку  unpad_groundtruth_tensors: false  fine_tune_checkpoint_type: "classification"  fine_tune_checkpoint_version: V2}...

После этого измените значения “max_total_detections” и “max_detections_per_class”, которые находятся внутри “post_processing” детектора объектов:

post_processing {  batch_non_max_suppression {    score_threshold: 9.99999993922529e-09    iou_threshold: 0.6000000238418579    max_detections_per_class: 2 # <------------------ измените эту строку    max_total_detections: 4     # <------------------ измените эту строку    use_static_shapes: false  }  score_converter: SIGMOID}

Эти изменения важны, особенно в моем случае, поскольку на изображении одновременно появляются только три объекта и два класса. Уменьшение количества предсказаний уменьшает количество итераций, необходимых для удаления перекрывающихся ограничивающих рамок с помощью метода Non-maximum Suppression (NMS). Поэтому, если у вас ограниченное количество классов для обнаружения и объекты появляются на сцене, изменение этого гиперпараметра может быть хорошей идеей.

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

Многорамочный детектор одного выстрела (SSD)

Всегда хорошая идея испытать разные разрешения при работе с обнаружением объектов. В этом проекте я использовал две версии модели, SSD320 и SSD640, с разрешениями входного изображения 320×320 и 640×640 пикселей соответственно.

Для обеих моделей одним из основных изменений было сокращение глубины сети сети Feature Pyramid Network (FPN) с 5 до 4 путем удаления наиболее поверхностного слоя. FPN является мощным механизмом извлечения признаков, который работает с несколькими размерами карты признаков. Однако для более крупных объектов наиболее поверхностный слой, предназначенный для более высокого разрешения изображения, может быть необязательным. Следовательно, если объекты, которые вы пытаетесь обнаружить, не слишком малы, вероятно, имеет смысл удалить этот слой. Чтобы внести это изменение, измените значение атрибута “min_level” с 3 на 4 внутри объекта “fpn”:

...feature_extractor {  type: "ssd_mobilenet_v2_fpn_keras"  depth_multiplier: 1.0  min_depth: 16  conv_hyperparams {    regularizer { ... }    initializer { ... }    activation: RELU_6    batch_norm {...}  }  use_depthwise: true  override_base_feature_extractor_hyperparams: true  fpn {    min_level: 4 # <------------------ измените эту строку    max_level: 7    additional_layer_depth: 108 # <------------------ измените эту строку  }}...

Также для модели более высокого разрешения (SSD640) я упростил ее, уменьшив значение “additional_layer_depth” с 128 до 108. Аналогично, для обеих моделей я изменил глубину “multiscale_anchor_generator” с 5 на 4 слоя, как показано ниже:

...anchor_generator {  multiscale_anchor_generator {    min_level: 4 # <------------------ измените эту строку    max_level: 7    anchor_scale: 4.0    aspect_ratios: 1.0    aspect_ratios: 2.0    aspect_ratios: 0.5    scales_per_octave: 2  }}...

Наконец, сеть, отвечающая за создание прогнозов ограничивающей рамки (“box_predictor“), имела количество слоев сокращено с 4 до 3. Что касается SSD640, глубина предсказателя рамки также уменьшилась с 128 до 96, как показано ниже:

...box_predictor {  weight_shared_convolutional_box_predictor {    conv_hyperparams {      regularizer { ... }      initializer { ... }      activation: RELU_6      batch_norm { ... }    }    depth: 96 # <------------------ измените эту строку    num_layers_before_predictor: 3 # <------------------ измените эту строку    kernel_size: 3    class_prediction_bias_init: -4.599999904632568    share_prediction_tower: true    use_depthwise: true  }}...

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

EfficinetDet-D0

Что касается EfficientDet-D0, я уменьшил глубину сети биполярных пирамид признаков (Bi-FPN) с 5 до 4. Кроме того, я уменьшил количество итераций Bi-FPN с 3 до 2 и размеры ядер карт признаков с 64 до 48. Bi-FPN – это сложный метод многоуровневого слияния признаков, который может давать отличные результаты. Однако это приводит к более высоким требованиям к вычислительным мощностям, что может быть пустой тратой ресурсов для более простых задач. Чтобы внести вышеуказанные изменения, просто обновите атрибуты объекта “bifpn” следующим образом:

...bifpn {      min_level: 4 # <------------------ измените эту строку      max_level: 7      num_iterations: 2 # <------------------ измените эту строку      numyaml_filters: 48 # <------------------ измените эту строку    }...

Кроме того, также важно уменьшить глубину “multiscale_anchor_generator” таким же образом, как мы сделали с SSD. Наконец, я уменьшил количество слоев сети предсказателя рамки с 3 до 2:

...box_predictor {  weight_shared_convolutional_box_predictor {    conv_hyperparams {      regularizer { ... }      initializer { ... }      activation: SWISH      batch_norm { ... }      force_use_bias: true    }    depth: 64    num_layers_before_predictor: 2 # <------------------ измените эту строку    kernel_size: 3    class_prediction_bias_init: -4.599999904632568    use_depthwise: true  }}...

Faster R-CNN

Модель Faster R-CNN полагается на сеть региональных предложений (RPN) и якорные рамки как основные техники. Якорные рамки – это центральная точка скользящего окна, которое перебирает последнюю карту признаков базовой сверточной нейронной сети. Для каждой итерации классификатор определяет вероятность наличия объекта в предложении, а регрессор корректирует координаты ограничивающей рамки. Чтобы обеспечить инвариантность к сдвигу детектора, он использует три разных масштаба и три соотношения сторон для якорных рамок, что увеличивает количество предложений на итерацию.

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

Для этого первая важная модификация заключается в уменьшении количества сгенерированных предложений с 300 до 50. Это сокращение возможно, потому что в изображении одновременно присутствует только несколько объектов. Вы можете внести это изменение, настроив свойство “first_stage_max_proposals”, как показано ниже:

...first_stage_box_predictor_conv_hyperparams {  op: CONV  regularizer { ... }  initializer { ... }}first_stage_nms_score_threshold: 0.0first_stage_nms_iou_threshold: 0.7first_stage_max_proposals: 50 # <------------------ измените эту строкуfirst_stage_localization_loss_weight: 2.0first_stage_objectness_loss_weight: 1.0initial_crop_size: 14maxpool_kernel_size: 2maxpool_stride: 2...

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

first_stage_anchor_generator {  grid_anchor_generator {    scales: [0.25, 0.5, 1.0] # <----------------- измените эту строку    aspect_ratios: [0.5, 1.0] # <----------------- измените эту строку    height_stride: 16    width_stride: 16  }}

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

YoloV7

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

backbone:  # [from, number, module, args]  [[-1, 1, Conv, [22, 3, 1]],  # 0   [-1, 1, Conv, [44, 3, 2]],  # 1-P1/2         [-1, 1, Conv, [44, 3, 1]],   [-1, 1, Conv, [89, 3, 2]],  # 3-P2/4     [-1, 1, Conv, [44, 1, 1]],   [-2, 1, Conv, [44, 1, 1]],   [-1, 1, Conv, [44, 3, 1]],   [-1, 1, Conv, [44, 3, 1]],   [-1, 1, Conv, [44, 3, 1]],   [-1, 1, Conv, [44, 3, 1]],   [[-1, -3, -5, -6], 1, Concat, [1]],   [-1, 1, Conv, [179, 1, 1]],  # 11   [-1, 1, MP, []],   [-1, 1, Conv, [89, 1, 1]],   [-3, 1, Conv, [89, 1, 1]],   [-1, 1, Conv, [89, 3, 2]],   [[-1, -3], 1, Concat, [1]],  # 16-P3/8     [-1, 1, Conv, [89, 1, 1]],   [-2, 1, Conv, [89, 1, 1]],   [-1, 1, Conv, [89, 3, 1]],   [-1, 1, Conv, [89, 3, 1]],   [-1, 1, Conv, [89, 3, 1]],   [-1, 1, Conv, [89, 3, 1]],   [[-1, -3, -5, -6], 1, Concat, [1]],   [-1, 1, Conv, [512, 1, 1]],  # 24   [-1, 1, MP, []],   [-1, 1, Conv, [89, 1, 1]],   [-3, 1, Conv, [89, 1, 1]],   [-1, 1, Conv, [89, 3, 2]],   [[-1, -3], 1, Concat, [1]],  # 29-P4/16     [-1, 1, Conv, [89, 1, 1]],   [-2, 1, Conv, [89, 1, 1]],   [-1, 1, Conv, [89, 3, 1]],   [-1, 1, Conv, [89, 3, 1]],   [-1, 1, Conv, [89, 3, 1]],   [-1, 1, Conv, [89, 3, 1]],   [[-1, -3, -5, -6], 1, Concat, [1]],   [-1, 1, Conv, [716, 1, 1]],  # 37   [-1, 1, MP, []],   [-1, 1, Conv, [256, 1, 1]],   [-3, 1, Conv, [256, 1, 1]],   [-1, 1, Conv, [256, 3, 2]],   [[-1, -3], 1, Concat, [1]],  # 42-P5/32     [-1, 1, Conv, [128, 1, 1]],   [-2, 1, Conv, [128, 1, 1]],   [-1, 1, Conv, [128, 3, 1]],   [-1, 1, Conv, [128, 3, 1]],   [-1, 1, Conv, [128, 3, 1]],   [-1, 1, Conv, [128, 3, 1]],   [[-1, -3, -5, -6], 1, Concat, [1]],   [-1, 1, Conv, [716, 1, 1]],  # 50  ]# yolov7 headhead:  [[-1, 1, SPPCSPC, [358]], # 51   [-1, 1, Conv, [179, 1, 1]],   [-1, 1, nn.Upsample, [None, 2, 'nearest']],   [37, 1, Conv, [179, 1, 1]], # route backbone P4   [[-1, -2], 1, Concat, [1]],   [-1

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

С этими корректировками я сократил количество параметров с 36,4 миллиона до всего 14,1 миллиона, что составляет примерно 61%. Кроме того, я использовал разрешение ввода 512x512 пикселей вместо предложенных 640x640 пикселей в оригинальной статье.

Дополнительный совет

Еще один ценный совет при обучении детекторов объектов - использовать модель Kmeans для безнадзорной настройки пропорций привязки якорных рамок, подгоняя ширину и высоту фигур для максимизации отношения пересечения-объединения (IoU) внутри обучающего набора. Таким образом, мы можем лучше адаптировать якоря к данной проблемной области, тем самым улучшая сходимость модели путем начальной настройки соответствующих соотношений сторон. Ниже приведена иллюстрация этого процесса, сравнивающая три якорные рамки, используемые по умолчанию в алгоритме SSD (красный), с тремя рамками с оптимизированными пропорциями для задачи обнаружения рук и лица (зеленый).

Сравнение пропорций различных ограничивающих рамок. Изображение автора.

Показ результатов

Я обучал и оценивал каждый детектор с использованием собственного набора данных, называемого набор данных дактильного и мимического языка рук (Hand and Face Sign Language, HFSL), при этом в качестве основных метрик рассматривались mAP и количество кадров в секунду (FPS). В таблице ниже представлена сводка результатов, а значение в скобках представляет собой значение FPS детектора до внедрения описанных оптимизаций.

Результаты обнаружения объектов.

Можно заметить, что большинство моделей показало значительное сокращение времени вывода при сохранении высокого значения mAP при различных уровнях пересечения-объединения (IoU). Более сложные архитектуры, такие как Faster R-CNN и EfficientDet, увеличили значение FPS на GPU соответственно на 200,80% и 231,78%. Даже архитектуры на основе SSD показали огромное улучшение производительности с увеличением на 280,23% и 159,59% для версий 640 и 320 соответственно. При рассмотрении YoloV7, хотя разница в FPS наблюдается больше на процессоре, оптимизированная модель имеет на 61% меньше параметров, уменьшая требования к памяти и делая ее более подходящей для встраиваемых устройств.

Заключение

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

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