Вчера я опубликовал статью про машинное обучение и NVIDIA DIGITS. Как и обещал, сегодняшняя статья — почему всё не так уж и хорошо + пример выделения объектов в кадре на DIGITS.

NVIDIA подняла волну пиара по поводу разработанной и имплиментированной в DIGITS сетки DetectNet. Сетка позиционируется как решение для поиска одинаковых/похожих объектов на изображении.



Что это такое


В начале года я несколько раз упоминал про забавную сетку Yolo. В целом, весь народ, с которым я общался, отнеслись к ней скорее негативно, со словами, что Faster-RCNN куда быстрее и проще. Но, инженеры NVIDIA ею вдохновились и собрали свою сетку на Caffe, назвав её DetectNet.
Принцип сетки такой же как и в Yolo. Выходом сети для изображения (N*a*N*a) является массив N*N*5, в котором для каждого региона исходного изображения размером a*a вводиться 5 параметров: наличие объекта и его размер:

image

Плюс сетки:

  • Быстро считает. У меня получалось по 10-20ms на кадр. В то время, когда Faster-RCNN тратил по 100-150.
  • Просто обучается и настраивается. С Faster-RCNN нужно было долго возиться.

Минус один: есть решения с более качественным детектированием.

Общие слова, перед тем как начну рассказ


В отличие от распознавания категорий, про которое я писал вчера, детектирование объектов сделано плохо. Не user friendly. Большая часть статьи будет на тему того, как всё же это чудо запустить. К сожалению, такой подход убивает изначальную идею DIGITS, что можно сделать что-то не разбираясь в логике системы и её математике.
Но если всё же запустили — пользоваться удобно.

Что будем распознавать


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

Я решил воспользоваться частью наработок и подетектировать номера через DIGITS. Так что их-то и будем использовать.

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

Поехали


Выбрав в главном меню «New Dataset->Images->Object Detection» мы попадаем в меню создания датасета. Здесь нужно обязательно указать:

  • Training image folder — папку с изображениями
  • Training label folder — папку с текстовичками-подписями к изображениям
  • Validation image folder — папку с изображениями для проверки
  • Validation label folder — папку с текстовичками-подписями к ним
  • Pad image — Если изображение меньше указанного тут, то оно будет дополнено чёрным фоном. Если больше — создание базы упадёт ? \ _ (?) _ / ?
  • Resize image — к какому размеру ресайзнуть изображение
  • Minimum box size — лучше всего установить это значение. Это минимальный размер объекта при валидации

Тут есть сложность. Как делать текстовик-подпись к изображению с его описанием? Пример на ГитХабе от NVIDIA в официальном репозитории DIGITS скромно об этом умалчивает, упоминая лишь, что он такой же, как в датасете kitti. Меня несколько удивил такой подход к пользователям готового из коробки фреймворка. Но ок. Пошёл, скачал базу и доки к ней, прочитал. Формат файла:

Car 0.00 0 1.95 96.59 181.90 405.06 371.40 1.52 1.61 3.59 -3.49 1.62 7.68 1.53
Car 0.00 0 1.24 730.55 186.66 1028.77 371.36 1.51 1.65 4.28 2.61 1.69 8.27 1.53
Car 0.00 0 1.77 401.35 177.13 508.22 249.68 1.48 1.64 3.95 -3.52 1.59 16.82 1.57

Описание файла:

#Values    Name      Description
----------------------------------------------------------------------------
   1    type         Describes the type of object: 'Car', 'Van', 'Truck',
                     'Pedestrian', 'Person_sitting', 'Cyclist', 'Tram',
                     'Misc' or 'DontCare'
   1    truncated    Float from 0 (non-truncated) to 1 (truncated), where
                     truncated refers to the object leaving image boundaries
   1    occluded     Integer (0,1,2,3) indicating occlusion state:
                     0 = fully visible, 1 = partly occluded
                     2 = largely occluded, 3 = unknown
   1    alpha        Observation angle of object, ranging [-pi..pi]
   4    bbox         2D bounding box of object in the image (0-based index):
                     contains left, top, right, bottom pixel coordinates
   3    dimensions   3D object dimensions: height, width, length (in meters)
   3    location     3D object location x,y,z in camera coordinates (in meters)
   1    rotation_y   Rotation ry around Y-axis in camera coordinates [-pi..pi]
   1    score        Only for results: Float, indicating confidence in
                     detection, needed for p/r curves, higher is better.

Естественно, большая часть параметров тут не нужна. Реально можно оставить только параметр «bbox», остальное всё равно не будет использоваться.

Как выяснилось позже, для DIGITS был ещё второй тьюториал, где формат файла всё же подписывался. Но был он не в репозитории DIGITS ? \ _ (?) _ / ?

Там подтверждено, что мои догадки о том, что нужно использовать были верны:

image

Начинаем обучать


Класс. База сделана, Начинаем обучать. Для обучения нужно выставить такие же настройки, как указанные в примере:

  • Subtract Mean в None
  • base learning rate в 0.0001
  • ADAM solver
  • Выбрать вашу базу
  • Выбрать вкладку «Custom Network». Скопировать в неё текст из файла "/caffe-caffe-0.15/examples/kitti/detectnet_network.prototxt" (это в форке caffe от nvidia, понятно).
  • Так же, рекомендуется скачать предварительно натренированную модель GoogleNet вот тут. Указать её в «Pretrained model(s)»

Так же, я сделал следующее. Для скопированной сетки «detectnet_network.prototxt» все значения размера изображения «1248, 352» я заменил на размеры изображений из своей базы. Без этого обучение падало. Ну, естественно, ни в одном тьюторивале этого нет… ? \ _ (?) _ / ?

График Loss падает, обучение пошло. Но… График точности стоит на нуле. Что такое?!
Ни один из двух тьюториалов которые я нашел не отвечал на этот вопрос. Пошёл копаться в описание сетки. Где копаться, было понятно сразу. Раз падают loss — обучение идёт. Ошибка в validation пайплайне. И действительно. В конфигурации сети есть блок:

layer {
  name: "cluster"
  type: "Python"
  bottom: "coverage"
  bottom: "bboxes"
  top: "bbox-list"
  python_param {
    module: "caffe.layers.detectnet.clustering"
    layer: "ClusterDetections"
    param_str: "1024, 640, 16, 0.05, 1, 0.02, 5, 1"
  }
}

Выглядит подозрительно. Открыв описание слоя clustering можно найти комментарий:

# parameters - img_size_x, img_size_y, stride,
# gridbox_cvg_threshold,gridbox_rect_threshold,gridbox_rect_eps,min_height,num_classes

Становится понятно, что это пороги. Зарандомил там 3 числа не вникая в суть. Обучение пошло + начал расти validation. Часов за 5 достиг каких-то разумных порогов.



Но вот облом. При успешном обучении 100% картинок не распонзавалось. Пришлось копаться и разбираться, что этот слой значит.

Слой реализует сбор полученных гипотез в единое решение. Как основной инструмент тут применяется OpenCV модуль «cv.groupRectangles». Это функция, которая ассоциирует группы прямоугольников в один прямоугольник. Как вы помните, у сети такая структура, что в окрестности объекта — должно быть много срабатываний. Их нужно собрать в единое решение. У алгоритма сбора есть куча параметров.

  • gridbox_cvg_threshold (0.05) — порог детектирования объекта. По сути достоверность того, что мы нашли номер. Чем меньше — тем больше детекций.
  • gridbox_rect_threshold (1) — сколько детекторов должно сработать, чтобы было принято решение «есть номер»
  • gridbox_rect_eps (0.02) — во сколько раз могут отличаться размеры прямоугольников, чтобы объединить их в одну гипотезу
  • min_height — минимальная высота объекта

Теперь их достаточно просто подобрать, чтобы всё заработало. А теперь юмор. Таки был ещё и третий тьюториал, где часть всего этого дела описана.
Но не вся ? \ _ (?) _ / ?

Что в итоге


В итоге можно посмотреть что сетка выделила:


Работает неплохо. На первый взгляд лучше, чем Хаар, который мы использовали. Но сразу стало понятно, что маленькая обучающая база (~1500 кадров) — даёт о себе знать. В базе не учли грязные номера => они не детектируются. В базе не учли сильную перспективу номера => они не детектируются. Не учли слишком крупные/слишком мелкие. Ну, вы поняли. Короче нужно не полениться и разметить тысяч 5 номеров нормально.

При распознавании можно посмотреть прикольные картинки с картами активации (1,2,3). Видно, что на каждом следующем уровне номер виден всё чётче и чётче.

Как запустить


Приятный момент — результат можно запустить кодом из ~20 строчек. И это будет готовый детектор номеров:

import numpy as np
import sys
caffe_root = '../'  # путь в корень каффе
sys.path.insert(0, caffe_root + 'python')
import caffe
caffe.set_mode_cpu() # Если на проце. Иначе:
#caffe.set_device(0)
#caffe.set_mode_gpu()
model_def = caffe_root + 'models/DetectNet/deploy.prototxt' #описание сети
model_weights = caffe_root + 'models/DetectNet/DetectNet.caffemodel' #веса сети
net = caffe.Net(model_def,      # defines the structure of the model
                model_weights,  # contains the trained weights
                caffe.TEST)     # use test mode (e.g., don't perform dropout)

#Как преобразовывать картинки перед отправкой в сеть
mean=np.array([128.0,128.0,128.0])
transformer = caffe.io.Transformer({'data': net.blobs['data'].data.shape})
transformer.set_transpose('data', (2,0,1))  # move image channels to outermost dimension
transformer.set_mean('data', mean)            # subtract the dataset-mean value in each channel
transformer.set_raw_scale('data', 255)      # rescale from [0, 1] to [0, 255]
transformer.set_channel_swap('data', (2,1,0))  # swap channels from RGB to BGR
# Вход сети на всякий случай поставим корректный
net.blobs['data'].reshape(1,        # batch size
                          3,         # 3-channel (BGR) images
                          640, 1024)  # image size is 227x227
image = caffe.io.load_image('/media/anton/Bazes/ReInspect/CARS/test/0.jpg')# тестовое изображение загружаем
transformed_image = transformer.preprocess('data', image)# подготовим дял укладывания в сеть
output = net.forward() # распознаем
output_prob = output['bbox-list'][0] # массив результатов в формате нужном нам
print output_prob[0]

Вот тут вот я выложил деплой файл для сетки и веса обученой сети, если кому надо.
Поделиться с друзьями
-->

Комментарии (32)


  1. A-Stahl
    18.10.2016 20:11

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


  1. Zifix
    18.10.2016 20:36

    Спасибо за статью.

    Быстро считает. У меня получалось по 10-20ms на кадр. В то время, когда Faster-RCNN тратил по 100-150.
    А что за видеокарта?


    1. ZlodeiBaal
      18.10.2016 20:39
      +1

      1080, так что да, это не очень репредентативно для простых устройств


      1. Zifix
        18.10.2016 20:53

        Понятно, а более быстрые сети в природе существуют? Насколько реально получить такие скорости на видеокарте среднего уровня типа 950?


        1. BelBES
          18.10.2016 21:14
          +1

          Упомянуый в статье YOLO можно разогнать вплоть до 100fps.


        1. ZlodeiBaal
          18.10.2016 21:56
          +1

          Я думаю, что на 950 максимум раза в 2 упадёт. А так должна работать.


          1. Zifix
            18.10.2016 22:06

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

            Кстати, какая зависимость размера кадра и скорости? Уменьшения кадра и падения качества распознавания? Точных цифр не надо, достаточно одним словом охарактеризовать.


            1. BelBES
              18.10.2016 22:14
              +1

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

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


            1. ZlodeiBaal
              18.10.2016 22:21
              +1

              В DetectNet вычислений на верхнем уровне практически нет, как я понимаю. А для свёрточной сети объём вычислений будет прямо пропорционален площади. Ускорения тут скорее от железа будут зависить. Вроде NVIDIA сделало нативную поддержку ядер 3*3. Но каких-то тестов не от NVIDIA не видел.


        1. Nikobraz
          18.10.2016 22:08
          +2

          Не могу не заметить, что в новый Titan X и Теслы подвезли INT8 инструкции оптимизированные для машинного обучения. Также хочу услышать комментарий автора, относительно них.


          1. ZlodeiBaal
            19.10.2016 00:29
            +1

            На новых не работал. Видел тесты NVIDIA, что они просто разрывают всех по производительности. Но реально не читал про опыт использования. И сам не тестировал.


            1. Nikobraz
              19.10.2016 00:53

              Тесл еще в продаже нет, только Титан(в P100 поддержки INT8 нет). Еще нагуглил, что Intel Xeon Phi уже поддерживают часть инструкций, запихивая по 8 штук за раз в AVX-512. Видимо будет отчаянная грызня до последней капли крови за нейросети.


              1. ZlodeiBaal
                19.10.2016 00:58

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


      1. masha_kupina
        18.10.2016 20:55

        А ATI какие нибудь интересные решения для своих железок не предлагает? И у кого из этих производителей больше взаимопонимания с юзерами и разработчиками под GPU?


        1. ZlodeiBaal
          18.10.2016 20:59
          +3

          У NVIDIA. Считайте что они захватили рынок. Нет, какая-то поддержка ATI есть. Но обычно кривая и бажная. В Theano вроде есть, в TensorFlow. Но ATI реагирует на желания пользователей лишь после того как на них прореагирует NVIDIA. Или даже позже.
          А так, даже Intel прикрутил для своей IntelPHI какой-то форк Caffe. Но вот стоит ли этим пользоваться? Не думаю.


          1. masha_kupina
            18.10.2016 22:05

            Спасибо за развернутый ответ. Стало быть, хоть жефорсовые флопсы и дороже радеоновских, зато это какие надо флопсы)


        1. enclis
          18.10.2016 21:11

          В Torch есть поддержка OpenCL, но оно работает заментно медленее чем CUDA/cuDNN.


          1. masha_kupina
            18.10.2016 22:09

            спасибо, помогли дилетанту определиться с выбором)


        1. Nikobraz
          18.10.2016 22:17

          AMD что-то не видно и не слышно. Топовое решение у них S9300 x2, судя по ттх FP32 считает как боженька, а FP64 плохо. Да и ориентируются, судя по сайту на облачные игры. Поддерживает только C++AMP и OpenCL.


          1. BelBES
            18.10.2016 22:21
            +1

            У AMD долгов столько, что некогда им заниматься венчурными вложениями в Deep Learning :(


  1. BelBES
    18.10.2016 21:13
    +2

    Быстро считает. У меня получалось по 10-20ms на кадр. В то время, когда Faster-RCNN тратил по 100-150.

    Чет мне кажется, что Faster тоже полетит, если у него в качестве feature extractor будет использоваться GoogLeNet вместо VGG-16...


    1. ZlodeiBaal
      18.10.2016 21:55
      +1

      Кстати, тут есть любопытный вопрос. Если YOLO заапдейтить верхушку вот таким образом (назвается SSD). То у него качество распознавания раза в 1.2 вырастает. Но этот апдейт, по сути, просто напросто Residual Connection.
      Возникает логичный вопрос: где сетки для детектирования на базе полноценного ResNet??? Они должны хорошо работать. Почему никто на базе ResNet не пересобрал Faster-RCNN? Или всё это уже давно сделано в недрах крупных компаний, просто не публикуются результаты?

      Чет мне кажется, что Faster тоже полетит, если у него в качестве feature extractor будет использоваться GoogLeNet вместо VGG-16...

      Как я понял YOLO на VGG16 всё же даёт ~50 fps. По крайней мере вот эти ребята недавно эксперементировали вроде. А. Вы чуть выше это тоже упомянули. Но 100fps я не видел сам:)
      так что не думаю, что Faster прямо таки в 10 раз ускориться. Но раза в 2 может.


      1. sim0nsays
        18.10.2016 22:11
        +1

        Топовый результат в object detection на ImageNet как раз продемонстрирован ResNet, воткнутым в Faster-RCNN


        1. ZlodeiBaal
          18.10.2016 22:28
          +1

          А там опубликована статья? Ссылочкой не поделитесь? Я переодически табличку по VOC2012 отслеживаю — http://host.robots.ox.ac.uk:8080/leaderboard/displaylb.php?cls=mean&challengeid=11&compid=6&submid=8822#KEY_RRR-ResNet152-COCO-MultiScale
          Но там по тем сеткам, где архитектура открыта она какая-то мутная. То марковские модели впилят, то какие-то карты фич полу-вручную разгребают.


          1. BelBES
            19.10.2016 01:24
            +1

            А чего там публиковать то? Ну воткнули и воткнули, в faster'е же feature extraction впиливается достаточно прямолинейно. А вообще вот, например, статейка про то, как чего-нибудь сильно кастомное приделать к faster'у: https://arxiv.org/pdf/1608.08021.pdf


          1. sim0nsays
            21.10.2016 20:44

            В изначальной статье про ResNets это обсуждается в Appendix: https://arxiv.org/abs/1512.03385


      1. BelBES
        18.10.2016 22:20
        +1

        Как я понял YOLO на VGG16 всё же даёт ~50 fps. По крайней мере вот эти ребята недавно эксперементировали вроде. А. Вы чуть выше это тоже упомянули. Но 100fps я не видел сам:)

        С VGG-16 у меня получалось ~90мс на кадр (на 980Ti), а вот при использовании упрощенных фич 100fps он действительно выдает, но детектирует совсем плохо.


  1. Nidirg
    19.10.2016 23:36

    Спасибо за обзоры, с помощью чего приводили базу к стандарту описания KITTY?


    1. ZlodeiBaal
      19.10.2016 23:37

      Ручками не то в C# не то в Python за 10 строчек.


  1. nazargulov
    22.10.2016 04:02

    А зачем вы удалили приложение по распознаванию номеров из App Store? Где его можно скачать?


  1. mrgloom
    23.10.2016 15:18

    То что там GoogLeNet используется несет какой то смысл или можно любую воткнуть?


    1. ZlodeiBaal
      23.10.2016 23:49

      Можно любую. Более того, DIGITS можно поверх любой более-менее адекватной современной версии caffe зацепить. Просто перед стартом указать переменную в папку где Caffe.
      Конечно, после этого DetectNet и прочие свистелки-перделки перестают работать, но графички потерь и всё такое он кажет.