




版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)
文檔簡(jiǎn)介
第Keras搭建M2Det目標(biāo)檢測(cè)平臺(tái)示例目錄什么是M2det目標(biāo)檢測(cè)算法M2det實(shí)現(xiàn)思路一、預(yù)測(cè)部分1、主干網(wǎng)絡(luò)介紹2、FFM1特征初步融合3、細(xì)化U型模塊TUM4、FFM2特征加強(qiáng)融合5、注意力機(jī)制模塊SFAM6、從特征獲取預(yù)測(cè)結(jié)果7、預(yù)測(cè)結(jié)果的解碼8、在原圖上進(jìn)行繪制二、訓(xùn)練部分1、真實(shí)框的處理2、利用處理完的真實(shí)框與對(duì)應(yīng)圖片的預(yù)測(cè)結(jié)果計(jì)算loss訓(xùn)練自己的M2Det模型一、數(shù)據(jù)集的準(zhǔn)備二、數(shù)據(jù)集的處理三、開始網(wǎng)絡(luò)訓(xùn)練四、訓(xùn)練結(jié)果預(yù)測(cè)
什么是M2det目標(biāo)檢測(cè)算法
一起來看看M2det的keras實(shí)現(xiàn)吧,順便訓(xùn)練一下自己的數(shù)據(jù)。
常見的特征提取方法如圖所示有SSD形,F(xiàn)PN形,STDN形:
SSD型:使用了主干網(wǎng)絡(luò)的最后兩層,再加上4個(gè)使用stride=2卷積的下采樣層構(gòu)成;
FPN型:也稱為U型網(wǎng)絡(luò),經(jīng)過上采樣操作,然后對(duì)應(yīng)融合相同的scale;
STDN型:基于DenseNet的最后一個(gè)denseblock,通過池化和scale-transfer操作來構(gòu)建;
這三者有一定的缺點(diǎn):
一是均基于分類網(wǎng)絡(luò)作為主干提取,對(duì)目標(biāo)檢測(cè)任務(wù)而言特征表示可能不夠;
二是每個(gè)featuremap僅由主干網(wǎng)絡(luò)的singlelevel給出,不夠全面
M2det論文新提出MLFPN型,整體思想是Multi-levelMulti-scale。是一種更加有效的適合于檢測(cè)的特征金字塔結(jié)構(gòu)。
源碼下載
M2det實(shí)現(xiàn)思路
一、預(yù)測(cè)部分
1、主干網(wǎng)絡(luò)介紹
M2det采用可以采用VGG和ResNet101作為主干特征提取網(wǎng)絡(luò),上圖的backbonenetwork指的就是VGG和Resnet101,本文以VGG為例介紹。
M2DET采用的主干網(wǎng)絡(luò)是VGG網(wǎng)絡(luò),關(guān)于VGG的介紹大家可以看我的另外一篇博客
/article/246917.htm
在m2det中,我們?nèi)サ袅巳康娜B接層,只保留了卷積層和最大池化層,即Conv1到Conv5。
1、一張?jiān)紙D片被resize到(320,320,3)。
2、conv1兩次[3,3]卷積網(wǎng)絡(luò),輸出的特征層為64,輸出為(320,320,64),再2X2最大池化,輸出net為(160,160,64)。
3、conv2兩次[3,3]卷積網(wǎng)絡(luò),輸出的特征層為128,輸出net為(160,160,128),再2X2最大池化,輸出net為(80,80,128)。
4、conv3三次[3,3]卷積網(wǎng)絡(luò),輸出的特征層為256,輸出net為(80,80,256),再2X2最大池化,輸出net為(40,40,256)。
5、conv4三次[3,3]卷積網(wǎng)絡(luò),輸出的特征層為512,輸出net為(40,40,512),再2X2最大池化,此時(shí)不進(jìn)行池化,輸出net為(40,40,512)。conv4-3的結(jié)果會(huì)進(jìn)入FFM1進(jìn)行特征的融合。
6、conv5三次[3,3]卷積網(wǎng)絡(luò),輸出的特征層為1024,輸出net為(40,40,1024),再2X2最大池化,輸出net為(20,20,1024)。池化后的結(jié)果會(huì)進(jìn)入FFM1進(jìn)行特征的融合。
fromkerasimportModel
fromkeras.layersimportConv2D,MaxPooling2D
defVGG16(inputs):
net={}
#------------------------#
#輸入默認(rèn)為320,320,3
#------------------------#
net['input']=inputs
#------------------------------------------------#
#第一個(gè)卷積部分320,320,3-160,160,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'])
#------------------------------------------------#
#第二個(gè)卷積部分160,160,64-80,80,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'])
y0=net['pool2']
#------------------------------------------------#
#第三個(gè)卷積部分80,80,128-40,40,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'])
y1=net['pool3']
#------------------------------------------------#
#第四個(gè)卷積部分40,40,256-40,40,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='block4_pool')(net['conv4_3'])
y2=net['conv4_3']
#------------------------------------------------#
#第五個(gè)卷積部分40,40,512-20,20,1024
#------------------------------------------------#
net['conv5_1']=Conv2D(1024,kernel_size=(3,3),
activation='relu',
padding='same',
name='conv5_1')(net['conv4_3'])
net['conv5_2']=Conv2D(1024,kernel_size=(3,3),
activation='relu',
padding='same',
name='conv5_2')(net['conv5_1'])
net['conv5_3']=Conv2D(1024,kernel_size=(3,3),
activation='relu',
padding='same',
name='conv5_3')(net['conv5_2'])
net['pool5']=MaxPooling2D((3,3),strides=(2,2),padding='same',
name='pool5')(net['conv5_3'])
y3=net['pool5']
model=Model(inputs,[y0,y1,y2,y3],name='vgg16')
returnmodel
2、FFM1特征初步融合
FFM1具體的結(jié)構(gòu)如下:
FFM1會(huì)對(duì)VGG提取到的特征進(jìn)行初步融合。
在利用VGG進(jìn)行特征提取的時(shí)候,我們會(huì)取出shape為(40,40,512)、(20,20,1024)的特征層進(jìn)行下一步的操作。
在FFM1中,其會(huì)對(duì)(20,20,1024)的特征層進(jìn)行進(jìn)行一個(gè)通道數(shù)為512、卷積核大小為3x3、步長(zhǎng)為1x1的卷積,然后再進(jìn)行上采樣,使其Shape變?yōu)?40,40,512);
同時(shí)會(huì)對(duì)(40,40,512)的特征層進(jìn)行進(jìn)行一個(gè)通道數(shù)為256、卷積核大小為1x1,步長(zhǎng)為1x1的卷積,使其Shape變?yōu)?40,40,256);
然后將兩個(gè)卷積后的結(jié)果進(jìn)行堆疊,變成一個(gè)(40,40,768)的初步融合特征層
實(shí)現(xiàn)代碼為:
defFFMv1(C4,C5,feature_size_1=256,feature_size_2=512,name='FFMv1'):
#------------------------------------------------#
#C4特征層40,40,512
#C5特征層20,20,1024
#------------------------------------------------#
#40,40,512-40,40,256
F4=conv2d(C4,filters=feature_size_1,kernel_size=(3,3),strides=(1,1),padding='same',name='F4')
#20,20,1024-20,20,512
F5=conv2d(C5,filters=feature_size_2,kernel_size=(1,1),strides=(1,1),padding='same',name='F5')
#20,20,512-40,40,512
F5=keras.layers.UpSampling2D(size=(2,2),name='F5_Up')(F5)
#40,40,256+40,40,512-40,40,768
outputs=keras.layers.Concatenate(name=name)([F4,F5])
returnoutputs
3、細(xì)化U型模塊TUM
Tum的結(jié)構(gòu)具體如下:
當(dāng)我們給Tum輸入一個(gè)(40,40,256)的有效特征層之后,Tum會(huì)對(duì)輸入進(jìn)來的特征層進(jìn)行U型的特征提取,這里的結(jié)構(gòu)比較類似特征金字塔的結(jié)構(gòu),先對(duì)特征層進(jìn)行不斷的特征壓縮,然后再不斷的上采樣進(jìn)行特征融合,利用Tum我們可以獲得6個(gè)有效特征層,大小分別是(40,40,128)、(20,20,128)、(10,10,128)、(5,5,128)、(3,3,128)、(1,1,128)。
defTUM(stage,inputs,feature_size=256,name="TUM"):#---------------------------------##進(jìn)行下采樣的部分#---------------------------------##40,40,256f1=inputs#40,40,256-20,20,256f2=conv2d(f1,filters=feature_size,kernel_size=(3,3),strides=(2,2),padding='same',name=name+"_"+str(stage)+'_f2')#20,20,256-10,10,256f3=conv2d(f2,filters=feature_size,kernel_size=(3,3),strides=(2,2),padding='same',name=name+"_"+str(stage)+'_f3')#10,10,256-5,5,256f4=conv2d(f3,filters=feature_size,kernel_size=(3,3),strides=(2,2),padding='same',name=name+"_"+str(stage)+'_f4')#5,5,256-3,3,256f5=conv2d(f4,filters=feature_size,kernel_size=(3,3),strides=(2,2),padding='same',name=name+"_"+str(stage)+'_f5')#3,3,256-1,1,256f6=conv2d(f5,filters=feature_size,kernel_size=(3,3),strides=(2,2),padding='valid',name=name+"_"+str(stage)+'_f6')size_buffer=[]#40,40size_buffer.append([int(f1.shape[1]),int(f1.shape[2])])#20,20size_buffer.append([int(f2.shape[1]),int(f2.shape[2])])#10,10size_buffer.append([int(f3.shape[1]),int(f3.shape[2])])#5,5size_buffer.append([int(f4.shape[1]),int(f4.shape[2])])#3,3size_buffer.append([int(f5.shape[1]),int(f5.shape[2])])#---------------------------------##進(jìn)行上采樣與特征融合的部分#---------------------------------#c6=f6#1,1,256-1,1,256c5=conv2d(c6,filters=feature_size,kernel_size=(3,3),strides=(1,1),padding='same',name=name+"_"+str(stage)+'_c5')#1,1,256-3,3,256c5=keras.layers.Lambda(lambdax:tf.image.resize_bilinear(x,size=size_buffer[4]),name=name+"_"+str(stage)+'_upsample_add5')(c5)c5=keras.layers.Add()([c5,f5])#3,3,256-3,3,256c4=conv2d(c5,filters=feature_size,kernel_size=(3,3),strides=(1,1),padding='same',name=name+"_"+str(stage)+'_c4')#3,3,256-5,5,256c4=keras.layers.Lambda(lambdax:tf.image.resize_bilinear(x,size=size_buffer[3]),name=name+"_"+str(stage)+'_upsample_add4')(c4)c4=keras.layers.Add()([c4,f4])#5,5,256-5,5,256c3=conv2d(c4,filters=feature_size,kernel_size=(3,3),strides=(1,1),padding='same',name=name+"_"+str(stage)+'_c3')#5,5,256-10,10,256c3=keras.layers.Lambda(lambdax:tf.image.resize_bilinear(x,size=size_buffer[2]),name=name+"_"+str(stage)+'_upsample_add3')(c3)c3=keras.layers.Add()([c3,f3])#10,10,256-10,10,256c2=conv2d(c3,filters=feature_size,kernel_size=(3,3),strides=(1,1),padding='same',name=name+"_"+str(stage)+'_c2')#10,10,256-20,20,256c2=keras.layers.Lambda(lambdax:tf.image.resize_bilinear(x,size=size_buffer[1]),name=name+"_"+str(stage)+'_upsample_add2')(c2)c2=keras.layers.Add()([c2,f2])#20,20,256-20,20,256c1=conv2d(c2,filters=feature_size,kernel_size=(3,3),strides=(1,1),padding='same',name=name+"_"+str(stage)+'_c1')#20,20,256-40,40,256c1=keras.layers.Lambda(lambdax:tf.image.resize_bilinear(x,size=size_buffer[0]),name=name+"_"+str(stage)+'_upsample_add1')(c1)c1=keras.layers.Add()([c1,f1])#---------------------------------##利用1x1卷積調(diào)整通道數(shù)后輸出#---------------------------------#output_features=feature_size//2#40,40,256-40,40,128o1=conv2d(c1,filters=output_features,kernel_size=(1,1),strides=(1,1),padding='valid',name=name+"_"+str(stage)+'_o1')#20,20,256-20,20,128o2=conv2d(c2,filters=output_features,kernel_size=(1,1),strides=(1,1),padding='valid',name=name+"_"+str(stage)+'_o2')#10,10,256-10,10,128o3=conv2d(c3,filters=output_features,kernel_size=(1,1),strides=(1,1),padding='valid',name=name+"_"+str(stage)+'_o3')#5,5,256-5,5,128o4=conv2d(c4,filters=output_features,kernel_size=(1,1),strides=(1,1),padding='valid',name=name+"_"+str(stage)+'_o4')#3,3,256-3,3,128o5=conv2d(c5,filters=output_features,kernel_size=(1,1),strides=(1,1),padding='valid',name=name+"_"+str(stage)+'_o5')#1,1,256-1,1,128o6=conv2d(c6,filters=output_features,kernel_size=(1,1),strides=(1,1),padding='valid',name=name+"_"+str(stage)+'_o6')outputs=[o1,o2,o3,o4,o5,o6]returnoutputs
defTUM(stage,inputs,feature_size=256,name="TUM"):
#---------------------------------#
#進(jìn)行下采樣的部分
#---------------------------------#
#40,40,256
f1=inputs
#40,40,256-20,20,256
f2=conv2d(f1,filters=feature_size,kernel_size=(3,3),strides=(2,2),padding='same',name=name+"_"+str(stage)+'_f2')
#20,20,256-10,10,256
f3=conv2d(f2,filters=feature_size,kernel_size=(3,3),strides=(2,2),padding='same',name=name+"_"+str(stage)+'_f3')
#10,10,256-5,5,256
f4=conv2d(f3,filters=feature_size,kernel_size=(3,3),strides=(2,2),padding='same',name=name+"_"+str(stage)+'_f4')
#5,5,256-3,3,256
f5=conv2d(f4,filters=feature_size,kernel_size=(3,3),strides=(2,2),padding='same',name=name+"_"+str(stage)+'_f5')
#3,3,256-1,1,256
f6=conv2d(f5,filters=feature_size,kernel_size=(3,3),strides=(2,2),padding='valid',name=name+"_"+str(stage)+'_f6')
size_buffer=[]
#40,40
size_buffer.append([int(f1.shape[1]),int(f1.shape[2])])
#20,20
size_buffer.append([int(f2.shape[1]),int(f2.shape[2])])
#10,10
size_buffer.append([int(f3.shape[1]),int(f3.shape[2])])
#5,5
size_buffer.append([int(f4.shape[1]),int(f4.shape[2])])
#3,3
size_buffer.append([int(f5.shape[1]),int(f5.shape[2])])
#---------------------------------#
#進(jìn)行上采樣與特征融合的部分
#---------------------------------#
c6=f6
#1,1,256-1,1,256
c5=conv2d(c6,filters=feature_size,kernel_size=(3,3),strides=(1,1),padding='same',name=name+"_"+str(stage)+'_c5')
#1,1,256-3,3,256
c5=keras.layers.Lambda(lambdax:tf.image.resize_bilinear(x,size=size_buffer[4]),name=name+"_"+str(stage)+'_upsample_add5')(c5)
c5=keras.layers.Add()([c5,f5])
#3,3,256-3,3,256
c4=conv2d(c5,filters=feature_size,kernel_size=(3,3),strides=(1,1),padding='same',name=name+"_"+str(stage)+'_c4')
#3,3,256-5,5,256
c4=keras.layers.Lambda(lambdax:tf.image.resize_bilinear(x,size=size_buffer[3]),name=name+"_"+str(stage)+'_upsample_add4')(c4)
c4=keras.layers.Add()([c4,f4])
#5,5,256-5,5,256
c3=conv2d(c4,filters=feature_size,kernel_size=(3,3),strides=(1,1),padding='same',name=name+"_"+str(stage)+'_c3')
#5,5,256-10,10,256
c3=keras.layers.Lambda(lambdax:tf.image.resize_bilinear(x,size=size_buffer[2]),name=name+"_"+str(stage)+'_upsample_add3')(c3)
c3=keras.layers.Add()([c3,f3])
#10,10,256-10,10,256
c2=conv2d(c3,filters=feature_size,kernel_size=(3,3),strides=(1,1),padding='same',name=name+"_"+str(stage)+'_c2')
#10,10,256-20,20,256
c2=keras.layers.Lambda(lambdax:tf.image.resize_bilinear(x,size=size_buffer[1]),name=name+"_"+str(stage)+'_upsample_add2')(c2)
c2=keras.layers.Add()([c2,f2])
#20,20,256-20,20,256
c1=conv2d(c2,filters=feature_size,kernel_size=(3,3),strides=(1,1),padding='same',name=name+"_"+str(stage)+'_c1')
#20,20,256-40,40,256
c1=keras.layers.Lambda(lambdax:tf.image.resize_bilinear(x,size=size_buffer[0]),name=name+"_"+str(stage)+'_upsample_add1')(c1)
c1=keras.layers.Add()([c1,f1])
#---------------------------------#
#利用1x1卷積調(diào)整通道數(shù)后輸出
#---------------------------------#
output_features=feature_size//2
#40,40,256-40,40,128
o1=conv2d(c1,filters=output_features,kernel_size=(1,1),strides=(1,1),padding='valid',name=name+"_"+str(stage)+'_o1')
#20,20,256-20,20,128
o2=conv2d(c2,filters=output_features,kernel_size=(1,1),strides=(1,1),padding='valid',name=name+"_"+str(stage)+'_o2')
#10,10,256-10,10,128
o3=conv2d(c3,filters=output_features,kernel_size=(1,1),strides=(1,1),padding='valid',name=name+"_"+str(stage)+'_o3')
#5,5,256-5,5,128
o4=conv2d(c4,filters=output_features,kernel_size=(1,1),strides=(1,1),padding='valid',name=name+"_"+str(stage)+'_o4')
#3,3,256-3,3,128
o5=conv2d(c5,filters=output_features,kernel_size=(1,1),strides=(1,1),padding='valid',name=name+"_"+str(stage)+'_o5')
#1,1,256-1,1,128
o6=conv2d(c6,filters=output_features,kernel_size=(1,1),strides=(1,1),padding='valid',name=name+"_"+str(stage)+'_o6')
outputs=[o1,o2,o3,o4,o5,o6]
returnoutputs
4、FFM2特征加強(qiáng)融合
通過TUM,我們可以獲得六個(gè)有效特征層,為了進(jìn)一步加強(qiáng)網(wǎng)絡(luò)的特征提取能力,M2det將6個(gè)有效特征層中的(40,40,128)特征層取出,和FFM1提取出來的初步融合特征層進(jìn)行加強(qiáng)融合,再次輸出一個(gè)(40,40,256)的加強(qiáng)融合的特征層。
此時(shí)FFM2輸出的加強(qiáng)融合特征層可以再一次傳入到TUM中進(jìn)行U形特征提取。
如上圖所示,我們可以進(jìn)一步利用多個(gè)TUM模塊進(jìn)行特征提取,利用多個(gè)TUM模塊我們可以獲得多次有效特征層。
TUM模塊的數(shù)量我們可以根據(jù)自身需要進(jìn)行修改,本文使用4次TUM模塊,可以分別獲得四次(40,40,128)、(20,20,128)、(10,10,128)、(5,5,128)、(3,3,128)、(1,1,128)的有效特征層。(論文中做了實(shí)驗(yàn),用8次TUM模塊會(huì)有比較好的效果)。
我們可以將獲得的有效特征層,按照shape進(jìn)行堆疊,最終獲得(40,40,512)、(20,20,512)、(10,10,512)、(5,5,512)、(3,3,512)、(1,1,512)六個(gè)有效特征層。
defFFMv2(stage,base,tum,base_size=(40,40,768),tum_size=(40,40,128),feature_size=128,name='FFMv2'):
#40,40,128
outputs=conv2d(base,filters=feature_size,kernel_size=(1,1),strides=(1,1),padding='same',name=name+"_"+str(stage)+'_base_feature')
outputs=keras.layers.Concatenate(name=name+"_"+str(stage))([outputs,tum])
#40,40,256
returnoutputs
def_create_feature_pyramid(base_feature,stage=8):
features=[[],[],[],[],[],[]]
#將輸入進(jìn)來的
inputs=keras.layers.Conv2D(filters=256,kernel_size=1,strides=1,padding='same')(base_feature)
#第一個(gè)TUM模塊
outputs=TUM(1,inputs)
max_output=outputs[0]
forjinrange(len(features)):
features[j].append(outputs[j])
#第2,3,4個(gè)TUM模塊,需要將上一個(gè)Tum模塊輸出的40x40x128的內(nèi)容,傳入到下一個(gè)Tum模塊中
foriinrange(2,stage+1):
#將Tum模塊的輸出和基礎(chǔ)特征層傳入到FFmv2層當(dāng)中
#輸入為base_feature40x40x768,max_output40x40x128
#輸出為40x40x256
inputs=FFMv2(i-1,base_feature,max_output)
#輸出為40x40x128、20x20x128、10x10x128、5x5x128、3x3x128、1x1x128
outputs=TUM(i,inputs)
max_output=outputs[0]
forjinrange(len(features)):
features[j].append(outputs[j])
#進(jìn)行了4次TUM
#將獲得的同樣大小的特征層堆疊到一起
concatenate_features=[]
forfeatureinfeatures:
concat=keras.layers.Concatenate()([fforfinfeature])
concatenate_features.append(concat)
returnconcatenate_features
5、注意力機(jī)制模塊SFAM
注意力機(jī)制模塊如下:
其會(huì)對(duì)上一步獲得的(40,40,512)、(20,20,512)、(10,10,512)、(5,5,512)、(3,3,512)、(1,1,512)六個(gè)有效特征層。進(jìn)行各個(gè)通道的注意力機(jī)制調(diào)整,判斷每一個(gè)通道數(shù)應(yīng)該有的權(quán)重。
#注意力機(jī)制
defSE_block(inputs,input_size,compress_ratio=16,name='SE_block'):
pool=keras.layers.GlobalAveragePooling2D()(inputs)
reshape=keras.layers.Reshape((1,1,input_size[2]))(pool)
fc1=keras.layers.Conv2D(filters=input_size[2]//compress_ratio,kernel_size=1,strides=1,padding='valid',
activation='relu',name=name+'_fc1')(reshape)
fc2=keras.layers.Conv2D(filters=input_size[2],kernel_size=1,strides=1,padding='valid',activation='sigmoid',
name=name+'_fc2')(fc1)
reweight=keras.layers.Multiply(name=name+'_reweight')([inputs,fc2])
returnreweight
defSFAM(feature_pyramid,input_sizes,compress_ratio=16,name='SFAM'):
outputs=[]
foriinrange(len(input_sizes)):
input_size=input_sizes[i]
_input=feature_pyramid[i]
_output=SE_block(_input,input_size,compress_ratio=compress_ratio,name='SE_block_'+str(i))
outputs.append(_output)
returnoutputs
6、從特征獲取預(yù)測(cè)結(jié)果
通過第五步,我們獲取了6個(gè)融合了注意力機(jī)制的有效特征層。
對(duì)獲取到的每一個(gè)有效特征層,我們分別對(duì)其進(jìn)行一次num_anchorsx4的卷積、一次num_anchorsxnum_classes的卷積、并需要計(jì)算每一個(gè)有效特征層對(duì)應(yīng)的先驗(yàn)框。而num_anchors指的是該特征層所擁有的先驗(yàn)框數(shù)量。
其中:
num_anchorsx4的卷積用于預(yù)測(cè)該特征層上每一個(gè)網(wǎng)格點(diǎn)上每一個(gè)先驗(yàn)框的變化情況。(為什么說是變化情況呢,這是因?yàn)镸2DET的預(yù)測(cè)結(jié)果需要結(jié)合先驗(yàn)框獲得預(yù)測(cè)框,預(yù)測(cè)結(jié)果就是先驗(yàn)框的變化情況。)
num_anchorsxnum_classes的卷積用于預(yù)測(cè)該特征層上每一個(gè)網(wǎng)格點(diǎn)上每一個(gè)預(yù)測(cè)框?qū)?yīng)的種類。
每一個(gè)有效特征層對(duì)應(yīng)的先驗(yàn)框?qū)?yīng)著該特征層上每一個(gè)網(wǎng)格點(diǎn)上預(yù)先設(shè)定好的六個(gè)框。
所有的特征層對(duì)應(yīng)的預(yù)測(cè)結(jié)果的shape如下:
實(shí)現(xiàn)代碼為:
defm2det(input_shape,num_classes=21,num_anchors=6):
inputs=keras.layers.Input(shape=input_shape)
#------------------------------------------------#
#利用主干特征提取網(wǎng)絡(luò)獲得兩個(gè)有效特征層
#分別是C440,40,512
#分別是C520,20,1024
#------------------------------------------------#
C4,C5=VGG16(inputs).outputs[2:]
#base_feature的shape為40,40,768
base_feature=FFMv1(C4,C5,feature_size_1=256,feature_size_2=512)
#---------------------------------------------------------------------------------------------------#
#在_create_feature_pyramid函數(shù)里,我們會(huì)使用TUM模塊對(duì)輸入進(jìn)來的特征層進(jìn)行特征提取
#最終輸出的特征層有六個(gè),由于進(jìn)行了四次的TUM模塊,所以六個(gè)有效特征層由4次TUM模塊的輸出堆疊而成
#o140,40,128*440,40,512
#o220,20,128*420,20,512
#o310,10,128*410,10,512
#o45,5,128*45,5,512
#o53,3,128*43,3,512
#o61,1,128*41,1,512
#---------------------------------------------------------------------------------------------------#
feature_pyramid=_create_feature_pyramid(base_feature,stage=4)
#-------------------------------------------------#
#給合并后的特征層添加上注意力機(jī)制
#-------------------------------------------------#
outputs=SFAM(feature_pyramid)
#-------------------------------------------------#
#將有效特征層轉(zhuǎn)換成輸出結(jié)果
#-------------------------------------------------#
classifications=[]
regressions=[]
forfeatureinoutputs:
classification=keras.layers.Conv2D(filters=num_anchors*num_classes,kernel_size=3,strides=1,padding='same')(feature)
classification=keras.layers.Reshape((-1,num_classes))(classification)
classification=keras.layers.Activation('softmax')(classification)
regression=keras.layers.Conv2D(filters=num_anchors*4,kernel_size=3,strides=1,padding='same')(feature)
regression=keras.layers.Reshape((-1,4))(regression)
classifications.append(classification)
regressions.append(regression)
classifications=keras.layers.Concatenate(axis=1,name="classification")(classifications)
regressions=keras.layers.Concatenate(axis=1,name="regression")(regressions)
pyramids=keras.layers.Concatenate(axis=-1,name="out")([regressions,classifications])
returnkeras.models.Model(inputs=inputs,outputs=pyramids)
7、預(yù)測(cè)結(jié)果的解碼
我們通過對(duì)每一個(gè)特征層的處理,可以獲得兩個(gè)內(nèi)容,分別是:
num_anchorsx4的卷積用于預(yù)測(cè)該特征層上每一個(gè)網(wǎng)格點(diǎn)上每一個(gè)先驗(yàn)框的變化情況。
num_anchorsxnum_classes的卷積用于預(yù)測(cè)該特征層上每一個(gè)網(wǎng)格點(diǎn)上每一個(gè)預(yù)測(cè)框?qū)?yīng)的種類。
每一個(gè)有效特征層對(duì)應(yīng)的先驗(yàn)框?qū)?yīng)著該特征層上每一個(gè)網(wǎng)格點(diǎn)上預(yù)先設(shè)定好的六個(gè)框。
我們利用num_anchorsx4的卷積與每一個(gè)有效特征層對(duì)應(yīng)的先驗(yàn)框獲得框的真實(shí)位置。
每一個(gè)有效特征層對(duì)應(yīng)的先驗(yàn)框就是,如圖所示的作用:
每一個(gè)有效特征層將整個(gè)圖片分成與其長(zhǎng)寬對(duì)應(yīng)的網(wǎng)格,如conv4-3和fl7組合成的特征層就是將整個(gè)圖像分成38x38個(gè)網(wǎng)格;然后從每個(gè)網(wǎng)格中心建立多個(gè)先驗(yàn)框,如conv4-3和fl7組合成的有效特征層就是建立了6個(gè)先驗(yàn)框;對(duì)于conv4-3和fl7組合成的特征層來講,整個(gè)圖片被分成38x38個(gè)網(wǎng)格,每個(gè)網(wǎng)格中心對(duì)應(yīng)6個(gè)先驗(yàn)框,一共包含了,38x38x6個(gè),8664個(gè)先驗(yàn)框。
先驗(yàn)框雖然可以代表一定的框的位置信息與框的大小信息,但是其是有限的,無法表示任意情況,因此還需要調(diào)整,RFBnet利用num_anchorsx4的卷積的結(jié)果對(duì)先驗(yàn)框進(jìn)行調(diào)整。
num_anchorsx4中的num_anchors表示了這個(gè)網(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í)框的寬與高相對(duì)于先驗(yàn)框的變化情況。
RFBnet解碼過程就是將每個(gè)網(wǎng)格的中心點(diǎn)加上它對(duì)應(yīng)的x_offset和y_offset,加完后的結(jié)果就是預(yù)測(cè)框的中心,然后再利用先驗(yàn)框和h、w結(jié)合計(jì)算出預(yù)測(cè)框的長(zhǎng)和寬。這樣就能得到整個(gè)預(yù)測(cè)框的位置了。
當(dāng)然得到最終的預(yù)測(cè)結(jié)構(gòu)后還要進(jìn)行得分排序與非極大抑制篩選這一部分基本上是所有目標(biāo)檢測(cè)通用的部分。
1、取出每一類得分大于self.obj_threshold的框和得分。
2、利用框的位置和得分進(jìn)行非極大抑制。
實(shí)現(xiàn)代碼如下:
importnumpyasnp
importtensorflowastf
importkeras.backendasK
classBBoxUtility(object):
def__init__(self,num_classes,nms_thresh=0.45,top_k=300):
self.num_classes=num_classes
self._nms_thresh=nms_thresh
self._top_k=top_k
self.boxes=K.placeholder(dtype='float32',shape=(None,4))
self.scores=K.placeholder(dtype='float32',shape=(None,))
self.nms=tf.image.non_max_suppression(self.boxes,self.scores,self._top_k,iou_threshold=self._nms_thresh)
self.sess=K.get_session()
defssd_correct_boxes(self,box_xy,box_wh,input_shape,image_shape,letterbox_image):
#-----------------------------------------------------------------#
#把y軸放前面是因?yàn)榉奖泐A(yù)測(cè)框和圖像的寬高進(jìn)行相乘
#-----------------------------------------------------------------#
box_yx=box_xy[...,::-1]
box_hw=box_wh[...,::-1]
input_shape=np.array(input_shape)
image_shape=np.array(image_shape)
ifletterbox_image:
#-----------------------------------------------------------------#
#這里求出來的offset是圖像有效區(qū)域相對(duì)于圖像左上角的偏移情況
#new_shape指的是寬高縮放情況
#-----------------------------------------------------------------#
new_shape=np.round(image_shape*np.min(input_shape/image_shape))
offset=(input_shape-new_shape)/2./input_shape
scale=input_shape/new_shape
box_yx=(box_yx-offset)*scale
box_hw*=scale
box_mins=box_yx-(box_hw/2.)
box_maxes=box_yx+(box_hw/2.)
boxes=np.concatenate([box_mins[...,0:1],box_mins[...,1:2],box_maxes[...,0:1],box_maxes[...,1:2]],axis=-1)
boxes*=np.concatenate([image_shape,image_shape],axis=-1)
returnboxes
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ù)測(cè)結(jié)果
#---------------------------------------------------#
mbox_loc=predictions[:,:,:4]
#---------------------------------------------------#
#獲得種類的置信度
#---------------------------------------------------#
mbox_conf=predictions[:,:,4:]
results=[]
#----------------------------------------------------------------------------------------------------------------#
#對(duì)每一張圖片進(jìn)行處理,由于在predict.py的時(shí)候,我們只輸入一張圖片,所以foriinrange(len(mbox_loc))只進(jìn)行一次
#----------------------------------------------------------------------------------------------------------------#
foriinrange(len(mbox_loc)):
results.append([])
#--------------------------------#
#利用回歸結(jié)果對(duì)先驗(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
8、在原圖上進(jìn)行繪制
通過第三步,我們可以獲得預(yù)測(cè)框在原圖上的位置,而且這些預(yù)測(cè)框都是經(jīng)過篩選的。這些篩選后的框可以直接繪制在圖片上,就可以獲得結(jié)果了。
二、訓(xùn)練部分
1、真實(shí)框的處理
從預(yù)測(cè)部分我們知道,每個(gè)特征層的預(yù)測(cè)結(jié)果,num_anchorsx4的卷積用于預(yù)測(cè)該特征層上每一個(gè)網(wǎng)格點(diǎn)上每一個(gè)先驗(yàn)框的變化情況。
也就是說,我們直接利用M2DET網(wǎng)絡(luò)預(yù)測(cè)到的結(jié)果,并不是預(yù)測(cè)框在圖片上的真實(shí)位置,需要解碼才能得到真實(shí)位置。
而在訓(xùn)練的時(shí)候,我們需要計(jì)算loss函數(shù),這個(gè)loss函數(shù)是相對(duì)于M2DET網(wǎng)絡(luò)的預(yù)測(cè)結(jié)果的。我們需要把圖片輸入到當(dāng)前的M2DET網(wǎng)絡(luò)中,得到預(yù)測(cè)結(jié)果;同時(shí)還需要把真實(shí)框的信息,進(jìn)行編碼,這個(gè)編碼是把真實(shí)框的位置信息格式轉(zhuǎn)化為M2DET預(yù)測(cè)結(jié)果的格式信息。
也就是,我們需要找到每一張用于訓(xùn)練的圖片的每一個(gè)真實(shí)框?qū)?yīng)的先驗(yàn)框,并求出如果想要得到這樣一個(gè)真實(shí)框,我們的預(yù)測(cè)結(jié)果應(yīng)該是怎么樣的。
從預(yù)測(cè)結(jié)果獲得真實(shí)框的過程被稱作解碼,而從
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁(yè)內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫(kù)網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。
最新文檔
- 地震急救安全知識(shí)培訓(xùn)課件
- 地鐵行車調(diào)度員培訓(xùn)課件
- 地鐵施工安全管控要點(diǎn)
- 地球運(yùn)行課件
- 2025授權(quán)代理協(xié)議范本
- 提升物流行業(yè)配送效率專項(xiàng)計(jì)劃
- 石家莊市藁城市2025年中考數(shù)學(xué)全真模擬試卷含解析
- 2025年房屋租賃市場(chǎng)中陰陽(yáng)合同的效力問題
- 國(guó)際金融服務(wù)協(xié)議
- 2025農(nóng)資買賣合同模板
- 【化學(xué) 云南卷】2025年云南省高考招生統(tǒng)一考試真題化學(xué)試卷(含答案)
- 景區(qū)美食攤位管理辦法
- 危險(xiǎn)廢物突發(fā)事故應(yīng)急演練方案
- 老年衰弱護(hù)理課件
- 供應(yīng)商準(zhǔn)入管理制度及流程
- 一級(jí)建造師法律教學(xué)課件
- excel培訓(xùn)課件制作
- 2025至2030中國(guó)酶載體樹脂行業(yè)發(fā)展模式及前景規(guī)劃研究報(bào)告
- 2025年輸電線路在線監(jiān)測(cè)系統(tǒng)市場(chǎng)環(huán)境分析
- 貨代公司操作管理制度
- 物流園區(qū)安全管理制度
評(píng)論
0/150
提交評(píng)論