Недавно ZlodeiBaal опубликовал статью «Нейрореволюция в головах и сёлах», в которой привел обзор возможностей современных нейронных сетей. Самым интересным, на мой взгляд, является подход с использованием сверточных сетей для сегментации изображений, про этот подход и пойдет речь в статье.

segnet.png


Уже давно появилось желание изучить сверточные сети и узнать что-то новое, к тому же под рукой есть несколько последних Tesla K40 с 12Гб памяти, Tesla c2050, обычные видеокарты, Jetson TK1 и ноутбук с мобильной GT525M, интереснее всего конечно попробовать на TK1, так как его можно использовать практически везде, хоть на столб фонарный повесить. Самое первое с чего начал, это распознавание цифр, тут конечно удивить нечем, цифры уже давно неплохо распознаются сетями, но при этом постоянно возникает потребность в новых приложениях, которые должны что-то распознавать: номера домов, номера автомобилей, номера вагонов и т.д. Все бы хорошо, но задача распознавания цифр является лишь частью более общих задач.


Свёрточные сети бывают разные. Какие-то умеют только распознавать объекты на изображении. Какие-то умеют выделить прямоугольник с объектом (RCNN, например). А какие-то могут профильтровать изображение и превратить его в некоторую логическую картинку. Мне больше всего понравились последние: они самые быстрые и красивее всего работают. Для тестирования была выбрана одна из самых последних сетей на этом фронте — SegNet, подробнее можно прочитать в статье. Основная идея этого метода заключается в том, что вместо lable подается не число, а изображение, добавляется новый слой «Upsample» для увеличения размерности слоя.

layer {
  	name: "data"
  	type: "DenseImageData"
  	top: "data"
  	top: "label"
  	dense_image_data_param {
   		source: "/path/train.txt"	// файл обучения: image1.png  label1.png
    		batch_size: 4   			   
   		shuffle: true
  	}
}


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

layer {
    	name: "loss"
    	type: "SoftmaxWithLoss"
   	 bottom: "conv_1D"
   	 bottom: "label"
   	 top: "loss"
    	softmax_param {engine: CAFFE}
    	loss_param: {
    	  	weight_by_label_freqs: true
    	  	class_weighting: 1
    	  	class_weighting: 80
  	}
}


Правильно распознать цифры это лишь часть задачи распознавания номеров и далеко не самая сложная, надо для начала этот номер найти, затем найти где примерно располагаются цифры, а затем уже их распознавать. Довольно часто на первых этапа появляются большие ошибки, и в итоге получить высокую достоверность распознавания номеров довольно сложно. Грязные и затертые номера плохо детектируются и с большими погрешностями, шаблон номера плохо накладывается, в результате возникает много неточностей и сложностей. Номер может быть вообще нестандартным с произвольными интервалами и т.д.
Например, номера вагонов имеют множество вариаций написания. Если правильно выделить границы номера, то можно получить хоть 99.9% на каждой цифре. А если цифры переплетены? Если сегментация будет давать разные цифры в разных частях вагона?

title.jpg


Или, например, задача обнаружения автомобильного номера. Конечно, её можно решить и через Haar и через Hog. Но почему бы не попробовать другой метод и сравнить? Тем более когда есть готовая для обучения и разметки база?
На вход сверточной сети подается изображение с автомобильным номером и маска, на которой единицей залит прямоугольник с номером, а все остальное нулем. После обучения проверяем работу на тестовой выборке, где на каждое входное изображение сеть выдает такого же размера маску, на которой закрашивает те пиксели, где по ее мнению есть номер. Результат на рисунках ниже.

8284.jpg

8300.jpg

8338.jpg

8413.jpg

8417.jpg


Просмотрев тестовую выборку можно понять, что этот метод работает достаточно хорошо и почти не дает сбоев, все зависит от качества обучения и настроек. Так как у Vasyutka и ZlodeiBaal была размеченная база номеров, то обучились именно на ней и проверили насколько хорошо всё работает. Результат был не хуже каскада Хаара, а во многих ситуациях даже лучше. Можно отметить некоторые минусы:
  • не детектирует наклонные номера (их не было в обучающей выборке)
  • не детектирует номера, отснятые в упор (их тоже не было в выборке)
  • иногда не детектирует белые номера на чистых белых машинах (скорее всего тоже из-за неполноты обучающей выборки, но, что интересно — этот же глюк был и у каскада Хаара)

