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


Этот проект не самый простой, поэтому возникает вопрос, зачем он нужен, если есть готовые фреймворки типа Keras, TensorFlow и Caffe, где, как говорится, «сел и поехал»? А ответ прост: нужен гибкий инструмент с возможностью расширения, с которым подружится Python. Научились мы отличать кита от чайки, но что нам это даст? IFunny серьёзно делает весёлое приложение и хочет удивлять пользователей новыми фичами, так почему бы не изучить такое богатое направление и применить?


Прочитав этот разбор, вы станете на шаг ближе к просветлению. Готовы? Тогда хватайте ручку, бумагу и приступаем!


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


Приступая к установке


Немного не доходя до момента, когда нужно написать первый git clone для их проекта, остановимся на этапе железа и окружения.


Вроде бы всё прозрачно и красиво. Но. Начнём с железа. Проект очень и очень требовательный. Особенно к памяти, свопу и видеокарте. Изначально сборка велась на AWS-инстансе с конфигурацией:


  • OS: Linux Redhat 2017.09 (EC2) > Ubuntu 16.04 LTS
  • CPU: 16 x Intel® Xeon® CPU E5-2686 v4 @ 2.30GHz;
  • HDD: 1 Tb;
  • Swap: 16 Gb >128 Gb (!!!);
  • RAM: 122 Gb (7 x 16 Gb, 1 x 10 Gb);
  • Graphic adapter: GRID K520 > Tesla M60.

Почему Ubuntu LTS?

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


Если хотите собрать всё это из любопытства, чтобы посмотреть, как это работает и поиграться, то обзаведитесь как минимум видеокартой с технологией CUDA. Без неё пытаться запуститься бесполезно. Чтобы понимать, какая видеокарта что может — вот линк на вики.


Видеокарта GRID K520 относится к CUDA capability 3.0+. С ней уже можно работать, но не так быстро как хотелось бы.


Поехали? Или постоим чуть-чуть…


Ставим пакет от Nvidia


Приступаем непосредственно к окружению. Для начала убедимся, что у нас g++ версии не ниже 4.9, иначе в самом конце нас может постигнуть фиаско. Берите самый новый, самый свежий g++, только что из репозитория. Далее ставим свеженькие драйвера от Nvidia.


sudo yum install nvidia #что-то такое, у амазона свой репозиторий
sudo yum install cuda #да, именно так, всё просто
nvcc --version #версия CUDA

Зарегистрируйтесь на http://developers.nvidia.com и скачайте cuDNN-библиотеку для вашей версии CUDA.



cd ~/
tar -xzvf cudnn-$VERSION-linux-x64-v7.tgz

Для Ubuntu 16.04 всё выглядит попроще:


sudo apt-get install nvidia-384*
sudo apt-get install nvidia-cuda-dev
sudo apt-get install nvidida-cuda-toolkit

И вот тут можно поступить по-разному. Официальный мануал требует, чтобы мы скопировали файлы в разные места. Можно согласиться с мануалом и скопировать так, как указано. Но можно поступить проще. Из cuda/include скопировать библиотеки в /usr/local/cuda/lib64.


Установка Lua и LuaRocks


Дальше. Нам нужна Lua и LuaRocks (ещё один менеджер пакетов — мало нам, мало). Тут всё просто для обеих ОС:


curl -R -O http://www.lua.org/ftp/lua-5.3.4.tar.gz
tar zxf lua-5.3.4.tar.gz
cd lua-5.3.4
make linux test
cd ~/
wget luarocks-2.4.3.tar.gz
tar -xvf luarocks-2.4.3.tar.gz
cd luarocks-2.4.3
./configure
make build
sudo make install
cd ~/

Устанавливаем Torch7 и его зависимости


У нас уже есть luarocks-менеджер зависимостей. А теперь самое интересное: нужно поставить часть зависимостей через LuaRocks (всё не выйдет, иначе поставленное и вовсе не взлетит). Вообще, при установке именно этих зависимостей проблем возникнуть не должно, но тут есть много-много маленьких грабелек (а как же без них).


  1. Torch (должен установиться Torch7).


    git clone https://github.com/torch/distro.git ~/torch --recursive
    cd ~/torch;
    bash install-deps;
    # ./install.sh — так не надо делать, в LUAJIT21 сломано получение содержимого 
    #файла в виде датасета
    TORCH_LUA_VERSION=LUA52 ./install.sh # ставим lua52 и радуемся: всё 
    #компилируется и работает, на warning внимания не обращайте
    source ~/.bashrc # тогда заработает запуск torch через th и будут экспортированы 
    #пути к библиотекам

    По идее, ничто не остановит вас в этот момент, кроме ошибки с 'half'. Данная проблема решается следующим образом: сначала выполняем в консоли команду


    export TORCH_NVCC_FLAGS="-D__CUDA_NO_HALF_OPERATORS__".

    Затем запускаем установку заново.


  2. COCO API: собирается только из исходников, но хотя бы ставится без проблем.


    git clone https://github.com/cocodataset/cocoapi.git
    cd cocoapi
    luarocks make LuaAPI/rocks/coco-scm-1.rockspec

  3. image: проблем нет. Ставим через luarocks: luarocks install image


  4. tds: внезапно это не пакет и вообще ставить надо lua ffi


    luarocks install --server=http://luarocks.org/dev luaffi

  5. cjson: по идее проблем нет, разве что пакет называется json.


    luarocks install json

  6. nnx: и вот тут засада. Вы ведь уже порадовались собранному фреймворку Torch? Тогда клонируем репозиторий в корень torch и ПЕРЕСОБИРАЕМ его целиком. Здесь необходимо уточнить, что сразу собрать с nnx не удастся, в его зависимостях как раз указаны пакеты COCO API, image, tds и cjson.


  7. optim: проблем не найдено, ставится напрямую luarocks install optim.


  8. inn: аналогично luarocks install inn.


  9. cutorch, cunn, cudnn — ставить именно в такой последовательности
    luarocks install cutorch
    luarocks install cunn
    luarocks install cudnn

