Ещё раз про Caffe2

Я уже писал как-то про caffe2. Довелось ещё немного с ним поработать и вставить его в один проект, так что хотелось описать свои впечатления.


Где-то весной мы сделали новую систему распознавания номеров. В первую очередь у нас была версия под Jetson и серверная версия. Но, учитывая мой небольшой опыт с запусканием caffe на RPi захотелось понять насколько это осуществимо. И, внезапно, оказалось что вполне. Конечно, с кучей ограничения и дополнительных условий, но за 3-4 секунды можно распознать номер на PRi. При этом на первичный поиск номера уходило вообще 0.2-0.3 секунды.
Посидев несколько дней мы сделали хороший прототип. Всё идеально. Решили запустить для испытания подключив к шлагбауму. Но, незадача. Раз в 5-6 минут всё падает. Оказалось, что на RPi3 сборка caffe нестабильна. Достаточно простая сетка с небольшой вероятностью крашится на forward’е.
Конечно, можно было бы попробовать отловить ошибку. Но было подозрение, что это наезд в памяти, что может долго ловиться.
Решили опробовать caffe2. Сетка и её описание экспортируются из caffe одной командой из bash.
Один вечер ушёл на переписывание вызовов. Всё очень просто, когда сетка уже готовая. Например, такая подготовка данных и вызов:

net_pos.blobs['data'].data[0, 0, :, :] = small[:, :, 0] / 255.0
net_pos.blobs['data'].data[0, 1, :, :] = small[:, :, 1] / 255.0
net_pos.blobs['data'].data[0, 2, :, :] = small[:, :, 2] / 255.0
w = net_pos.blobs['conv6'].shape[3]
h = net_pos.blobs['conv6'].shape[2]
net_pos.forward()
result = numpy.zeros((h, w))
result[:, :] = net_pos.blobs['conv6'].data[0, :, :]

Заменяется на такую:

small = cv2.resize(img, (Ws[i], Hs[i]), interpolation=cv2.INTER_NEAREST)
small = small/256.0
small = numpy.swapaxes(small,2,0)
small = numpy.swapaxes(small,1,2)
small = small[np.newaxis, :, :, :].astype(np.float32)
res = np.asarray(net_pos.run([small])) #Вызов сети
result = res[0,0,0,:,:]

Плюс 2-3 строчки на загрузку сети:

with open("./net_pos/init_net.pb") as f:
    init_net1 = f.read()
with open("./net_pos/predict_net.pb") as f:
    predict_net1 = f.read()
net_pos = workspace.Predictor(init_net1, predict_net1)

И всё замечательно работает. Но самое главное не в этом. На PRi3 при caffe2 производительность выросла на 20-30%, исчезла ошибка + нагрузка на процессор упала в 4 раза. Скорее всего на RPi3 есть дополнительный сопроцессор, который грузиться, но я пока не проверил. Круто, что у нас сразу ушла проблема с перегревом.
Короче, взлетело быстро и без проблем.

А теперь о плохом.
Получив такой классный результат я подумал, что надо изменить код и на Jetson и серверный, затестив его с caffe2.
Ха! Не тут то было.
Метод инициализации сети который я привёл выше, который считается “основным методом использования готовых сетей” внезапно не умеет использовать GPU. Для запуска на GPU требуется куча танцев с бубнами и использование других интерфейсов (1,2,3). Последний, самый свежий из Issue, вроде как показывает наиболее простой способ использования сетей. Но, если читать документацию и примеры, то там кода обёрток по полторы страницы на каждую сетку. И то, что приведено в Issue в качестве решения нужно выдумывать самому глубоко лезя в архитектуру.
Документация и примеры за те 3-4 месяца, что прошли с появления caffe так и не улучшились. Так же очень сырые питоновские обёртки. В keras’е я за 2 вечера разобрался как он работает, когда у меня не было инета. Просто изучая examples и восхитительно написанные описания всех процедур и того что какая переменная значит.
Тут даже активное гугление и багтрекинг в гитхабе не спасают. При этом архитектура проекта не отличается стройной логичностью. Взять хотя бы два несовместных способа запуска и использования сетей.

6 thoughts on “Ещё раз про Caffe2”

  1. Сопроцессор RPi3 это вряд ли. Там есть возможность GPU использовать, но надо писать кастомный код именно под этот GPU. Ни каких OpenCL. Скорее всего просто NEON команды задействованы. Делал подобный проект на Tensorflow, рекомендую посмотреть в сторону Odroid XU4 вместо RPi3

    1. GPU я и называю сопроцессором:) Просто производительность в сравнении с caffe выросла в 1.2-1.3 раза при уменьшении нагрузки на CPU. Логично предположить что вычисления где-то сидят.
      Про Odroid XU4 не соглашусь – это вопрос поддержки и стабильности. На RPi можно делать плюс-минус продакшн результат (хотят даже Caffe обычный его не давал, только Caffe2). А чем более китайская плата тем хуже там с отладкой, настройкой, запуском в серию.

      1. Odroid XU4 – не Китай. Это корейская фирма Hardkernal и:
        CPU: Samsung Exynos5422 Cortex™-A15 2Ghz and Cortex™-A7 Octa core CPUs
        GPU: Mali-T628 MP6(OpenGL ES 3.1/2.0/1.1 and OpenCL 1.2 Full profile)
        С отладкой там все нормально. Ubuntu mate 16.04 идтёт вся целиком. Я лично установил IDE netbeans прямо на борт и работаю(и дебажу) на C++ прямо на нем.

        1. А Raspberry Pi британская – и всё равно багов много. Как только дело касается сложного математического фреймворка с поддержкой железа который на самом bleeding edge – малоизвестные фирмы, у которых нет поддержки крупных фреймворков почти гарантированно остаются за бортом.

  2. Это все запускали на распбиане? Он уже вышел с 64-битным ядром?

    1. Да, на распбиане. Про ядро не помню.

Comments are closed.