版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進(jìn)行舉報(bào)或認(rèn)領(lǐng)
文檔簡介
第python神經(jīng)網(wǎng)絡(luò)Keras搭建RFBnet目標(biāo)檢測平臺目錄什么是RFBnet目標(biāo)檢測算法RFBnet實(shí)現(xiàn)思路一、預(yù)測部分1、主干網(wǎng)絡(luò)介紹2、從特征獲取預(yù)測結(jié)果3、預(yù)測結(jié)果的解碼4、在原圖上進(jìn)行繪制二、訓(xùn)練部分1、真實(shí)框的處理2、利用處理完的真實(shí)框與對應(yīng)圖片的預(yù)測結(jié)果計(jì)算loss訓(xùn)練自己的RFB模型一、數(shù)據(jù)集的準(zhǔn)備二、數(shù)據(jù)集的處理三、開始網(wǎng)絡(luò)訓(xùn)練四、訓(xùn)練結(jié)果預(yù)測
什么是RFBnet目標(biāo)檢測算法
RFBnet是SSD的一種加強(qiáng)版,主要是利用了膨脹卷積這一方法增大了感受野,相比于普通的ssd,RFBnet也是一種加強(qiáng)吧
RFBnet是改進(jìn)版的SSD,其整體的結(jié)構(gòu)與SSD相差不大,其主要特點(diǎn)是在SSD的特征提取網(wǎng)絡(luò)上用了RFB模塊。
RFB的全稱ReceptiveFieldBlock,是一種輕量級的、而且集成了各類檢測算法優(yōu)點(diǎn)的模塊,結(jié)合了Inception、蟲洞卷積的思想,以提高感受野的方式提高網(wǎng)絡(luò)的特征提取能力。
源碼下載
RFBnet實(shí)現(xiàn)思路
一、預(yù)測部分
1、主干網(wǎng)絡(luò)介紹
RFBnet采用的主干網(wǎng)絡(luò)是VGG網(wǎng)絡(luò),關(guān)于VGG的介紹大家可以看我的另外一篇博客/article/246917.htm,這里的VGG網(wǎng)絡(luò)相比普通的VGG網(wǎng)絡(luò)有一定的修改,主要修改的地方就是:
1、將VGG16的FC6和FC7層轉(zhuǎn)化為卷積層。
2、增加了RFB模塊。
主要使用到的RFB模塊有兩種,一種是BasicRFB,另一種是BasicRFB_a。
二者使用的思想相同,構(gòu)造有些許不同。
BasicRFB的結(jié)構(gòu)如下:
BasicRFB_a和BasicRFB類似,并聯(lián)結(jié)構(gòu)增加,有8個并聯(lián)。
實(shí)現(xiàn)代碼:
fromkeras.layersimport(Activation,BatchNormalization,Conv2D,Lambda,
MaxPooling2D,UpSampling2D,concatenate)
defconv2d_bn(x,filters,num_row,num_col,padding='same',stride=1,dilation_rate=1,relu=True):
x=Conv2D(
filters,(num_row,num_col),
strides=(stride,stride),
padding=padding,
dilation_rate=(dilation_rate,dilation_rate),
use_bias=False)(x)
x=BatchNormalization()(x)
ifrelu:
x=Activation("relu")(x)
returnx
defBasicRFB(x,input_filters,output_filters,stride=1,map_reduce=8):
#-------------------------------------------------------#
#BasicRFB模塊是一個殘差結(jié)構(gòu)
#主干部分使用不同膨脹率的卷積進(jìn)行特征提取
#殘差邊只包含一個調(diào)整寬高和通道的1x1卷積
#-------------------------------------------------------#
input_filters_div=input_filters//map_reduce
branch_0=conv2d_bn(x,input_filters_div*2,1,1,stride=stride)
branch_0=conv2d_bn(branch_0,input_filters_div*2,3,3,relu=False)
branch_1=conv2d_bn(x,input_filters_div,1,1)
branch_1=conv2d_bn(branch_1,input_filters_div*2,3,3,stride=stride)
branch_1=conv2d_bn(branch_1,input_filters_div*2,3,3,dilation_rate=3,relu=False)
branch_2=conv2d_bn(x,input_filters_div,1,1)
branch_2=conv2d_bn(branch_2,(input_filters_div//2)*3,3,3)
branch_2=conv2d_bn(branch_2,input_filters_div*2,3,3,stride=stride)
branch_2=conv2d_bn(branch_2,input_filters_div*2,3,3,dilation_rate=5,relu=False)
branch_3=conv2d_bn(x,input_filters_div,1,1)
branch_3=conv2d_bn(branch_3,(input_filters_div//2)*3,1,7)
branch_3=conv2d_bn(branch_3,input_filters_div*2,7,1,stride=stride)
branch_3=conv2d_bn(branch_3,input_filters_div*2,3,3,dilation_rate=7,relu=False)
#-------------------------------------------------------#
#將不同膨脹率的卷積結(jié)果進(jìn)行堆疊
#利用1x1卷積調(diào)整通道數(shù)
#-------------------------------------------------------#
out=concatenate([branch_0,branch_1,branch_2,branch_3],axis=-1)
out=conv2d_bn(out,output_filters,1,1,relu=False)
#-------------------------------------------------------#
#殘差邊也需要卷積,才可以相加
#-------------------------------------------------------#
short=conv2d_bn(x,output_filters,1,1,stride=stride,relu=False)
out=Lambda(lambdax:x[0]+x[1])([out,short])
out=Activation("relu")(out)
returnout
defBasicRFB_a(x,input_filters,output_filters,stride=1,map_reduce=8):
#-------------------------------------------------------#
#BasicRFB_a模塊也是一個殘差結(jié)構(gòu)
#主干部分使用不同膨脹率的卷積進(jìn)行特征提取
#殘差邊只包含一個調(diào)整寬高和通道的1x1卷積
#-------------------------------------------------------#
input_filters_div=input_filters//map_reduce
branch_0=conv2d_bn(x,input_filters_div,1,1,stride=stride)
branch_0=conv2d_bn(branch_0,input_filters_div,3,3,relu=False)
branch_1=conv2d_bn(x,input_filters_div,1,1)
branch_1=conv2d_bn(branch_1,input_filters_div,3,1,stride=stride)
branch_1=conv2d_bn(branch_1,input_filters_div,3,3,dilation_rate=3,relu=False)
branch_2=conv2d_bn(x,input_filters_div,1,1)
branch_2=conv2d_bn(branch_2,input_filters_div,1,3,stride=stride)
branch_2=conv2d_bn(branch_2,input_filters_div,3,3,dilation_rate=3,relu=False)
branch_3=conv2d_bn(x,input_filters_div,1,1)
branch_3=conv2d_bn(branch_3,input_filters_div,3,1,stride=stride)
branch_3=conv2d_bn(branch_3,input_filters_div,3,3,dilation_rate=5,relu=False)
branch_4=conv2d_bn(x,input_filters_div,1,1)
branch_4=conv2d_bn(branch_4,input_filters_div,1,3,stride=stride)
branch_4=conv2d_bn(branch_4,input_filters_div,3,3,dilation_rate=5,relu=False)
branch_5=conv2d_bn(x,input_filters_div//2,1,1)
branch_5=conv2d_bn(branch_5,(input_filters_div//4)*3,1,3)
branch_5=conv2d_bn(branch_5,input_filters_div,3,1,stride=stride)
branch_5=conv2d_bn(branch_5,input_filters_div,3,3,dilation_rate=7,relu=False)
branch_6=conv2d_bn(x,input_filters_div//2,1,1)
branch_6=conv2d_bn(branch_6,(input_filters_div//4)*3,3,1)
branch_6=conv2d_bn(branch_6,input_filters_div,1,3,stride=stride)
branch_6=conv2d_bn(branch_6,input_filters_div,3,3,dilation_rate=7,relu=False)
#-------------------------------------------------------#
#將不同膨脹率的卷積結(jié)果進(jìn)行堆疊
#利用1x1卷積調(diào)整通道數(shù)
#-------------------------------------------------------#
out=concatenate([branch_0,branch_1,branch_2,branch_3,branch_4,branch_5,branch_6],axis=-1)
out=conv2d_bn(out,output_filters,1,1,relu=False)
#-------------------------------------------------------#
#殘差邊也需要卷積,才可以相加
#-------------------------------------------------------#
short=conv2d_bn(x,output_filters,1,1,stride=stride,relu=False)
out=Lambda(lambdax:x[0]+x[1])([out,short])
out=Activation("relu")(out)
returnout
#--------------------------------#
#取Conv4_3和fc7進(jìn)行特征融合
#--------------------------------#
defNormalize(net):
#38,38,512-38,38,256
branch_0=conv2d_bn(net["conv4_3"],256,1,1)
#19,19,512-38,38,256
branch_1=conv2d_bn(net['fc7'],256,1,1)
branch_1=UpSampling2D()(branch_1)
#38,38,256+38,38,256-38,38,512
out=concatenate([branch_0,branch_1],axis=-1)
#38,38,512-38,38,512
out=BasicRFB_a(out,512,512)
returnout
defbackbone(input_tensor):
#----------------------------主干特征提取網(wǎng)絡(luò)開始---------------------------#
#RFB結(jié)構(gòu),net字典
net={}
#Block1
net['input']=input_tensor
#300,300,3-150,150,64
net['conv1_1']=Conv2D(64,kernel_size=(3,3),
activation='relu',
padding='same',
name='conv1_1')(net['input'])
net['conv1_2']=Conv2D(64,kernel_size=(3,3),
activation='relu',
padding='same',
name='conv1_2')(net['conv1_1'])
net['pool1']=MaxPooling2D((2,2),strides=(2,2),padding='same',
name='pool1')(net['conv1_2'])
#Block2
#150,150,64-75,75,128
net['conv2_1']=Conv2D(128,kernel_size=(3,3),
activation='relu',
padding='same',
name='conv2_1')(net['pool1'])
net['conv2_2']=Conv2D(128,kernel_size=(3,3),
activation='relu',
padding='same',
name='conv2_2')(net['conv2_1'])
net['pool2']=MaxPooling2D((2,2),strides=(2,2),padding='same',
name='pool2')(net['conv2_2'])
#Block3
#75,75,128-38,38,256
net['conv3_1']=Conv2D(256,kernel_size=(3,3),
activation='relu',
padding='same',
name='conv3_1')(net['pool2'])
net['conv3_2']=Conv2D(256,kernel_size=(3,3),
activation='relu',
padding='same',
name='conv3_2')(net['conv3_1'])
net['conv3_3']=Conv2D(256,kernel_size=(3,3),
activation='relu',
padding='same',
name='conv3_3')(net['conv3_2'])
net['pool3']=MaxPooling2D((2,2),strides=(2,2),padding='same',
name='pool3')(net['conv3_3'])
#Block4
#38,38,256-19,19,512
net['conv4_1']=Conv2D(512,kernel_size=(3,3),
activation='relu',
padding='same',
name='conv4_1')(net['pool3'])
net['conv4_2']=Conv2D(512,kernel_size=(3,3),
activation='relu',
padding='same',
name='conv4_2')(net['conv4_1'])
net['conv4_3']=Conv2D(512,kernel_size=(3,3),
activation='relu',
padding='same',
name='conv4_3')(net['conv4_2'])
net['pool4']=MaxPooling2D((2,2),strides=(2,2),padding='same',
name='pool4')(net['conv4_3'])
#Block5
#19,19,512-19,19,512
net['conv5_1']=Conv2D(512,kernel_size=(3,3),
activation='relu',
padding='same',
name='conv5_1')(net['pool4'])
net['conv5_2']=Conv2D(512,kernel_size=(3,3),
activation='relu',
padding='same',
name='conv5_2')(net['conv5_1'])
net['conv5_3']=Conv2D(512,kernel_size=(3,3),
activation='relu',
padding='same',
name='conv5_3')(net['conv5_2'])
net['pool5']=MaxPooling2D((3,3),strides=(1,1),padding='same',
name='pool5')(net['conv5_3'])
#FC6
#19,19,512-19,19,1024
net['fc6']=Conv2D(1024,kernel_size=(3,3),dilation_rate=(6,6),
activation='relu',padding='same',
name='fc6')(net['pool5'])
#x=Dropout(0.5,name='drop6')(x)
#FC7
#19,19,1024-19,19,1024
net['fc7']=Conv2D(1024,kernel_size=(1,1),activation='relu',
padding='same',name='fc7')(net['fc6'])
#----------------------------------------------------------#
#conv4_338,38,512-38,38,512net['norm']
#fc719,19,1024-
#----------------------------------------------------------#
net['norm']=Normalize(net)
#19,19,1024-19,19,1024
net['rfb_1']=BasicRFB(net['fc7'],1024,1024)
#19,19,1024-10,10,512
net['rfb_2']=BasicRFB(net['rfb_1'],1024,512,stride=2)
#10,10,512-5,5,256
net['rfb_3']=BasicRFB(net['rfb_2'],512,256,stride=2)
#5,5,256-5,5,128
net['conv6_1']=conv2d_bn(net['rfb_3'],128,1,1)
#5,5,128-3,3,256
net['conv6_2']=conv2d_bn(net['conv6_1'],256,3,3,padding="valid")
#3,3,256-3,3,128
net['conv7_1']=conv2d_bn(net['conv6_2'],128,1,1)
#3,3,128-1,1,256
net['conv7_2']=conv2d_bn(net['conv7_1'],256,3,3,padding="valid")
returnnet
2、從特征獲取預(yù)測結(jié)果
由上圖我們可以知道,我們?nèi)onv4的第三次卷積的特征、fc7的特征進(jìn)行組合后經(jīng)過一個BasicRFB_a獲得P3作為有效特征層、還有上圖的P4、P5、P6、P7、P8作為有效特征層,為了和普通特征層區(qū)分,我們稱之為有效特征層,來獲取預(yù)測結(jié)果。
對獲取到的每一個有效特征層,我們分別對其進(jìn)行一次num_anchorsx4的卷積、一次num_anchorsxnum_classes的卷積。而num_anchors指的是該特征層所擁有的先驗(yàn)框數(shù)量。
其中:num_anchorsx4的卷積用于預(yù)測該特征層上每一個網(wǎng)格點(diǎn)上每一個先驗(yàn)框的變化情況。(為什么說是變化情況呢,這是因?yàn)閟sd的預(yù)測結(jié)果需要結(jié)合先驗(yàn)框獲得預(yù)測框,預(yù)測結(jié)果就是先驗(yàn)框的變化情況。)
num_anchorsxnum_classes的卷積用于預(yù)測該特征層上每一個網(wǎng)格點(diǎn)上每一個預(yù)測框?qū)?yīng)的種類。
每一個有效特征層對應(yīng)的先驗(yàn)框?qū)?yīng)著該特征層上每一個網(wǎng)格點(diǎn)上預(yù)先設(shè)定好的三個框。
所有的特征層對應(yīng)的預(yù)測結(jié)果的shape如下:
實(shí)現(xiàn)代碼為:
fromkeras.layersimport(Activation,Concatenate,Conv2D,Flatten,Input,
Reshape)
fromkeras.modelsimportModel
fromnets.backboneimportbackbone
defRFB300(input_shape,num_classes=21):
#---------------------------------#
#典型的輸入大小為[300,300,3]
#---------------------------------#
input_tensor=Input(shape=input_shape)
#net變量里面包含了整個RFB的結(jié)構(gòu),通過層名可以找到對應(yīng)的特征層
net=backbone(input_tensor)
#-----------------------將提取到的主干特征進(jìn)行處理---------------------------#
#對conv4_3的通道進(jìn)行l(wèi)2標(biāo)準(zhǔn)化處理
#38,38,512
num_anchors=6
#預(yù)測框的處理
#num_anchors表示每個網(wǎng)格點(diǎn)先驗(yàn)框的數(shù)量,4是x,y,h,w的調(diào)整
net['norm_mbox_loc']=Conv2D(num_anchors*4,kernel_size=(3,3),padding='same',name='norm_mbox_loc')(net['norm'])
net['norm_mbox_loc_flat']=Flatten(name='norm_mbox_loc_flat')(net['norm_mbox_loc'])
#num_anchors表示每個網(wǎng)格點(diǎn)先驗(yàn)框的數(shù)量,num_classes是所分的類
net['norm_mbox_conf']=Conv2D(num_anchors*num_classes,kernel_size=(3,3),padding='same',name='norm_mbox_conf')(net['norm'])
net['norm_mbox_conf_flat']=Flatten(name='norm_mbox_conf_flat')(net['norm_mbox_conf'])
#對rfb_1層進(jìn)行處理
#19,19,1024
num_anchors=6
#預(yù)測框的處理
#num_anchors表示每個網(wǎng)格點(diǎn)先驗(yàn)框的數(shù)量,4是x,y,h,w的調(diào)整
net['rfb_1_mbox_loc']=Conv2D(num_anchors*4,kernel_size=(3,3),padding='same',name='rfb_1_mbox_loc')(net['rfb_1'])
net['rfb_1_mbox_loc_flat']=Flatten(name='rfb_1_mbox_loc_flat')(net['rfb_1_mbox_loc'])
#num_anchors表示每個網(wǎng)格點(diǎn)先驗(yàn)框的數(shù)量,num_classes是所分的類
net['rfb_1_mbox_conf']=Conv2D(num_anchors*num_classes,kernel_size=(3,3),padding='same',name='rfb_1_mbox_conf')(net['rfb_1'])
net['rfb_1_mbox_conf_flat']=Flatten(name='rfb_1_mbox_conf_flat')(net['rfb_1_mbox_conf'])
#對rfb_2進(jìn)行處理
#10,10,512
num_anchors=6
#預(yù)測框的處理
#num_anchors表示每個網(wǎng)格點(diǎn)先驗(yàn)框的數(shù)量,4是x,y,h,w的調(diào)整
net['rfb_2_mbox_loc']=Conv2D(num_anchors*4,kernel_size=(3,3),padding='same',name='rfb_2_mbox_loc')(net['rfb_2'])
net['rfb_2_mbox_loc_flat']=Flatten(name='rfb_2_mbox_loc_flat')(net['rfb_2_mbox_loc'])
#num_anchors表示每個網(wǎng)格點(diǎn)先驗(yàn)框的數(shù)量,num_classes是所分的類
net['rfb_2_mbox_conf']=Conv2D(num_anchors*num_classes,kernel_size=(3,3),padding='same',name='rfb_2_mbox_conf')(net['rfb_2'])
net['rfb_2_mbox_conf_flat']=Flatten(name='rfb_2_mbox_conf_flat')(net['rfb_2_mbox_conf'])
#對rfb_3進(jìn)行處理
#5,5,256
num_anchors=6
#預(yù)測框的處理
#num_anchors表示每個網(wǎng)格點(diǎn)先驗(yàn)框的數(shù)量,4是x,y,h,w的調(diào)整
net['rfb_3_mbox_loc']=Conv2D(num_anchors*4,kernel_size=(3,3),padding='same',name='rfb_3_mbox_loc')(net['rfb_3'])
net['rfb_3_mbox_loc_flat']=Flatten(name='rfb_3_mbox_loc_flat')(net['rfb_3_mbox_loc'])
#num_anchors表示每個網(wǎng)格點(diǎn)先驗(yàn)框的數(shù)量,num_classes是所分的類
net['rfb_3_mbox_conf']=Conv2D(num_anchors*num_classes,kernel_size=(3,3),padding='same',name='rfb_3_mbox_conf')(net['rfb_3'])
net['rfb_3_mbox_conf_flat']=Flatten(name='rfb_3_mbox_conf_flat')(net['rfb_3_mbox_conf'])
#對conv6_2進(jìn)行處理
#3,3,256
num_anchors=4
#預(yù)測框的處理
#num_anchors表示每個網(wǎng)格點(diǎn)先驗(yàn)框的數(shù)量,4是x,y,h,w的調(diào)整
net['conv6_2_mbox_loc']=Conv2D(num_anchors*4,kernel_size=(3,3),padding='same',name='conv6_2_mbox_loc')(net['conv6_2'])
net['conv6_2_mbox_loc_flat']=Flatten(name='conv6_2_mbox_loc_flat')(net['conv6_2_mbox_loc'])
#num_anchors表示每個網(wǎng)格點(diǎn)先驗(yàn)框的數(shù)量,num_classes是所分的類
net['conv6_2_mbox_conf']=Conv2D(num_anchors*num_classes,kernel_size=(3,3),padding='same',name='conv6_2_mbox_conf')(net['conv6_2'])
net['conv6_2_mbox_conf_flat']=Flatten(name='conv6_2_mbox_conf_flat')(net['conv6_2_mbox_conf'])
#對conv7_2進(jìn)行處理
#1,1,256
num_anchors=4
#預(yù)測框的處理
#num_anchors表示每個網(wǎng)格點(diǎn)先驗(yàn)框的數(shù)量,4是x,y,h,w的調(diào)整
net['conv7_2_mbox_loc']=Conv2D(num_anchors*4,kernel_size=(3,3),padding='same',name='conv7_2_mbox_loc')(net['conv7_2'])
net['conv7_2_mbox_loc_flat']=Flatten(name='conv7_2_mbox_loc_flat')(net['conv7_2_mbox_loc'])
#num_anchors表示每個網(wǎng)格點(diǎn)先驗(yàn)框的數(shù)量,num_classes是所分的類
net['conv7_2_mbox_conf']=Conv2D(num_anchors*num_classes,kernel_size=(3,3),padding='same',name='conv7_2_mbox_conf')(net['conv7_2'])
net['conv7_2_mbox_conf_flat']=Flatten(name='conv7_2_mbox_conf_flat')(net['conv7_2_mbox_conf'])
#將所有結(jié)果進(jìn)行堆疊
net['mbox_loc']=Concatenate(axis=1,name='mbox_loc')([net['norm_mbox_loc_flat'],
net['rfb_1_mbox_loc_flat'],
net['rfb_2_mbox_loc_flat'],
net['rfb_3_mbox_loc_flat'],
net['conv6_2_mbox_loc_flat'],
net['conv7_2_mbox_loc_flat']])
net['mbox_conf']=Concatenate(axis=1,name='mbox_conf')([net['norm_mbox_conf_flat'],
net['rfb_1_mbox_conf_flat'],
net['rfb_2_mbox_conf_flat'],
net['rfb_3_mbox_conf_flat'],
net['conv6_2_mbox_conf_flat'],
net['conv7_2_mbox_conf_flat']])
#11620,4
net['mbox_loc']=Reshape((-1,4),name='mbox_loc_final')(net['mbox_loc'])
#11620,21
net['mbox_conf']=Reshape((-1,num_classes),name='mbox_conf_logits')(net['mbox_conf'])
net['mbox_conf']=Activation('softmax',name='mbox_conf_final')(net['mbox_conf'])
#11620,25
net['predictions']=Concatenate(axis=-1,name='predictions')([net['mbox_loc'],net['mbox_conf']])
model=Model(net['input'],net['predictions'])
returnmodel
3、預(yù)測結(jié)果的解碼
我們通過對每一個特征層的處理,可以獲得三個內(nèi)容,分別是:
num_anchorsx4的卷積用于預(yù)測該特征層上每一個網(wǎng)格點(diǎn)上每一個先驗(yàn)框的變化情況。**
num_anchorsxnum_classes的卷積用于預(yù)測該特征層上每一個網(wǎng)格點(diǎn)上每一個預(yù)測框?qū)?yīng)的種類。
每一個有效特征層對應(yīng)的先驗(yàn)框?qū)?yīng)著該特征層上每一個網(wǎng)格點(diǎn)上預(yù)先設(shè)定好的多個框。
我們利用num_anchorsx4的卷積與每一個有效特征層對應(yīng)的先驗(yàn)框獲得框的真實(shí)位置。
每一個有效特征層對應(yīng)的先驗(yàn)框就是,如圖所示的作用:
每一個有效特征層將整個圖片分成與其長寬對應(yīng)的網(wǎng)格,如conv4-3和fl7組合成的特征層就是將整個圖像分成38x38個網(wǎng)格;然后從每個網(wǎng)格中心建立多個先驗(yàn)框,如conv4-3和fl7組合成的有效特征層就是建立了6個先驗(yàn)框;
對于conv4-3和fl7組合成的特征層來講,整個圖片被分成38x38個網(wǎng)格,每個網(wǎng)格中心對應(yīng)6個先驗(yàn)框,一共包含了,38x38x6個,8664個先驗(yàn)框。
先驗(yàn)框雖然可以代表一定的框的位置信息與框的大小信息,但是其是有限的,無法表示任意情況,因此還需要調(diào)整,RFBnet利用num_anchorsx4的卷積的結(jié)果對先驗(yàn)框進(jìn)行調(diào)整。
num_anchorsx4中的num_anchors表示了這個網(wǎng)格點(diǎn)所包含的先驗(yàn)框數(shù)量,其中的4表示了x_offset、y_offset、h和w的調(diào)整情況。
x_offset與y_offset代表了真實(shí)框距離先驗(yàn)框中心的xy軸偏移情況。h和w代表了真實(shí)框的寬與高相對于先驗(yàn)框的變化情況。
RFBnet解碼過程就是將每個網(wǎng)格的中心點(diǎn)加上它對應(yīng)的x_offset和y_offset,加完后的結(jié)果就是預(yù)測框的中心,然后再利用先驗(yàn)框和h、w結(jié)合計(jì)算出預(yù)測框的長和寬。這樣就能得到整個預(yù)測框的位置了。
當(dāng)然得到最終的預(yù)測結(jié)構(gòu)后還要進(jìn)行得分排序與非極大抑制篩選這一部分基本上是所有目標(biāo)檢測通用的部分。
1、取出每一類得分大于self.obj_threshold的框和得分。
2、利用框的位置和得分進(jìn)行非極大抑制。
實(shí)現(xiàn)代碼如下:
defdecode_boxes(self,mbox_loc,anchors,variances):
#獲得先驗(yàn)框的寬與高
anchor_width=anchors[:,2]-anchors[:,0]
anchor_height=anchors[:,3]-anchors[:,1]
#獲得先驗(yàn)框的中心點(diǎn)
anchor_center_x=0.5*(anchors[:,2]+anchors[:,0])
anchor_center_y=0.5*(anchors[:,3]+anchors[:,1])
#真實(shí)框距離先驗(yàn)框中心的xy軸偏移情況
decode_bbox_center_x=mbox_loc[:,0]*anchor_width*variances[0]
decode_bbox_center_x+=anchor_center_x
decode_bbox_center_y=mbox_loc[:,1]*anchor_height*variances[1]
decode_bbox_center_y+=anchor_center_y
#真實(shí)框的寬與高的求取
decode_bbox_width=np.exp(mbox_loc[:,2]*variances[2])
decode_bbox_width*=anchor_width
decode_bbox_height=np.exp(mbox_loc[:,3]*variances[3])
decode_bbox_height*=anchor_height
#獲取真實(shí)框的左上角與右下角
decode_bbox_xmin=decode_bbox_center_x-0.5*decode_bbox_width
decode_bbox_ymin=decode_bbox_center_y-0.5*decode_bbox_height
decode_bbox_xmax=decode_bbox_center_x+0.5*decode_bbox_width
decode_bbox_ymax=decode_bbox_center_y+0.5*decode_bbox_height
#真實(shí)框的左上角與右下角進(jìn)行堆疊
decode_bbox=np.concatenate((decode_bbox_xmin[:,None],
decode_bbox_ymin[:,None],
decode_bbox_xmax[:,None],
decode_bbox_ymax[:,None]),axis=-1)
#防止超出0與1
decode_bbox=np.minimum(np.maximum(decode_bbox,0.0),1.0)
returndecode_bbox
defdecode_box(self,predictions,anchors,image_shape,input_shape,letterbox_image,variances=[0.1,0.1,0.2,0.2],confidence=0.5):
#---------------------------------------------------#
#:4是回歸預(yù)測結(jié)果
#---------------------------------------------------#
mbox_loc=predictions[:,:,:4]
#---------------------------------------------------#
#獲得種類的置信度
#---------------------------------------------------#
mbox_conf=predictions[:,:,4:]
results=[]
#----------------------------------------------------------------------------------------------------------------#
#對每一張圖片進(jìn)行處理,由于在predict.py的時候,我們只輸入一張圖片,所以foriinrange(len(mbox_loc))只進(jìn)行一次
#----------------------------------------------------------------------------------------------------------------#
foriinrange(len(mbox_loc)):
results.append([])
#--------------------------------#
#利用回歸結(jié)果對先驗(yàn)框進(jìn)行解碼
#--------------------------------#
decode_bbox=self.decode_boxes(mbox_loc[i],anchors,variances)
forcinrange(1,self.num_classes):
#--------------------------------#
#取出屬于該類的所有框的置信度
#判斷是否大于門限
#--------------------------------#
c_confs=mbox_conf[i,:,c]
c_confs_m=c_confsconfidence
iflen(c_confs[c_confs_m])0:
#-----------------------------------------#
#取出得分高于confidence的框
#-----------------------------------------#
boxes_to_process=decode_bbox[c_confs_m]
confs_to_process=c_confs[c_confs_m]
#-----------------------------------------#
#進(jìn)行iou的非極大抑制
#-----------------------------------------#
idx=self.sess.run(self.nms,feed_dict={self.boxes:boxes_to_process,self.scores:confs_to_process})
#-----------------------------------------#
#取出在非極大抑制中效果較好的內(nèi)容
#-----------------------------------------#
good_boxes=boxes_to_process[idx]
confs=confs_to_process[idx][:,None]
labels=(c-1)*np.ones((len(idx),1))
#-----------------------------------------#
#將label、置信度、框的位置進(jìn)行堆疊。
#-----------------------------------------#
c_pred=np.concatenate((good_boxes,labels,confs),axis=1)
#添加進(jìn)result里
results[-1].extend(c_pred)
iflen(results[-1])0:
results[-1]=np.array(results[-1])
box_xy,box_wh=(results[-1][:,0:2]+results[-1][:,2:4])/2,results[-1][:,2:4]-results[-1][:,0:2]
results[-1][:,:4]=self.ssd_correct_boxes(box_xy,box_wh,input_shape,image_shape,letterbox_image)
returnresults
4、在原圖上進(jìn)行繪制
通過第三步,我們可以獲得預(yù)測框在原圖上的位置,而且這些預(yù)測框都是經(jīng)過篩選的。這些篩選后的框可以直接繪制在圖片上,就可以獲得結(jié)果了。
二、訓(xùn)練部分
1、真實(shí)框的處理
從預(yù)測部分我們知道,每個特征層的預(yù)測結(jié)果,num_anchorsx4的卷積用于預(yù)測該特征層上每一個網(wǎng)格點(diǎn)上每一個先驗(yàn)框的變化情況。
也就是說,我們直接利用ssd網(wǎng)絡(luò)預(yù)測到的結(jié)果,并不是預(yù)測框在圖片上的真實(shí)位置,需要解碼才能得到真實(shí)位置。
而在訓(xùn)練的時候,我們需要計(jì)算loss函數(shù),這個loss函數(shù)是相對于RFB網(wǎng)絡(luò)的預(yù)測結(jié)果的。我們需要把圖片輸入到當(dāng)前的RFB網(wǎng)絡(luò)中,得到預(yù)測結(jié)果;同時還需要把真實(shí)框的信息,進(jìn)行編碼,這個編碼是把真實(shí)框的位置信息格式轉(zhuǎn)化為RFB預(yù)測結(jié)果的格式信息。
也就是,我們需要找到每一張用于訓(xùn)練的圖片的每一個真實(shí)框?qū)?yīng)的先驗(yàn)框,并求出如果想要得到這樣一個真實(shí)框,我們的預(yù)測結(jié)果應(yīng)該是怎么樣的。
從預(yù)測結(jié)果獲得真實(shí)框的過程被稱作解碼,而從真實(shí)框獲得預(yù)測結(jié)果的過程就是編碼的過程。
因此我們只需要將解碼過程逆過來就是編碼過程了。
實(shí)現(xiàn)代碼如下:
defencode_box(self,box,return_iou=True,variances=[0.1,0.1,0.2,0.2]):
#---------------------------------------------#
#計(jì)算當(dāng)前真實(shí)框和先驗(yàn)框的重合情況
#iou[self.num_anchors]
#encoded_box[self.num_anchors,5]
#---------------------------------------------#
iou=self.iou(box)
encoded_box=np.zeros((self.num_anchors,4+return_iou))
#---------------------------------------------#
#找到每一個真實(shí)框,重合程度較高的先驗(yàn)框
#真實(shí)框可以由這個先驗(yàn)框來負(fù)責(zé)預(yù)測
#---------------------------------------------#
assign_mask=iouself.overlap_threshold
#---------------------------------------------#
#如果沒有一個先驗(yàn)框重合度大于self.overlap_threshold
#則選擇重合度最大的為正樣本
#---------------------------------------------#
ifnotassign_mask.any():
assign_mask[iou.argmax()]=True
#---------------------------------------------#
#利用iou進(jìn)行賦值
#---------------------------------------------#
ifreturn_iou:
encoded_box[:,-1][assign_mask]=iou[assign_mask]
#---------------------------------------------#
#找到對應(yīng)的先驗(yàn)框
#---------------------------------------------#
assigned_anchors=self.anchors[assign_mask]
#---------------------------------------------#
#逆向編碼,將真實(shí)框轉(zhuǎn)化為rfb預(yù)測結(jié)果的格式
#先計(jì)算真實(shí)框的中心與長寬
#---------------------------------------------#
box_center=0.5*(box[:2]+box[2:])
box_wh=box[2:]-box[:2]
#---------------------------------------------#
#再計(jì)算重合度較高的先驗(yàn)框的中心與長寬
#---------------------------------------------#
assigned_anchors_center=(assigned_anchors[:,0:2]+assigned_anchors[:,2:4])*0.5
assigned_anchors_wh=(assigned_anchors[:,2:4]-assigned_anchors[:,0:2])
#------------------------------------------------#
#逆向求取rfb應(yīng)該有的預(yù)測結(jié)果
#先求取中心的預(yù)測結(jié)果,再求取寬高的預(yù)測結(jié)果
#存在改變數(shù)量級的參數(shù),默認(rèn)為[0.1,0.1,0.2,0.2]
#------------------------------------------------#
encoded_box[:,:2][assign_mask]=box_center-assigned_anchors_center
encoded_box[:,:2][assign_mask]/=assigned_anchors_wh
encoded_box[:,:2][assign_mask]/=np.array(variances)[:2]
encoded_box[:,2:4][assign_mask]=np.log(box_wh/assigned_anchors_wh)
encoded_box[:,2:4][assign_mask]/=np.array(variances)[2:4]
returnencoded_box.ravel()
利用上述代碼我們可以獲得,真實(shí)框?qū)?yīng)的所有的iou較大先驗(yàn)框,并計(jì)算了真實(shí)框?qū)?yīng)的所有iou較大的先驗(yàn)框應(yīng)該有的預(yù)測結(jié)果。
在訓(xùn)練的時候我們只需要選擇iou最大的先驗(yàn)框就行了,這個iou最大的先驗(yàn)框就是我們用來預(yù)測這個真實(shí)框所用的先驗(yàn)框。
因此我們還要經(jīng)過一次篩選,將上述代碼獲得的真實(shí)框?qū)?yīng)的所有的iou較大先驗(yàn)框的預(yù)測結(jié)果中,iou最大的那個篩選出來。
通過assign_boxes我們就獲得了,輸入進(jìn)來的這張圖片,應(yīng)該有的預(yù)測結(jié)果是什么樣子的。
實(shí)現(xiàn)代碼如下:
defassign_boxes(self,boxes):
#---------------------------------------------------#
#assignment分為3個部分
#:4的內(nèi)容為網(wǎng)絡(luò)應(yīng)該有的回歸預(yù)測結(jié)果
#4:-1的內(nèi)容為先驗(yàn)框所對應(yīng)的種類,默認(rèn)為背景
#-1的內(nèi)容為當(dāng)前先驗(yàn)框是否包含目標(biāo)
#---------------------------------------------------#
assignment=np.zeros((self.num_anchors,4+self.num_classes+1))
assignment[:,4]=1.0
iflen(boxes)==0:
returnassignment
#對每一個真實(shí)框都進(jìn)行iou計(jì)算
encoded_boxes=np.apply_along_axis(self.encode_box,1,boxes[:,:4])
#---------------------------------------------------#
#在reshape后,獲得的encoded_boxes的shape為:
#[num_true_box,num_anchors,4+1]
#4是編碼后的結(jié)果,1為iou
#---------------------------------------------------#
encoded_boxes=encoded_boxes.reshape(-1,self.num_anchors,5)
#---------------------------------------------------#
#[num_anchors]求取每一個先驗(yàn)框重合度最大的真實(shí)框
#---------------------------------------------------#
best_iou=encoded_boxes[:,:,-1].max(axis=0)
best_iou_idx=encoded_boxes[:,:,-1].argmax(axis=0)
best_iou_mask=best_iou0
best_iou_idx=best_iou_idx[best_iou_mask]
#---------------------------------------------------#
#計(jì)算一共有多少先驗(yàn)框滿足需求
#---------------------------------------------------#
assign_num=len(best_iou_idx)
#將編碼后的真實(shí)框取出
encoded_boxes=encoded_boxes[:,best_iou_mask,:]
#---------------------------------------------------#
#編碼后的真實(shí)框的賦值
#---------------------------------------------------#
assignment[:,:4][best_iou_mask]=encoded_boxes[best_iou_idx,np.arange(assign_num),:4]
#----------------------------------------------------------#
#4代表為背景的概率,設(shè)定為0,因?yàn)檫@些先驗(yàn)框有對應(yīng)的物體
#----------------------------------------------------------#
assignment[:,4][best_iou_mask]=0
assignment[:,5:-1][best_iou_mask]=boxes[best_iou_idx,4:]
#----------------------------------------------------------#
#-1表示先驗(yàn)框是否有對應(yīng)的物體
#----------------------------------------------------------#
assignment[:,-1][best_iou_mask]=1
#通過assign_boxes我們就獲得了,輸入進(jìn)來的這張圖片,應(yīng)該有的預(yù)測結(jié)果是什么樣子的
returnassignment
2、利用處理完的真實(shí)框與對應(yīng)圖片的預(yù)測結(jié)果計(jì)算loss
loss的計(jì)算分為三個部分:
1、獲取所有正標(biāo)簽的框的預(yù)測結(jié)果的回歸loss。
2、獲取所有正標(biāo)簽的種類的預(yù)測結(jié)果的交叉熵loss。
3、獲取一定負(fù)標(biāo)簽的種類的預(yù)測結(jié)果的交叉熵loss。
由于在RFBnet的訓(xùn)練過程中,正負(fù)樣本極其不平衡,即存在對應(yīng)真實(shí)框的先驗(yàn)框可能只有十來個,但是不存在對應(yīng)真實(shí)框的負(fù)樣本卻有幾千個,這就會導(dǎo)致負(fù)樣本的loss值極大,因此我們可以考慮減少負(fù)樣本的選取,對于ssd的訓(xùn)練來講,常見的情況是取三倍正樣本數(shù)量的負(fù)樣本用于訓(xùn)練。這個三倍呢,也可以修改,調(diào)整成自己喜歡的數(shù)字。
實(shí)現(xiàn)代碼如下:
importtensorflowastf
classMultiboxLoss(object):
def__init__(self,num_classes,alpha=1.0,neg_pos_ratio=3.0,
background_label_id=0,negatives_for_hard=100.0):
self.num_classes=num_classes
self.alpha=alpha
self.neg_pos_ratio=neg_pos_ratio
ifbackground_label_id!=0:
raiseException('Only0asbackgroundlabelidissupported')
self.background_label_id=background_label_id
self.negatives_for_hard=negatives_for_hard
def_l1_smooth_loss(self,y_true,y_pred):
abs_loss=tf.abs(y_true-y_pred)
sq_loss=0.5*(y_true-y_pred)**2
l1_loss=tf.where(tf.less(abs_loss,1.0),sq_loss,abs_loss-0.5)
returntf.reduce_sum(l1_loss,-1)
def_softmax_loss(self,y_true,y_pred):
y_pred=tf.maximum(y_pred,1e-7)
softmax_loss=-tf.reduce_sum(y_true*tf.log(y_pred),
axis=-1)
returnsoftmax_loss
defcompute_loss(self,y_true,y_pred):
#---------------------------------------------#
#y_truebatch_size,11620,4+self.num_classes+1
#y_predbatch_size,11620,4+self.num_classes
#---------------------------------------------#
num_boxes=tf.to_float(tf.shape(y_true)[1])
#---------------------------------------------#
#分類的loss
#batch_size,11620,21-batch_size,11620
#---------------------------------------------#
conf_loss=self._softmax_loss(y_true[:,:,4:-1],
y_pred[:,:,4:])
#---------------------------------------------#
#框的位置的loss
#batch_size,11620,4-batch_size,11620
#--------------------------------
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 2025年醫(yī)療機(jī)構(gòu)信息化建設(shè)與網(wǎng)絡(luò)安全手冊
- 物流運(yùn)輸過程監(jiān)控與優(yōu)化指南(標(biāo)準(zhǔn)版)
- 2025年人力資源管理績效考核與激勵手冊
- 消防安全檢查與處置手冊
- 國防培訓(xùn)班管理制度
- 鄉(xiāng)村國語培訓(xùn)制度
- 2026年IT企業(yè)技術(shù)面試題目
- 鋼化廠安全教育培訓(xùn)制度
- 幼兒園教職員工培訓(xùn)制度
- 后愛培訓(xùn)制度
- 車輛日常安全檢查課件
- 成立合資公司合同范本
- 比亞迪索賠培訓(xùn)課件
- 民航安全法律法規(guī)課件
- 2026屆四川省瀘州高級中學(xué)高一生物第一學(xué)期期末經(jīng)典試題含解析
- 山東省濟(jì)寧市2026屆第一學(xué)期高三質(zhì)量檢測期末考試濟(jì)寧一模英語(含答案)
- 2026標(biāo)準(zhǔn)版離婚協(xié)議書-無子女無共同財(cái)產(chǎn)債務(wù)版
- 光伏電站巡檢培訓(xùn)課件
- 【期末必刷選擇題100題】(新教材)統(tǒng)編版八年級道德與法治上學(xué)期專項(xiàng)練習(xí)選擇題100題(含答案與解析)
- 年末節(jié)前安全教育培訓(xùn)
- GB/T 93-2025緊固件彈簧墊圈標(biāo)準(zhǔn)型
評論
0/150
提交評論