貓狗識別訓練-遷移學習
下載資料集
下載地址:https://www.kaggle.com/c/dogs-vs-cats/data
下載的訓練集中有2.5W張貓貓狗狗的圖片,我這裡只用訓練測試集壓縮包就行了,驗證集和測試集都可以從中切分。
觀察圖片可得知命名方式,貓圖片為cat.數字.jpg,狗圖片為dog.數字.jpg,各有12500張。
規劃資料
資料需要分成三份:訓練集、驗證集和測試集。
我打算使用1.9W張圖片作為訓練集,4000張圖片作為驗證集,2000張圖片作為測試集。
import os,shutil from tensorflow import keras import matplotlib.pyplot as plt from tensorflow.keras.applications.inception_v3 import InceptionV3 from tensorflow.keras import layers #原始圖片存放目錄 origin_dir = './origin/train' #訓練資料集儲存位置 base_dir = './data' #訓練集 驗證集 測試集 train_dir = base_dir + '/train' validation_dir = base_dir + '/validation' test_dir = base_dir + '/test' #如果目錄存在先刪掉 if True == os.path.exists(base_dir) : shutil.rmtree(base_dir) os.makedirs(base_dir) #建立子目錄 validation_dog_dir = validation_dir + '/dog' validation_cat_dir = validation_dir + '/cat' test_dog_dir = test_dir + '/dog' test_cat_dir = test_dir + '/cat' train_dog_dir = train_dir + '/dog' train_cat_dir = train_dir + '/cat' #建立目錄 os.makedirs(validation_dog_dir) os.makedirs(validation_cat_dir) os.makedirs(test_dog_dir) os.makedirs(test_cat_dir) os.makedirs(train_dog_dir) os.makedirs(train_cat_dir) #複製2000張狗圖片到驗證資料集狗目錄 files = ['dog.{}.jpg'.format(i) for i in range(2000)] for file in files : src = os.path.join(origin_dir,file) dst = os.path.join(validation_dog_dir,file) shutil.copy(src,dst) #複製2000張貓圖片到驗證資料集貓目錄 files = ['cat.{}.jpg'.format(i) for i in range(2000)] for file in files : src = os.path.join(origin_dir,file) dst = os.path.join(validation_cat_dir,file) shutil.copy(src,dst) #複製1000張狗圖片到測試資料集狗目錄 files = ['dog.{}.jpg'.format(i) for i in range(2000,3000)] for file in files : src = os.path.join(origin_dir,file) dst = os.path.join(test_dog_dir,file) shutil.copy(src,dst) #複製1000張貓圖片到測試資料集狗目錄 files = ['cat.{}.jpg'.format(i) for i in range(2000,3000)] for file in files : src = os.path.join(origin_dir,file) dst = os.path.join(test_cat_dir,file) shutil.copy(src,dst) #複製9500張狗圖片到訓練資料集狗目錄 files = ['dog.{}.jpg'.format(i) for i in range(3000,12500)] for file in files : src = os.path.join(origin_dir,file) dst = os.path.join(train_dog_dir,file) shutil.copy(src,dst) #複製9500張貓圖片到訓練資料集貓目錄 files = ['cat.{}.jpg'.format(i) for i in range(3000,12500)] for file in files : src = os.path.join(origin_dir,file) dst = os.path.join(train_cat_dir,file) shutil.copy(src,dst)
遷移網路
我將使用在ImageNet上訓練好的大型卷積神經網路Inception-V3進行遷移,ImageNet資料集包含140萬張圖片,1000多個分類,包含了很多動物類別,貓和狗自然也在其中,在ImageNet上訓練好的模型還有Eception、VGG16、VGG19、ResNet、MobileNet等。
conv_base = InceptionV3(weights='imagenet',include_top=False,input_shape=(299,299,3))
include_top=False表示不需要網路頂層,也就是輸出層,因為我只需要它提取的特徵,在其基礎上我要新增網路層繼續訓練。
凍結層
在訓練之前一定要將遷移的網路進行凍結,InceptionV3模型有兩千一百萬個引數,不凍結的話,其中的權重會在訓練過程中被修改,這會對原網路的優秀性造成破壞,再者說,不凍結的話相當於自己重新訓練一個InceptionV3模型,我估計我這電腦跑一個月都不一定能跑的完。
但是呢,我不能將它凍結完,因為遷移的模型是以識別1000個類別而誕生的,我這裡只有貓和狗2個類別,所以我需要微調模型讓它更能適應處理我此時此刻面對的問題,怎麼解決呢?辦法是凍結,但不凍結全部,留幾層出來參與訓練。
InceptionV3的網路結構是由一塊一塊的Inception組成,每一塊Inception包含了多種不同尺寸的卷積核、池化層、批標準化等組合。我解凍最後兩塊Inception,通過觀察網路結構可以定位到從第249層開始解凍。
conv_base.trainable = True for layer in conv_base.layers[:249]: layer.trainable = False for layer in conv_base.layers[249:]: layer.trainable = True
搭建網路結構
在Inception的基礎上,我再懟上兩層全接連網路,正則化和Dropout也懟上,讓我的曲線更加絲滑柔順。
model = keras.models.Sequential([ conv_base, keras.layers.Flatten(), keras.layers.Dropout(0.3), keras.layers.Dense(256,activation='relu',kernel_regularizer=keras.regularizers.l2()), keras.layers.Dropout(0.3), keras.layers.Dense(1,activation='sigmoid') ])
模型編譯
model.compile(optimizer=keras.optimizers.SGD(lr=0.0001,momentum=0.9),loss='binary_crossentropy', metrics=['accuracy'])
資料生成器
img_width=299 img_height=299 img_channel = 3 batch_size=32 epochs = 6 train_datagen = keras.preprocessing.image.ImageDataGenerator(rescale=1. / 255) validation_datagen = keras.preprocessing.image.ImageDataGenerator(rescale=1. / 255) test_datagen = keras.preprocessing.image.ImageDataGenerator(rescale=1. / 255) train_generator = train_datagen.flow_from_directory( train_dir, target_size=(img_width, img_height), batch_size=batch_size, class_mode='binary') validation_generator = validation_datagen.flow_from_directory( validation_dir, target_size=(img_width, img_height), batch_size=batch_size, class_mode='binary') test_generator = test_datagen.flow_from_directory( test_dir, target_size=(img_width, img_height), batch_size=batch_size, class_mode='binary')
執行訓練
history = model.fit( train_generator, steps_per_epoch=train_generator.n // batch_size, epochs=epochs, validation_data=validation_generator, validation_steps=validation_generator.n // batch_size, verbose=1)
這個生成器的訓練以前是model.fit_generator方法,從TensorFlow2.1開始就廢棄了,可直接使用model.fit方法傳入生成器。
模型評估
score = model.evaluate(test_generator, steps=test_generator.n // batch_size) print('測試準確率:{}, 測試loss值: {}'.format(score[1], score[0]))
視覺化acc和loss曲線
plt.rcParams['font.sans-serif']=['SimHei'] acc = history.history['accuracy'] val_acc = history.history['val_accuracy'] loss = history.history['loss'] val_loss = history.history['val_loss'] plt.subplot(1, 2, 1) plt.plot(acc, label='訓練Acc') plt.plot(val_acc, label='測試Acc') plt.title('Acc曲線') plt.legend() plt.subplot(1, 2, 2) plt.plot(loss, label='訓練Loss') plt.plot(val_loss, label='測試Loss') plt.title('Loss曲線') plt.legend() plt.show()
&n