内容简介:之前在简书的文章,搬迁过来 ^-^本文是作者原创,如有理解错误,恳请大家指出,如需引用,请注明出处。#Caffe FeatureMap数据流的建立 ##用语解释
之前在简书的文章,搬迁过来 ^-^
本文是作者原创,如有理解错误,恳请大家指出,如需引用,请注明出处。
#Caffe FeatureMap数据流的建立 ##用语解释
- FeatureMap: 输入的图片信息或者经过多层处理后的图片信息。
- weights: 只针对卷积层存在的权重系数。
- caffe :文中提到的caffe均指caffed1.0,如果使用caffe2.0会特别指出。
在讲解FeatureMap的数据流之前,首先需要明确一下caffe的大体结构,caffe的整体逻辑结构分为3层,分别是Net,Layer和Blob,分别的作用如下:
- Net: 该层处于CAFFE的最顶层,主要负责对模型文件的读写,根据模型文件的内容建立相应的Layer,填充对应层的数据并进行相关的调用。
- Layer: 该层是实际的执行单元,常见的如卷积层,Pooling层都是处于这一逻辑层。
- Blob:该层是一个内存管理的模块,为Layer和Net提供相应的存储空间,屏蔽上层对于内存分配,CPU,GPU切换的感知。
由上面的讲解分层关系不难看出,FeatureMap在整个Caffe框架中,不属于任何一个Layer,所以它被最顶层的Net层所持有。Net层就需要能够通过caffe的模型文件推倒出每一层所依赖的输入,这样才能构建出一个完整的数据链。在这种需求下Caffe引入了两个定义:
- bottom: Layer的输入数据。
- top: Layer的输出数据。 具体的形式如下图(单输入和多输入的情形):
所以Net在调用Layer之前就一定知道了Layer的所需要的输入数据,也就是需要Net层所持有的Blob变量需要被那些层所引用。这些在模型文件中也有直观的反应(为了方便截图,删除了下图proto中关于Convlution的参数配置):
上述的工作都在Net的Init( void Net::Init(const NetParameter& in_param)
)函数里面进行了处理,主要实现的就是根据上图左侧的模型文件得到需要建立的Layer的类型,并将各个Layer间的数据链接起来。函数中的关键参数如下:
名称 | 功能 |
---|---|
in_param | 存放由protobuf转换出的模型文件 |
bottom_vecs_ | 存放每一层中的输入数据类型为:vector<vector<Blob*> > |
top_vecs_ | 存放每一层中的输出数据类型为:vector<vector<Blob*> > |
available_blobs | 存放每一层中的输出数据类型为:vector<vector<Blob*> > |
##常规的数据链建立流程是(单输入单输出的场景):
-
链接本层的bottom数据(
int Net::AppendBottom(const NetParameter& param, const int layer_id, const int bottom_id, set<string>* available_blobs, map<string, int>* blob_name_to_idx)
),该函数会使用从当前layer持有的bottom信息中得到对应bottom的层名,然后利用该名称找到对应的blob,并加入到bottom_vecs_。 -
链接本层的top数据(
void Net::AppendTop(const NetParameter& param, const int layer_id,const int top_id, set<string>* available_blobs, map<string, int>* blob_name_to_idx)
),该操作就是将本层的输出数据加入到top_vecs_中,并与 layer_id相关联,这里同时负责Blob对象的申请。 需要指出的是,新的Blob对象是在top中进行创建的,在Bottom中只是将上一层top的指针添加进来,同时在这个过程中CAFFE还利用available_blobs进行了异常校验,在每次新加入top的时候记录对应的Blob名称,在bottom中链接上一层top之后,在available_blobs中将对应的Blob名称剔除。相关伪代码如下:for (int layer_id = 0; layer_id < param.layer_size(); ++layer_id) { AppendBottom(); AppendTop(); } 复制代码
##多输入的数据链的建立: 细心的同学应该已经发现,当数据为多bottom输入的时候,因为available_blobs的数据被上一次的链接过程删掉,则再次链接相同bottom的时候,会出先异常告警,在这种情况下我们就要引入CAFFE的另外一处理函数 void InsertSplits(const NetParameter& param, NetParameter* param_split)
,该函数的主要功能就是对 top输出到多个 Layer的情况进行分割。 整个函数分为两个部分:
-
遍历整个网络,记录每一个Layer的top的使用情况,记录结构放在
top_idx_to_bottom_count
中。 -
遍历整个网络,对
top_idx_to_bottom_count > 1
的情况进行处理: a. 首先是对top被多个层使用的Layer进行分割,主要的做法是在该层的后面新建一个Layer ,这个新的Layer的会按照top_idx_to_bottom_count
的个数和约定的分割名称(SplitBlobName
)去新建top,添加层的代码如下(此处只展示核心的创建过程,具体调用流程请自行跟踪)://该函数执行新层的添加 void ConfigureSplitLayer(const string& layer_name, const string& blob_name, const int blob_idx, const int split_count, const float loss_weight, LayerParameter* split_layer_param) { split_layer_param->Clear(); split_layer_param->add_bottom(blob_name); split_layer_param->set_name(SplitLayerName(layer_name, blob_name, blob_idx)); split_layer_param->set_type("Split"); for (int k = 0; k < split_count; ++k) {//split_count就是该top被引用的个数 //添加了分割后的top //命名由SplitBlobName生成 split_layer_param->add_top( SplitBlobName(layer_name, blob_name, blob_idx, k)); if (loss_weight) { if (k == 0) { split_layer_param->add_loss_weight(loss_weight); } else { split_layer_param->add_loss_weight(0); } } } } 复制代码
b. 之后,是对使用同一个top的后续层的bottom的blob进行改名,使用与上一步相同的命名规则进行改名。
下面以SqueezeNet1.1为例,展示了添加新的分割层的实例:
![Upload new_split_layer.jpg failed. Please try again.]
通过这样一个分割的转化,达到了对多输入数据流的建立。
##遗留问题 上面讲的是在初始化阶段对FeatureMap数据的链接关系的建立,但是对于weights的填充和初始图片的输入并没有进行分析。
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- 大数据技术 DataPipeline在大数据平台的数据流实践
- DataPipeline在大数据平台的数据流实践
- 我对前后端数据模型和数据流的理解
- stream – 数据流处理
- 浅谈hdfs架构与数据流
- 我所认识的前端数据流
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
数据库索引设计与优化
【美】Tapio Lahdenmaki、【美】Michael Leach / 曹怡倩、赵建伟 / 电子工业出版社 / 2015-6 / 79.00元
《数据库索引设计与优化》提供了一种简单、高效、通用的关系型数据库索引设计方法。作者通过系统的讲解及大量的案例清晰地阐释了关系型数据库的访问路径选择原理,以及表和索引的扫描方式,详尽地讲解了如何快速地估算SQL 运行的CPU 时间及执行时间,帮助读者从原理上理解SQL、表及索引结构、访问方式等对关系型数据库造成的影响,并能够运用量化的方法进行判断和优化,指导关系型数据库的索引设计。 《数据库索......一起来看看 《数据库索引设计与优化》 这本书的介绍吧!