В целом проявление этих недостатков закономерно, сеть плохо находит то, чего не было в обучающей выборке. Если тщательно подойти к процессу подготовки базы, то и результат будет высокого качества.
Полученное решение можно применить для большого класса задач поиска объектов, не только автомобильных номеров. Хорошо, номер найден, теперь надо найти там цифры и распознать их. Это тоже не простая задача, как кажется на первый взгляд, нужно проверить множество гипотез их расположения, а что если номер не стандартный, не подходит под маску, то дело труба. Если автомобильные номера изготавливаются по госту и имеют определенный формат, то есть номера, которые могут быть написаны как угодно, от руки, с разным интервалом. Например, номера вагонов, пишутся с пробелами, единички занимают гораздо меньше места, чем другие цифры.
К нам на помощь снова спешат сверточные сети. А что если использовать туже самую сеть для поиска и распознавания. Будем искать и распознавать номера вагонов. На вход сети подается изображение, на котором есть номер и маска, где квадратики с цифрами заполняются значениями от 1 до 10, а фон заполняется нулем.

number mask.png


После не очень долгого обучения на Tesla K40 получен результат. Чтобы результат был более читабельным, разные цифры раскрашены в разные цвета. Определить номер по цветам большого труда уже не составит.

vagon in.png

vagon_res.png


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

3.png

3_res.png


А что если попробовать что-то более необычное, интересное и сложное, например выделение и сегментацию на медицинских изображениях. Для теста снимки флюорографии были взяты с открытой базы КТ и X-RAY снимков, по ним было проведено обучение сегментации легких и в результате удалось достаточно точно выделить интересующую область. На вход сети также подавалось исходное изображение и маска с нулем и единицей. Справа результат, который выдает сверточная сеть, а слева выделена та же область на изображении.

f1.jpg


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

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

Работает данный метод достаточно быстро, на видеокарте Tesla обработка одного снимка происходит за 10-15 мс, а на Jetson TK1 за 1.4 секунды. Про то, как запустить Caffe на Jetson TK1 и какой скорости обработки можно на нем достичь в этих задачах, наверно лучше посвятить отдельную статью.

P.S.
Обучение занимало не более 12 часов.
Размер базы по номерам 1200 изображений.
Размер базы по вагонам 6000 изображений.
Размер базы по легким 480 изображений.

1. SegNet
2. A Deep Convolutional Encoder-Decoder Architecture for Image Segmentation (pdf)
3. Haar
4. Hog
5. Сегментация легких на изображениях (pdf)

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


  1. mrgloom
    24.02.2016 00:34
    +1

    А какой был размер обучающей выборки? Каково время обучения? Работает ли сеть для сегментации объектов у которых изменяется скейл\поворот?

    вот можете ознакомится еще с подходами
    https://github.com/mrgloom/Semantic-Segmentation-Evaluation


    1. ZlodeiBaal
      24.02.2016 00:49

      А какой был размер обучающей выборки?

      По автомобилям было порядка 1000 размеченных, по лёгким было порядка 500 размеченных. По поездам была сильно больше выборка, думаю в районе 3х тысяч.

      Каково время обучения?

      На одной тэсле за ночь обучается.

      Работает ли сеть для сегментации объектов у которых изменяется скейл\поворот?

      Более-менее, но зависит, конечно, от того какая была обучающая выборка. Например с номерами автомобилей почти вся выборка была фронтальной. В результате номера под углом детектируются. Не все, конечно, но результат неплох. Лучше, чем каскад Хаара обученный по той же выборке или HOG.

      вот можете ознакомится еще с подходами
      github.com/mrgloom/Semantic-Segmentation-Evaluation

      Да, мы смотрели некоторые из них. В принципе в своей прошлой статье я частично рассказывал — https://habrahabr.ru/post/277069/


      1. BelBES
        24.02.2016 00:59

        По автомобилям было порядка 1000 размеченных, по лёгким было порядка 500 размеченных. По поездам была сильно больше выборка, думаю в районе 3х тысяч.

        В обоих случаях выборки милипизерные по меркам Deep Learning… вы использовали фичи на чем-то типа ImageNet предобученные?


        1. ZlodeiBaal
          24.02.2016 01:04

          Нет, всё чисто по входным изображениям даже без их модификации дополнительных. Мы тоже были несколько в шоке, когда после обучения по 500 картинкам такую разметку лёгких получили:)
          Но, в принципе, авторы SegNet утверждают, что вот это они получили по выборке 5000 размеченных, без дополнительных их изменений:


          1. BelBES
            24.02.2016 01:12

            Хм, если я правильно помню, то там первый кусок сети (тот что сворачивает картинку) — это ошметок от VGG16… или вы и его тоже обучали сами?


            1. ZlodeiBaal
              24.02.2016 01:47

              Ну, либо я слепой, либо всё же сами. Вот сеть которую использовали при обучении на одном из примеров: https://yadi.sk/d/yTQ6WhRkpPDKi. Никаких загрузок посторонних коэффициентов не вижу.
              Вот работа авторов — http://arxiv.org/pdf/1511.00561v2.pdf http://arxiv.org/pdf/1511.02680v1.pdf
              Да, первые 13 свёрточных уровней взяты из "VGG-16", но про предзагрузку весов нигде ни слова нет. Обучается всё одновременно.


        1. Nikkolo
          24.02.2016 09:47

          Они милипизерные если из классифицировать 0 или 1, а здесь классификация пикселей, это увеличение данных для обучения в десятки раз.
          В легких было 250 в первой версии и 480 во второй.

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


          1. BelBES
            24.02.2016 13:22

            Всё равно выглядит довольно удивительно. DeepLab обучают вроде как на большем числе сэмплов, да и переиспользуют часть весов от VGG16. Вообщем надо поковырять эту сетку...


      1. mrgloom
        26.02.2016 21:58

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


        1. Nikkolo
          27.02.2016 00:01

          отдельно не смотрел, когда сеть конфигурируется, каффе вроде суммарный объем пишет и он был около 4 Гб кажется.


          1. mrgloom
            27.02.2016 14:23

            https://github.com/alexgkendall/caffe-segnet/issues/21

            Попробовал так же на CPU, тоже зафейлилось, но видимо по какой то другой причине, хотя вроде как все 8Gb памяти сожрало.

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


        1. BelBES
          27.02.2016 10:43

          Если оно запустилось на TK1, то сетка жрет не больше 1Гб памяти, т.к. больше там нету


          1. Nikkolo
            27.02.2016 10:59

            При распознавании 480 Мб, Можно будет посмотреть сколько при обучении сеть выделяет памяти каким нибудь монитором для GPU.


            1. BelBES
              27.02.2016 11:08

              Так можно тупо nvidia-smi запустить из стандартного пакета от nvidia. Оно показвыает какой процесс сколько памяти GPU потребляет, ну и другую полезную информацию о карточках.


              1. mrgloom
                27.02.2016 14:18

                Так интересно сколько в пике, а nvidia-smi даст только для текущего момента, если только дергать её через какие то короткие интервалы и логировать и потом парсить.


                1. BelBES
                  27.02.2016 14:59

                  По моим наблюдениям, caffe во время тренировки грузит карту стабильно и особых всплесков там не бывает.


          1. mrgloom
            27.02.2016 14:17

            Не знаю как это реализованно в caffe, но по логике чтобы прогнать через сетку изображение надо загрузить обученные веса + в каждый момент времени в памяти можно держать данные только для curr и prev слоя, т.е. должно получится сильно меньше чем в тренировочной фазе, еще учитывая то что в блобах в тестовой фазе и градиенты не надо хранить.


            1. BelBES
              27.02.2016 18:45

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


  1. 0serg
    24.02.2016 07:23

    А откуда брались тестовые выборки?


    1. Nikkolo
      24.02.2016 09:41

      По номерам были у ZlodeiBaal а для легких часть скачали с сайта


      1. 0serg
        24.02.2016 14:14

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


        1. ZlodeiBaal
          24.02.2016 14:43

          Конечно различная. Иначе некорректно.
          При этом по машинам она была принципиально разная — ту по которой обучались мы собирали руками, а то по чему тестировались — это всё что нам народ наприсылал сюда — https://habrahabr.ru/company/recognitor/blog/222539/


          1. 0serg
            24.02.2016 22:02

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


            1. Nikkolo
              24.02.2016 22:16

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