【深度學習】keras + tensorflow 實現貓和狗圖像分類


本文主要是使用【監督學習】實現一個圖像分類器,目的是識別圖片是貓還是狗。

從【數據預處理】到 【圖片預測】實現一個完整的流程, 當然這個分類在 Kaggle 上已經有人用【遷移學習】(VGG,Resnet)做過了,遷移學習我就不說了,我自己用 Keras + Tensorflow 完整的實現了一遍。

 

准備工作:

  1. 數據集: Dogs vs. Cats  注冊激活困難,自己想想辦法,Ps:實在注冊不了百度雲有下載自己搜搜
  2. 使用編程語言:當然是Python 3,你問我為什么,當然是人生苦短。
  3. 使用機器學習庫:Numpy(科學計算的庫,主要是矩陣運算,真特么好用),sklearn( 機器學習庫), Keras(高層神經網絡API,真特么好用,馬雲用了都說好),Tensorflow-GPU版(深度學習框架,用的人都說好,沒用的也說好)
  4. 編輯器:Visual studio code (巨硬大法好),安裝Python插件

 Ps:NVIDIA的顯卡才支持GPU加速運算,具體哪些卡,看它的官網,使用GPU比CPU要節省四五倍的時間。

先導入用到的庫:

import os  
os.environ['TF_CPP_MIN_LOG_LEVEL'] = '2' 
import numpy as np
from keras import callbacks
from keras.models import Sequential, model_from_yaml, load_model
from keras.layers import Dense, Conv2D, Flatten, Dropout, MaxPool2D
from keras.optimizers import Adam, SGD
from keras.preprocessing import image
from keras.utils import np_utils, plot_model
from sklearn.model_selection import train_test_split
from keras.applications.resnet50 import preprocess_input, decode_predictions

注意: os.environ['TF_CPP_MIN_LOG_LEVEL'] = '2'  

這一行代碼是為了不讓在控制錢顯示Tensorflow輸出的一堆信息,不寫就可以看到 tensorflow 輸出的一堆日志。

 

  • 加載數據:下載的壓縮包解壓出來,目錄下面有25,000 貓和狗的圖片

線上代碼,后面解釋

def load_data():
    path = './data/train/'
    files = os.listdir(path)
    images = []
    labels = []
    for f in files:
        img_path = path + f
        img = image.load_img(img_path, target_size=image_size)
        img_array = image.img_to_array(img)
        images.append(img_array)

        if 'cat' in f:
            labels.append(0)
        else:
            labels.append(1)

    data = np.array(images)
    labels = np.array(labels)

    labels = np_utils.to_categorical(labels, 2)
    return data, labels

 

因為計算機不能直接對圖片,視頻,文字等直接進行運算,所以首先要把圖片轉成數值類型的矩陣,並且保證你訓練的圖片大小一樣,我在這里使用keras自帶的圖片處理類    from keras.preprocessing import image ,主要是就是兩個函數 :

 image.load_img(img_path, target_size=image_size)  第一個參數圖片的路徑,第二個參數target_size 是個tuple 類型,(img_w,img_h)

  image.img_to_array(img)  圖片轉成矩陣,當然你也可以使用Numpy的   asarray  效果應該一樣

 

  • 構建模型:圖片分類肯定是卷積模型最好,這個目前位置不用質疑,應該Imagenet 已經證明了,下面先看代碼:
    model = Sequential()

    model.add(Conv2D(32, kernel_size=(5, 5), input_shape=(img_h, img_h, 3), activation='relu', padding='same'))
    model.add(MaxPool2D())
    model.add(Dropout(0.3))

    model.add(Conv2D(64, kernel_size=(5, 5), activation='relu', padding='same'))
    model.add(MaxPool2D())
    model.add(Dropout(0.3))

    model.add(Conv2D(128, kernel_size=(5, 5), activation='relu', padding='same'))
    model.add(MaxPool2D())
    model.add(Dropout(0.5))

    model.add(Conv2D(256, kernel_size=(5, 5), activation='relu', padding='same'))
    model.add(MaxPool2D())
    model.add(Dropout(0.5))

    model.add(Flatten())
    model.add(Dense(512, activation='relu'))
    model.add(Dropout(0.5))

    model.add(Dense(2, activation='softmax'))

    model.summary()  //這一句只是輸出網絡結構

模型:使用序貫模型,然后加了4個卷積層,Conv2D 第一個參數就是卷基層的輸出維度,為什么我寫了32呢,因為我電腦渣啊,GPU顯存太小了,否則我就寫64了。參考了VGG,Resnet 等的網絡結構

