Уже давно появилось желание изучить сверточные сети и узнать что-то новое, к тому же под рукой есть несколько последних 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% на каждой цифре. А если цифры переплетены? Если сегментация будет давать разные цифры в разных частях вагона?
Или, например, задача обнаружения автомобильного номера. Конечно, её можно решить и через Haar и через Hog. Но почему бы не попробовать другой метод и сравнить? Тем более когда есть готовая для обучения и разметки база?
На вход сверточной сети подается изображение с автомобильным номером и маска, на которой единицей залит прямоугольник с номером, а все остальное нулем. После обучения проверяем работу на тестовой выборке, где на каждое входное изображение сеть выдает такого же размера маску, на которой закрашивает те пиксели, где по ее мнению есть номер. Результат на рисунках ниже.
Просмотрев тестовую выборку можно понять, что этот метод работает достаточно хорошо и почти не дает сбоев, все зависит от качества обучения и настроек. Так как у Vasyutka и ZlodeiBaal была размеченная база номеров, то обучились именно на ней и проверили насколько хорошо всё работает. Результат был не хуже каскада Хаара, а во многих ситуациях даже лучше. Можно отметить некоторые минусы:
- не детектирует наклонные номера (их не было в обучающей выборке)
- не детектирует номера, отснятые в упор (их тоже не было в выборке)
- иногда не детектирует белые номера на чистых белых машинах (скорее всего тоже из-за неполноты обучающей выборки, но, что интересно — этот же глюк был и у каскада Хаара)
В целом проявление этих недостатков закономерно, сеть плохо находит то, чего не было в обучающей выборке. Если тщательно подойти к процессу подготовки базы, то и результат будет высокого качества.
Полученное решение можно применить для большого класса задач поиска объектов, не только автомобильных номеров. Хорошо, номер найден, теперь надо найти там цифры и распознать их. Это тоже не простая задача, как кажется на первый взгляд, нужно проверить множество гипотез их расположения, а что если номер не стандартный, не подходит под маску, то дело труба. Если автомобильные номера изготавливаются по госту и имеют определенный формат, то есть номера, которые могут быть написаны как угодно, от руки, с разным интервалом. Например, номера вагонов, пишутся с пробелами, единички занимают гораздо меньше места, чем другие цифры.
К нам на помощь снова спешат сверточные сети. А что если использовать туже самую сеть для поиска и распознавания. Будем искать и распознавать номера вагонов. На вход сети подается изображение, на котором есть номер и маска, где квадратики с цифрами заполняются значениями от 1 до 10, а фон заполняется нулем.
После не очень долгого обучения на Tesla K40 получен результат. Чтобы результат был более читабельным, разные цифры раскрашены в разные цвета. Определить номер по цветам большого труда уже не составит.
На самом деле получился очень хороший результат, даже самые плохие номера, которые до этого плохо распознавались, удалось найти, разделить на цифры и распознать весь номер. Получился универсальный метод, который позволяет не только распознать цифры, но и в целом найти объект на изображении, выделить и классифицировать его, если таких объектов может быть несколько.
А что если попробовать что-то более необычное, интересное и сложное, например выделение и сегментацию на медицинских изображениях. Для теста снимки флюорографии были взяты с открытой базы КТ и X-RAY снимков, по ним было проведено обучение сегментации легких и в результате удалось достаточно точно выделить интересующую область. На вход сети также подавалось исходное изображение и маска с нулем и единицей. Справа результат, который выдает сверточная сеть, а слева выделена та же область на изображении.
Например, в статье используется модель легких для сегментации. Полученный с помощью сверточных сетей результат нисколько не уступает, а в некоторых случаях даже лучше. При этом, обучить сеть занимает куда быстрее, чем создавать и отлаживать алгоритм.
В целом данный подход показал высокую работоспособность и гибкость в большом спектре задач, с помощью него можно решить всевозможные задачи поиска объектов, сегментации и распознавания, а не только классификации.
- выделение автомобильных номеров;
- распознавание автомобильных номеров;
- распознавание номеров на вагонах, платформах, контейнерах и т.д;
- сегментация и выделение объектов: легкие, котики, пешеходы и т.д.
Работает данный метод достаточно быстро, на видеокарте 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)
0serg
24.02.2016 07:23А откуда брались тестовые выборки?
Nikkolo
24.02.2016 09:41По номерам были у ZlodeiBaal а для легких часть скачали с сайта
0serg
24.02.2016 14:14Я в том плане что была ли разделена тестовая выборка от обучающей или тестовые примеры брались из обучающей выборки?
ZlodeiBaal
24.02.2016 14:43Конечно различная. Иначе некорректно.
При этом по машинам она была принципиально разная — ту по которой обучались мы собирали руками, а то по чему тестировались — это всё что нам народ наприсылал сюда — https://habrahabr.ru/company/recognitor/blog/222539/0serg
24.02.2016 22:02Ну по тексту это неясно, а примеров когда используют подмножество обучающей выборки я, к сожалению, видел не мало
Поэтому хорошо когда явно указывается что было обучающей, а что тестовой выборкойNikkolo
24.02.2016 22:16Бывает, но это скорее ради публикаций, ну или так просто от нечего делать.
Интересно что на легких если подсунуть из обучающей выборки, то края даже получаются ломанные, как я вырезал вручную.
mrgloom
А какой был размер обучающей выборки? Каково время обучения? Работает ли сеть для сегментации объектов у которых изменяется скейл\поворот?
вот можете ознакомится еще с подходами
https://github.com/mrgloom/Semantic-Segmentation-Evaluation
ZlodeiBaal
По автомобилям было порядка 1000 размеченных, по лёгким было порядка 500 размеченных. По поездам была сильно больше выборка, думаю в районе 3х тысяч.
На одной тэсле за ночь обучается.
Более-менее, но зависит, конечно, от того какая была обучающая выборка. Например с номерами автомобилей почти вся выборка была фронтальной. В результате номера под углом детектируются. Не все, конечно, но результат неплох. Лучше, чем каскад Хаара обученный по той же выборке или HOG.
Да, мы смотрели некоторые из них. В принципе в своей прошлой статье я частично рассказывал — https://habrahabr.ru/post/277069/
BelBES
В обоих случаях выборки милипизерные по меркам Deep Learning… вы использовали фичи на чем-то типа ImageNet предобученные?
ZlodeiBaal
Нет, всё чисто по входным изображениям даже без их модификации дополнительных. Мы тоже были несколько в шоке, когда после обучения по 500 картинкам такую разметку лёгких получили:)
Но, в принципе, авторы SegNet утверждают, что вот это они получили по выборке 5000 размеченных, без дополнительных их изменений:
BelBES
Хм, если я правильно помню, то там первый кусок сети (тот что сворачивает картинку) — это ошметок от VGG16… или вы и его тоже обучали сами?
ZlodeiBaal
Ну, либо я слепой, либо всё же сами. Вот сеть которую использовали при обучении на одном из примеров: https://yadi.sk/d/yTQ6WhRkpPDKi. Никаких загрузок посторонних коэффициентов не вижу.
Вот работа авторов — http://arxiv.org/pdf/1511.00561v2.pdf http://arxiv.org/pdf/1511.02680v1.pdf
Да, первые 13 свёрточных уровней взяты из "VGG-16", но про предзагрузку весов нигде ни слова нет. Обучается всё одновременно.
Nikkolo
Они милипизерные если из классифицировать 0 или 1, а здесь классификация пикселей, это увеличение данных для обучения в десятки раз.
В легких было 250 в первой версии и 480 во второй.
Никаких дополнительных коэффициентов, предобработки не делалось. Бралось Caffe, сеть и подготавливалась база для обучения.
BelBES
Всё равно выглядит довольно удивительно. DeepLab обучают вроде как на большем числе сэмплов, да и переиспользуют часть весов от VGG16. Вообщем надо поковырять эту сетку...
mrgloom
Попробовал запустить код из туториала, но похоже эта сеть требует очень много памяти, вы не смотрели сколько памяти занимает в пике?
Nikkolo
отдельно не смотрел, когда сеть конфигурируется, каффе вроде суммарный объем пишет и он был около 4 Гб кажется.
mrgloom
https://github.com/alexgkendall/caffe-segnet/issues/21
Попробовал так же на CPU, тоже зафейлилось, но видимо по какой то другой причине, хотя вроде как все 8Gb памяти сожрало.
Попробовал так же уменьшать размер изображения, но тогда надо подбирать параметры
upsample_param
, автоматом (как сверточные слои) это не работает.BelBES
Если оно запустилось на TK1, то сетка жрет не больше 1Гб памяти, т.к. больше там нету
Nikkolo
При распознавании 480 Мб, Можно будет посмотреть сколько при обучении сеть выделяет памяти каким нибудь монитором для GPU.
BelBES
Так можно тупо nvidia-smi запустить из стандартного пакета от nvidia. Оно показвыает какой процесс сколько памяти GPU потребляет, ну и другую полезную информацию о карточках.
mrgloom
Так интересно сколько в пике, а nvidia-smi даст только для текущего момента, если только дергать её через какие то короткие интервалы и логировать и потом парсить.
BelBES
По моим наблюдениям, caffe во время тренировки грузит карту стабильно и особых всплесков там не бывает.
mrgloom
Не знаю как это реализованно в caffe, но по логике чтобы прогнать через сетку изображение надо загрузить обученные веса + в каждый момент времени в памяти можно держать данные только для curr и prev слоя, т.е. должно получится сильно меньше чем в тренировочной фазе, еще учитывая то что в блобах в тестовой фазе и градиенты не надо хранить.
BelBES
Могу ошибаться (но можно уточнить в коде), но вроде бы caffe при загрузке сети аллоцирует памяти столько, сколько требуется и в процессе работы с сеткой, пасмять не освобождает