1. 程式人生 > >P7畢業專案,貓狗大戰。詳解,含全部程式碼

P7畢業專案,貓狗大戰。詳解,含全部程式碼

準備工作:

1、申請一個aws雲機器,因為本地訓練實在太慢了,當然,土豪請略過……

2、申請一個kaggle賬號,資料集可以利用winscp上傳到雲端,但上傳速度過慢,建議利用kaggle-cli下載資料集到aws雲端(申請kgggle賬號時註冊碼無法顯示,你需要下載一個外掛,用chrome瀏覽器。)

3、利用putty連線到雲端後,啟用tensorflow_p36的虛擬環境,升級並安裝相關的包,比如sklearn,opencv等等;

4、下載資料集到雲端:

kg download -u <username> -p <password> -c dogs-vs-cats-redux-kernels-edition -f <filename>

now,備份你的ami,開始吧。

正式開始:


# coding: utf-8

# # 貓狗大戰

# 本質是圖片分類問題,根據資料集訓練後,檢測圖片屬於哪個類別,共兩類,dog and cat.

# ## 1、第一步:資料預處理

# In[1]:

#匯入資料
import numpy as np
import tensorflow as tf
from PIL import Image
import matplotlib.pyplot as plt
trainCwd = "./train/"
testCwd = "./test1/"
classes = ['cat','dog']


# ### 1.1定義檔案分離函式,將訓練資料一部分拆成驗證資料。利用keras的flow_from_directory建立datagenerator。
# 

# In[2]:

import os
import shutil
def filesplit(trainCwd):
    for i in classes:
        trainDir = os.path.join(trainCwd,i)
        print(trainDir)
        if os.path.exists(trainDir)==False:
            os.makedirs(trainDir)
            print("%s is created"%(trainDir))
    filedir = os.listdir(trainCwd)          #資料類別
    catfolder = os.path.join(trainCwd,"cat")
    dogfolder = os.path.join(trainCwd,"dog")
    for root,sub_folders,files in os.walk(trainCwd):
        for name in files:
            try:
                if os.path.exists(os.path.join(root,name))==True:
                    if name[0:3]=="cat":
                        shutil.move(os.path.join(root,name),catfolder)
                    if name[0:3]=="dog":
                        shutil.move(os.path.join(root,name),dogfolder)
            except:
                pass
    if os.path.exists(os.path.join(testCwd,"test"))==False:
        os.makedirs(os.path.join(testCwd,"test"))
    testFolder = os.path.join(testCwd,"test")
    for root,sub_folders,files in os.walk(testCwd):
        for name in files:
            try:
                if os.path.exists(os.path.join(root,name))==True:
                        shutil.move(os.path.join(root,name),testFolder)
            except:
                pass
    


# 定義圖片讀取函式
#    將圖片讀入列表中,進行圖片和標籤的一一對應,並進行shuffle處理。同時拆分出驗證集。

# In[3]:

from sklearn.model_selection import train_test_split

def get_file(file_dir,dataName,width,height,test_size=0.2):
    images = []
    labels = []
    temp =[]
    X_train = []
    Y_train = []
    X_val = []
    Y_val = []
    X_test = []
    Y_test =[]
    file_name = os.path.join(file_dir+dataName)
    if (dataName == "trainData.pkl"):
        if os.path.exists(file_name):           #判斷之前是否有存到檔案中
            X_train, y_train,X_test, y_test = pickle.load(open(file_name,"rb"))
            return X_train, y_train,X_test, y_test
        else:
            for root,sub_folders,files in os.walk(file_dir):
                for name in files:
                    #img = cv2.imread(os.path.join(root,name))
                    images.append(os.path.join(root,name))
                    if name[0:3]=="cat":
                        labels.append(0)
                    else:
                        labels.append(1)

            tmp = np.array([images,labels])
            tmp = tmp.transpose()
            np.random.shuffle(tmp)
            x = tmp[:,0]
            y = tmp[:,1]    
            X_train, X_val, Y_train, Y_val = train_test_split(x, y, test_size=test_size,random_state=1)
            
            pickle.dump((X_train, Y_train, X_val, Y_val),open(file_name,"wb"))
            print("file created!")
            return  X_train,Y_train,X_val,Y_val
    else:
        if os.path.exists(file_name):
            X_test,Y_test = pickle.load(open(file_name,"rb"))
        else:
            for root,sub_folders,files in os.walk(file_dir):
                for name in files:
                    images.append(os.path.join(root,name))
                    x_test = np.array([images])
                    pickle.dump(x_test,open(file_name,"wb"))
                    return  x_test


# In[4]:

import math
import cv2
def display_img(img_list, summary = True):
    fig = plt.figure(figsize=(15, 3 * math.ceil(len(img_list)/5)))
    for i in range(0, len(img_list)):
        img = cv2.imread(img_list[i])
        img = img[:,:,::-1]#BGR->RGB
        if summary:
            print("---->image: {}  - shape: {}".format(img_list[i], img.shape))
        ax = fig.add_subplot(math.ceil(len(img_list)/5),5,i+1)
        ax.set_title(os.path.basename(img_list[i]))
        ax.set_xticks([])
        ax.set_yticks([])
        img = cv2.resize(img, (128,128))
        ax.imshow(img)
    plt.show()


# ###  列出dog 和cat的類別,為預測做準備

# In[5]:

