目录html
以前感受SSD很简单,这两天从头至尾把论文和源码都看了一下,发现以前不少细节都没掌握。python
这篇文章只说一些以前遗漏的点,读者阅读有必定基础ios
@算法
以前看Fast-RCNN
代码对Selective Search
的操做一直有很大的疑惑?网络
为何一张图会分割成这样大大小小的区域?分割后有啥意义呢?app
贪心算法
和图论
方面的知识,区域合并等算法。学习成本
的问题。。。看上图的resnet
核心模块,就是下降了学习成本
,使得网络更容易学习ide
下面这张图预测区域经过两次平移到达目标区域函数
下面这张图预测区域先经过放大再作两次平移到达目标区域学习
下面这张图经过多个预测区域对不一样的目标进行预测spa
经过上面的三幅图能够发现,回归的方式须要付出不一样的代价
固然代价越低越容易回归,能够看我以前的文章EAST和改进的EAST,就是经过回归的代价不一样,最后效果提高挺大的。
最后一幅图,经过打不一样的回归点(Anchor),比盲目的回归效果好不少
那么咱们怎么知道目标在哪?怎么打候选框(Anchor)呢?
假设上图是一个4 * 4
的feature map
,咱们既然不知道实际目标在哪,那就以每一个像素为中心生成不少个候选框
上面生成的候选框数量也就是4 * 4 * 2=32个
会不会以为那么多框进行回归效率很低?
首先那么多框都是固定的,好比上图的32个,回归的时候SSD也考虑到了这些,hard sample才须要回归,easy sample是不须要回归的
这个比较简单了,就是一个让输出对称的pooling操做。
这个也比较简单,就是利用双线性差值对中心的坐标进行计算出来
这是笔者没看源代码,比较糊涂的想法,问了其余在跑ssd的人也没回答出来。。。
笔者大概画了一个上图,这个问题很简单,想不通就很麻烦。。。
Anchor的特征主要包括几个方面:ratio(长宽比例)、scale(面积开根号,也就是正方形边长)、step/stride(步长,也就是原图和feature的比例)
feature map
计算出来的(由于不一样的特征图确定得设置不一样大小的scale)必定要理解上面几个参数的含义,具体公式的计算就很简单了,读者能够本身跑一下源代码
def default_prior_box(): mean_layer = [] for k,f in enumerate(Config.feature_map): mean = [] for i,j in product(range(f),repeat=2): f_k = Config.image_size/Config.steps[k]#当前feature map 的大小(经过步数从新计算) #anchor中心点坐标(cx / cy已经归一化操做) cx = (j+0.5)/f_k cy = (i+0.5)/f_k s_k = Config.sk[k]/Config.image_size mean += [cx,cy,s_k,s_k] s_k_prime = sqrt(s_k * Config.sk[k+1]/Config.image_size) mean += [cx,cy,s_k_prime,s_k_prime] for ar in Config.aspect_ratios[k]: mean += [cx, cy, s_k * sqrt(ar), s_k/sqrt(ar)] mean += [cx, cy, s_k / sqrt(ar), s_k * sqrt(ar)] if Config.use_cuda: mean = torch.Tensor(mean).cuda().view(Config.feature_map[k], Config.feature_map[k], -1).contiguous() else: mean = torch.Tensor(mean).view( Config.feature_map[k],Config.feature_map[k],-1).contiguous() mean.clamp_(max=1, min=0) mean_layer.append(mean)
疑点:刚开始看网上说的:
prior box
是:(中心X,中心Y,宽,高)按照这个推理:
\[ l^{cx}= b^{cx} - d^{cx} \]
\[ l^{cy}= b^{cy} - d^{cy} \]
\[ l^{w}= b^{w} / d^{w} \]
\[ l^{h}= b^{h} / d^{h} \]
其中\(b\)表明实际框,\(d\)表明default box
,\(l\)表明回归参数
而实际的表达式以下所示:
\[ l^{cx}= (b^{cx} - d^{cx})/d^w \]
\[ l^{cx}= (b^{cx} - d^{cx})/d^h \]
\[ l^{w}= log(b^{w}/d^{w}) \]
\[ l^{h}= log(b^{h}/d^{h}) \]
笔者认为无论回归什么东西,只要是一种映射关系便可
定义完LOSS,神经网络会帮咱们完成这种表达式的关系
因此这里做者也是为了方便,因此使用了除以d
,又使用log
函数
有专门的论文会解释这类事件,笔者这里只关注SSD的作法
SSD生成8732
个prior box
框,而实际的一张图中目标只有几个
有无数个预先设定的框,而实际和目标相交大于阈值的框不多
假设直接进行回归操做?
全部的框都进行回归=正样本的框+负样本的框
由于后者占比很是大,LOSS基本由负样本控制,最后的训练的结果以下:
目标能检测到,可是对于边界的处理很是很差,由于细节基本由负样本控制
SSD如何进行操做?
回归分为两个部分=位置回归+类别回归
位置回归按照上述方式进行
种类按照1 :3
的方式进行
首先计算出种类的loss
- 把正样本的loss置0(正样本所有保留)
- 负样本进行排序,按照3倍的正样本保留(保留大的loss属于hard sample)
最后正负样本叠加
loss_c = utils.log_sum_exp(batch_conf) - batch_conf.gather(1, target_conf.view(-1, 1)) loss_c = loss_c.view(batch_num, -1) # 将正样本设定为0 loss_c[pos] = 0 # 将剩下的负样本排序,选出目标数量的负样本 _, loss_idx = loss_c.sort(1, descending=True) _, idx_rank = loss_idx.sort(1) num_pos = pos.long().sum(1, keepdim=True) num_neg = torch.clamp(3*num_pos, max=pos.size(1)-1) # 提取出正负样本 neg = idx_rank < num_neg.expand_as(idx_rank) pos_idx = pos.unsqueeze(2).expand_as(conf_data) neg_idx = neg.unsqueeze(2).expand_as(conf_data) conf_p = conf_data[(pos_idx+neg_idx).gt(0)].view(-1, Config.class_num) targets_weighted = target_conf[(pos+neg).gt(0)] loss_c = F.cross_entropy(conf_p, targets_weighted, size_average=False)
图画的很差,由于正好叠合就看不到效果了
https://arxiv.org/pdf/1512.02325.pdf
http://www.javashuo.com/article/p-eimmbhyo-bu.html
https://blog.csdn.net/u010167269/article/details/52563573
http://www.javashuo.com/article/p-mnvbeznb-hv.html
http://www.javashuo.com/article/p-nrkqcogz-x.html
https://blog.csdn.net/u010712012/article/details/86555814
https://deepsense.ai/satellite-images-semantic-segmentation-with-deep-learning/