正樣本全稱是anchor正樣本,正樣本所指的對象是anchor box,即先驗框。 先驗框:YOLO v2吸收了Faster RCNN的優(yōu)點(diǎn),設(shè)置了一定數(shù)量的預(yù)選框,使得模型不需要直接預(yù)測物體尺度與坐標(biāo),只需要預(yù)測先驗框到真實物體的偏移,降低了預(yù)測難度。
正樣本全稱是anchor正樣本,正樣本所指的對象是anchor box,即先驗框。
先驗框:從YOLO v2開始吸收了Faster RCNN的優(yōu)點(diǎn),設(shè)置了一定數(shù)量的預(yù)選框,使得模型不需要直接預(yù)測物體尺度與坐標(biāo),只需要預(yù)測先驗框到真實物體的偏移,降低了預(yù)測難度。
Yolov5算法使用如下3種方式增加正樣本個數(shù):
假設(shè)一個GT框落在了某個預(yù)測分支的某個網(wǎng)格內(nèi),該網(wǎng)格具有3種不同大小anchor,若GT可以和這3種anchor中的多種anchor匹配,則這些匹配的anchor都可以來預(yù)測該GT框,即一個GT框可以使用多種anchor來預(yù)測。
具體方法:
不同于IOU匹配,yolov5采用基于寬高比例的匹配策略,GT的寬高與anchors的寬高對應(yīng)相除得到ratio1,anchors的寬高與GT的寬高對應(yīng)相除得到ratio2,取ratio1和ratio2的最大值作為最后的寬高比,該寬高比和設(shè)定閾值(默認(rèn)為4)比較,小于設(shè)定閾值的anchor則為匹配到的anchor。
anchor_boxes=torch.tensor([[1.25000, 1.62500],[2.00000, 3.75000],[4.12500, 2.87500]])
gt_box=torch.tensor([5,4])
ratio1=gt_box/anchor_boxes
ratio2=anchor_boxes/gt_box
ratio=torch.max(ratio1, ratio2).max(1)[0]
print(ratio)
anchor_t=4
res=ratio
tensor([4.0000, 2.5000, 1.3913])
tensor([False, True, True])
與 GT 相匹配的的 anchor 為 **anchor2 **和
anchor3
。
假設(shè)一個GT框落在了某個預(yù)測分支的某個網(wǎng)格內(nèi),則該網(wǎng)格有左、上、右、下4個鄰域網(wǎng)格,根據(jù)GT框的中心位置,將最近的2個鄰域網(wǎng)格也作為預(yù)測網(wǎng)格,也即一個GT框可以由3個網(wǎng)格來預(yù)測。
計算例子:
GT box中心點(diǎn)處于grid1中,grid1被選中。為了增加增樣本,grid1的上下左右grid為候選網(wǎng)格,因為GT中心點(diǎn)更靠近grid2和grid3,grid2和grid3也作為匹配到的網(wǎng)格。
根據(jù)上個步驟中的anchor匹配結(jié)果,GT與anchor2、anchor3相匹配,因此GT在當(dāng)前層匹配到的正樣本有6個,分別為:
假設(shè)一個GT框可以和2個甚至3個預(yù)測分支上的anchor匹配,則這2個或3個預(yù)測分支都可以預(yù)測該GT框。即一個GT框可以在3個預(yù)測分支上匹配正樣本,在每一個分支上重復(fù)anchor匹配和grid匹配的步驟,最終可以得到某個GT 匹配到的所有正樣本。
如下圖在Prediction的3個不同尺度的輸出中,gt都可以去匹配正樣本。
正樣本篩選主要做了四件事情:
anchors:
- [10,13, 16,30, 33,23] # P3/8
- [30,61, 62,45, 59,119] # P4/16
- [116,90, 156,198, 373,326] # P5/32
yolov5的網(wǎng)絡(luò)有三個尺寸的輸出,不同大小的輸出對應(yīng)不同尺寸:
yolov5/utils/loss.py
def build_targets(self, p, targets):
# Build targets for compute_loss(), input targets(image,class,x,y,w,h)
"""
p: 預(yù)測值
targets:gt
(Pdb) pp p[0].shape
torch.Size([1, 3, 80, 80, 7])
(Pdb) pp p[1].shape
torch.Size([1, 3, 40, 40, 7])
(Pdb) pp p[2].shape
torch.Size([1, 3, 20, 20, 7])
(Pdb) pp targets.shape
torch.Size([23, 6])
"""
na, nt = self.na, targets.shape[0] # number of anchors, targets
tcls, tbox, indices, anch = [], [], [], []
"""
tcls 保存類別id
tbox 保存的是gt中心相對于所在grid cell左上角偏移量。也會計算出gt中心相對擴(kuò)展anchor的偏移量
indices 保存的內(nèi)容是:image_id, anchor_id, grid x刻度 grid y刻度
anch 保存anchor的具體寬高
"""
gain = torch.ones(7, device=self.device) # normalized to gridspace gain
ai = torch.arange(na, device=self.device).float().view(na, 1).repeat(1, nt) # same as .repeat_interleave(nt)
"""
(Pdb) ai
tensor([[0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
[1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.],
[2., 2., 2., 2., 2., 2., 2., 2., 2., 2., 2., 2., 2., 2., 2., 2., 2., 2., 2., 2., 2., 2., 2.]], device='cuda:0')
(Pdb) ai.shape
torch.Size([3, 23])
"""
targets = torch.cat((targets.repeat(na, 1, 1), ai[..., None]), 2) # append anchor indices
g = 0.5 # bias
off = torch.tensor(
[
[0, 0],
[1, 0],
[0, 1],
[-1, 0],
[0, -1], # j,k,l,m
# [1, 1], [1, -1], [-1, 1], [-1, -1], # jk,jm,lk,lm
],
device=self.device).float() * g # offsets
for i in range(self.nl):
anchors, shape = self.anchors[i], p[i].shape
"""
(Pdb) anchors
tensor([[1.25000, 1.62500],
[2.00000, 3.75000],
[4.12500, 2.87500]], device='cuda:0')
(Pdb) shape
torch.Size([1, 3, 80, 80, 7])
"""
gain[2:6] = torch.tensor(shape)[[3, 2, 3, 2]] # xyxy gain
"""
(Pdb) gain
tensor([ 1., 1., 80., 80., 80., 80., 1.], device='cuda:0')
"""
# Match targets to anchors
t = targets * gain # shape(3,n,7) # 將grid cell還原到當(dāng)前feature map上
"""
(Pdb) t.shape
torch.Size([3, 23, 7])
"""
if nt:
# Matches
r = t[..., 4:6] / anchors[:, None] # wh ratio
j = torch.max(r, 1 / r).max(2)[0] < self.hyp['anchor_t'] # compare
# j = wh_iou(anchors, t[:, 4:6]) > model.hyp['iou_t'] # iou(3,n)=wh_iou(anchors(3,2), gwh(n,2))
t = t[j] # filter
"""
(Pdb) t.shape
torch.Size([3, 23, 7]) -> torch.Size([62, 7])
"""
# Offsets
gxy = t[:, 2:4] # grid xy
gxi = gain[[2, 3]] - gxy # inverse
j, k = ((gxy % 1 < g) & (gxy > 1)).T
"""
(Pdb) ((gxy % 1 < g) & (gxy > 1)).shape
torch.Size([186, 2])
(Pdb) ((gxy % 1 < g) & (gxy > 1)).T.shape
torch.Size([2, 186])
"""
l, m = ((gxi % 1 < g) & (gxi > 1)).T
j = torch.stack((torch.ones_like(j), j, k, l, m))
"""
torch.ones_like(j) 代表gt中心所在grid cell
j, k, l, m 代表擴(kuò)展的上下左右grid cell
torch.Size([5, 51])
"""
t = t.repeat((5, 1, 1))[j]
"""
標(biāo)簽也重復(fù)5次,和上面的擴(kuò)展gird cell一起篩選出所有的,符合條件的grid cell
(Pdb) pp t.shape
torch.Size([153, 7])
(Pdb) t.repeat((5, 1, 1)).shape
torch.Size([5, 153, 7])
(Pdb) pp t.shape
torch.Size([232, 7])
"""
offsets = (torch.zeros_like(gxy)[None] + off[:, None])[j]
"""
計算出所有g(shù)rid cell的偏移量,作用在標(biāo)簽上之后就能得到最終的grid cell
(Pdb) pp offsets.shape
torch.Size([529, 2])
"""
else:
t = targets[0]
offsets = 0
# Define
bc, gxy, gwh, a = t.chunk(4, 1) # (image, class), grid xy, grid wh, anchors
a, (b, c) = a.long().view(-1), bc.long().T # anchors, image, class
gij = (gxy - offsets).long()
"""
用gt中心點(diǎn)的坐標(biāo)減去偏移量,得到最終的grid cell的坐標(biāo)。其中中心點(diǎn)也在。
gxy 是在當(dāng)前feature map下的gt中心點(diǎn),如80*80下的 (55.09, 36.23),減去偏移量,再取整就能得到一個grid cell的坐標(biāo),如 (55,36)
Pdb) pp gij.shape
torch.Size([529, 2])
(Pdb) pp gij
tensor([[ 9, 22],
[ 2, 23],
[ 6, 23],
...,
[ 5, 19],
[ 5, 38],
[15, 36]], device='cuda:0')
"""
gi, gj = gij.T # grid indices
# Append
# indices 保存的內(nèi)容是:image_id, anchor_id(0,1,2), grid x刻度 grid y刻度。這里的刻度就是正樣本
indices.append((b, a, gj.clamp_(0, shape[2] - 1), gi.clamp_(0, shape[3] - 1))) # image, anchor, grid
# tbox保存的是gt中心相對于所在grid cell左上角偏移量。也會計算出gt中心相對擴(kuò)展anchor的偏移量
tbox.append(torch.cat((gxy - gij, gwh), 1)) # box
"""
(Pdb) pp tbox[0].shape
torch.Size([312, 4])
(Pdb) pp tbox[0]
tensor([[ 0.70904, 0.50893, 4.81701, 5.14418],
[ 0.28421, 0.45330, 3.58872, 4.42822],
[ 0.44398, 0.60475, 3.79576, 4.98174],
...,
[ 0.59653, -0.37711, 3.97289, 4.44963],
[ 0.32074, -0.05419, 5.19988, 5.59987],
[ 0.28691, -0.38742, 5.79986, 6.66651]], device='cuda:0')
(Pdb) gxy
tensor([[ 9.19086, 22.46842],
[ 2.50407, 23.72271],
[ 6.35452, 23.75447],
...,
[ 5.91273, 18.75906],
[ 5.16037, 37.97290],
[15.64346, 35.80629]], device='cuda:0')
(Pdb) gij
tensor([[ 9, 22],
[ 2, 23],
[ 6, 23],
...,
[ 5, 19],
[ 5, 38],
[15, 36]], device='cuda:0')
(Pdb) gxy.shape
torch.Size([529, 2])
(Pdb) gij.shape
torch.Size([529, 2])
"""
anch.append(anchors[a]) # anchors # 保存anchor的具體寬高
tcls.append(c) # class 保存類別id
"""
(Pdb) pp anch[0].shape
torch.Size([312, 2])
(Pdb) pp tcls[0].shape
torch.Size([312])
"""
return tcls, tbox, indices, anch
在進(jìn)入正樣本篩選之前,需要做一些準(zhǔn)備工作,主要是獲取必要的參數(shù)。
def build_targets(self, p, targets):
pass
輸入的參數(shù):
targets 是這一批圖片的標(biāo)注信息,每一行的內(nèi)容分別是:
image, class, x, y, w, h。
(Pdb) pp targets.shape
torch.Size([63, 6])
tensor([[0.00000, 1.00000, 0.22977, 0.56171, 0.08636, 0.09367],
[0.00000, 0.00000, 0.06260, 0.59307, 0.07843, 0.08812],
[0.00000, 0.00000, 0.15886, 0.59386, 0.06021, 0.06430],
[0.00000, 0.00000, 0.31930, 0.58910, 0.06576, 0.09129],
[0.00000, 0.00000, 0.80959, 0.70458, 0.23025, 0.26275],
[1.00000, 1.00000, 0.85008, 0.07597, 0.09781, 0.11827],
[1.00000, 0.00000, 0.22484, 0.09267, 0.14065, 0.18534]
p 模型預(yù)測數(shù)據(jù)。主要用于獲取每一層的尺度
(Pdb) pp p[0].shape
torch.Size([1, 3, 80, 80, 7])
(Pdb) pp p[1].shape
torch.Size([1, 3, 40, 40, 7])
(Pdb) pp p[2].shape
torch.Size([1, 3, 20, 20, 7])
獲取anchor的數(shù)量和標(biāo)注的數(shù)據(jù)的個數(shù)。設(shè)置一批讀入的數(shù)據(jù)為6張圖片,產(chǎn)生了66個標(biāo)注框。
na, nt = self.na, targets.shape[0] # number of anchors, targets
tcls, tbox, indices, anch = [], [], [], []
pp na
3
(Pdb) pp nt
66
(Pd
targets保存的標(biāo)注信息,首先將標(biāo)注信息復(fù)制成三份,同時給每一份標(biāo)注信息分配一個不同大小的anchor。 相當(dāng)于同一個標(biāo)注框就擁有三個不同的anchor 。
在targets張量最后增加一個數(shù)據(jù)用于保存anchor的index。后續(xù)的篩選都是以單個anchor為顆粒度。targets 每一行內(nèi)容: image, class, x, y, w, h,anchor_id
targets = torch.cat((targets.repeat(na, 1, 1), ai[..., None]), 2)
>>>
(Pdb) pp targets.shape
torch.Size([3, 63, 7])
定義長寬比的比例g=0.5和擴(kuò)展網(wǎng)格的選擇范圍off
g = 0.5 # bias
off = torch.tensor(
[
[0, 0],
[1, 0],
[0, 1],
[-1, 0],
[0, -1], # j,k,l,m
# [1, 1], [1, -1], [-1, 1], [-1, -1], # jk,jm,lk,lm
],
device=self.device).float() * g # offsets
遍歷三種尺度,在每一種尺度上獲取正樣本anchor和擴(kuò)展網(wǎng)格
首先將標(biāo)注框還原到當(dāng)前尺度上。從傳入的預(yù)測數(shù)據(jù)中獲取尺度,如80 * 80,那么就是將中心點(diǎn)和寬高還原到80*80的尺度上,還原之前的尺度都是0-1之間歸一化處理的,還原之后范圍就是在0-80。
anchors, shape = self.anchors[i], p[i].shape
"""
(Pdb) anchors
tensor([[1.25000, 1.62500],
[2.00000, 3.75000],
[4.12500, 2.87500]], device='cuda:0')
(Pdb) shape
torch.Size([1, 3, 80, 80, 7])
"""
gain[2:6] = torch.tensor(shape)[[3, 2, 3, 2]] # xyxy gain
"""
(Pdb) gain
tensor([ 1., 1., 80., 80., 80., 80., 1.], device='cuda:0')
"""
# Match targets to anchors
t = targets * gain # shape(3,n,7) # 將grid cell還原到當(dāng)前feature map上
targets此時一行數(shù)據(jù)分別是:image_id, clss_id, 當(dāng)前尺度下的x,當(dāng)前尺度下的y,當(dāng)前尺度下的寬,當(dāng)前尺度下的高,當(dāng)前尺度下的anchor_id。
(Pdb) pp t.shape
torch.Size([3, 63, 7])
(Pdb) pp t[0,0]
tensor([ 0.00000, 1.00000, 18.38171, 44.93684, 6.90862, 7.49398, 0.00000], device='cuda:0')
(Pdb) pp t
tensor([[[ 0.00000, 1.00000, 18.38171, ..., 6.90862, 7.49398, 0.00000],
[ 0.00000, 0.00000, 5.00814, ..., 6.27480, 7.04943, 0.00000],
[ 0.00000, 0.00000, 12.70904, ..., 4.81701, 5.14418, 0.00000],
...,
[ 5.00000, 0.00000, 10.32074, ..., 5.19988, 5.59987, 0.00000],
[ 5.00000, 0.00000, 31.28691, ..., 5.79986, 6.66651, 0.00000],
[ 5.00000, 0.00000, 51.81977, ..., 5.66653, 5.93320, 0.00000]],
[[ 0.00000, 1.00000, 18.38171, ..., 6.90862, 7.49398, 1.00000],
[ 0.00000, 0.00000, 5.00814, ..., 6.27480, 7.04943, 1.00000],
[ 0.00000, 0.00000, 12.70904, ..., 4.81701, 5.14418, 1.00000],
...,
[ 5.00000, 0.00000, 10.32074, ..., 5.19988, 5.59987, 1.00000],
[ 5.00000, 0.00000, 31.28691, ..., 5.79986, 6.66651, 1.00000],
[ 5.00000, 0.00000, 51.81977, ..., 5.66653, 5.93320, 1.00000]],
[[ 0.00000, 1.00000, 18.38171, ..., 6.90862, 7.49398, 2.00000],
[ 0.00000, 0.00000, 5.00814, ..., 6.27480, 7.04943, 2.00000],
[ 0.00000, 0.00000, 12.70904, ..., 4.81701, 5.14418, 2.00000],
...,
[ 5.00000, 0.00000, 10.32074, ..., 5.19988, 5.59987, 2.00000],
[ 5.00000, 0.00000, 31.28691, ..., 5.79986, 6.66651, 2.00000],
[ 5.00000, 0.00000, 51.81977, ..., 5.66653, 5.93320, 2.00000]]], device='cuda:0')
yolov5 正樣本選取規(guī)則
yolov5中正負(fù)樣本的計算規(guī)則是:比較標(biāo)注框和anchor的寬高,比例在0.25-4以內(nèi)就是正樣本。如下圖所示:
gt的原本面積為藍(lán)色,虛線標(biāo)注了0.25倍和4倍。只要anchor在0.25-4之間,就是匹配成功。
如果存在標(biāo)注框,則計算anchor和標(biāo)注框的寬高比
if nt:
# 獲取寬高比
r = t[..., 4:6] / anchors[:, None]
# 獲取 寬高比或?qū)捀弑鹊箶?shù) 中最大的一個,和4比較。self.hyp['anchor_t'] = 4
j = torch.max(r, 1 / r).max(2)[0] < self.hyp['anchor_t'] # compare
# 將正樣本過濾出來
t = t[j] # filter
此時t保存的就是所有符合條件的標(biāo)注框,后續(xù)用于計算anchor和網(wǎng)格信息。這一階段的結(jié)束之后,輸出的是所有符合條件的anchor。t保存的是 image, class, x, y, w, h,anchor_id, 同一個圖片會對應(yīng)多個標(biāo)注框,多個標(biāo)注框可能會對應(yīng)多個anchor。
跨anchor匹配
r計算的過程中包含了跨anchor匹配。在準(zhǔn)備工作中已經(jīng)介紹過將標(biāo)注框復(fù)制了三份,每一份都分配了一個anchor,相當(dāng)于一個標(biāo)注框擁有三種不同大小的anchor,F(xiàn)在計算寬高比獲得的結(jié)果只要符合條件的都會認(rèn)為是正樣本,3種anchor之間互不干擾,所以會出現(xiàn)一個標(biāo)注框匹配多個anchor。
(Pdb) pp t.shape
torch.Size([3, 63, 7])
(Pdb) pp t
tensor([[[ 0.00000, 1.00000, 18.38171, ..., 6.90862, 7.49398, 0.00000],
[ 0.00000, 0.00000, 5.00814, ..., 6.27480, 7.04943, 0.00000],
[ 0.00000, 0.00000, 12.70904, ..., 4.81701, 5.14418, 0.00000],
...,
[ 5.00000, 0.00000, 10.32074, ..., 5.19988, 5.59987, 0.00000],
[ 5.00000, 0.00000, 31.28691, ..., 5.79986, 6.66651, 0.00000],
[ 5.00000, 0.00000, 51.81977, ..., 5.66653, 5.93320, 0.00000]],
[[ 0.00000, 1.00000, 18.38171, ..., 6.90862, 7.49398, 1.00000],
[ 0.00000, 0.00000, 5.00814, ..., 6.27480, 7.04943, 1.00000],
[ 0.00000, 0.00000, 12.70904, ..., 4.81701, 5.14418, 1.00000],
...,
[ 5.00000, 0.00000, 10.32074, ..., 5.19988, 5.59987, 1.00000],
[ 5.00000, 0.00000, 31.28691, ..., 5.79986, 6.66651, 1.00000],
[ 5.00000, 0.00000, 51.81977, ..., 5.66653, 5.93320, 1.00000]],
[[ 0.00000, 1.00000, 18.38171, ..., 6.90862, 7.49398, 2.00000],
[ 0.00000, 0.00000, 5.00814, ..., 6.27480, 7.04943, 2.00000],
[ 0.00000, 0.00000, 12.70904, ..., 4.81701, 5.14418, 2.00000],
...,
[ 5.00000, 0.00000, 10.32074, ..., 5.19988, 5.59987, 2.00000],
[ 5.00000, 0.00000, 31.28691, ..., 5.79986, 6.66651, 2.00000],
[ 5.00000, 0.00000, 51.81977, ..., 5.66653, 5.93320, 2.00000]]], device='cuda:0')
(Pdb) pp t[0,0]
tensor([ 0.00000, 1.00000, 18.38171, 44.93684, 6.90862, 7.49398, 0.00000], device='cuda:0')
在yolov5中除了將gt中心點(diǎn)所在網(wǎng)格的anchor匹配為正樣本之外,還會將網(wǎng)格相鄰的上下左右四個網(wǎng)格中的對應(yīng)anchor作為正樣本。獲取擴(kuò)展網(wǎng)格的規(guī)則就是根據(jù)中心點(diǎn)距離上下左右哪個更近來確定擴(kuò)展的網(wǎng)格。如下圖中心點(diǎn)更靠近上和右,那么上和右網(wǎng)格中對應(yīng)的anchor就會成為正樣本。
獲取擴(kuò)展網(wǎng)格主要分為幾步走:
gxy = t[:, 2:4] # grid xy
gxi = gain[[2, 3]] - gxy # inverse
j, k = ((gxy % 1 < g) & (gxy > 1)).T
"""
(Pdb) ((gxy % 1 < g) & (gxy > 1)).shape
torch.Size([186, 2])
(Pdb) ((gxy % 1 < g) & (gxy > 1)).T.shape
torch.Size([2, 186])
"""
l, m = ((gxi % 1 < g) & (gxi > 1)).T
j = torch.stack((torch.ones_like(j), j, k, l, m))
"""
torch.ones_like(j) 代表gt中心所在grid cell
j, k, l, m 代表擴(kuò)展的上下左右grid cell
torch.Size([5, 51])
"""
t = t.repeat((5, 1, 1))[j]
"""
標(biāo)簽也重復(fù)5次,和上面的擴(kuò)展gird cell一起篩選出所有的,符合條件的grid cell
(Pdb) pp t.shape
torch.Size([153, 7])
(Pdb) t.repeat((5, 1, 1)).shape
torch.Size([5, 153, 7])
(Pdb) pp t.shape
torch.Size([232, 7])
"""
offsets = (torch.zeros_like(gxy)[None] + off[:, None])[j]
"""
計算出所有g(shù)rid cell的偏移量,作用在標(biāo)簽上之后就能得到最終的grid cell
(Pdb) pp offsets.shape
torch.Size([529, 2])
"""
gxy 是中心點(diǎn)的坐標(biāo),中心點(diǎn)坐標(biāo)是相對于整個80*80網(wǎng)格的左上角(0,0)的距離,而gxi是80減去中心點(diǎn)坐標(biāo),得到的結(jié)果相當(dāng)于是中心點(diǎn)距離(80,80)的距離。將中心點(diǎn)取余1之后相當(dāng)于縮放到一個網(wǎng)格中,如上圖所示。
gxy = t[:, 2:4] # grid xy
gxi = gain[[2, 3]] - gxy # inverse
j, k = ((gxy % 1 < g) & (gxy > 1)).T
模擬以上操作,j,k得到的是一組布爾值
>>> import torch
>>>
>>> arr = torch.tensor([[1,2,3], [4,5,6]])
>>> one = arr % 2 < 2
>>> two = arr > 3
>>> one
tensor([[True, True, True],
[True, True, True]])
>>> two
tensor([[False, False, False],
[ True, True, True]])
>>> one & two
tensor([[False, False, False],
[ True, True, True]])
距離的計算過程:
j, k = ((gxy % 1 < g) & (gxy > 1)).T
"""
(Pdb) ((gxy % 1 < g) & (gxy > 1)).shape
torch.Size([186, 2])
(Pdb) ((gxy % 1 < g) & (gxy > 1)).T.shape
torch.Size([2, 186])
"""
l, m = ((gxi % 1 < g) & (gxi > 1)).T
gxy % 1 < g 代表x或y離左上角距離小于0.5,小于0.5也就意味著靠的更近
gxy > 1 代表x或y必須大于1,x必須大于1也就是說第一行的網(wǎng)格不能向上擴(kuò)展;y必須大于1就是說第一列的網(wǎng)格不能向左擴(kuò)展。
同理gxi是相對下邊和右邊的距離,得到布爾張量。
l, m = ((gxi % 1 < g) & (gxi > 1)).T
獲取所有的正樣本網(wǎng)格結(jié)果
j = torch.stack((torch.ones_like(j), j, k, l, m))
t = t.repeat((5, 1, 1))[j]
j 保存上面擴(kuò)展網(wǎng)格和中心點(diǎn)網(wǎng)格的匹配結(jié)果,是bool數(shù)組。torch.ones_like(j) 表示中心點(diǎn)匹配到的網(wǎng)格,jklm中保存的上下左右匹配的網(wǎng)格。
t是將gt中心點(diǎn)的網(wǎng)格復(fù)制出來5份,用于計算所有網(wǎng)格。第一份是中心點(diǎn)匹配結(jié)果,剩余四份是上下左右網(wǎng)格匹配結(jié)果。
用j來篩選t,最終留下所有選中的網(wǎng)格。
計算出從中心點(diǎn)網(wǎng)格出發(fā)到擴(kuò)展網(wǎng)格的需要的偏移量。后續(xù)使用使用該偏移量即可獲取所有網(wǎng)格,包括中心點(diǎn)網(wǎng)格和擴(kuò)展網(wǎng)格。計算的過程中涉及到了廣播機(jī)制。
offsets = (torch.zeros_like(gxy)[None] + off[:, None])[j]
示例如下:
>>> off
tensor([[ 0, 0],
[ 1, 0],
[ 0, 1],
[-1, 0],
[ 0, -1]])
>>> arr = torch.tensor([10])
>>>
>>>
>>> arr + off
tensor([[10, 10],
[11, 10],
[10, 11],
[ 9, 10],
[10, 9]])
以下圖為例,可視化正樣本anchor。
經(jīng)過mosaic處理的圖片,藍(lán)色為標(biāo)注框
三種尺度下的正樣本網(wǎng)格
三種尺度下的正樣本anchor
三種尺度下原圖的正樣本網(wǎng)格
三種尺度下原圖的anchor
從t中獲取相關(guān)數(shù)據(jù),包括:
bc, gxy, gwh, a = t.chunk(4, 1) # (image, class), grid xy, grid wh, anchors
a, (b, c) = a.long().view(-1), bc.long().T # anchors, image, class
gij = (gxy - offsets).long()
獲取所有正樣本網(wǎng)格:
gij = (gxy - offsets).long()
gi, gj = gij.T # grid indices
gxy是gt中心點(diǎn)的坐標(biāo),減去對應(yīng)偏移量再取整, 得到所有正樣本所在網(wǎng)格。然后將xy拆分出來得到gi,gj。
(Pdb) pp gij
tensor([[74, 24],
[37, 28],
[72, 9],
[75, 11],
[67, 5],
[73, 5],
[70, 5],
[75, 1],
...)
indices: 保存圖片,anchor,網(wǎng)格等信息
# indices 保存的內(nèi)容是:image_id, anchor_id(0,1,2), grid x刻度 grid y刻度。這里的刻度就是正樣本
indices.append((b, a, gj.clamp_(0, shape[2] - 1), gi.clamp_(0, shape[3] - 1))) # image, anchor, grid
(Pdb) pp a.shape
torch.Size([367])
(Pdb) pp gij.shape
torch.Size([367, 2])
保存中心點(diǎn)偏移量
# tbox保存的是gt中心相對于所在grid cell左上角偏移量。也會計算出gt中心相對擴(kuò)展anchor的偏移量
tbox.append(torch.cat((gxy - gij, gwh), 1)) # box
gij是網(wǎng)格起始坐標(biāo),gxy是gt中心點(diǎn)坐標(biāo)。gxy-gij就是獲取gt中心點(diǎn)相對于網(wǎng)格左上角坐標(biāo)的偏移量。
在后續(xù)的損失函數(shù)計算中,用這個偏移量和網(wǎng)絡(luò)預(yù)測出來的偏移量計算損失函數(shù)。
保存anchor具體的寬高和類別id
anch.append(anchors[a]) # anchors # 保存anchor的具體寬高
tcls.append(c) # class 保存類別id
自此正樣本篩選的流程就結(jié)束了,最終返回了4個張量:
返回的正樣本anchor會在后續(xù)損失函數(shù)的計算中使用。用 indices 保存的網(wǎng)格篩選出模型輸出的中對應(yīng)的網(wǎng)格里的內(nèi)容,用 tbox中中心點(diǎn)相對網(wǎng)格的偏移 和 模型輸出的預(yù)測中心點(diǎn)相對于網(wǎng)格左上角偏移量 計算偏差,并不斷修正。
一、正樣本指的是anchor,anchor匹配如何體現(xiàn)在過程?
targets 是這一批圖片的標(biāo)注信息,每一行的內(nèi)容分別是:
image, class, x, y, w, h。
(Pdb) pp targets.shape
torch.Size([63, 6])
tensor([[0.00000, 1.00000, 0.22977, 0.56171, 0.08636, 0.09367],
[0.00000, 0.00000, 0.06260, 0.59307, 0.07843, 0.08812],
[0.00000, 0.00000, 0.15886, 0.59386, 0.06021, 0.06430],
[0.00000, 0.00000, 0.31930, 0.58910, 0.06576, 0.09129],
[0.00000, 0.00000, 0.80959, 0.70458, 0.23025, 0.26275],
[1.00000, 1.00000, 0.85008, 0.07597, 0.09781, 0.11827],
[1.00000, 0.00000, 0.22484, 0.09267, 0.14065, 0.18534]
targets = torch.cat((targets.repeat(na, 1, 1), ai[..., None]), 2)
>>>
(Pdb) pp targets.shape
torch.Size([3, 63, 7])
targets保存的標(biāo)注信息,首先將標(biāo)注信息復(fù)制成三份,因為每一個尺度每一個網(wǎng)格上有三個anchor,
相當(dāng)于給一份標(biāo)注框分配了一個anchor
。
在后續(xù)的操作中,先通過先將標(biāo)注框還原到對應(yīng)的尺度上,通過寬高比篩選anchor,獲得符合正樣本的anchor。到這里就獲得所有正樣本的anchor。
然后再通過中心點(diǎn)的坐標(biāo)獲得擴(kuò)展網(wǎng)格。
j = torch.stack((torch.ones_like(j), j, k, l, m))
t = t.repeat((5, 1, 1))[j]
此時將t復(fù)制5份,每一份的每一行內(nèi)容代表:
image, class, x, y, w, h,anchor_id。
復(fù)制的過程中就攜帶了anchor_id的信息,最終通過擴(kuò)展獲取上下左右兩個網(wǎng)格,相當(dāng)于獲得了兩個網(wǎng)格中的anchor。
最后將所有的anchor保存起來,在計算損失函數(shù)時使用到anchor的兩個功能:
二、 跨anchor匹配體現(xiàn)在哪里?
targets保存的標(biāo)注信息,首先將標(biāo)注信息復(fù)制成三份,因為每一個尺度每一個網(wǎng)格上有三個anchor, 相當(dāng)于給一份標(biāo)注框分配了一個anchor 。
r = t[..., 4:6] / anchors[:, None]
# 獲取 寬高比或?qū)捀弑鹊箶?shù) 中最大的一個,和0.5比較
j = torch.max(r, 1 / r).max(2)[0] < self.hyp['anchor_t'] # compare
# 將正樣本過濾出來
t = t[j] # filter
r計算的過程中包含了跨anchor匹配。t是將原有的標(biāo)注信息復(fù)制了三份,而每一個網(wǎng)格也有三個anchor,也就是說一份標(biāo)注信息對應(yīng)一個anchor,F(xiàn)在計算寬高比獲得的結(jié)果只要符合條件的都會認(rèn)為是正樣本,3種anchor之間互不干擾。
那么有可能存在的情況是三種anchor和gt的寬高比都符合條件,那么這3個標(biāo)注數(shù)據(jù)都會保存下來,相應(yīng)的anchor都會成為正樣本。
三、 跨網(wǎng)格匹配體現(xiàn)在哪里?
所謂跨網(wǎng)格匹配就是除了gt中心點(diǎn)所在網(wǎng)格,還會選擇擴(kuò)展網(wǎng)格。
擴(kuò)展網(wǎng)格的篩選過程就是跨網(wǎng)格匹配的過程
gxy = t[:, 2:4] # grid xy
gxi = gain[[2, 3]] - gxy # inverse
j, k = ((gxy % 1 < g) & (gxy > 1)).T
l, m = ((gxi % 1 < g) & (gxi > 1)).T
j = torch.stack((torch.ones_like(j), j, k, l, m))
t = t.repeat((5, 1, 1))[j]
四、 跨尺度匹配體現(xiàn)在哪里?
一個標(biāo)注框可以在不同的預(yù)測分支上匹配上anchor。anchor的匹配在不同的尺度上分開單獨(dú)處理,三個尺度互相不干擾,所以一個標(biāo)注框最多能在三個尺度上都匹配上anchor。
for i in range(self.nl):
anchors, shape = self.anchors[i], p[i].shape
...
indices.append((b, a, gj.clamp_(0, shape[2] - 1), gi.clamp_(0, shape[3] - 1))) # image, anchor, grid
# tbox保存的是gt中心相對于所在grid cell左上角偏移量。也會計算出gt中心相對擴(kuò)展anchor的偏移量
tbox.append(torch.cat((gxy - gij, gwh), 1)) # box
可以看到以下三個不同尺度的anchor匹配中,右上角目標(biāo)都匹配上了。
五、擴(kuò)展的網(wǎng)格中用哪一個anchor?
通過寬高比篩選出來的正樣本才會被復(fù)制,也就是說一個網(wǎng)格中的anchor匹配上gt之后,然后才有可能被擴(kuò)展網(wǎng)格選中。
在擴(kuò)展網(wǎng)格之前,就已經(jīng)篩選出正樣本,有一個確定大小的anchor。擴(kuò)展網(wǎng)格的獲得過程是將正樣本復(fù)制5份。復(fù)制的過程就將中心點(diǎn)匹配的anchor_id攜帶過去。
j = torch.stack((torch.ones_like(j), j, k, l, m))
t = t.repeat((5, 1, 1))[j]
復(fù)制的是正樣本,那么擴(kuò)展網(wǎng)格最終獲得的也是中心點(diǎn)所在網(wǎng)格上匹配好的anchor
一個網(wǎng)格中有兩個anchor成為正樣本,那么擴(kuò)展網(wǎng)格中就有兩個anchor為正樣本。擴(kuò)展網(wǎng)格的anchor_id 和中心點(diǎn)網(wǎng)格保持一致。
六、擴(kuò)展網(wǎng)格中g(shù)t的偏移量如何計算?
計算gt中心點(diǎn)相對于網(wǎng)格左上角的偏移量中有幾個變量:
gij = (gxy - offsets).long()
gij 是通過中心點(diǎn)減去偏移量再取整獲得的
# tbox保存的是gt中心相對于所在grid cell左上角偏移量。也會計算出gt中心相對擴(kuò)展anchor的偏移量
tbox.append(torch.cat((gxy - gij, gwh), 1)) # box
gxy - gij 的計算過程中,對于那些擴(kuò)展的網(wǎng)格,也會同樣計算偏移量。所以擴(kuò)展網(wǎng)格的偏移量就是網(wǎng)格的左上角到gt中心點(diǎn)的距離。
機(jī)器學(xué)習(xí):神經(jīng)網(wǎng)絡(luò)構(gòu)建(下)
閱讀華為Mate品牌盛典:HarmonyOS NEXT加持下游戲性能得到充分釋放
閱讀實現(xiàn)對象集合與DataTable的相互轉(zhuǎn)換
閱讀鴻蒙NEXT元服務(wù):論如何免費(fèi)快速上架作品
閱讀算法與數(shù)據(jù)結(jié)構(gòu) 1 - 模擬
閱讀5. Spring Cloud OpenFeign 聲明式 WebService 客戶端的超詳細(xì)使用
閱讀Java代理模式:靜態(tài)代理和動態(tài)代理的對比分析
閱讀Win11筆記本“自動管理應(yīng)用的顏色”顯示規(guī)則
閱讀本站所有軟件,都由網(wǎng)友上傳,如有侵犯你的版權(quán),請發(fā)郵件[email protected]
湘ICP備2022002427號-10 湘公網(wǎng)安備:43070202000427號© 2013~2025 haote.com 好特網(wǎng)