ResNet50。 自己实施

大家好 我的上一篇文章描述了神经网络库。 在这里,我决定向您展示如何使用TF(Tensorflow)中经过训练的网络进行决策,以及这样做是否值得。

在剪辑下,与TF的原始实现(一个用于识别图片的演示应用程序)进行了比较,并得出了结论。 请谁在乎。

例如,您可以在此处找到ResNet的工作方式。

以下是数字的网络结构:



事实证明,该代码没有比python更简单,也没有更复杂的代码。

用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。

识别结果的值最多匹配第三个字符。

测试代码
CPU:时间/ img,毫秒GPU:时间/ img,毫秒CPU:RAM,MbGPU:RAM,Mb
天网4101206001200
张量流250254001400


实际上,一切当然都是令人遗憾的。

对于CPU,我决定不使用MKL-DNN,我本人认为是要完成它:重新分配内存以进行顺序读取,将向量寄存器加载到最大。 也许有必要导致矩阵乘法和/或其他一些黑客行为。 在这里休息,起初情况更糟,完全使用MKL会更正确。

在GPU上,会花费时间从/到视频卡的内存中复制内存,并且并非所有操作都在GPU上执行。

从所有这些大惊小怪中可以得出什么结论:

-不是炫耀,而是使用知名的久经考验的解决方案,它们或多或少都已经浮现在脑海。 他本人曾经坐在mxnet上,并为本地使用而苦苦挣扎,以下更多内容。

-不要尝试使用ML框架的本机C接口。 并以开发人员关注的语言(即python)使用它们。

从您的语言使用ML功能的一种简单方法是在python上创建服务进程,并在套接字上向其发送图片,这样您就可以分担责任,并且无需繁重的代码。

也许一切。 这篇文章很短,但是我认为结论很有价值,不仅适用于ML。

谢谢啦

PS:
如果有人有意愿和力量尝试仍然赶上TF, 欢迎光临 !)

PS2:
早点放下手。 他抽了点烟,再抽了一下,一切都解决了。
正如我所想,对于CPU,转换为矩阵乘法很有帮助。
对于GPU,我在单独的库中选择了所有操作,因此在不复制到CPU的情况下(反之亦然),这种方法的唯一缺点是我不得不重写(复制)所有运算符,尽管有些事情是重合的,但我没有将它们连接起来。
通常,现在是这样:
CPU:时间/ img,毫秒GPU:时间/ img,毫秒CPU:RAM,MbGPU:RAM,Mb
天网19515600800
张量流250254001400

也就是说,至少推断的速度甚至比TF更快。
测试代码未更改。

Source: https://habr.com/ru/post/zh-CN449864/


All Articles