python神經(jīng)網(wǎng)絡(luò)Keras搭建RFBnet目標(biāo)檢測平臺_第1頁
python神經(jīng)網(wǎng)絡(luò)Keras搭建RFBnet目標(biāo)檢測平臺_第2頁
python神經(jīng)網(wǎng)絡(luò)Keras搭建RFBnet目標(biāo)檢測平臺_第3頁
python神經(jīng)網(wǎng)絡(luò)Keras搭建RFBnet目標(biāo)檢測平臺_第4頁
python神經(jīng)網(wǎng)絡(luò)Keras搭建RFBnet目標(biāo)檢測平臺_第5頁
已閱讀5頁,還剩32頁未讀, 繼續(xù)免費(fèi)閱讀

下載本文檔

版權(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)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論