Вроде бы всё поставили. Но не покидает чувство что что-то забыли… Что-то важное… Так и есть. Сам проект кто клонировать будет?


cd ~/
git clone https://github.com/facebookresearch/deepmask.git
cd deepmask

Устанавливаем модели для обучения


Затянули? Молодцы. А вот теперь будет самая долгая часть. Скачиваем обучающие паки и сетки. И у нас есть два пути. Первый путь — скачать готовые модели и, не обучая сеть, сразу приступить к проверке своей картинки (тут можно смело пропускать шаг тренировки нейросети и сразу ставить зависимости дальше).


Скачиваем и запускаем
mkdir -p pretrained/deepmask;
cd pretrained/deepmask
wget https://s3.amazonaws.com/deepmask/models/deepmask/model.t7
mkdir -p pretrained/sharpmask;
cd pretrained/sharpmask
wget https://s3.amazonaws.com/deepmask/models/sharpmask/model.t7
cd ~/deepmask/
th computeProposals.lua pretrained/deepmask # run DeepMask
th computeProposals.lua pretrained/sharpmask # run SharpMask
th computeProposals.lua pretrained/sharpmask -img /path/to/image.jpg

Как уже было сказано, скачанных моделей достаточно для быстрого старта. Так можно поступить, чтобы не обучать нейросеть в течение нескольких дней, но этого мало для чего-то более серьёзного. Второй путь начинается здесь. Поэтому продолжаем.


mkdir -p pretrained
wget https://s3.amazonaws.com/deepmask/models/resnet-50.t7 # ой ой ой, 250 Мб.
mkdir -p ~/deepmask/data; 
cd ~/deepmask/data
wget http://msvocds.blob.core.windows.net/annotations-1-0-3/instances_train-val2014.zip # ~158 Mb
wget http://msvocds.blob.core.windows.net/coco2014/train2014.zip # ~13 Gb >85k файлов
wget http://msvocds.blob.core.windows.net/coco2014/val2014.zip # ~6.2 Gb >40k файлов

Решаем ошибки с CUDA


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


th train.lua --help
....
THCudaCheck FAIL file=/home/ubuntu/torch/extra/cutorch/lib/THC/THCGeneral.c line=70 error=30 : unknown error
/home/ubuntu/torch/install/bin/lua: /home/ubuntu/torch/install/share/lua/5.2/trepl/init.lua:389: cuda runtime error (30) : unknown error at /home/ubuntu/torch/extra/cutorch/lib/THC/THCGeneral.c:70


Когда не хватило CWRAP-модуля

Смотрим trace. Хм… Очень похоже на то, что не хватает установленных зависимостей: cunn, cudnn установился криво. Ладно, переустанавливаем. Но дело не в них. Let's make an investigation! Проверяем дальше. Пробуем снова установить cutorch и… Бинго! Модуль cwrap не обнаружен.


Устанавливаем, что ж ещё остаётся делать.


luarockt install cwrap
luarockt install cutorch 

И тут мы снова получаем ошибку, что модуль cwrap не установлен. WTF? Не буду углубляться в механику LuaRocks, но именно пакет cwrap нельзя поставить вот так, сходу. Копаемся в помощнике и находим следующее: «в случае если пакет установлен, но по сути его нет, примените директиву --local». Пакет Шрёдингера. Вроде есть, но его нет. Ладно.


luarockt install --local wrap

И снова те же грабли. А вот это уже не смешно. Ставить всё это под sudo категорически не рекомендую. Ну вот зачем математическому пакету суперпользователь? Несколько шагов, которые помогают установить этот пакет из rockspec-файла. Проверьте пути LUA_PATH и LUA_CPATH и выполните следующие команды:


cd ~/torch
git clone https://github.com/torch/cwrap.git
cd cwrap
cmake .
cd ..
TORCH_LUA_VERSION=LUA52 ./install.sh
cd cwrap
luarocks make rocks/cwrap-scm-1.rockspec
luarocks install cutorch
luarocks install inn
luarocks install cunn
luarocks install cudnn

Обучаем нейросеть (если модели были скачаны — пропускайте этот шаг)


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


cd ~/deepmask
th train.lua --help
```bash
Смотрим, какие опции он нам предлагает по умолчанию. Описываю только те, которые можно менять или буду менять сам. 
```bash
-nthreads # параметр очень интересный — количество потоков обработки. 
#Максимальное значение равно количеству (!) ФИЗИЧЕСКИХ (!) процессоров, 
#в случае с нашим инстансом амазона — 16
-batch # размер пачки для обработки. По умолчанию 32
-maxload # я оставил его как есть, поскольку мне нужно обучение на полной 
#базе. По умолчанию 4000
-testmaxload # число пачек для валидации. Можно поиграть с параметром, 
#но учитывайте, что это напрямую влияет на количество памяти, которое 
#требуется; по умолчанию 500
-maxepoch  # число эпох. По умолчанию 300 эпох. Это слишком много, 
#далее станет ясно почему

Итак, приступим. Раз уж нам достался такой мощный инстанс, что же нам мешает загрузить по максимуму процессоры?


th train.lua -nthreads 16 -batch 500

Упс… А ведь Nvidia установила свои библиотеки и отмахивается от всяких vcc --version правильной инфой. Только вот библиотеки лежат в /home/ubuntu/cuda/lib64/libcudnn.so.5. А это значит, что в самый конец файла .bashrc добавляем директиву:


export CUDNN_PATH="/home/ubuntu/cuda/lib64/libcudnn.so.5"

