目錄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/