Dogs = [ 'n02085620','n02085782','n02085936','n02086079','n02086240','n02086646','n02086910','n02087046','n02087394','n02088094','n02088238',
        'n02088364','n02088466','n02088632','n02089078','n02089867','n02089973','n02090379','n02090622','n02090721','n02091032','n02091134',
        'n02091244','n02091467','n02091635','n02091831','n02092002','n02092339','n02093256','n02093428','n02093647','n02093754','n02093859',
        'n02093991','n02094114','n02094258','n02094433','n02095314','n02095570','n02095889','n02096051','n02096177','n02096294','n02096437',
        'n02096585','n02097047','n02097130','n02097209','n02097298','n02097474','n02097658','n02098105','n02098286','n02098413','n02099267',
        'n02099429','n02099601','n02099712','n02099849','n02100236','n02100583','n02100735','n02100877','n02101006','n02101388','n02101556',
        'n02102040','n02102177','n02102318','n02102480','n02102973','n02104029','n02104365','n02105056','n02105162','n02105251','n02105412',
        'n02105505','n02105641','n02105855','n02106030','n02106166','n02106382','n02106550','n02106662','n02107142','n02107312','n02107574',
        'n02107683','n02107908','n02108000','n02108089','n02108422','n02108551','n02108915','n02109047','n02109525','n02109961','n02110063',
        'n02110185','n02110341','n02110627','n02110806','n02110958','n02111129','n02111277','n02111500','n02111889','n02112018','n02112137',
        'n02112350','n02112706','n02113023','n02113186','n02113624','n02113712','n02113799','n02113978']
Cats=['n02123045','n02123159','n02123394','n02123597','n02124075','n02125311','n02127052']


# In[6]:

from keras.preprocessing import image
from keras.applications.inception_v3 import InceptionV3,preprocess_input
from keras.applications.vgg19 import VGG19
from keras.optimizers import SGD
import numpy as np
from keras.applications.resnet50 import ResNet50
from keras.applications import *
import h5py
from keras.models import *
from keras.layers import *
from keras.applications.xception import Xception



# In[ ]:

#model_vgg = VGG19(weights='imagenet')   #當啟用vgg進行異常值判斷時
model_vgg =Model()
#model_xce = Xception(weights='imagenet')#當啟用xception進行異常值判斷時
model_xce=Model()
model_res = Model()
#model_res = ResNet50(weights='imagenet')#當啟用resnet進行異常值判斷時
###不在此處一次進行全部初始化,是因為可以避免佔用更多的記憶體,為後續的訓練節省資源。


# #建立generator

# In[8]:

from keras.preprocessing.image import ImageDataGenerator
def creatDataGen(dir,width,height,batch_size):
    datagen = ImageDataGenerator()
    generator = datagen.flow_from_directory(
      dir,
      target_size=(width, height),
      shuffle=False,
      batch_size=batch_size,
      class_mode=None)
    print("generator created:",generator)
    return generator


# ## 預測異常資料

# In[9]:

#得到所有的影象地址列表
train_img_list = []
def get_image_list(path_name, list_name):
    for file_name in os.listdir(path_name):
        subPath = os.path.join(path_name,file_name)
        for file in os.listdir(subPath):
            list_name.append(os.path.join(subPath, file))

get_image_list(trainCwd, train_img_list)
with open("./filelist.txt", 'w') as f:
            for item in train_img_list:
                f.write("{}\n".format(item))
print("train image sample:{}".format(len(train_img_list)))


# In[15]:

#計算得到錯誤圖片,根據前期定義的三個模型,判斷後取並集

def getErrorImg(img_path_list,model,preprocess_input,decode_predictions,width,height,top_num=50):
    ret_img_path = []
    if os.path.exists("./abnormal.txt"):
        with open("./abnormal.txt", 'r') as f:
            items = f.readlines()
            ret_img_path = [item.strip('\n') for item in items]
    for index in range(len(img_path_list)):
        img = image.load_img(img_path_list[index], target_size=(width, height))
        x = image.img_to_array(img)
        x = np.expand_dims(x, axis=0)
        x = preprocess_input(x)
        preds = model.predict(x)
        dps = decode_predictions(preds, top = top_num)[0]
        for i in range(len(dps)):
            if (dps[i][0] in Dogs):
                break;
            elif (dps[i][0] in Cats):
                break;
            if i==len(dps)-1:
                ret_img_path.append(img_path_list[index])
                print(dps[i][0],"abnormal found!")
    print("total {} jpgs found",len(ret_img_path))
    with open("./abnormal.txt", 'w') as f:
          for item in ret_img_path:
                f.write("{}\n".format(item))
    return ret_img_path
ImgRes = getErrorImg(train_img_list,model_res,resnet50.preprocess_input,resnet50.decode_predictions,224,224)       


# In[16]:

imgVgg = getErrorImg(train_img_list,model_vgg,vgg19.preprocess_input,vgg19.decode_predictions,224,224)


# In[17]:

imgXce = getErrorImg(train_img_list,model_xce,xception.preprocess_input,xception.decode_predictions,299,299)


# In[28]:

def joinAbnormal_Imglist():
    vgg_xce_union = list(set(imgVgg).union(set(imgXce)))
    abnormal_v = list(set(ImgRes).union(set(vgg_xce_union)))
    return abnormal_v

abnormal = joinAbnormal_Imglist()
print(len(abnormal))
display_img(abnormal, summary = False)    


# # 刪除異常值

# In[ ]:

for i in range(len(abnormal)):
    
    os.remove(abnormal[i])


# #建立模型

# In[10]:


def IncepModel(width,height):
    input_tensor = Input((height, width, 3))
    x = input_tensor
    x = Lambda(preprocess_input)(x)
    base_model = InceptionV3(input_tensor=x, weights='imagenet', include_top=False)
    model = Model(base_model.input, GlobalAveragePooling2D()(base_model.output))
    
    return model


# #VGGModel,vgg模型特徵層提取

# In[11]:

def VGGModel(width,height):
    input_tensor = Input((height, width, 3))
    x = input_tensor
    x = Lambda(vgg19.preprocess_input)(x)
    base_model = VGG19(input_tensor=x, weights='imagenet', include_top=False)
    #base_model = VGG19(weights='imagenet', include_top=False)
    model = Model(base_model.input, GlobalMaxPooling2D()(base_model.output))
        
    return model


# Res50模型特徵層提取

# In[12]:

def Res50Model():

    base_model = ResNet50(weights='imagenet', include_top=False)

    model = Model(base_model.input, GlobalAveragePooling2D()(base_model.output))
        
    return model


# 匯出模型特徵檔案,將訓練資料和測試資料分別送入各模型,匯出最後一層的輸出向量結果,將最後一層連線;作為最終輸出前的特徵向量。
# 在最後一層利用sigmoid函式輸出分類結果。

# In[13]:

WIDTH, HEIGHT = 299, 299   #fixed size for InceptionV3

BAT_SIZE = 32

filesplit(trainCwd = trainCwd)

if os.path.exists(os.path.join("./","IncepWeights.h5"))==False:
    print("yes ,false!")
    train_gen = creatDataGen(trainCwd,WIDTH,HEIGHT,BAT_SIZE)
    test_gen = creatDataGen(testCwd,WIDTH,HEIGHT,BAT_SIZE)

    model = IncepModel(WIDTH,HEIGHT)
    train = model.predict_generator(train_gen)

    test = model.predict_generator(test_gen)
    with h5py.File("IncepWeights.h5") as h:
            h.create_dataset("train", data=train)
            h.create_dataset("test", data=test)
            h.create_dataset("label", data=train_gen.classes)
            
if os.path.exists(os.path.join("./","VGG19Weights.h5"))==False: 
    train_gen2 = creatDataGen(trainCwd,224,224,BAT_SIZE)
    test_gen2 = creatDataGen(testCwd,224,224,BAT_SIZE)
    model2 = VGGModel()
    train2 = model2.predict_generator(train_gen2)
    test2 = model2.predict_generator(test_gen2)

    with h5py.File("VGG19Weights.h5") as h:
            h.create_dataset("train", data=train2)
            h.create_dataset("test", data=test2)
            h.create_dataset("label", data=train_gen2.classes)

    print("VGGweights done!")
    
if os.path.exists(os.path.join("./","Res50Weights.h5"))==False: 
    model3 = Res50Model()
    train3 = model3.predict_generator(train_gen2)
    test3 = model3.predict_generator(test_gen2)

    with h5py.File("Res50Weights.h5") as h:
            h.create_dataset("train", data=train3)
            h.create_dataset("test", data=test3)
            h.create_dataset("label", data=train_gen2.classes)        
    print("Res50weights done!")


# In[14]:

from sklearn.utils import shuffle
def bulid_Input(filename='Res50Weights.h5',base_net_num=1):
    
    np.random.seed(2017)
    X_train = []
    X_test = []
    y_train = []
    if base_net_num == 3:
        for filename in ["Res50Weights.h5", "VGG19Weights.h5", "IncepWeights.h5"]:
            with h5py.File(filename, 'r') as h:
                X_train.append(np.array(h['train']))
                X_test.append(np.array(h['test']))
                y_train = np.array(h['label'])
        X_train = np.concatenate(X_train, axis=1)
        X_test = np.concatenate(X_test, axis=1)
    elif base_net_num ==1:
        with h5py.File(filename,'r') as h:
                X_train.append(np.array(h['train']))
                X_test.append(np.array(h['test']))
                y_train = np.array(h['label'])
        X_train = np.concatenate(X_train, axis=1)
        X_test = np.concatenate(X_test, axis=1)
    X_train,y_train = shuffle(X_train,y_train)
    return X_train,X_test,y_train




# In[15]:

#X_train,X_test,y_train = bulid_Input("VGG19Weights.h5",base_net_num =1)
#X_train,X_test,y_train = bulid_Input("Res50Weights.h5",base_net_num =1)
#X_train,X_test,y_train = bulid_Input("IncepWeights.h5",base_net_num =1)
X_train,X_test,y_train = bulid_Input(base_net_num =3)
input_tensor = Input(X_train.shape[1:])
x = input_tensor
x = Dropout(0.5)(x)
x = Dense(60,activation = "relu")(x)
x = Dense(1, activation='sigmoid')(x)
model = Model(input_tensor, x)
model.summary()
model.compile(optimizer='adadelta',
              loss='binary_crossentropy',
              metrics=['accuracy'])


# In[16]:

from keras.callbacks import Callback
class LossHistory(Callback):
    def on_train_begin(self, logs={}):
        self.losses = {'batch':[], 'epoch':[]}
        self.accuracy = {'batch':[], 'epoch':[]}
        self.val_loss = {'batch':[], 'epoch':[]}
        self.val_acc = {'batch':[], 'epoch':[]}

    def on_batch_end(self, batch, logs={}):
        self.losses['batch'].append(logs.get('loss'))
        self.accuracy['batch'].append(logs.get('acc'))
        self.val_loss['batch'].append(logs.get('val_loss'))
        self.val_acc['batch'].append(logs.get('val_acc'))

    def on_epoch_end(self, batch, logs={}):
        self.losses['epoch'].append(logs.get('loss'))
        self.accuracy['epoch'].append(logs.get('acc'))
        self.val_loss['epoch'].append(logs.get('val_loss'))
        self.val_acc['epoch'].append(logs.get('val_acc'))

    def loss_plot(self, loss_type):
        iters = range(len(self.losses[loss_type]))
        plt.figure()
        # acc
        plt.plot(iters, self.accuracy[loss_type], 'r', label='train acc')
        # loss
        plt.plot(iters, self.losses[loss_type], 'g', label='train loss')
        if loss_type == 'epoch':
            # val_acc
            plt.plot(iters, self.val_acc[loss_type], 'b', label='val acc')
            # val_loss
            plt.plot(iters, self.val_loss[loss_type], 'k', label='val loss')
        plt.grid(True)
        plt.xlabel(loss_type)
        plt.ylabel('acc-loss')
        plt.legend(loc="upper right")
        plt.show()


# In[17]:

from keras.callbacks import TensorBoard
from keras.callbacks import EarlyStopping
history = LossHistory()
early_stopping = EarlyStopping(monitor='val_loss', patience=10, verbose=2)
model.fit(X_train, y_train, batch_size=96, epochs=50, validation_split=0.2,
          callbacks=[TensorBoard(log_dir='./temp/log/union'),early_stopping,history])

model.save('model.h5',overwrite=True)

y_pred = model.predict(X_test, verbose=1)
y_pred = y_pred.clip(min=0.005, max=0.995)


# In[18]:

history.loss_plot('epoch')


# # 驗證模型

# In[19]:

def build_test_tensor(img):
    '''建立三個模型的輸入向量
    '''
    y=[]
    img = img.resize((299,299))
    x = image.img_to_array(img)
    x = np.expand_dims(x, axis=0)
    x = inception_v3.preprocess_input(x)
    model =  IncepModel(299,299)
    incPre = model.predict(x)
    print(incPre.shape)
    
    img = img.resize((224,224))
    x = image.img_to_array(img)
    x = np.expand_dims(x, axis=0)
    vgg_x =vgg19.preprocess_input(x)    
    model2 = VGGModel(224,224)
    vggPre = model2.predict(vgg_x)
    print(vggPre.shape) 
    model3 = Res50Model()
    resPre = model3.predict(x)
    print(resPre.shape)
    y.append(np.array(incPre))
    y.append(np.array(vggPre))
    y.append(np.array(resPre))
    y =  np.concatenate(y, axis=1)
    print(y.shape)
    out = y.reshape(4608,)
    print(out.shape)
    return out
    
    


# In[ ]:

test_img_list=[]
get_image_list(testCwd, test_img_list)
def display_pred_img(img_list,model):
    for img_path in img_list:
        img = Image.open(img_path)
        x = build_test_tensor(img)
        x = np.expand_dims(x, axis=0)
        pred = model.predict(x, verbose=1)
        print(pred)
        pred = pred.clip(min=0.005, max=0.995)
        print(pred)
        plt.figure("Image") # 影象視窗名稱
        plt.imshow(img)
        plt.axis('on') # 關掉座標軸為 off
        if pred[0][0]>0.5:
            x_title = '{} : {:.2f}% is dog'.format(os.path.basename(img_path), pred[0][0] * 100)
            plt.title(x_title) # 影象題目
        else:
            x_title = '{} : {:.2f}% is cat'.format(os.path.basename(img_path), (1-pred[0][0]) * 100)
            plt.title(x_title) # 影象題目    
        plt.show()
        
#model = load_model("model.h5")
dis_pred_img_list = test_img_list[:10] + test_img_list[-10:]
display_pred_img(dis_pred_img_list,model)


# 將預測結果寫入pred.csv。

# In[31]:

import pandas as pd
from keras.preprocessing.image import *

df = pd.DataFrame({"ID":np.arange(12500)+1,"label":-1.0})

image_size = (224, 224)
gen = ImageDataGenerator()
test_generator = gen.flow_from_directory("test1", image_size, shuffle=False,batch_size=32, class_mode=None)

for i, fname in enumerate(test_generator.filenames):
    index = int(fname[fname.rfind('/')+1:fname.rfind('.')])
    df.at[index-1, 'label'] =  y_pred[i]

df.to_csv('inc_pred.csv', index=None)
df.head()
    


# In[ ]:



正式報告:

畢業報告

  1. 問題定義
    1. 專案概覽

貓狗大戰專案(dogs vs cats)主要是解決計算機識別貓和狗的問題。給定了一箇中等規模的資料集,資料中通過檔名標定了貓和狗的標籤,同時包含了一些干擾和異常資料,要求開發一個模型或者識別程式,能夠從資料集中學習,而後對測試資料進行區分,最終以模型在測試資料上的識別準確率來衡量模型效能。

專案來自於kaggle比賽,相關資料集下載自kaggle。該專案本質是一個圖片二分類問題,多年以來,傳統的程式設計方法對於圖片分類一直沒有好的方法;近年來,隨著計算機的飛速發展,深度學習的方法大放異彩,在計算機視覺領域取得了飛速進展,利用多層神經網路學習一定的資料量後,模型可具備較高的效能,在某些應用領域甚至超越了人類。

本專案利用keras封裝後的tensorflow介面,搭建一個深度學習網路模型,利用kaggle比賽《Dogs vs. Cats Redux: Kernels Edition》中的資料集對模型進行訓練、優化,利用優化後的模型對未曾見過的貓狗圖片(test資料集)進行分類。

    1. 問題說明

該專案本質是一個影象分類問題。採用深度學習的方法,構建一個多層的神經網路模型,通過訓練資料的學習,使得模型能夠區分貓和狗的特徵,進而識別貓和狗。影象處理和特徵提取,不可避免要用到卷積神經網路CNN,借鑑成熟的VGG、Inception、Resnet等著名的神經網路構建技巧,構建一個卷積神經網路,不斷調整其引數,通過多代次的訓練,最後達到專案要求。

    1. 評估指標討論

模型的評估指標選擇:

識別正確率: 對於給定的測試資料集,分類器正確分類的樣本數與總樣本數之比。其中識別正確率越高,模型效能越好。

模型的損失函式選擇:

平均交叉熵:模型在測試資料集樣本上的歸屬概率輸出與真實資料樣本歸屬的差異程度。平均交叉熵越小,模型效能越好。計算公式如下所示:

其中y:圖片為狗時值為1,否則為0;

a為神經網路實際輸出即模型判斷一張圖片是狗的概率;

n測試集中圖片的個數

當logloss較小時,模型表現能力強,正確預測貓狗圖片的能力強;當logloss較大時,模型表現能力差,正確預測貓狗圖片的能力弱。

模型的優化方向即是使交叉熵輸出最小。

  1. 問題分析與解決
    1. 總體思路

主要資料預處理階段和模型構建階段需要對相關演算法予以整理和思考。基於kerastensorflow靈活性,解決該問題可能存在多種方法,需要在實際過程中根據自身掌握情況進行權衡和選擇。

問題解決整體思路如下所示:

1 總體解決思路

在實施過程中:

  1. 上傳資料集。初始方案是從本機上傳到雲端,但速度太慢,不得已放棄,後來採用的kaggle-cli庫直接下載到AWS雲端;
  2. 圖片進行復制操作。初始方案是利用winscp對檔案進行復制操作,後因速度太慢放棄;於是寫了一個filesplit()函式直接程式碼處理,效率大大提高。
    1. 資料研究

Cats vs. Dogs(貓狗大戰)是Kaggle大資料競賽的一道賽題,利用給定的資料集,用演算法實現貓和狗的識別。 Kaggle提供的資料集包含了貓和狗圖片各12500幅,都是以cat.<數字>.jpg或dog.<數字>.jpg命名,因此可以根據檔名分類打標籤。資料集連結:https://www.kaggle.com/c/dogs-vs-cats-redux-kernels-edition/data

資料集包括兩部分:train資料集和test資料集。資料集由訓練資料和測試資料組成,訓練資料包含貓和狗各12500張圖片,各佔比例50%。測試資料包含12500張貓和狗的圖片,具體比例未做統計。訓練資料分佈如下所示:

2 訓練資料集分佈

訓練資料集與測試資料集大小分佈如下所示:

3訓練資料集與測試資料集分佈

圖片的長-寬scatter分佈圖如下所示:

4 訓練資料長分佈散點圖

影象的拍攝角度各異,但基本都是以貓或狗為主體,通過資料增強,如調整亮度、隨機裁切、輕度旋轉等處理,可進一步增大樣本量,使模型有更多的資料樣本進行訓練,但在本次專案中並未利用資料增強,因為未採用資料增強的模型測試結果已經可以達到要求了

影象的大小不同,尺寸都在500*500以內,但大小各異,送入模型進行學習前,需要做資料預處理,裁剪成為大小一致的圖片,如採用不同的預訓練模型時,其對影象的大小有不同的要求。InceptionV3要求影象資料大小為299*299;Vgg19及Resnet50的影象資料大小為224*224;keras庫中匯入相應模型時,可以同時匯入相應的preprocess_input函式,用於圖片的預處理。

存在個別異常值,如dog.1895.jpg等,並非拍攝照片;如下所示:

5 異常值示例照片1

如dog.6405.jpg(如下所示),在訓練中,會干擾模型的迭代。

6 異常值示例照片2

模型資料集總體上難度不高,雖然存在個別異常值但主體清晰,解析度較高。對於異常值應當進行刪除。

    1. 資料預處理
        1. 異常值檢

從資料集-寬散點圖來看,圖片大部分長款分佈在500*500以內,存在個別異常值,經檢視本地資料集,發現兩張圖片分別為cat.835.jpg和dog.2317.jpg,並非異常值。如下所示:

7 cat.835.jpg和dog.2317.jpg

ImageNet資料集中包含有貓狗的具體分類,對一個圖片在載有ImageNet上預訓練權值的Inception\VGG19\Resnet50模型上進行預測,如果其預測結果top50不包含貓狗真實的標籤分類(圖片預測值前50都沒有正常分類),那麼就將其視為異常值最終異常值取值為上述三個模型檢測出異常值的並集。而後再對並集做手動檢測處理

        1. 資料送入模型

將原始資料集轉換為圖片資料和標籤資料,供模型使用資料如何送入模型,對於tensorflow有3種方法:

1、採用TFRecord的方法,將資料集和標籤轉換為TFRecord,由tensorflow控制資料的流向和吞吐;該方法編碼難度較高;

2、直接使用資料和標籤feed模型;該方法效率較低,頻繁的讀寫磁碟將極大的拖慢訓練效率;

3、採用pickle檔案的方法,將所有資料讀入numpy array中,再寫入到pickle檔案裡,在使用時,再從pickle檔案中讀出該方法效率較高,但對記憶體要求大;

Keras有兩種方法可以參考:

1、採用numpy array的方法。將圖片直接讀入numpy array中,利用kerasfit函式送入模型訓練;

2、利用ImageDataGenerator函式,先將圖片分類(分成catdog資料夾,分存catdog圖片),建立train_generator、test_generator;而後利用flow_from_directoryfit_generator函式實現。

在實際應用中,利用keras庫結合了1、2兩種方法,先利用ImagedataGenerator讀入圖片,經過模型的特徵提取層後,將最後一層的輸出向量連線,然後利用numpy array的shuffle方法處理,最後利用fit函式送入模型。因為,如果僅僅採用第二種方法,那麼必須將訓練集再拆分出一個驗證集資料夾,從而建立validation_generator來送入模型,而這實際並不是必要步驟。

        1. 訓練集驗證集劃分

train資料夾中的cat圖片(前12500張)和dog圖片利用pthon的shutil庫函式,分別拷貝進入兩個不同的資料夾中(catdog),為了便於後期利用kerasflow_from_directory函式送入模型。

對於訓練集和驗證集的劃分,有兩種方法:

  • 將所有的影象檔案和標籤讀入numpyarray中,使用np.random.shuffle()打亂圖片和標籤;利用SKlearn的資料預處理模組中的train_test_split劃分訓練集和驗證集。
  • 匯出融合模型的特徵向量時,將資料和特徵同時進行shuffle處理,在模型訓練時,在模型的fit函式中,利用validation_split引數設定訓練集驗證集劃分比例。

第一種方法對記憶體要求高,第二種方法對記憶體要求低一些。實際選用第二種。

        1. 影象預處理

影象預處理既可以手動逐個處理,也可以利用匯入模型的preprocess_input函式進行處理。包括手動和自動兩種方法:

手動處理:利用PIL庫CV2的影象處理函式,圖片逐一進行尺寸修正處理,如需資料增強,則進一步進行翻轉、裁切、旋轉處理,豐富資料樣本;

自動處理:利用模型的preprocess_input函式對影象進行預處理;或者利用ImageDataGeneratorflow_from_directory()函式改變影象尺寸

在編碼過程中,資料預處理採用模型自帶的preprocess_input函式進行;inceptionV3需要處理為299*299大小, 同時對所有的圖片檔案還應對其RGB值進行歸一化處理。VGG19Res50需要處理為224*224大小,利用ImageDataGeneratorflow_from_directory()函式改變影象尺寸即可

在實施過程中,處理過程主要包括以下幾個主要步驟:

  1. 將原始的train和test1檔案夾中的圖片利用shutil分別複製到相應的資料夾下,為了適應keras的flow_from_directory函式

圖片進行大小修正時,vgg19Res50模型利用的是在flow_from_directory函式中傳入圖片尺寸,在該函式中完成圖片的統一大小;對於InceptionV3vgg19模型,由於模型需要對圖片資料還需進行歸一化減均值處理,因此需利用模型的preprocess_input函式

    1. 模型構建
      1. 卷積神經網路

卷積神經網路最初是為解決影象識別等問題而設計的,當前應用於影象和視訊,也可用於時間序列訊號。在卷積神經網路中,第一個卷積層會直接接受影象畫素的輸入,每一個卷積操作只處理一小塊影象,進行卷積變化後再傳到後面的網路。每一層卷積都會提取資料中有效的特徵。一般卷積神經網路由多個卷層構成,每個卷積層會進行如下幾個操作。

  1. 影象通過多個不同的卷積核的濾波,並加上偏置,提取出區域性特徵,每一個卷積核會映射出一個新的2D影象。
  2. 前面卷積核的濾波輸出結果,進行非線性的啟用函式處理目前常見的Relu函式。
  3. 啟用函式的結果再進行池化操作(即降取樣目前常見的有最大池化平均池化。
  4. 以上三個步驟構成了一個最常見的卷積層。也可加上一個區域性相應歸一化層(LRN

卷積層最關鍵的特徵如下,區域性連線、權值共享和池化取樣。如下所示:

區域性連線:CNN通過加強神經網路中相鄰層之間節點的區域性連線模式來挖掘自然影象的空間區域性關聯資訊,m層節點的輸入第m-1節點的一部分,這些節點具有空間相鄰的視覺感受野。卷積神經網路中每個神經元的權重個數均為卷積核的大小,即每個神經元只與圖片部分畫素相連線。

權值共享:一個卷積層可以有多個不同的卷積核,而每個卷積核都對應個濾波後映射出的新影象,同一個新影象中每一個畫素都來自相同的卷積核,這就是卷積核的權值共享,用以降低模型複雜度同時賦予了卷積網路對平移的容忍性

池化取樣:池化層主要是針對卷積後的特徵圖,按照卷積的方法對影象部分區域求均值或最大值,用來代表其取樣的區域。這是為了描述大的影象,對不同位置的特徵進行聚合統計,這個均值或者最大值就是聚合統計的方法,也就是池化。

池化取樣進一步降低了輸出引數量,並賦予模型對於輕度形變的容忍能力,提高了模型的泛效能。

另外,一個CNN模型一般還包括:

全連線層:兩層之間所有的神經元都有連線,也就是FC層。

Dropout:訓練時,使用Dropout隨機忽略一部分神經元,以避免模型過擬合。一般模型的全連線層使用dropout,增強了模型的健壯性。

重疊的池化:在CNN中,池化步長一般比池化核尺寸小,這樣池化層的輸出之間會有重疊和覆蓋,提升了特徵的豐富性。

近年來,CNN取得了飛速進展,下圖展示了CNN的架構演化過程:

8 CNN架構演化過程

      1. 遷移學習

利用遷移學習,我們可以利用已經存在的相關人物或域的有標記資料處理新的任務場景,在源問題域中,通過解決源任務所獲得的知識將其用於新的另外的任務。

本專案所用資料集實際是ImageNet資料集的子集,並且是一個二分類問題,因此,問題域實際是ImageNet分類域的子問題。鑑於傳統的經典神經網路均已在ImageNet上訓練過,並獲得了較好的分類效果,可以利用已訓練網路的權值,進行特徵提取,模型的最後加入全新的全連線層和分類層輸出分類結果。考慮多個模型特徵提取結果並不相同,融合多個模型的特徵提取結果能夠讓資料特徵更加豐富,因此,可以結合若干成熟模型進行遷移學習

最終實現過程中,我將vgg19Resnet50InceptionV3三類卷積網路的卷積層對圖片進行特徵提取,並將特徵合併,最後通過1個全連線FC層(利用Relu進行啟用),最後輸出層連線sigmoid啟用函式輸出分類結果。下圖所示。

9模型融合結構圖

      1. 優化器

優化器用來更新和計算模型引數,使其更加逼近或者達到最優值,從而使loss 損失函數最小。 神經網路中最常用優化演算法是梯度下降,其核心是:對於每一個變數,按照目標函式在該變數的梯度下降的方向(梯度的反方向)進行更新,學習率決定了每次更新的步長。即在超平面上目標函式沿著斜率下降的方向前進,直到到達超平面的谷底。梯度下降法包括:

1批量梯度下降(Batch GradientDescent):在整個資料集上對每個引數求目標函數的偏導數,其反方向即為此引數變數的梯度下降方向。批量梯度下降中,每次更新都需要計算整個資料集上求出所有引數變數的偏導數,因此速度比較慢。批量梯度下降對於凸函式可以收斂到全域性最小值,對於非凸函式可以收斂到區域性最小值。  

2、隨機梯度下降(Stochastic GradientDescent):相對於批量梯度下降,隨機梯度下降每次更新是針對資料集中的一個樣本求損失函式,然後對其求相應的偏導數,SGD執行速度大大加快。SGD更新值的方差很大,在頻繁的更新之下,目標函式會有劇烈的波動。當降低學習率的時候,SGD 表現出了與批量梯度下降相似的過程。  

3小批量梯度下降法(Mini-batch GradientDescent):在每次更新中,對n 個樣本構成的一批資料,計算損失函式,並對相應的引數求導;這種演算法降低了引數的方差,使得收斂過程更穩定。小批量梯度下降法,通常使我們訓練神經網路的首先演算法。

相關推薦

P7畢業專案大戰全部程式碼

準備工作: 1、申請一個aws雲機器,因為本地訓練實在太慢了,當然,土豪請略過…… 2、申請一個kaggle賬號,資料集可以利用winscp上傳到雲端,但上傳速度過慢,建議利用kaggle-cli下載資料集到aws雲端(申請kgggle賬號時註冊碼無法顯示,你需要下載一個

Tensorflow學習筆記:資料集加工和轉化為TensorFlow專用格式——Finetuning大戰VGGNet的重新針對訓練

Kaggle 貓狗大戰 貓狗大戰的資料集來源於Kaggle上的一個競賽:Dogs vs. Cats    貓狗大戰的資料集下載地址http://www.kaggle.com/c/dogs-vs-cats,其中資料集有12500只貓和12500只狗 ,官方資料集下載需要帳號,大

Tensorflow學習筆記:VGG16模型——Finetuning大戰VGGNet的重新針對訓練

這一篇介紹一下VGG16模型的修改 Step 1: 對模型的修改 首先是對模型的修改(VGG16_model.py檔案),在這裡原先的輸出結果是對1000個不同的類別進行判定,而在此是對2個影象,也就是貓和狗的判斷,因此首先第一步就是修改輸出層的全連線資料。

Tensorflow學習筆記:VGG16訓練——Finetuning大戰VGGNet的重新針對訓練

這篇介紹如何用資料對vgg16進行訓練 Finetuning最重要的一個步驟就是模型的重新訓練與儲存。  首先對於模型的值的輸出,在類中已經做了定義,因此只需要將定義的模型類初始化後輸出賦予一個特定的變數即可。 vgg = model.vgg16(x_imgs)

web服務處理過程各種I/O模型

web服務處理過程 各種i/o模型詳解 一, 進程,線程?進程是具有一定獨立功能的,在計算機中已經運行的程序的實體。在linux2.4以前,進程是基本運作的單位,在只是線程的系統中,線程才是最基本的運作單位,而進程只是線程的容器,程序本身只是指令,數據及其組織形式的描述,進程才是程序的真正運行實例。若

佇列實現棧兩個佇列實現一個棧方法實現程式碼

本節介紹一下如何用兩個佇列實現棧。 棧的主要操作就是入棧和出棧,其特點就是後進先出。我們先將兩個佇列分別定義為 queue1 與 queue2。 方案 1 入棧和出棧,都在 queue1 中完成,而 queue2 作為中轉空間。 入棧:直接入 queue1 即可。 出棧:把 queue1 中除最後一

棧實現佇列用兩個棧實現佇列方法實現程式碼

棧怎樣才能實現和佇列一樣從棧的底層抽出元素呢?一般會用兩個棧來實現佇列。 首先,我們將兩個棧分別定義為 stack1 與 stack2。 實現方案 1 我們讓入隊操作在 stack1 中執行,而出隊操作在 stack2 中執行。執行方式如下。 入隊:直接向 stack1 中入棧。 出隊:將 stac

大戰:融合了三種模型的Keras程式碼準確率直升到99%

使用keras的resnet,inceptionV3,xception模型,首先載入預訓練模型的權重,通過預訓練權重生成對貓狗的訓練值和測試值的特徵向量 預訓練模型下載地址:http://pan.baidu.com/s/1geHmOpH from ker

利用resnet 做kaggle大戰影象識別秒上98準確率

1、資料介紹 這份資料集來源於Kaggle,資料集有12500只貓和12500只狗。在這裡簡單介紹下整體思路 1、1從圖片中直接訓練一個小網路(作為基準方法),也就是普通的cnn方法 2、2後面我會用到最新的預訓練好的resnet等方法進行訓練 2

luogu P1489 大戰

經典 main while 輸出格式 badge 輸入格式 pan for getch 題目描述 新一年度的貓狗大戰通過SC(星際爭霸)這款經典的遊戲來較量,野貓和飛狗這對冤家為此已經準備好久了,為了使戰爭更有難度和戲劇性,雙方約定只能選擇Terran(人族)並且只能造機

大戰的TFrecord數據集制作

AD load example std contest from string listdir label import tensorflow as tfimport numpy as npimport osfrom PIL import Image#沒有下面兩句德華會出現

大戰

IT img 以及 給定 || 足夠 span 星際 技術分享 新一年度的貓狗大戰通過SC(星際爭霸)這款經典的遊戲來較量,野貓和飛狗這對冤家為此已經準備好久了,為了使戰爭更有難度和戲劇性,雙方約定只能選擇Terran(人族)並且只能造機槍兵。 比賽開始了,很快,野貓已

tensorflow實現大戰(分類算法)

sse sin output 行操作 ogr cast bytes 序列 raw 本次使用了tensorflow高級API在規範化網絡編程做出了嘗試。 第一步:準備好需要的庫 tensorflow-gpu 1.8.0 opencv-python 3.3.1 nu

tfrecord數據集訓練驗證-大戰

圖片大小 cat rac exc 兩個 bin span loss error: #!/usr/bin/env python # -*- coding:utf-8 -*- from mk_tfrecord import * #from model import * fr

Python使用tensorflow實現影象識別(大戰)-01

Python使用tensorflow實現影象識別(貓狗大戰)-01 import_data.py import tensorflow as tf import numpy as np import os #引入tensorflow、numpy、os 三個第三方模組 img_widt

kaggle大戰之AlexNet(一)

這篇文章主要介紹如何利用AlexNet預訓練模型來訓練一個貓狗分類器,主要內容包括: 專案結構介紹 資料探索 資料的準備 AlexNet模型的構建 模型的訓練和效能評估 結果的提交 一、專案結構介紹 1、相關資料下載地址 專案地址:http

我的大戰資料集圖片缺失處理

前面 找了一份540M的貓狗大戰的資料集,想使用這個資料集在小型資料集上從頭開始訓練一個卷積神經網路,使用了其中的2500個樣本,這個貓狗大戰的資料集總的是25000張圖片,所以在前面2500張圖片缺失的時候我就自己從後面的資料集中拷貝圖片補齊前面的,但是發現缺失圖片比較多,手動去查詢太麻煩,所以乾

Python使用tensorflow實現影象識別(大戰)-02

import tensorflow as tf def inference(images, batch_size, n_classes): # cov1, shape = [kernel size, kernel size, channels, ke

大戰2.0 使用tensorflow和tfrecord

      距離上次的部落格已經過去了半個月兩週左右的時間,自己在b站和部落格上學習了很多相關的知識,自我感覺自己的tensorflow的水平已經算是到了入門的水平,在部落格上有相關的非tensorboard匯入資料,實測有效(傳送門由於時間久了,暫時找不到了,自己找一下吧)

大戰V1

%matplotlib inline import numpy as np import os import matplotlib.pyplot as plt fnames = np.array([f'train/{f}' for f in sorted(os.