По идее, нас ждёт счастье, но нет. Мы снова можем наступить на грабли. Достаточно серьёзные.
В случае, если грабли найдены не были, то увидите текст, похожий на текст на скриншоте.



Либо одно из сообщений об ошибке. Сами ошибки и методы их решения подробно описаны под спойлерами.


th train.lua -nthreads 16 -batch 500

Вывод в консоль с ошибкой
Found Environment variable CUDNN_PATH = /home/ubuntu/cuda/lib64/libcudnn.so.5-- ignore option dm
-- ignore option reload 
-- ignore option datadir
nthreads        16      2
-- ignore option rundir 
-- ignore option gpu
batch   500     32
| running in directory /home/ubuntu/deepmask/exps/deepmask/exp,batch=500,nthreads=16
| number of paramaters trunk: 15198016  
| number of paramaters mask branch: 1608768
| number of paramaters score branch: 526337
| number of paramaters total: 17333121  
convert: data//annotations/instances_train2014.json --> .t7 [please be patient]
convert: data//annotations/instances_train2014.json --> .t7 [please be patient]
convert: data//annotations/instances_train2014.json --> .t7 [please be patient]
convert: data//annotations/instances_train2014.json --> .t7 [please be patient]
convert: data//annotations/instances_train2014.json --> .t7 [please be patient]
/home/ubuntu/torch/install/bin/lua: ...e/ubuntu/torch/install/share/lua/5.2/threads/threads.lua:183: [thread 16 callback] /home/ubuntu/torch/install/share/lua/5.2/coco/CocoApi.lua:142: Expected value but found T_END at character 1

Если не сконвертировались модели с ошибкой T_END

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


cd ~
rm -rf torch/
git clone https://github.com/torch/distro.git ~/torch --recursive
cd ~/torch;
bash install-deps;
TORCH_LUA_VERSION=LUA52 ./install.sh
git checkout 5961f52a65fe33efa675f71e5c19ad8de56e8dad
./clean.sh
bash install-reps
TORCH_LUA_VERSION-LUA52 ./install.sh
luarocks install cudnn
luarocks install cutorch # именно так! И не спрашивайте. Там ещё достаточно граблей
luarocks install cunn
luarocks install inn
luarocks install tds
luarocks install optim
luarocks install nnx
luarocks install image
cd ~/coco/
luarocks make LuaAPI/rocks/coco-scm-1.rockspec

Если не сконвертировались модели из-за нехватки памяти

Пересобрали. Доустановили. Запускаемся опять с теми же опциями и видим:


th train.lua -nthreads 16 -batch 500
-- ignore option rundir
-- ignore option dm
-- ignore option reload
-- ignore option gpu
-- ignore option datadir
nthreads        1       2
| running in directory /Users/ryan/mess/2016/34/deepmask/exps/deepmask/exp,nthreads=1
| number of paramaters trunk: 15198016
| number of paramaters mask branch: 1608768
| number of paramaters score branch: 526337
| number of paramaters total: 17333121
convert: data//annotations/instances_train2014.json --> .t7 [please be patient]
FATAL THREAD PANIC: (write) not enough memory
Поток в панике. Что делать? Тушить! Workaround на этот раз очень лайтовый. Даже не надо ничего пересобирать. Просто руками конвертируем аннотации тренировки и валидации. Ошибка чтения файлов лечится так же.
```bash
cd ~/deepmask/
th
coco = require 'coco'
coco.CocoApi("data/annotations/instances_train2014.json")
#Увидим строчки, которые ниже
#convert: data/annotations/instances_train2014.json --> .t7 [please be patient]
#converting: annotations
#converting: categories
#converting: images
#convert: building indices
#convert: complete [57.22 s]
#CocoApi

coco.CocoApi("data/annotations/instances_val2014.json")
#Увидим строчки, которые ниже
#convert: data/annotations/instances_val2014.json --> .t7 [please be patient]
#converting: annotations
#converting: categories
#converting: images
#convert: building indices
#convert: complete [26.07 s]
#CocoApi

Обучаем нейросеть


Вот теперь приготовления завершены. Все модели есть, сети есть, изображения для тренировки и валидации есть. Снова запускаем. Попробуем максимально нагрузить.


th train.lua -batch 500 -nthreads 16

Параллельно смотрим htop (не самая лучшая идея, поскольку он не покажет разделение потребляемой памяти по типу).



Мягко говоря, жарковато. Напоминаю: 16 физических процессоров, каждый по 8 ядер, с виртуализацией. И память. Неудивительно, что через несколько минут работы терминал выдал «Убито»!


Повторяем попытку, делаем условия "полегче":


th train.lua -maxepoch 2 -nthreads 4

Вывод из консоли тут
Found Environment variable CUDNN_PATH = /home/ubuntu/cuda/lib64/libcudnn.so.5maxepoch   2       300
-- ignore option datadir
-- ignore option gpu
-- ignore option dm
nthreads        4       2
-- ignore option rundir 
-- ignore option reload 
| running in directory /home/ubuntu/deepmask/exps/deepmask/exp,maxepoch=2,nthreads=4
| number of paramaters trunk: 15198016  
| number of paramaters mask branch: 1608768
| number of paramaters score branch: 526337
| number of paramaters total: 17333121  
| start training

Ещё раз. Использованные 154 Гб памяти — это все банки RAM, часть свопа и часть виртуальной памяти. Останавливаем. Остановка через kill или Ctrl + C не критична вообще. При перезапуске обучения все старые модели затираются.


Даже на амазонской машине можно спокойно оставить скрипт работать и идти заниматься своими делами часа полтора-два.


Как не терять сессию при работе с инстансом Amazon

Кстати, важная пометка: чтобы увидеть, что что-то получилось, результаты тренировки нейросети и т.д., при работе через ssh используйте screen. Внезапно пропала связь и прочее? Не беда. Переподключились к машине, ввели screen -r — и мы снова видим то, на чём вылетели.


Опция -nthreads 4 вымучена перезапусками и засеканием времени тренировки нейросети. Потому что при большей загрузке начинается конкуренция за оперативную память, а параметров у нас очень и очень много (в сумме 17 млн).


На обучающую выборку в 85К изображений. Кстати, каждый чётный шаг обучения скрипта проводит валидацию нейросети.


Вывод в консоль информации по обучению, 16 потоков, 12 эпох
th train.lua -nthreads 16 -maxepoch 12
Found Environment variable CUDNN_PATH = /home/ubuntu/cuda/lib64/libcudnn.so.5-- ignore option reload
-- ignore option dm
maxepoch        12      300
-- ignore option gpu
nthreads        16      2
-- ignore option datadir
-- ignore option rundir
| running in directory /home/ubuntu/deepmask/exps/deepmask/exp,maxepoch=12,nthreads=16
| number of paramaters trunk: 15198016
| number of paramaters mask branch: 1608768
| number of paramaters score branch: 526337
| number of paramaters total: 17333121
| start training
[train] | epoch 00001 | s/batch 0.67 | loss: 0.31743
[train] | epoch 00002 | s/batch 0.67 | loss: 0.18296
[test]  | epoch 00002 | IoU: mean 054.60 median 061.36 suc@.5 062.63 suc@.7 036.53 | acc 092.91 | bestmodel *
[train] | epoch 00003 | s/batch 0.67 | loss: 0.16256
[train] | epoch 00004 | s/batch 0.67 | loss: 0.15217
[test]  | epoch 00004 | IoU: mean 059.10 median 065.86 suc@.5 068.97 suc@.7 043.31 | acc 093.93 | bestmodel *
[train] | epoch 00005 | s/batch 0.67 | loss: 0.14583
[train] | epoch 00006 | s/batch 0.67 | loss: 0.14183
[test]  | epoch 00006 | IoU: mean 056.79 median 064.92 suc@.5 065.88 suc@.7 042.34 | acc 094.27 | bestmodel x
[train] | epoch 00007 | s/batch 0.67 | loss: 0.13739
[train] | epoch 00008 | s/batch 0.67 | loss: 0.13489
[test]  | epoch 00008 | IoU: mean 059.53 median 067.17 suc@.5 069.44 suc@.7 045.33 | acc 094.69 | bestmodel *
[train] | epoch 00009 | s/batch 0.67 | loss: 0.13417
[train] | epoch 00010 | s/batch 0.67 | loss: 0.13290
[test]  | epoch 00010 | IoU: mean 061.80 median 069.41 suc@.5 072.71 suc@.7 048.92 | acc 094.67 | bestmodel *
[train] | epoch 00011 | s/batch 0.67 | loss: 0.13070
[train] | epoch 00012 | s/batch 0.67 | loss: 0.12711
[test]  | epoch 00012 | IoU: mean 060.16 median 067.43 suc@.5 070.71 suc@.7 046.34 | acc 094.85 | bestmodel x

Провели обучение и можно приступать к следующему этапу. На основе полученных DeepMask-моделей необходимо сгенерировать SharpMask.


th train.lua -dm ~/deepmask/exps/deepmask/exp\,maxepoch\=2\,nthreads\=4/ 
# необходимо точно указать путь, где лежат наши модели

Если была запущена тренировка с другими параметрами, то название папки с моделями будет иное. Но основной путь до неё тот же. Пока идёт обучение DeepMask или построение SharpMask, продолжим установку.


Ставим google-glog


С ним проблем не возникло, потому что он и правда простой и беспроблемный.


cd ~/
git clone https://github.com/google/glog.git
cd glog
./autogen.sh
./configure
make
sudo make install

Multipathnet, fbpython и целый ворох зависимостей


Теперь от нас всего-то требуется клонировать проект multipathnet, установить к нему зависимости и запустить распознавание. Кажется, я где-то уже слышал что всё «легко и просто».


cd ~/
git clone https://github.com/facebookresearch/multipathnet.git
luarocks install torchnet
luarocks install class
luarocks install fbpython 

Не пытайтесь ставить fbpython через Conda или Anaconda и прочее. Как выражается Facebook в инструкции: note that this won't work with anaconda as it ships with it's own libraries which conflict with torch. Если перевести примерно и с долей юмора, то «это их собственная баржа, на ней свои заморочки и свой капитан».


Итак, кто-то опять умолчал о зависимостях.


...
-- Found Torch7 in /home/ubuntu/torch/install
-- REQUIRED_ARGS (missing:  THPP_INCLUDE_DIR THPP_LIBRARIES) 
-- Looking for pthread.h
...

Гуглим во все стороны, что это за переменные и почему их у нас до сих пор нет.


cd ~/
git clone https://github.com/facebook/folly.git
git clone https://github.com/facebook/fbthrift.git
git clone https://github.com/facebook/thpp
git clone https://github.com/facebookarchive/fblualib.git 
# не пользуйтесь форком, форк не работает, клонируем из архива

Я считаю, это прекрасно. Ещё небольшой зоопарк. Ладно. Для тех, кто на Ubuntu:


Команды и вывод в консоль тут, очень много
sudo apt-get install zlib1g-dev binutils-dev libjemalloc-dev libssl-dev
sudo apt-get install libevent-dev
sudo apt-get install libsnappy-dev
sudo apt-get install libboost-all-dev libgoogle-glog-dev libgflags-dev liblz4-dev liblzma-dev libsnappy-dev
Linux:
cd ~/
git clone https://github.com/gflags/gflags.git

cd gflags
mkdir build && cd build
$ ccmake ..
  - Press 'c' to configure the build system and 'e' to ignore warnings.
# Обязательно включаем SHARED_LIB
  - Set CMAKE_INSTALL_PREFIX and other CMake variables and options.
# Жмём 'c' ещё раз
  - Continue pressing 'c' until the option 'g' is available.
# Жмём 'g' и радуемся
  - Then press 'g' to generate the configuration files for GNU Make.
cd ..
cmake -fpic -shared configure .
make
sudo make install

cd ~/
git clone https://github.com/google/glog.git
export LDFLAGS='-L/usr/local/lib'
cd glog
autoreconf -ivf
./configure
make
sudo make install

cd ~/
git clone https://github.com/google/double-conversion.git
cd double-conversion
cmake . -DBUILD_SHARED_LIBS=ON # не спрашивайте. ну не хавает 
#здесь cmake по дефолту -fpic -shared
make
sudo make install
Теперь для всех:
wget https://github.com/google/googletest/archive/release-1.8.0.tar.gz && tar zxf release-1.8.0.tar.gz && rm -f release-1.8.0.tar.gz && cd googletest-release-1.8.0 && cmake configure . && make && sudo make install

cd ~/folly
cmake -fpic -shared configure ..
make (optionally -j$(proc))
sudo make install

Дальше всё идёт только для Ubuntu, посколько на RHEL было бессмысленно пытаться обновить всё окружение. На момент написания статьи дальше в RHEL продвинуться просто было невозможно, ограничение на максимальную версию пакетов для инстансов «Амазона» тому виной.


Пользователи Linux могут самостоятельно продолжить установку пакетов, но искать необходимые пакеты необходимо будет вам (ну а если в процессе наткнетесь на грабли — добро пожаловать в комментарии, с радостью помогу решить проблемы).


Ставим fbthrift


НИКОГДАне трогайте build_folly_fbthrift.sh. Иначе придётся откатиться к моменту, когда ничего нет, кроме тренировки модели.


Все команды по установке
sudo apt-get install flex bison
git clone https://github.com/no1msd/mstch.git
cd mstch
cmake .
make
sudo make install
cd ~/
git clone https://github.com/facebook/wangle.git
cd wangle/wangle
cmake .
make
sudo make install
cd ~/
git clone https://github.com/facebook/zstd.git
cd zstd
make
sudo make install
cd ~/
git clone https://github.com/krb5/krb5.git
cd krb5/src
autoreconf -ivf
./configure
make
sudo make install
cd ~/fbthrift/build
cmake -fpic -shared configure .
make
sudo make install

И теперь думаете, что счастье есть и можно устанавливать thpp. Как показала практика — нет. Упираетесь в тысячу косяков… Fbthrift! Для начала:


cd ~/fbthrift/thrift/compiler/py
sudo python setup.py install

Это установит недостающий thrift_compiler. Далее оказывается, что вообще отсутствует frontend.so. Забыли скомпилироть и положить в папку. Ну ладно (для справки: ниже строка генерации frontend.so для g++ ~v5.4).


cd ~/fbthrift/thrift/compiler/py
g++ -I /usr/include/python2.7 -I ~/fbthrift -std=c++1y -fpic -shared -o frontend.so compiler.cc -lboost_python -lpython2.7 -L/build/lib -lcompiler_base -lcompiler_ast -lboost_system -lboost_filesystem -lssl -lcrypto
sudo cp frontend.so /usr/local/lib/python2.7/dist-packages/thrift_py-0.9.0-py2.7.egg/thrift_compiler/

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


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


Вооот, теперь нас ждёт счастье. Ну почти. Добрые самаритяне из Facebook поменяли вызовы функций в torch7, но между делом забыли сделать правильный вызов в thpp. Как мило.


cd ~/thpp/thpp/detail/
nano TensorGeneric.h

Нужно поправить следующие вызовы (кэп подсказывает: после dim добавилась часть ", 0"):


static void _max(THTensor* values, THLongTensor* indices,
                   THTensor* t, int dim) {
    return THTensor_(max)(values, indices, t, dim, 0); 
  }
  static void _min(THTensor* values, THLongTensor* indices,
                   THTensor* t, int dim) {
    return THTensor_(min)(values, indices, t, dim, 0);
  }
  static void _sum(THTensor* r, THTensor* t, int dim) {
    return THTensor_(sum)(r, t, dim, 0);
  }
  static void _prod(THTensor* r, THTensor* t, int dim) {
    return THTensor_(prod)(r, t, dim, 0);
  }

Собираем пакет заново, но это ещё не всё...


cd ~/thpp/thpp
ccmake .
#Делаем опцию NO_TEST = ON, жмём 'c', потом 'g'
cmake . 
make
sudo make install

Ставим fblualib


Теперь на очереди fblualib. С ним можно расправиться легко и просто. Повторюсь: ни в коем случае не пользуйтесь их скриптами, которые носят заманчивые имена install-all.sh. Накроются все ваши пляски, и придётся начинать почти с нуля.


sudo apt-get install   libedit-dev   libmatio-dev   libpython-dev   python-numpy

Поставим, вдруг упустили. Не мудрено с такой «простой» инструкцией от разработчиков. Проверили, что у нас есть numpy для ВСЕХ версий питона.


cd ~/fblualib/fblualib
cmake -fpic -shared configure .
make
sudo make install

И снова куча ошибок. А всё почему? Да потому, что кто-то не стал править вызовы функций и в rocks-пакетах. Значит, правим вызовы руками. Просто писали инструкцию для Lua версии ниже 5.2, а он уже у нас 5.2, и искать старую нужную версию нет смысла. Проще поправить вызов функций.


cd ~/fblualib/fblualib/python
luarocks make rockspec/*

Выдаст ошибку при компиляции типа «недоопределённый тип». Всё просто. В файле с ошибкой правим определение типа luaL_reg на luaL_Reg (ЗАЧЕМ меняли вызов, я не знаю!)


Файлов с ошибкой будет два. Правки должны быть сделаны на 191 и 341 строках в двух файлах, которые будут в сообщении об ошибке.


Исправили вызов — попробовали. Скомпилировалось? Можно радоваться.


В принципе, уже после данных манипуляций можно было бы просто из ~/fblualib/fblualib ещё раз вызвать ./build.sh, но это уже дело вкуса — руками надёжнее.


Продолжаем мучения:


cd ~/coco
luarocks make LuaAPI/rocks/coco-scm-1.rockspec 
# на всякий случай обновим зависимости
sudo pip install Cython
cd PythonAPI
make
sudo make install

Запускаем построение SharpMask для обученной нейросети


По идее, мы уже достигли дна, в смысле — дошли до конца инструкции по установке. Осталась самая приятная часть. Кстати, как раз закончился процесс создания SharpMask.


th train.lua -dm exps/deepmask/exp\,maxepoch\=2\,nthreads\=4/ -maxepoch 2 -nthreads 4
Found Environment variable CUDNN_PATH = /home/ubuntu/cuda/lib64/libcudnn.so.5gSz        160     112
nthreads        4       2
-- ignore option gpu
-- ignore option reload 
maxepoch        2       300
-- ignore option datadir
-- ignore option dm
hfreq   0       0.5
-- ignore option rundir 
| running in directory /home/ubuntu/deepmask/exps/sharpmask/exp,gSz=160,hfreq=0,maxepoch=2,nthreads=4   
| number of paramaters net h: 1090466   
| number of paramaters net v: 1660050   
| number of paramaters total: 2750516   
| start training
[train] | epoch 00001 | s/batch 1.95 | loss: 0.16018    
[train] | epoch 00002 | s/batch 1.95 | loss: 0.13267    
[test]  | epoch 00002 | IoU: mean 059.74 median 065.92 suc@.5 069.00 suc@.7 043.39 | bestmodel *

Вполне себе хорошие данные для дальнейшей работы с моделями. Не забывайте добавлять в LUA_PATH /home//?.lua и в .bashrc тоже. Исходя из опыта установки всего проекта и тренировки моделей, просто сделайте линки из multipathnet на deepmask/data и линки sharpmask/model.t7 и deepmask/model.t7 и не занимайтесь копированием файлов.

cd ~/deepmask
mkdir data && cd data
mkdir models && mkdir proposals
ln -s ~/deepmask/data ~/multipathnet/data
#deepMask и sharpMask — если были другие параметры обучения,
# то папки другие будут
ln -s ~/deepmask/exps/deepmask/exp,maxepoch=2,nthreads=4/model.t7 ~/deepmask/data/models/deepmask.t7 
ln -s ~/deepmask/exps/sharpmask/exp,gSz=160,hfreq=0,maxepoch=2,nthreads=4/model.t7 ~/deepmask/data/models/sharpmask.t7

Если не запускается из-за ошибки в тестах


Из файла fbcoco.lua убираем следующие строки:


require 'testCoco.init'
require 'Tester_FRCNN'

Теперь точно не зависнет и не сегфолтнется.


Долгожданная проверка!


Пора запускать проверку!


th demo.lua -img data/test6.jpg

Вывод в консоль
Found Environment variable CUDNN_PATH = /home/ubuntu/cuda/lib64/libcudnn.so.5nn.Sequential {
  [input -> (1) -> (2) -> (3) -> (4) -> (5) -> (6) -> output]
  (1): nn.ParallelTable {
    input
      |`-> (1): nn.Sequential {
      |      [input -> (1) -> (2) -> (3) -> output]
      |      (1): NoBackprop: nn.Sequential {
      |        [input -> (1) -> (2) -> (3) -> (4) -> (5) -> output]
      |        (1): nn.SpatialConvolution(3 -> 64, 7x7, 2,2, 3,3) without bias
      |        (2): inn.ConstAffine
      |        (3): nn.ReLU
      |        (4): nn.SpatialMaxPooling(3x3, 2,2, 1,1)
      |        (5): nn.Sequential {
      |          [input -> (1) -> (2) -> output]
      |          (1): nn.Sequential {
      |            [input -> (1) -> (2) -> (3) -> output]
      |            (1): nn.ConcatTable {
      |              input
      |                |`-> (1): nn.Sequential {
      |                |      [input -> (1) -> (2) -> (3) -> (4) -> (5) -> output]
      |                |      (1): nn.SpatialConvolution(64 -> 64, 3x3, 1,1, 1,1) without bias
      |                |      (2): inn.ConstAffine
      |                |      (3): nn.ReLU
      |                |      (4): nn.SpatialConvolution(64 -> 64, 3x3, 1,1, 1,1) without bias
      |                |      (5): inn.ConstAffine
      |                |    }
      |                 `-> (2): nn.Identity
      |                 ... -> output
      |            }
      |            (2): nn.CAddTable
      |            (3): nn.ReLU
      |          }
      |          (2): nn.Sequential {
      |            [input -> (1) -> (2) -> (3) -> output]
      |            (1): nn.ConcatTable {
      |              input
      |                |`-> (1): nn.Sequential {
      |                |      [input -> (1) -> (2) -> (3) -> (4) -> (5) -> output]
      |                |      (1): nn.SpatialConvolution(64 -> 64, 3x3, 1,1, 1,1) without bias
      |                |      (2): inn.ConstAffine
      |                |      (3): nn.ReLU
      |                |      (4): nn.SpatialConvolution(64 -> 64, 3x3, 1,1, 1,1) without bias
      |                |      (5): inn.ConstAffine
      |                |    }
      |                 `-> (2): nn.Identity
      |                 ... -> output
      |            }
      |            (2): nn.CAddTable
      |            (3): nn.ReLU
      |          }
      |        }
      |      }
      |      (2): nn.Sequential {
      |        [input -> (1) -> (2) -> output]
      |        (1): nn.Sequential {
      |          [input -> (1) -> (2) -> (3) -> output]
      |          (1): nn.ConcatTable {
      |            input
      |              |`-> (1): nn.Sequential {
      |              |      [input -> (1) -> (2) -> (3) -> (4) -> (5) -> output]
      |              |      (1): nn.SpatialConvolution(64 -> 128, 3x3, 2,2, 1,1) without bias
      |              |      (2): inn.ConstAffine
      |              |      (3): nn.ReLU
      |              |      (4): nn.SpatialConvolution(128 -> 128, 3x3, 1,1, 1,1) without bias
      |              |      (5): inn.ConstAffine
      |              |    }
      |               `-> (2): nn.SpatialConvolution(64 -> 128, 1x1, 2,2) without bias
      |               ... -> output
      |          }
      |          (2): nn.CAddTable
      |          (3): nn.ReLU
      |        }
      |        (2): nn.Sequential {
      |          [input -> (1) -> (2) -> (3) -> output]
      |          (1): nn.ConcatTable {
      |            input
      |              |`-> (1): nn.Sequential {
      |              |      [input -> (1) -> (2) -> (3) -> (4) -> (5) -> output]
      |              |      (1): nn.SpatialConvolution(128 -> 128, 3x3, 1,1, 1,1) without bias
      |              |      (2): inn.ConstAffine
      |              |      (3): nn.ReLU
      |              |      (4): nn.SpatialConvolution(128 -> 128, 3x3, 1,1, 1,1) without bias
      |              |      (5): inn.ConstAffine
      |              |    }
      |               `-> (2): nn.Identity
      |               ... -> output
      |          }
      |          (2): nn.CAddTable
      |          (3): nn.ReLU
      |        }
      |      }
      |      (3): nn.Sequential {
      |        [input -> (1) -> (2) -> output]
      |        (1): nn.Sequential {
      |          [input -> (1) -> (2) -> (3) -> output]
      |          (1): nn.ConcatTable {
      |            input
      |              |`-> (1): nn.Sequential {
      |              |      [input -> (1) -> (2) -> (3) -> (4) -> (5) -> output]
      |              |      (1): nn.SpatialConvolution(128 -> 256, 3x3, 2,2, 1,1) without bias
      |              |      (2): inn.ConstAffine
      |              |      (3): nn.ReLU
      |              |      (4): nn.SpatialConvolution(256 -> 256, 3x3, 1,1, 1,1) without bias
      |              |      (5): inn.ConstAffine
      |              |    }
      |               `-> (2): nn.SpatialConvolution(128 -> 256, 1x1, 2,2) without bias
      |               ... -> output
      |          }
      |          (2): nn.CAddTable
      |          (3): nn.ReLU
      |        }
      |        (2): nn.Sequential {
      |          [input -> (1) -> (2) -> (3) -> output]
      |          (1): nn.ConcatTable {
      |            input
      |              |`-> (1): nn.Sequential {
      |              |      [input -> (1) -> (2) -> (3) -> (4) -> (5) -> output]
      |              |      (1): nn.SpatialConvolution(256 -> 256, 3x3, 1,1, 1,1) without bias
      |              |      (2): inn.ConstAffine
      |              |      (3): nn.ReLU
      |              |      (4): nn.SpatialConvolution(256 -> 256, 3x3, 1,1, 1,1) without bias
      |              |      (5): inn.ConstAffine
      |              |    }
      |               `-> (2): nn.Identity
      |               ... -> output
      |          }
      |          (2): nn.CAddTable
      |          (3): nn.ReLU
      |        }
      |      }
      |    }
       `-> (2): nn.Identity
       ... -> output
  }
  (2): inn.ROIPooling
  (3): nn.Sequential {
    [input -> (1) -> (2) -> (3) -> output]
    (1): nn.Sequential {
      [input -> (1) -> (2) -> output]
      (1): nn.Sequential {
        [input -> (1) -> (2) -> (3) -> output]
        (1): nn.ConcatTable {
          input
            |`-> (1): nn.Sequential {
            |      [input -> (1) -> (2) -> (3) -> (4) -> (5) -> output]
            |      (1): nn.SpatialConvolution(256 -> 512, 3x3, 2,2, 1,1) without bias
            |      (2): inn.ConstAffine
            |      (3): nn.ReLU
            |      (4): nn.SpatialConvolution(512 -> 512, 3x3, 1,1, 1,1) without bias
            |      (5): inn.ConstAffine
            |    }
             `-> (2): nn.SpatialConvolution(256 -> 512, 1x1, 2,2) without bias
             ... -> output
        }
        (2): nn.CAddTable
        (3): nn.ReLU
      }
      (2): nn.Sequential {
        [input -> (1) -> (2) -> (3) -> output]
        (1): nn.ConcatTable {
          input
            |`-> (1): nn.Sequential {
            |      [input -> (1) -> (2) -> (3) -> (4) -> (5) -> output]
            |      (1): nn.SpatialConvolution(512 -> 512, 3x3, 1,1, 1,1) without bias
            |      (2): inn.ConstAffine
            |      (3): nn.ReLU
            |      (4): nn.SpatialConvolution(512 -> 512, 3x3, 1,1, 1,1) without bias
            |      (5): inn.ConstAffine
            |    }
             `-> (2): nn.Identity
             ... -> output
        }
        (2): nn.CAddTable
        (3): nn.ReLU
      }
    }
    (2): nn.SpatialAveragePooling(7x7, 1,1)
    (3): nn.View(512)
  }
  (4): nn.ConcatTable {
    input
      |`-> (1): nn.Linear(512 -> 81)
      |`-> (2): nn.Linear(512 -> 81)
      |`-> (3): nn.Linear(512 -> 81)
      |`-> (4): nn.Linear(512 -> 81)
      |`-> (5): nn.Linear(512 -> 81)
      |`-> (6): nn.Linear(512 -> 81)
       `-> (7): nn.Linear(512 -> 324)
       ... -> output
  }
  (5): nn.ModeSwitch {
    input
      |`-> (1): nn.ConcatTable {
      |      input
      |        |`-> (1): nn.SelectTable(4)
      |         `-> (2): nn.SelectTable(7)
      |         ... -> output
      |    }
       `-> (2): nn.Sequential {
             [input -> (1) -> (2) -> output]
             (1): nn.ParallelTable {
               input
                 |`-> (1): nn.Sequential {
                 |      [input -> (1) -> (2) -> output]
                 |      (1): nn.SoftMax
                 |      (2): nn.View(1, -1, 81)
                 |    }
                 |`-> (2): nn.Sequential {
                 |      [input -> (1) -> (2) -> output]
                 |      (1): nn.SoftMax
                 |      (2): nn.View(1, -1, 81)
                 |    }
                 |`-> (3): nn.Sequential {
                 |      [input -> (1) -> (2) -> output]
                 |      (1): nn.SoftMax
                 |      (2): nn.View(1, -1, 81)
                 |    }
                 |`-> (4): nn.Sequential {
                 |      [input -> (1) -> (2) -> output]
                 |      (1): nn.SoftMax
                 |      (2): nn.View(1, -1, 81)
                 |    }
                 |`-> (5): nn.Sequential {
                 |      [input -> (1) -> (2) -> output]
                 |      (1): nn.SoftMax
                 |      (2): nn.View(1, -1, 81)
                 |    }
                 |`-> (6): nn.Sequential {
                 |      [input -> (1) -> (2) -> output]
                 |      (1): nn.SoftMax
                 |      (2): nn.View(1, -1, 81)
                 |    }
                  `-> (7): nn.Identity
                  ... -> output
             }
             (2): nn.ConcatTable {
               input
                 |`-> (1): nn.Sequential {
                 |      [input -> (1) -> (2) -> (3) -> output]
                 |      (1): nn.NarrowTable
                 |      (2): nn.JoinTable
                 |      (3): nn.Mean
                 |    }
                  `-> (2): nn.SelectTable(7)
                  ... -> output
             }
           }
       ... -> output
  }
  (6): nn.ParallelTable {
    input
      |`-> (1): nn.Identity
       `-> (2): nn.BBoxNorm
       ... -> output
  }
}
{
  1 : 
    {
      1 : CudaTensor - size: 2x81
      2 : CudaTensor - size: 2x324
    }
}
{
  1 : 
    {
      1 : CudaTensor - size: 2x3x224x224
      2 : CudaTensor - size: 2x5
    }
}
{
  1 : 0.17677669529664
  2 : 0.25
  3 : 0.35355339059327
  4 : 0.5
  5 : 0.70710678118655
  6 : 1
  7 : 1.4142135623731
}

0.66218614578247    17  dog 
| done


Очевидно, на картинке собака! Пора подвести итоги.


Результаты бега по граблям


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


Задачка была не из лёгких, а информации в интернете было очень и очень мало, особенно по багам с несовместимостью версий.


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


Вообще сам набор после нормальной сборки и приведения в рабочий вид оставил приятное впечатление, если бы не процесс приведения в чувство всей этой Санта-Барбары.


Все трудности преодолели, теперь можно немного поговорить о планах на будущее. А они у нас далекоидущие: в целях эксперимента перейти на CUDA 9.1, cuDNN 7, который обещает почти тройной прирост производительности для алгоритмов свёрточной нейронной сети и усовершенствованной свёрточной нейронной сети. Правда, это потянет за собой пересборку части пакетов (точнее, пересборку inn, cunn, cudnn, cutorch через LuaRocks и правку зависимостей).


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

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


  1. roryorangepants
    27.03.2018 11:40
    +1

    Этот проект не самый простой, поэтому возникает вопрос, зачем он нужен, если есть готовые фреймворки типа Keras, TensorFlow и Caffe, где, как говорится, «сел и поехал»? А ответ прост: нужен гибкий инструмент с возможностью расширения, с которым подружится Python.

    Этот ответ отлично применим и к Keras + TF. А если настройка инфраструктуры FB Image Recognition Package — это действительно такая головная боль, как вы описали, то его бы я гибким и удобным как раз не назвал.


    1. Trevil Автор
      27.03.2018 11:52
      +1

      Есть надежда, что все ошибки несовместимости версий уйдут и тогда действительно станет легче устанавливать и пользоваться.
      Согласен. Keras и TF из разряда «сел и поехал», но для них все равно необходимо либо писать собственный код, либо искать подходящий проект по распознаванию.
      В случае с FB — готовый комплект, который они успешно применяют в продакшн среде (я уверен, что версия в репозитории не самая последняя).
      Но в итоге получили полноценный рабочий инстанс, который можно использовать для распознавания образов.


  1. cebka
    27.03.2018 17:14

    О да, напоминает мои мучения, когда я этот Torch встраивал в Rspamd. Вначале я удивлялся, почему же нигде нет deb/rpm пакетов, чтобы не эмбеддить огромный фреймворк себе в код, а потом столкнулся с непревзойденным качеством кода от Facebook, который собирается и работает только в Facebook. Например, они ставят -march=native на компиляции и "проверяют" так наличие всяких смешных наборов инструкций, например, avx2. Стоит ли говорить, как "счастливы" от этого пользователи бинарных пакетов, у которых этих инструкций внезапно нет?


    Или, например, они хотят собираться с sse4.2, при этом используя только интрисики из sse2 — зачем им там sse4.2, непонятно.


    Еще забавно, что они используют генерацию C кода через lua на этапе сборки, что сломано примерно везде, где только можно.


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


    1. Trevil Автор
      27.03.2018 17:29

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