1 圖像分類的概念
1.1 什么是圖像分類?
圖像分類,根據圖像信息中所反映出來的不同特征,把不同類別的目標區分開來的圖像處理方法
1.2 圖像分類的難度
●任何拍攝情況的改變都將提升分類的難度
?1.3 CNN如何進行圖像分類
●數據驅動型方法通用流程
1.收集圖像以及對應的標簽,形成數據集
2.使用機器學習訓練一個分類器
3.在新的圖像.上測試這個分類器
?1.4 圖像分類指標
精確率:查得準不準?? ? ? ??
召回率:查得全不全?? ? ? ??
True positives (TP):飛機的圖片被正確的識別成了飛機。
True negatives (TN): 大雁的圖片沒有被識別出來,系統正確地認為它們是大雁。
False positives (FP):大雁的圖片被錯誤地識別成了飛機。
False negatives (FN):飛機的圖片沒有被識別出來,系統錯誤地認為它們是大雁。
True negatives (TN): 4,四個大雁? ? ? False negatives (FN): 2,二個飛機
True positives (TP): 3,綠框.? ? ? ? ? ? ? ? ??False positives (FP): 1,紅框,
平均精確度( Average Precision,AP) :PR曲線下的面積,這里的average,等于是對precision進行取平均。
?1.5 經典CNN網絡性能演化
2 GoogleNet? ?
2.1 深度網絡有什么好處?
1.豐富了低、中、高等級的特征
邊緣、紋理、形狀、顏色.....高緯度的人類無法理解的特征
2.越深、越寬的網絡具有越強的表達能力
有學者證明,一個寬度為K、深度為H的網絡,能夠產生至少條線段
線段越多,擬合得越準確
因此,網絡加寬、加深可以提升性能,并且加深效果比加寬好:
2.2 如何設計一個卷積層?
●選擇什么樣的層(Layer ) ?
3x3卷積核
5X5卷積核
池化層( Pooling Layer )
2.2.1 感受層
在卷積神經網絡中,感受野( Receptive Field )的定義是卷積神經網絡每層輸出的特征圖.上的像素點在輸入圖片.上映射的區域大小。換句話說,感受野是特征圖上的一個點對應輸入圖上的區域。
假設兩個卷積層的卷積核尺寸都為2x2,步長都為1,輸入為4x4
經過兩次卷積后,特征圖的尺寸分別為3x3和2x2
對于特征圖2的左上角像素點,它在特征圖1上的感受范圍為左上方的2x2區域,而此區域在輸入。上的感受范圍是左,上方的3x3區域,因此,感受野尺寸為3x3。
越深層的特征圖, 感受野越大
對同層而言,卷積核尺寸越大,感受野越大
大的感受野對大的物體更敏感,反之,小的感受野對小的物體更敏感
貓可以在圖片里有大有小,可以在圖片的局部,也可以整張圖片都是;對一張圖片而言,至少有RGB三個通道,如果這幾多個卷積核則會導致計算量過大。?
2.2.2 如何降低計算量——1x1卷積核
1 x 1卷積做了什么?
它在深度( Depth). 上進行了融合深度為D的輸入經過一-個1 x 1卷積核,得到深度為1的輸出(S=1, P=0);同理,尺寸為DxHxW的輸入,經過D/2個1 X 1卷積核,將會得到D/2xHxW的輸出(S=1,P=0);最終,在不損失太多信息的情況下,對輸入進行了降維。
小結:1X1的卷積是--個非常優秀的結構它可以跨通道組織信息提高網絡的表達能力,同時可以對輸出通道升維和降維。[想象一下:兩片面包壓縮成一-片的寬度又或者加點膨化劑,膨脹成4片的寬度]
2.3 Inception模塊
在1x1卷積后,添加不同的卷積分支
實現同一卷積層的多尺度特征提取與融合
2.4 整體網絡結構
一個潛在的問題
?在較深的網絡中進行反向傳播可能會出現“梯度消失”,導致訓練無法繼續進行
一種解決方案
?網絡的中間層具有很高的判別能力
?在這些中間層增加輔助分類器
?在訓練中,這些中間層分類器得到的L .oss以0.3的權重加到最終Loss
3 GoogleNet的keras實現
3.0 貓狗大戰
本次實戰采用的數據集來自kaggle . 上的一一個競賽: Dogs Vs. Cats
3.1 圖像讀取一圖像增 強-圖像生成器
數據增強策略
●翻轉變換(lip):沿著水平或者垂直方向翻轉圖像;
●縮放變換(zoom):按照一定的比例放大或者縮小圖像;
●平移變換(shift):在圖像平面上對圖像以一定方式進行平移;
●可以采用隨機或人為定義的方式指定平移范圍和平移步長,沿水平或豎直方向進行平移.圖像內容的位置
●尺度變換(scale):對圖像按照指定的尺度因子,進行放大或縮小;或者參照SIFT特征提取思想,利用指定的尺度因子對圖像濾波構造尺度空間.改變圖像內容的大小或模糊程度;
●對比度變換(contrast):在圖像的HSV顏色空間,改變飽和度S和V亮度分量,保持色調H不變.對每個像素的S和V分量進行指數運算(指數因子在0.25到4之間),增加光照變化;
●噪聲擾動(noise):對圖像的每個像素RGB進行隨機擾動,常用的噪聲模式是椒鹽噪聲和高斯噪聲;
代碼如下:
?
?
import numpy as np from keras.preprocessing.image import ImageDataGenerator from PIL import Image import matplotlib.pyplot as plt %matplotlib inline train_dir="train" #訓練集路徑 test_dir="test" #測試集路徑 IM_WIDTH=224 #圖像寬度 IM_HEIGHT=224 #圖像高度 batch_size=32 #定義訓練和測試的圖像生成器 #train and val data train_val_datagen = ImageDataGenerator (rotation_range=30, width_shift_range=0.2, height_shift_range=0.2, shear_range=0.2, z0om_range=0.2, horizontal_flip=True, validation__split=0.1) #劃分出驗證集 #test data test_datagen=ImageDataGenerator() #測試集就不用數據增強了 #訓練集圖像生成器 train_generator=train_val_datagen.flow_from_directory(train_dir, target_size=(IM_WIDTH,IM_HEIGHT), #將目標圖片縮放多大的尺寸 batch_size=batch_size, #分批次抽取 subset='training') #驗證集圖像生成器 vaild_generator=train_val_datagen.flow_from_directory(train_dir, target_size=(IM_WIDTH,IM_HEIGHT), batch_size=batch_size, subset='validation') #測試集圖像生成器 test_generator=test_datagen.flow_from_directory(test_dir, target__size=(IM_WIDTH,IM_HEIGHT), batch_size=batch_size,) #驗證圖片生成器的效果,選取生成器的下一個圖片并打印出來 samples_batch=train_generator.next() print(samples_batch[0].shape)#第0位保存的是圖像 print(samples_batch[1].shape)#第1位保存的是標簽 #顯示一張圖片 fig1=samples_batch[0][0] r=Image.fromarray(fig1[:,:,0]).convert('L')#讀第0個通道內的值,轉為灰度值 g=Image.fromarray(fig1[:,:,1]).convert('L') b=Image.fromarray(fig1[:,:,2]).convert('L') image=Image.merge("RGB",(r,g,b))#RGB合并起來 plt.imshow(image) plt.show() print(samples_batch[1][0])#打印標簽——熱編碼
?
?
運行結果:
3.2 自定義圖像生成器
?
?
#自定義訓練集生成器 def myTrainDataGenerator(): while True: trainDataBatch=train_generator.next() #取出一個批次的數據 images=trainDataBatch[0] #取圖像 labels= [trainDataBatch[1] , trainDataBatch[1] , trainDataBatch[1]]#取標簽 yield images, labels #自定義驗證集生成器 def myVaildDataGenerator(): while True: vaildDataBatch=vaild_generator.next() #取出一個批次的數據 images=vaildDataBatch[0] #取圖像 labels= [vaildDataBatch[1] , vaildDataBatch[1] , vaildDataBatch[1]]#取標簽 yield images, labels #自定義測試集生成器 def myTestDataGenerator(): while True: testDataBatch=test_generator.next() #取出一個批次的數據 images=testDataBatch[0] #取圖像 labels= [testDataBatch[1] , testDataBatch[1] , testDataBatch[1]]#取標簽 yield images, labels my_train_generator=myTrainDataGenerator() my_vaild_generator=myVaildDataGenerator() my_test_generator=myTestDataGenerator() a=my_train_generator.__next__() #顯示一張圖片 fig1=a[0][0] r=Image.fromarray(fig1[:,:,0]).convert('L')#讀第0個通道內的值,轉為灰度值 g=Image.fromarray(fig1[:,:,1]).convert('L') b=Image.fromarray(fig1[:,:,2]).convert('L') image=Image.merge("RGB",(r,g,b))#RGB合并起來 plt.imshow(image) plt.show() print(samples_batch[1][0][0])
?
?
3.3 模型實現
?
?
#導入需要使用的包 from keras.models import Model from keras.layers import Input , Dense, Dropout , BatchNormalization, Conv2D , MaxPool2D , AveragePooling2D, concatenate, Flatten from keras.layers.convolutional import Conv2D ,MaxPooling2D, AveragePooling2D from keras.callbacks import ReduceLROnPlateau,ModelCheckpoint , EarlyStopping from keras.preprocessing.image import ImageDataGenerator from keras.models import load_model from keras.preprocessing.image import ImageDataGenerator from PIL import Image #若需要復現,可以把隨機數固定下來 seed=42 np.random.seed(seed) #卷積+BN def Conv2d_BN(prev_layer, #卷積前一層網絡 filters, #卷積核數量 kernel_size, #卷積核大小 padding='same', #‘sanme'指卷積填充是大小保持不小 strides=(1,1) , #步長 name=None #名字 ): if name is not None: bn_name = name+'_bn' conv_name = name +'_conv' else: bn_name = None conv_name = None x = Conv2D(filters, kernel_size, padding=padding,strides=strides , activation='relu',name=conv_name) (prev_layer) #2D圖像卷積參數 x = BatchNormalization(axis=3, name=bn_name)(x) #批次歸一化,- -種標準化操作,防止過擬合的手段 return x #inception模塊 def inception_block(prev_layer, num_filters, name, use_whistle = False, numclasses = -1): #num_filters: [b0,(b11, b12)。(b21,b22)。 b3] 代表不同分支的通道數,即卷積核個數 #use_ whistle:是否要輸出輔助分類器 #1x1卷積分支 branch0=Conv2d_BN(prev_layer=prev_layer,filters=num_filters[0],kernel_size=(1,1),name=name+'-br0-1x1') #3x3卷積分支,1x1-3x3 branch1=Conv2d_BN(prev_layer=prev_layer,filters=num_filters[1][0],kernel__size=(1,1),name=name+'-br1-1x1') branch1=Conv2d_BN(prev__layer=branch1,filters=num_filters[1][1],kernel_size=(3,3),name=name+'-br1-3x3') #5x5卷積分支,1x1-5x5 branch2=Conv2d_BN(prev__layer=prev_layer,filters=num_filters[2][0],kernel_size=(1,1),name=name+'-br2-1x1') branch2=Conv2d_BN(prev_layer=branch2,filters=num_filters[2][1],kernelsize=(5,5),name=name+'-br2-5x5') #池化分支 branch3=MaxPool2D(pool_size=(3,3),strides=(1,1),padding='same',name=name+'-br3-pooL')(prev_layer) branch3=Conv2d_BN(branch3,filters=num_filters[3],kernel__size=(1,1),name=name+'-br3-1x1') #融合 x = concatenate([branch0, branch1, branch2, branch3], axis = 3,name = name) #是否輸出輔助分類器 if(use_whistle): out = aux_whistle(prev_layer, numclasses = numclasses, name = name + '-whistle') return x,out return x #輔助分類器 def aux_whistle(prev_layer,numclasses,name): aux_clf=AveragePooling2D(pool_size=(5,5),strides=(3,3),name=name+'-averagePool')(prev_layer) #池化 aux_clf=Conv2d_BN(aux_clf,filters=128,kernel__size=(1,1),name=name+'-1x1conv') #卷積 aux_clf=Flatten(name=name+'-flatten')(aux_clf) aux_clf=Dense(1024,activation='relu')(aux_clf) #全連接 aux_clf=Dropout(0.3,name=name+'-dropout')(aux_clf) aux_clf=Dense(num_classes,activation='softmax',name=name+'-predictions')(aux_clf) return aux_clf def inceptionNet(input_shape,numclasses): inp=Input(shape=input_shape) #「卷積+池化」x2 x=Conv2d_BN(inp,filters=64,kernel_size=(7,7),strides=(2,2),name='2a') x=MaxPool2D(poolsize=(3,3),strides=(2,2),padding='same',name='2pool-1')(x) x=Conv2d_BN(x,filters=192,kernel_size=(3,3),name='2b') x=MaxPool2D(pool__size=(3,3),strides=(2,2),padding='same',name='2pool-2')(x) #第-Inception模塊組,3a.3b x=inception_block(x,(64,(96,128),(16,32),32),name='inception3a') x=inception_block(x,(128,(128,192),(32,96),64),name='inception3b') x=MaxPoo12D(pool_size=(3,3),strides=(2,2),padding='same',name='3pool')(x) #第二Inception模塊組,4a、4b(輔助)、4c.4d(輔助)、4e x=inception_block(x,(192,(96,208),(16,48),64),name='inception4a') x,whistle1=inception_block(x,(160,(112,224),(24,64),64),name='inception4b',use_whistle=True,numclasses=numclasses) x=inception_block(x,(128,(128,256),(24,64),64),name='inception4c') x,whistle2=inception_block(x,(112,(144,288),(32,64),64),name='inception4d',use_whistle=True,numclasses=numclasses) x=inception_block(x,(256,(160,320),(32,128),128),name='inception4e') x=MaxPool2D(poolsize=(3,3),strides=(2,2),padding='same',name='4pool')(x) #第三Inception模塊組,5a.5b x=inception_block(x,(256,(160,320),(32,128),128),name='inception5a') x=inception_block(x,(384,(192,384),(48,128),128),name='inception5b') #全局平均池化 x=AveragePooling2D(pool_size=(7,7),strides=(1,1),padding='valid',name='avg7x7')(x) #X=Dropout(0.4)(x) # FC+Softmax分類 x = Flatten (name='flatten')(x) x = Dense(numclasses, activation= 'softmax',name= 'predictions')(x) model = Model( inp, [x, whistle1,whistle2] ,name=' inception_v1') return model
?
?
3.4 模型編譯
?
?
num_classes=len(train_generator.classindices) #獲取類別數 model=inceptionNet(input_shape=(224,224,3),numclasses=num_classes)#獲取model對象 model.compile(optimizer='adam', #優化器 loss='categorical_crossentropy', #損失函數 loss_weights=[1.0,0.3,0.3], #損失函數權重 metrics=['accuracy']) #評價標準(錯誤率),如果要用top-k:['accuracy',metric.top_k__categorical_accuracy] model.summary()#打印出模型概述信息
?
?
3.5 模型訓練
?
?
EPOCH=10 #一個Epoch代表遍歷- -次所有數據 batch_size=32 #一 個批次內的圖片數量 modelfilepath='model.best.hdf5' #保存路勁 #無法更優則自動終止 earlyStop=EarlyStopping(monitor='val_predictions__acc', patience=30, verbose=1, mode='auto') #保存最好的模型 checkpoint=ModelCheckpoint(modelfilepath, monitor='val_predictions__acc', verbose=1, save__best_only=True, mode='max') #根據不同階段,降低學習率 reduce_Ir=ReduceLROnPlateau(monitor='val_predictions_loss', factor=0.1, patience=10, verbose=1, mode='auto', min_delta=0.00001, C0oldown=0, min__lr=0) history=model.fit_generator(my_train_generator,validation_data=my_vaild_generator,epochs=EPOCH,steps_per_epoch=train_generator.n/batch_size ,validation_steps=vaild_generator.n/batch_size,callbacks=[checkpoint,reduce_lr,earlyStop])
?
?
訓練結果:
3.6 模型測試
?
?
#=====模型測試========= testmodel=load_model (modelfilepath) loss,predictions_loss,aux1_loss, aux2_loss, predictions_acc,aux1_acc, aux2_acc=testmodel. evaluate_generator(my_test_generator,steps=test_generator.n/batch_size) #繪制訓練&驗證的準確率值 plt.plot(history.history['predictions_acc']) plt.plot(history.history['val_predictions_acc']) plt.title('Model accuracy') plt.ylabel('Accuracy') plt.xlabel('Epoch') plt.legend(['Train','Val'],loc='upper left') plt.show() #繪制訓練&驗證的損失值 plt.plot(history.history['loss']) plt.plot(history.history['val_loss']) plt.title('Model loss') plt.ylabel('Loss') plt.xlabel('Epoch') plt.legend(['Train','Val'],loc='upper left') plt.show()
?
?
測試結果:
?
?
?
?
?
編輯:黃飛
?
評論
查看更多