激活函數:卷基層的激活函數使用非線性激活函數: relu。輸出層的激活函數使用 softmax, 多分類就用這個。

池化層(MaxPool2D):主要是降維,減少參數加速運算,防止過擬合,為了防止過擬合還加入了 Dropout

 

  • 編譯設計的模型:
    sgd = Adam(lr=0.0003)
    model.compile(loss='binary_crossentropy',optimizer=sgd, metrics=['accuracy'])

講一下優化器: Adam(lr=0.0003) 效果最好,基本都是用這個,lr:學習速率,學習速率越小,理論上來說損失函數越小,精度越高,但是計算越慢,默認是 0.001

注意:不加  metrics=['accuracy'] 參數不會輸出日志,在控制台看不到變化。

 

  • 切分數據:從訓練數據分割80% 用來訓練,20%訓練驗證
    images, lables = load_data()
    images /= 255
    x_train, x_test, y_train, y_test = train_test_split(images, lables, test_size=0.2)

除以 255 是為了數據歸一化,理論上來說歸一化,會減少損失函數的震盪,有助於減小損失函數提高精度。

 

  • 訓練:加了Tensorlow 可視化,使用的是 TensorBoard,可以清晰的看到 損失函數和精度的變化趨勢
  print("train.......")
    tbCallbacks = callbacks.TensorBoard(log_dir='./logs', histogram_freq=1, write_graph=True, write_images=True)
    model.fit(x_train, y_train, batch_size=nbatch_size, epochs=nepochs, verbose=1, validation_data=(x_test, y_test), callbacks=[tbCallbacks])

運行 TensorBoard 只需要兩行代碼,在cmd,cd D:\Learning\learn_python\   先切到你的logs目錄的上一級,然后執行 tensorboard --logdir="logs" 即可。

 

  • 評估:返回兩個數據,這個沒什么說的
    scroe, accuracy = model.evaluate(x_test, y_test, batch_size=nbatch_size)
    print('scroe:', scroe, 'accuracy:', accuracy)

 

  • 保存模型:保存是為了可以方便的遷移學習,把網絡結構和權重分開保存,當然也可以直接一起保存,需要的導入: from keras.models import model_from_yaml, load_model 
    yaml_string = model.to_yaml()
    with open('./models/cat_dog.yaml', 'w') as outfile:
        outfile.write(yaml_string)
    model.save_weights('./models/cat_dog.h5')

 

  • 調參:調參其實就是在對抗過擬合和欠擬合,欠擬合可以消除,但是過擬合只能緩解沒辦法消除。有人說

深度學習工程師50%的時間在調參數,49%的時間在對抗過/欠擬合,剩下1%時間在修改網上down下來的程序

深以為然啊,剛開始我的網絡結構不是這樣的,卷基層只有2層,kernel_size=(3,3), 學習速率采用的默認參數,全連接層是: Dense(256),訓練之后發現欠擬合,精度只有86%左右,后來增加了卷積層數量,調小學習速率等幾輪的調參,精度接近93%,還可以繼續提升,但我不想調了,因為筆記本的GPU太渣(1050ti)訓練一次差不多需要一個多小時。

調參也沒什么好辦法,只能一次次的去試,如果采用遷移學習,VGG,Resnet的網絡結構和權重的話,分分鍾能上98%,毫無難度。

 

  • 預測真實數據:保存的網絡結構.yaml文件 和權重 .h5 文件先加載進來,然后編譯,然后直接預測
def pred_data():

    with open('./models/cat_dog.yaml') as yamlfile:
        loaded_model_yaml = yamlfile.read()
    model = model_from_yaml(loaded_model_yaml)
    model.load_weights('./models/cat_dog.h5')

    sgd = Adam(lr=0.0003)
    model.compile(loss='categorical_crossentropy',optimizer=sgd, metrics=['accuracy'])

    images = []
    path='./data/test/'
    for f in os.listdir(path):
        img = image.load_img(path + f, target_size=image_size)
        img_array = image.img_to_array(img)

        x = np.expand_dims(img_array, axis=0)
        x = preprocess_input(x)
        result = model.predict_classes(x,verbose=0)

        print(f,result[0])
    

因為做的分類任務,我在加載數據的時候寫的是 cat 索引為 0 ,dog索引為 1,所以輸出的時候,預測的值與之對應,我從百度找了20張圖片貓狗個10張,圖片長這樣:

 

預測的結果如下:

 

可以看到,貓 有一張錯誤,狗 有兩張錯誤,這個精度在小樣本數據集不適用遷移學習的情況下還是可以的。

 

完整代碼:https://github.com/jarvisqi/deep_learning

 

 

參考:http://keras-cn.readthedocs.io/en/latest/

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM