PS:要转载请注明出处,本人版权所有。

PS: 这个只是基于《我自己》的理解,

如果和你的原则及想法相冲突,请谅解,勿喷。

前置说明

  本文作为本人csdn blog的主站的备份。(BlogID=068)

  本文发布于 2018-07-19 11:05:52,现用MarkDown+图床做备份更新。blog原图已丢失,使用csdn所存的图进行更新。(BlogID=068)

环境说明

  时间:2018.07.19

  ncnn master commit id:b3e24cafc37483dcc97ee61e6f0f6ff1b094300e

前言


  前面两篇文章我们分析了ncnn的加载参数和网络的基本工作流程。其实这一切只是为了给这篇文章做准备。因为我觉得ncnn作为一个前向框架,写的还是比较简单的,方便我们这些小菜鸟对其工作原理进行分析。而分析的方法,还是得从哪里来,从哪里去(读源码)。

前置内容(非常重要的)


本文作为例子的网络
7767517
9 9
Input data 0 1 data 0=28 1=28 2=1
Convolution conv1 1 1 data conv1 0=20 1=5 2=1 3=1 4=0 5=1 6=500
Pooling pool1 1 1 conv1 pool1 0=0 1=2 2=2 3=0 4=0
Convolution conv2 1 1 pool1 conv2 0=50 1=5 2=1 3=1 4=0 5=1 6=25000
Pooling pool2 1 1 conv2 pool2 0=0 1=2 2=2 3=0 4=0
InnerProduct ip1 1 1 pool2 ip1 0=500 1=1 2=400000
ReLU relu1 1 1 ip1 ip1_relu1
InnerProduct ip2 1 1 ip1_relu1 ip2 0=10 1=1 2=5000
Softmax prob 1 1 ip2 prob 0=0
ncnn的基本调用流程
#include "net.h"
ncnn::Net abc_net;
ncnn::Mat in_img;
ncnn::Mat out_img; abc_net.load_param(param_path);
abc_net.load_model(model_path); ncnn::Extractor ex = abc_net.create_extractor(); ex.set_num_threads(4);
ex.set_light_mode(true); ex.input("data", in_img); ex.extract("prob", out_img);

  这里简要说明一下:

    前两篇文章分别介绍了load_param load_model的基本工作原理,这里要介绍的就是剩下的所有内容。

相关数据结构准备(此小节内容可作为前两篇文章的内容补充)

  在load_param时:

    ncnn::Net::blobs存放着每一个blob的相关信息,主要信息为name、producer、consumers,含义分别为:名字、产生这个blob数据的层、消费这个blob数据的层。

  ncnn::Net::layers存放的是:

  • ncnn::Net::layers::type
  • ncnn::Net::layers::name
  • ncnn::Net::layers::bottoms 存的是此层需要的输入blob的idx
  • ncnn::Net::layers::tops 存的是此层输出的blob的idx

  在load_param中会根据我们读入的type来create_layer,这里建立这个layer也挺有意思的。

//这里的layer_to_index会去layer_registry去查找对应的层类型的idx,这里的layer_registry数组是我们在编译ncnn的时候初始化的,里面存放的是如下的东西。
#if NCNN_STRING
{"Convolution",Convolution_x86_layer_creator},
#else
{Convolution_x86_layer_creator}, //这个数组的作用就是用来查询具体层的idx和其提供的构造接口creator。每个层都会实现这个creator,比如Convolution层在x86架构下,其构造接口名字叫做Convolution_x86_layer_creator。其原理如下:
DEFINE_LAYER_CREATOR(Convolution_x86)//通过宏定义Convolution_x86_layer_creator这个全局函数
#define DEFINE_LAYER_CREATOR(name) \
::ncnn::Layer* name##_layer_creator() { return new name; }
以前文网络为例分析

  这里的分析入口为:

    ncnn::Extractor::input()

//图中blob_mats 就是整个框架工作时的数据存放向量。
std::vector<Mat> blob_mats;//blob_mats 定义 //blob_mats的大小初始化在ncnn::Net::create_extractor()中完成,这里唯一需要注意的是,此函数是类Extractor的友元类成员函数,这样写的原因是为了访问其protect的构造函数。
Extractor Net::create_extractor() const
{
return Extractor(this, blobs.size());
}
Extractor::Extractor(const Net* _net, int blob_count) : net(_net)
{
blob_mats.resize(blob_count);
lightmode = true;
num_threads = 0;
} //find_blob_index_by_name 就是在ncnn::Net::blobs中去循环遍历,得到其idx。
//然后把输入的mat数据,放入到blob_mats中相应的位置去。到这里,输入数据就填充完了。

    ncnn::Extractor::extract()

//此调用的开始时,根据名字通过find_blob_index_by_name 查找我们需要的输出层的idx,然后把blob_mats(携带输入数据)、lightmode、我们需要的输出层的idx一起传入给ncnn::Net::forward_layer()。然后将上述调用处理好的数据放入feature返回。一个网络的前向计算就完成了。

    ncnn::Net::forward_layer()

  上图这个if语句是这层网络只有一个输入和输出

  此图的else是这层网络非一个输入和输出

//这里我只分析只有一个输入和输出的情况,另外一种和它非常相近。
//图中line 637-line 642,这里通过递归调用一层层倒推回去,直到我们的网络输入层。因为这一层在input的时候给blob_mats赋值,其dims不为零。 //图中line646-line 655是set_light_mode的作用,其作用为是否释放计算过程中,存入blob_mats中,在前面层计算的得到的数据 //图中line 658-line 691是开始前向计算。这里的计算分为两类,一类是在输入数据上计算,并把输出数据放入到输入数据的变量中。另外一种就是分别传入两个变量,一个存输入,一个存输出。至于为啥这样写,不知道,节约内存? //后续只会分析一种情况,分别传入两个变量,一个存输入,一个存输入。这里以前文网络中的第二层Convolution层为例。在line 677-line 690中,layer->forward()就是执行具体层的计算。

    ncnn::Convolution::forward()

//这里只分析kernel为1*1*1的这种卷积
//这里构造了一个InnerProduct层操作,这里简短的几句话,其实就是我前面几篇文章中的部分内容,load_param做了什么,load_model做了什么

    ncnn::InnerProduct::forward()

  这里核心是计算两个向量的内积。

  到这里为止,一个卷积操作就完成了。然后forward_layer会从递归中一级级返回,最后得到我们需要的那一层的值。

后记


  无

参考文献


打赏、订阅、收藏、丢香蕉、硬币,请关注公众号(攻城狮的搬砖之路)

PS: 请尊重原创,不喜勿喷。

PS: 要转载请注明出处,本人版权所有。

PS: 有问题请留言,看到后我会第一时间回复。

《TencentNCNN系列》 之工作原理简要解析(以LeNet-5为例)的更多相关文章

  1. android多线程-AsyncTask之工作原理深入解析(下)

    关联文章: Android 多线程之HandlerThread 完全详解 Android 多线程之IntentService 完全详解 android多线程-AsyncTask之工作原理深入解析(上) ...

  2. android多线程-AsyncTask之工作原理深入解析(上)

    关联文章: Android 多线程之HandlerThread 完全详解 Android 多线程之IntentService 完全详解 android多线程-AsyncTask之工作原理深入解析(上) ...

  3. 路由及路由器工作原理深入解析3:路由与port

        日志"路由及路由器工作原理深入解析1"http://user.qzone.qq.com/2756567163/blog/1438322342介绍了"为什么要使用路 ...

  4. NS域名工作原理及解析

    DNS域名工作原理及解析   0x00 定义 DNS( Domain Name System)是“域名系统”的英文缩写,它作为将域名和IP地址相互映射的一个分布式数据库,能够使人更方便地访问互联网.D ...

  5. JavaScript的工作原理:解析、抽象语法树(AST)+ 提升编译速度5个技巧

    这是专门探索 JavaScript 及其所构建的组件的系列文章的第 14 篇. 如果你错过了前面的章节,可以在这里找到它们: JavaScript 是如何工作的:引擎,运行时和调用堆栈的概述! Jav ...

  6. Java web每天学之Servlet工作原理详情解析

    上篇文章中我们介绍了Servlet的实现方式以及Servlet的生命周期,我们这篇文章就来介绍一下常用对象. 点击回顾:<Java Web每天学之Servlet的工作原理解析>:<J ...

  7. Android ListView工作原理完全解析,带你从源码的角度彻底理解

    版权声明:本文出自郭霖的博客,转载必须注明出处.   目录(?)[+] Adapter的作用 RecycleBin机制 第一次Layout 第二次Layout 滑动加载更多数据   转载请注明出处:h ...

  8. Android ListView工作原理完全解析(转自 郭霖老师博客)

    原文地址:http://blog.csdn.net/guolin_blog/article/details/44996879 在Android所有常用的原生控件当中,用法最复杂的应该就是ListVie ...

  9. Android ListView工作原理全然解析,带你从源代码的角度彻底理解

    转载请注明出处:http://blog.csdn.net/guolin_blog/article/details/44996879 在Android全部经常使用的原生控件其中.使用方法最复杂的应该就是 ...

  10. DNS的工作原理及解析

    DNS协议是互联网核心协议之一.不管是上网浏览,还是编程开发,都需要了解一点它的知识. 一.什么是DNS? DNS( Domain Name System)是“域名系统”的英文缩写,是一种组织成域层次 ...

随机推荐

  1. P9247 [集训队互测 2018] 完美的队列题解

    题目链接:[集训队互测 2018] 完美的队列 神仙数据结构题,看了很多题解才搞懂.在做此题之前,最好对分块很熟悉,对各类标记非常熟练.考虑题意说的种类是相对于全局的.我们可以考虑局部影响对全局影响. ...

  2. Golang中make和new的区别

    1. 相同点 都是内建函数,都是在堆上分配内存,都需要传递类型参数 2. 不同点 传递的参数不一样,new函数只接收一个参数,make函数可以接收一个以上的参数 package main import ...

  3. DbgridEh 导出 Excel 如果字段长度超过255会截断,那如何导出,另一种神奇的方法

    由于DbgridEh 导出 Excel 如果字段长度超过255会截断,所以必须换一种方法来导出,百度  谷歌 看了上百帖,都是有这句: xlApp := CreateOleObject('Excel. ...

  4. Nginx+uwsgi+ssl配置https

    Nginx+uwsgi+ssl配置https 使用原始django,太过于笨重和杂多nginx是一个轻量级的web服务器,在处理静态资源和高并发有优势uwsgi是一个基于python的高效率的协议,处 ...

  5. JS leetcode 翻转字符串里的单词 题解分析

    壹 ❀ 引 今天来做一道难度中等,但实际难度并不是很高的题目,题目来源leetcode151. 翻转字符串里的单词,题目描述如下: 给定一个字符串,逐个翻转字符串中的每个单词. 示例 1: 输入: & ...

  6. NC20477 [ZJOI2008]树的统计COUNT

    题目链接 题目 题目描述 一棵树上有n个节点,编号分别为1到n,每个节点都有一个权值w. 我们将以下面的形式来要求你对这棵树完成 一些操作: I. CHANGE u t : 把结点u的权值改为t II ...

  7. 未配置Datasource时, 启动 SpringBoot 程序报错的问题

    SpringBoot will show error if there is no datasource configuration in application.yml/application.pr ...

  8. Java并发编程实例--19.在一个锁中使用多个条件

    一个锁可能关联了一个或多个条件.这些条件可以在Condition接口中声名. 使用这些条件的目的是去控制一个锁并且可以检查一个条件是true或false,如果为false,则暂停直到 另一个线程来唤醒 ...

  9. cdn缓存立刻刷新

    现在例如有一个业务需求是客户更新图片,那我们需要及时更新,可是正常的上传是无法及时更新的,因为七牛云会有客户端缓存和cdn缓存,这时候可能有多种处理方式:   1.cdn和客户端缓存的时间调短,例如1 ...

  10. Redis原理再学习03:数据结构-链表 list

    链表list介绍 1. 链表list简介 链表(linked list)是一种基础数据结构,是一种线性表,但是不会按照线性表的顺序存储数据,而是在每一个节点里存到下一个节点的指针. 链表插入节点时是 ...