ResNet50. Своя реализация

МЕНЮ


Искусственный интеллект
Поиск
Регистрация на сайте
Помощь проекту

ТЕМЫ


Новости ИИРазработка ИИВнедрение ИИРабота разума и сознаниеМодель мозгаРобототехника, БПЛАТрансгуманизмОбработка текстаТеория эволюцииДополненная реальностьЖелезоКиберугрозыНаучный мирИТ индустрияРазработка ПОТеория информацииМатематикаЦифровая экономика

Авторизация



RSS


RSS новости


Всем привет. Библиотека нейросети описана в моей прошлой статье. Здесь решил показать как можно использовать обученную сеть из TF (Tensorflow) в своем решении, и стоит ли. Под катом сравнение с оригинальной реализацией TF, демо приложение для распознавания картинок, ну и… выводы. Кому интересно, прошу.
Как устроена ResNet можете узнать, например, здесь. Вот так выглядит структура сети в цифрах:

По коду получилось не проще и не сложнее чем на питоне.
Код C++ для создания сети:
  auto net = sn::Net();      net.addNode("In", sn::Input(), "conv1")        .addNode("conv1", sn::Convolution(64, 7, 3, 2, sn::batchNormType::beforeActive, sn::active::none, mode), "pool1_pad")        .addNode("pool1_pad", sn::Pooling(3, 2, sn::poolType::max, mode), "res2a_branch1 res2a_branch2a");          convBlock(net, vector<uint32_t>{ 64, 64, 256 }, 3, 1, "res2a_branch", "res2b_branch2a res2b_branchSum", mode);     idntBlock(net, vector<uint32_t>{ 64, 64, 256 }, 3, "res2b_branch", "res2c_branch2a res2c_branchSum", mode);     idntBlock(net, vector<uint32_t>{ 64, 64, 256}, 3, "res2c_branch", "res3a_branch1 res3a_branch2a", mode);      convBlock(net, vector<uint32_t>{ 128, 128, 512 }, 3, 2, "res3a_branch", "res3b_branch2a res3b_branchSum", mode);     idntBlock(net, vector<uint32_t>{ 128, 128, 512 }, 3, "res3b_branch", "res3c_branch2a res3c_branchSum", mode);     idntBlock(net, vector<uint32_t>{ 128, 128, 512 }, 3, "res3c_branch", "res3d_branch2a res3d_branchSum", mode);     idntBlock(net, vector<uint32_t>{ 128, 128, 512 }, 3, "res3d_branch", "res4a_branch1 res4a_branch2a", mode);      convBlock(net, vector<uint32_t>{ 256, 256, 1024 }, 3, 2, "res4a_branch", "res4b_branch2a res4b_branchSum", mode);     idntBlock(net, vector<uint32_t>{ 256, 256, 1024 }, 3, "res4b_branch", "res4c_branch2a res4c_branchSum", mode);     idntBlock(net, vector<uint32_t>{ 256, 256, 1024 }, 3, "res4c_branch", "res4d_branch2a res4d_branchSum", mode);     idntBlock(net, vector<uint32_t>{ 256, 256, 1024 }, 3, "res4d_branch", "res4e_branch2a res4e_branchSum", mode);     idntBlock(net, vector<uint32_t>{ 256, 256, 1024 }, 3, "res4e_branch", "res4f_branch2a res4f_branchSum", mode);     idntBlock(net, vector<uint32_t>{ 256, 256, 1024 }, 3, "res4f_branch", "res5a_branch1 res5a_branch2a", mode);      convBlock(net, vector<uint32_t>{ 512, 512, 2048 }, 3, 2, "res5a_branch", "res5b_branch2a res5b_branchSum", mode);     idntBlock(net, vector<uint32_t>{ 512, 512, 2048 }, 3, "res5b_branch", "res5c_branch2a res5c_branchSum", mode);     idntBlock(net, vector<uint32_t>{ 512, 512, 2048 }, 3, "res5c_branch", "avg_pool", mode);      net.addNode("avg_pool", sn::Pooling(7, 7, sn::poolType::avg, mode), "fc1000")        .addNode("fc1000", sn::FullyConnected(1000, sn::active::none, mode), "LS")        .addNode("LS", sn::LossFunction(sn::lossType::softMaxToCrossEntropy), "Output"); 
? Полный код доступен здесь Можно поступить проще, загрузить архитектуру сети и веса из файлов,

вот так:
 string archPath = "c:/cpp/other/skyNet/example/resnet50/resNet50Struct.json",            weightPath = "c:/cpp/other/skyNet/example/resnet50/resNet50Weights.dat";      std::ifstream ifs;     ifs.open(archPath, std::ifstream::in);      if (!ifs.good()){         cout << "error open file : " + archPath << endl;         system("pause");         return false;     }         ifs.seekg(0, ifs.end);     size_t length = ifs.tellg();     ifs.seekg(0, ifs.beg);      string jnArch; jnArch.resize(length);     ifs.read((char*)jnArch.data(), length);          // Create net     sn::Net snet(jnArch, weightPath);   
Сделал приложение для интереса. Скачать можете отсюда. Объем большой из-за весов сети. Исходники там есть, можете использовать для примера. Приложение создано только для статьи, поддерживаться не будет, поэтому не включал в репозиторий проекта.

Теперь, что получилось по сравнению с TF.
Показания после прогона 100 изображений, в среднем. Машина: i5-2400, GF1050, Win7, MSVC12.

Значения результатов распознавания совпадают до 3-го знака.

? Код теста
CPU: time/img, ms GPU: time/img, ms CPU: RAM, Mb GPU: RAM, Mb
Skynet 410 120 600 1200
Tensorflow 250 25 400 1400
На самом деле плачевно все конечно.

Для CPU решил не использовать MKL-DNN, сам думал довести: перераспределил память для последовательного чтения, по максимуму загрузил векторные регистры. Возможно надо было приводить к матричному умножению, и/или еще какие хаки. Упирался здесь, по началу было хуже, правильней было бы использовать MKL все таки.

На GPU время тратится на копирование памяти из/в память видеокарты, и не все операции выполняются на GPU.

Выводы какие можно сделать из всей этой суеты:

— не выпендриваться, а использовать известные проверенные решения, дошли до ума уже более-менее вроде. Сидел сам на mxnet когда то, да маялся с нативным использованием, об этом ниже;

— не пытаться использовать нативный С интерфейс ML фреймворков. А юзать их на языке, на который ориентировались разработчики, то есть python.

Легкий путь использования функционала ML из своего языка, — сделать сервис-процесс на питоне, и по сокету слать ему картинки, получится разделение ответственности и отсутствие тяжелого кода.

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

Спасибо.

PS: если у кого есть желание и силы попытаться все таки догнать до TF, welcome!)

Источник: habr.com

Комментарии: