ssd源码解读(caffe)

ssd是经典的one-stage目标检测算法,作者是基于caffe来实现的,这需要加入新的层来完成功能,caffe自定义层可以使用python和c++,faster rcnn既使用了c++定义如smoothl1layer,又使用了python定义,如proposaltargetlayer、roidatalayer等。而ssd完全使用c++来定义层,包括:

1)annotateddatalayer数据读取层,用于读取图像和标签数据,并且支持数据增强

2)permutelayer用于改变blob的读取顺序

3)priorboxlayer用于生成defalutbox

4)multiboxlosslayer,边框回归集成了l2loss、smoothl1,于分类集成了softmax和logistic,并且支持困难样本挖掘、defaultbox匹配等功能。

name: "mbox_loss"

type: "MultiBoxLoss"

bottom: "mbox_loc"

bottom: "mbox_conf"

bottom: "mbox_priorbox"

bottom: "label"

top: "mbox_loss"

整个网络比较繁杂,不过相对于fasterrcnn算简单一些,我们从loss顺藤摸瓜,首先查看label的存储格式,这需要查看annotateddatalayer层是如何对数据进行读取的,查看网络定义文件发现数据读取层的输入要求是lmdb或leveldb格式,那么问题来了,如何将voc格式的数据制作成lmdb呢?原生caffe好像只支持分类的数据制作,github上说通过./data/VOC0712/create_list.sh和./data/VOC0712/create_data.sh来制作数据,查看create_data.sh文件,里面又调用了$root_dir/scripts/create_annoset.py,继续查看create_annoset.py 发现最终调用的是/build/tools/convert_annoset进而查看tools/convert_annoset.cpp,里面实现了将分散的文件转换为lmdb格式,但是依然还没找到哪里解析了voc格式标注的xml文件,convert_annoset.cpp调用了ReadRichImageToAnnotatedDatum函数,终于在utils/io.cpp中找到了读取xml标注文件的实现。数据层的输出有data和label,data的格式是n×3×300×300,而label的格式是n*1×nofboxes*8。每个物体包含了8个信息[item_id, group_label, instance_id, xmin, ymin, xmax, ymax, diff]

,含义是batchsize个图像中的第item_id幅图像中的第group_label个类别下的第instance_id个目标的坐标为[xmin, ymin, xmax, ymax]

loss层的label输入格式清楚了,接下来解析mbox_priorbox的格式,mbox_priorbox是由多个priorboxconcat起来的,ssd3006featuremap上生成了priorbox,例如在conv4_3featuremap上的priorbox数量为w×h×numprior,其存储方式为chw,维度为2×1*(w×h×numprior*4),可以看出是3维的,而不是nchw,这是因为一个batch的图像大小是相同的,为它们生成的priorbox也是完全相同的,所以不需要batch这个维度,另外注意channel维度是2,是存入了对应位置的方差varirance。多层的priorbox数据concat起来时,axis设为2,也就是在w的维度将bloob接起来形成一个如图所示长条。

接着解析mbox_loc的维度,以大小为38×38conv4_3fmap为例,3*3的卷积在fmap上卷积得到bsize×16×38×38map,注意通道数量是16,38*38的空间位置上,每个空间位置对应16个数,而这16个数就是对4priorbox也就是16个点的回归,但是麻烦的是priorbox是被拉成了1维向量(不考虑variance的话),暂时不考虑bsize的话,那如何将16×38×38blob拉成与priorbox对应的1维向量呢?直接flatten的话将按照whc从低到高的顺序flatten,这是不行的,需要按照cwh的顺序flatten才能与priorbox的存储方式对应得上,于是ssd定义了permutelayerblobnchw顺序改为nhwc,然后进行flatten即可。这里需要注意flatten的用法,文件指定从1axis开始flatten,也就是从permute过后hwcflatten1维,整体flatten过后的维度为n×(h*w*c)conv4也就是bsize×23104,四维blob变成了2维,如果对flatten有疑惑可直接查看源码。各层的二维blob再继续concat,第1维是bsize,那么肯定在第2维上进行concat,所以mbox_locmbox_conf层拼接的axis都设为1。所以mbox_locblob的维度最终为bsize×27130

mbox_conf层的维度与mbox_loc层维度的推算类似,3*3的卷积在fmap上卷积得到bsize×84×38×38map,其中84=4×21,4是每个位置4个不同的priorbox21代表了卷积网络预测出该priorbox属于21类的概率。后续计算方式参考mbox_loc即可

p { margin-bottom: 0.1in; line-height: 120% }
code.cjk { font-family: "Nimbus Mono L", monospace }

原文地址:https://www.cnblogs.com/anti-tao/p/9991334.html

时间: 2024-10-19 03:29:54

ssd源码解读(caffe)的相关文章

SSD源码解读——损失函数的构建

之前,对SSD的论文进行了解读,可以回顾之前的博客:https://www.cnblogs.com/dengshunge/p/11665929.html. 为了加深对SSD的理解,因此对SSD的源码进行了复现,主要参考的github项目是ssd.pytorch. 搭建SSD的项目,可以分成以下三个部分: 数据读取: 网络搭建: 损失函数的构建. 接下来,本篇博客重点分析损失函数的构建. 检测任务的损失函数,与分类任务的损失函数具有很大不同.在检测的损失函数中,不仅需要计类别置信度的差异,坐标的差

QCustomplot使用分享(二) 源码解读

一.头文件概述 从这篇文章开始,我们将正式的进入到QCustomPlot的实践学习中来,首先我们先来学习下QCustomPlot的类图,如果下载了QCustomPlot源码的同学可以自己去QCustomPlot的目录下documentation/qcustomplot下寻找一个名字叫做index.html的文件,将其在浏览器中打开,也是可以找到这个库的类图.如图1所示,是组成一个QCustomPlot类图的可能组成形式. 一个图表(QCustomPlot):包含一个或者多个图层.一个或多个ite

vue源码解读预热-0

vueJS的源码解读 vue源码总共包含约一万行代码量(包括注释)特别感谢作者Evan You开放的源代码,访问地址为Github 代码整体介绍与函数介绍预览 代码模块分析 代码整体思路 总体的分析 从图片中可以看出的为采用IIFE(Immediately-Invoked Function Expression)立即执行的函数表达式的形式进行的代码的编写 常见的几种插件方式: (function(,){}(,))或(function(,){})(,)或!function(){}()等等,其中必有

SpringMVC源码解读 - RequestMapping注解实现解读 - RequestCondition体系

一般我们开发时,使用最多的还是@RequestMapping注解方式. @RequestMapping(value = "/", param = "role=guest", consumes = "!application/json") public void myHtmlService() { // ... } 台前的是RequestMapping ,正经干活的却是RequestCondition,根据配置的不同条件匹配request. @Re

jdk1.8.0_45源码解读——HashMap的实现

jdk1.8.0_45源码解读——HashMap的实现 一.HashMap概述 HashMap是基于哈希表的Map接口实现的,此实现提供所有可选的映射操作.存储的是<key,value>对的映射,允许多个null值和一个null键.但此类不保证映射的顺序,特别是它不保证该顺序恒久不变.  除了HashMap是非同步以及允许使用null外,HashMap 类与 Hashtable大致相同. 此实现假定哈希函数将元素适当地分布在各桶之间,可为基本操作(get 和 put)提供稳定的性能.迭代col

15、Spark Streaming源码解读之No Receivers彻底思考

在前几期文章里讲了带Receiver的Spark Streaming 应用的相关源码解读,但是现在开发Spark Streaming的应用越来越多的采用No Receivers(Direct Approach)的方式,No Receiver的方式的优势: 1. 更强的控制自由度 2. 语义一致性 其实No Receivers的方式更符合我们读取数据,操作数据的思路的.因为Spark 本身是一个计算框架,他底层会有数据来源,如果没有Receivers,我们直接操作数据来源,这其实是一种更自然的方式

jdk1.8.0_45源码解读——Set接口和AbstractSet抽象类的实现

jdk1.8.0_45源码解读——Set接口和AbstractSet抽象类的实现 一. Set架构 如上图: (01) Set 是继承于Collection的接口.它是一个不允许有重复元素的集合.(02) AbstractSet 是一个抽象类,它继承于AbstractCollection.AbstractCollection实现了Set中的绝大部分函数,为Set的实现类提供了便利.(03) HastSet 和 TreeSet 是Set的两个实现类.        HashSet依赖于HashMa

线程本地变量ThreadLocal源码解读

  一.ThreadLocal基础知识   原始线程现状: 按照传统经验,如果某个对象是非线程安全的,在多线程环境下,对对象的访问必须采用synchronized进行线程同步.但是Spring中的各种模板类并未采用线程同步机制,因为线程同步会影响并发性和系统性能,而且实现难度也不小. ThreadLocal在Spring中发挥着重要的作用.在管理request作用域的bean,事务管理,任务调度,AOP等模块中都出现了它的身影. ThreadLocal介绍: 它不是一个线程,而是线程的一个本地化

hadoop源码解读namenode高可靠:HA;web方式查看namenode下信息;dfs/data决定datanode存储位置

点击browserFilesystem,和命令查看结果一样 当我们查看hadoop源码时,我们看到hdfs下的hdfs-default.xml文件信息 我们查找${hadoop.tmp.dir}这是引用变量,肯定在其他文件有定义,在core-default.xml中查看到,这两个配置文件有个共同点: 就是不要修改此文件,但可以复制信息到core-site.xml和hdfs-site.xml中修改 usr/local/hadoop 是我存放hadoop文件夹的地方 几个关于namenode的重要文