深度學(xué)習(xí)圖像分類:植物幼苗圖像分類入門(mén)(Plant Seedlings Classification)

前言:深度學(xué)習(xí)考試期末的題目,植物幼苗分類,可以幫助農(nóng)業(yè)領(lǐng)域的進(jìn)步。
題目介紹:kaggle原題:可以下載數(shù)據(jù)集,查看一些參與者的思路等。
易用的深度學(xué)習(xí)框架Keras簡(jiǎn)介及使用
部分圖片如下:

20180709200946233.png

20180709202306317.png

思路:

由于是圖像分類問(wèn)題,tensorflow官網(wǎng)提供了深度學(xué)習(xí)做圖片分類的入門(mén)教材都是MNIST或者CIFAR-10的例子。但這里數(shù)據(jù)都是圖片,還是需要自己讀入和預(yù)處理,采用keras搭建的網(wǎng)絡(luò)。
1.圖片的讀入和預(yù)處理
2.模型的搭建
3.訓(xùn)練
4.評(píng)價(jià)


一、數(shù)據(jù)的讀入和預(yù)處理

數(shù)據(jù)的讀入:用的cv2,每個(gè)文件夾的名字就是其標(biāo)簽,但是名字不可以當(dāng)作lable,所以建立了name_dic 字典轉(zhuǎn)換為數(shù)字;
數(shù)據(jù)集劃分:數(shù)據(jù)集并沒(méi)有幫我們劃分?jǐn)?shù)據(jù)集,所以我用的sklearn*train_test_split()函數(shù);
矩陣的保存:由于每次加載數(shù)據(jù)很消耗時(shí)間,所以將四個(gè)文件(訓(xùn)練、測(cè)試集的特征和標(biāo)簽用
numpy*進(jìn)行了保存)
數(shù)據(jù)的打亂:因?yàn)樽x取時(shí)是按照順序讀取的,直接按這個(gè)順序訓(xùn)練,訓(xùn)練效果可能會(huì)受影響,hstack((a,b))的功能是將a和b以水平的方式連接,經(jīng)過(guò)轉(zhuǎn)置np.random.shuffle()方法進(jìn)行亂序
數(shù)據(jù)的預(yù)處理:訓(xùn)練特征需要進(jìn)行歸一化處理,標(biāo)簽需要進(jìn)行one-hot編碼
由于圖片數(shù)據(jù)過(guò)少,用到了圖像增強(qiáng):

  # 30°旋轉(zhuǎn) 0.1的隨機(jī)平移 0.2隨機(jī)縮放
    aug = ImageDataGenerator(rotation_range=180, width_shift_range=0.3,
                             height_shift_range=0.3, shear_range=0.2, zoom_range=0.2,
                             horizontal_flip=True, fill_mode="nearest")
# 獲取文件路徑和標(biāo)簽
def get_files(file_dir):
    # file_dir: 文件夾路徑
    # return: 亂序后的圖片和標(biāo)簽
    # 直接讀取數(shù)據(jù),會(huì)節(jié)約時(shí)間
    if (os.path.exists('train_image_list1.csv.npy')
        & os.path.exists('test_image_list1.csv.npy')
        & os.path.exists('test_label_list.csv.npy')
        &os.path.exists('train_label_list.csv.npy')
        &os.path.exists('hunxiao.csv.npy')):
        train_image_list_1 =  np.load('train_image_list1.csv.npy')
        train_label_list_1 = np.load('train_label_list.csv.npy')
        test_image_list_1 = np.load('test_image_list1.csv.npy')
        test_label_list_1 = np.load('test_label_list.csv.npy')
        test_label_list = np.load('hunxiao.csv.npy')
        print("訓(xùn)練集一共有%d張圖\n" % len(train_label_list_1))
        print("測(cè)試集一共有%d張圖\n" % len(test_label_list_1))
        return train_image_list_1, train_label_list_1, test_image_list_1, test_label_list_1,test_label_list
    image_list = []
    label_list = []
    name_dic = {'Black-grass': 0, 'Charlock': 1, 'Cleavers': 2, 'Common Chickweed': 3, 'Common wheat': 4,
                'Fat Hen': 5, 'Loose Silky-bent': 6, 'Maize': 7, 'Scentless Mayweed': 8, 'Shepherds Purse': 9,
                'Small-flowered Cranesbill': 10, 'Sugar beet': 11}
    # 載入數(shù)據(jù)路徑并寫(xiě)入標(biāo)簽值
    for file in os.listdir(file_dir):
        name = str(file)
        name_count = 0
        for key in os.listdir(file_dir + file):
            name_count+=1
            image_list.append(file_dir + '\\' + file + '\\' + key)
            label_list.append(name_dic[file])
        print(name+"種類有"+str(name_count)+"張圖片")
    print("一共有%d張圖\n" % len(image_list))
    image_list = np.hstack(image_list)
    label_list = np.hstack(label_list)
    temp = np.array([image_list, label_list])
    temp = temp.transpose()  # 轉(zhuǎn)置
    np.random.shuffle(temp)

    train_img, test_img = train_test_split(temp, train_size=0.7)
    train_image_list = list(train_img[:, 0])
    test_image_list = list(test_img[:, 0])
    train_label_list = list(train_img[:, 1])
    train_label_list = [int(i) for i in train_label_list]
    test_label_list = list(test_img[:, 1])
    test_label_list = [int(i) for i in test_label_list]

    train_image_list1 = []
    test_image_list1 = []
    for m in range(len(train_image_list)):
        image = cv2.imread(train_image_list[m])
        # print(image.shape) # 查看部分圖片的shape
        image = cv2.resize(image, (norm_size, norm_size))
        image = img_to_array(image)
        train_image_list1.append(image)
    for m in range(len(test_image_list)):
        image1 = cv2.imread(test_image_list[m])
        image1 = cv2.resize(image1, (norm_size, norm_size))
        image1 = img_to_array(image1)
        test_image_list1.append(image1)
    # 標(biāo)準(zhǔn)化:提高模型預(yù)測(cè)精準(zhǔn)度,加快收斂
    train_image_list1 = np.array(train_image_list1, dtype="float") / 255.0
    test_image_list1 = np.array(test_image_list1, dtype="float") / 255.0
    # convert the labels from integers to vectors one-hot編碼
    train_label_list1 = to_categorical(train_label_list, num_classes=CLASS_NUM)
    test_label_list1 = to_categorical(test_label_list, num_classes=CLASS_NUM)
    # 第一運(yùn)行 把處理好的數(shù)據(jù)保存下來(lái)
    np.save('train_image_list1.csv',train_image_list1)
    np.save('test_image_list1.csv',test_image_list1)
    np.save('test_label_list.csv',test_label_list1)
    np.save('train_label_list.csv',train_label_list1)
    np.save('hunxiao.csv',test_label_list)
    return  train_image_list1,train_label_list1,test_image_list1,test_label_list1,np.array(test_label_list)

二、模型的搭建

卷積神經(jīng)網(wǎng)絡(luò)CNN經(jīng)典模型
用深度學(xué)習(xí)做圖片分類選的網(wǎng)絡(luò)肯定是卷積神經(jīng)網(wǎng)絡(luò),但是現(xiàn)在CNN的種類這么多,哪一個(gè)會(huì)在我們這個(gè)標(biāo)志分類任務(wù)表現(xiàn)最好?在實(shí)驗(yàn)之前,沒(méi)有人會(huì)知道。一般而言,先選一個(gè)最簡(jiǎn)單又最經(jīng)典的網(wǎng)絡(luò)跑一下看看分類效果是的策略是明智的選擇,那么LeNet肯定是最符合以上的要求啦,實(shí)現(xiàn)簡(jiǎn)單,又相當(dāng)經(jīng)典。
選取了cnn中最簡(jiǎn)單LeNet網(wǎng)絡(luò),只有七層,參數(shù)相對(duì)較少,可以在本機(jī)上運(yùn)行。
LeNet如下:由兩個(gè)卷積層,兩個(gè)池化層,以及兩個(gè)全連接層組成。 卷積都是5*5的模板,stride=1,池化都是MAX。注意:圖片上的參數(shù)和我的模型不一致,借助于理解。

20180709205446446.jpg

如下為L(zhǎng)eNet模型的摘要:

_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
conv2d_1 (Conv2D)            (None, 32, 32, 20)        1520      kernel_size=(5, 5)
_________________________________________________________________
dropout_1 (Dropout)          (None, 32, 32, 20)        0         (0.25)
_________________________________________________________________
max_pooling2d_1 (MaxPooling2 (None, 16, 16, 20)        0         pool_size=(2, 2), strides=(2, 2)
_________________________________________________________________
conv2d_2 (Conv2D)            (None, 16, 16, 50)        25050     kernel_size=(5, 5)
_________________________________________________________________
dropout_2 (Dropout)          (None, 16, 16, 50)        0         (0.25)
_________________________________________________________________
max_pooling2d_2 (MaxPooling2 (None, 8, 8, 50)          0         
_________________________________________________________________pool_size=(2, 2), strides=(2, 2)
flatten_1 (Flatten)          (None, 3200)              0         
_________________________________________________________________
dense_1 (Dense)              (None, 500)               1600500   
_________________________________________________________________
activation_1 (Activation)    (None, 500)               0         
_________________________________________________________________
dropout_3 (Dropout)          (None, 500)               0         
_________________________________________________________________(0.25)
dense_2 (Dense)              (None, 12)                6012      
_________________________________________________________________
activation_2 (Activation)    (None, 12)                0         
=================================================================
Total params: 1,633,082
Trainable params: 1,633,082
Non-trainable params: 0

代碼里增加了Dropout用于解決過(guò)擬合,激活函數(shù)relu函數(shù)

class LeNet:
    def build(width, height, depth, classes):
        '''參數(shù)分別為:長(zhǎng) 寬 高 分類'''
        # initialize the model
        model = Sequential() # 建立線性堆疊模型
        inputShape = (height, width, depth)
        # if we are using "channels last", update the input shape
        if K.image_data_format() == "channels_first":   #for tensorflow
            inputShape = (depth, height, width)
        # first set of CONV => RELU => POOL layers
        # 卷積1 過(guò)濾器大小為 5 * 5,會(huì)產(chǎn)生20個(gè)圖像,卷積不會(huì)改變圖像大小,起到了濾鏡效果,設(shè)置ReLU激活函數(shù)
        model.add(Conv2D(filters=20,kernel_size=(5, 5),padding="same",input_shape=inputShape,activation='relu'))
        # 添加激活層
        # model.add(Activation("relu"))
        # 加入Dropout避免過(guò)擬合。
        model.add(Dropout(0.25))
        # 最大池化1 過(guò)濾器大小為 2 * 2,長(zhǎng)和寬的步長(zhǎng)均為2,不會(huì)改變圖像的數(shù)量(仍舊是20),會(huì)改變大小(32*32變成16*16)
        model.add(MaxPooling2D(pool_size=(2, 2), strides=(2, 2)))
        #second set of CONV => RELU => POOL layers
        # 卷積2 過(guò)濾器大小為 5 * 5,會(huì)產(chǎn)生50個(gè)圖像,卷積不會(huì)改變圖像大小,起到了濾鏡效果,設(shè)置ReLU激活函數(shù)
        model.add(Conv2D(filters=50, kernel_size = (5, 5), padding="same",activation='relu'))
        # 激活函數(shù)
        # model.add(Activation("relu"))
        # 加入Dropout避免過(guò)擬合。
        # model.add(Dropout(0.25))
        # 最大池化2 過(guò)濾器大小為2 * 2,長(zhǎng)和寬的步長(zhǎng)均為2,不會(huì)改變圖像的數(shù)量(仍舊是50),會(huì)改變大?。?6*16變成8*8)
        model.add(MaxPooling2D(pool_size=(2, 2), strides=(2, 2)))
        # first (and only) set of FC => RELU layers
        # Flatten層用來(lái)將輸入“壓平”
        model.add(Flatten())
        # Dense表示全連接層(500個(gè)神經(jīng)元)
        model.add(Dense(500))
        model.add(Activation("relu"))
        # 加入Dropout避免過(guò)擬合。
        # model.add(Dropout(0.25))

        # softmax classifier
        # 建立輸出層(分類數(shù)個(gè)神經(jīng)元),softmax可以將輸出預(yù)測(cè)為每一個(gè)圖像的概率
        model.add(Dense(classes,activation='softmax'))
        # 多分類
        model.add(Activation("softmax"))
        # 查看模型的摘要
        print(model.summary())
        # return the constructed network architecture
        return model

其中conv2d表示執(zhí)行卷積,maxpooling2d表示執(zhí)行最大池化,Activation表示特定的激活函數(shù)類型,F(xiàn)latten層用來(lái)將輸入“壓平”,用于卷積層到全連接層的過(guò)渡,Dense表示全連接層(500個(gè)神經(jīng)元)。

三、訓(xùn)練

訓(xùn)練小技巧:每次訓(xùn)練都要記得保存模型,在模型未改變的基礎(chǔ)上下載加載重新訓(xùn)練,可以分時(shí)分段訓(xùn)練,效果很好的。
混淆矩陣:


20180709210617147.png

左邊欄是數(shù)據(jù)的真實(shí)的類別,右欄是預(yù)測(cè)出的類別。簡(jiǎn)介一下TP,TN,FP,FN含義。
TP 就是 Ture Positive :原來(lái)是+,判別為 + 簡(jiǎn)記為—->“判對(duì)為正”
FP 就是 False Positive :原來(lái)是 -,判別為 + 簡(jiǎn)記為—-> “錯(cuò)判為正”
FN 就是False Negative :原來(lái)是 +,判別為 - 簡(jiǎn)記為—-> “錯(cuò)判成負(fù)”
TN 就是 True Negative:原來(lái)是 -,判別為 - 簡(jiǎn)記為—-> “判對(duì)為負(fù)”
很顯然上述混淆矩陣適合而分類問(wèn)題。
sensitivity: 正,判對(duì)的概率為 TP / (TP + FN)
specificity: 負(fù),判對(duì)的概率為 TN/ (FP + TN)
precision : TP / (TP + FP) 在判為正的里面,判對(duì)的概率
recall :TP / (TP + FN) 正的里面判對(duì)的概率。== sensitivity
模型結(jié)果的混淆矩陣:
其中0行6列的12含義:準(zhǔn)確的標(biāo)簽應(yīng)該是0,但是模型預(yù)測(cè)是6.

predict  0    1   2    3   4    5    6   7    8   9    10   11
label                                                         
0        59    0   0    0   0    1   12   0    0   0    0    1
1         0  100   5    0   0    0    0   0    0   1    0    0
2         0    0  69    0   0    0    0   1    0   0    0    0
3         1    0   0  152   0    0    0   0    1   0    0    0
4         0    0   0    0  54    2    2   0    0   0    0    0
5         1    1   0    1   0  114    1   0    0   0    0    0
6        14    0   0    0   1    0  174   0    0   0    1    0
7         0    0   0    0   0    1    0  61    1   0    0    0
8         1    1   6    1   0    0    0   1  142   3    0    0
9         0    1   0    0   0    0    0   0    0  55    0    0
10        0    1   0    0   0    0    0   0    0   0  131    0
11        0    0   0    0   0    2    0   0    0   0    1  105
def train(aug, trainX, trainY, testX, testY,test_label_list):
    # initialize the model
    print("開(kāi)始構(gòu)建模型···")
    model = LeNet.build(width=norm_size, height=norm_size, depth=3, classes=CLASS_NUM)
    # 加載已經(jīng)存在的模型
    try:
        model.load_weights('saveModel/plant_sign.model')
        print("加載模型成功!繼續(xù)訓(xùn)練模型")
    except:
        print("加載模型失敗!開(kāi)始訓(xùn)練一個(gè)新的模型")
    print("定義訓(xùn)練方式···")
    # 定義訓(xùn)練方式,三個(gè)參數(shù),分別是loss:設(shè)置損失函數(shù);optimizer:使用adam優(yōu)化器收斂更快,metrics:設(shè)置評(píng)估模型的方式是準(zhǔn)確率
    opt = Adam(lr=INIT_LR, decay=INIT_LR / EPOCHS)
    model.compile(loss="categorical_crossentropy", optimizer=opt,
                  metrics=["accuracy"])

    # train the network,開(kāi)始訓(xùn)練
    print("開(kāi)始訓(xùn)練網(wǎng)絡(luò)···")
    H = model.fit_generator(aug.flow(trainX, trainY, batch_size=BS),
                            validation_data=(testX, testY), steps_per_epoch=len(trainX) // BS,
                            epochs=EPOCHS, verbose=1)
    # 輸入訓(xùn)練數(shù)據(jù)集,劃分方式是0.8+0.2 訓(xùn)練20個(gè)訓(xùn)練周期,每一個(gè)批次128項(xiàng)數(shù)據(jù),verbose=2為顯示訓(xùn)練過(guò)程
    predY = model.predict_classes(testX)
    # print(predY.shape)
    # print(test_label_list.shape)
    # 打印混淆矩陣
    matrix = pd.crosstab(test_label_list,predY, rownames=['label'], colnames=['predict'])
    print(matrix)
    # save the model to disk
    print("[INFO] serializing network...")
    # model.save('saveModel/traffic_sign_result.model') # 保存模型
    # 畫(huà)出準(zhǔn)確率執(zhí)行結(jié)果
    show_train_history(H)
    # prediction_probability = model.predict(True_Train_X) # 預(yù)測(cè)可能性
    # prediction = model.predict_classes(True_Train_X) # 直接預(yù)測(cè)分類結(jié)果

在這里我們使用了Adam優(yōu)化器,由于這個(gè)任務(wù)是一個(gè)多分類問(wèn)題,可以使用類別交叉熵(categorical_crossentropy)。但如果執(zhí)行的分類任務(wù)僅有兩類,那損失函數(shù)應(yīng)更換為二進(jìn)制交叉熵?fù)p失函數(shù)(binary cross-entropy)

參數(shù)的定義

EPOCHS = 32 # 迭代次數(shù)
INIT_LR = 1e-3
BS = 32 # 總批次
CLASS_NUM = 12 #結(jié)果類數(shù)
norm_size = 32 # 圖片統(tǒng)一大小輸入

我們還需要為訓(xùn)練設(shè)置一些參數(shù),比如訓(xùn)練的epoches,batch_szie等。這些參數(shù)不是隨便設(shè)的,比如batch_size的數(shù)值取決于你電腦內(nèi)存的大小,內(nèi)存越大,batch_size就可以設(shè)為大一點(diǎn)。又比如norm_size(圖片歸一化尺寸)是根據(jù)你得到的數(shù)據(jù)集,經(jīng)過(guò)分析后得出的,因?yàn)槲覀冞@個(gè)數(shù)據(jù)集大多數(shù)圖片的尺度都在這個(gè)范圍內(nèi),所以我覺(jué)得32這個(gè)尺寸應(yīng)該比較合適,但是不是最合適呢?那還是要通過(guò)實(shí)驗(yàn)才知道的,也許64的效果更好呢?

主函數(shù)

if __name__=='__main__':
    train_file_path = "../dataset\\"

    trainX,trainY,testX,testY,test_label_list = get_files(train_file_path) # 導(dǎo)入數(shù)據(jù)集
    aug = ImageDataGenerator(rotation_range=30, width_shift_range=0.1,
                             height_shift_range=0.1, shear_range=0.2, zoom_range=0.2,
                             horizontal_flip=True, fill_mode="nearest")
    train(aug, trainX, trainY, testX, testY,test_label_list)

四、評(píng)價(jià)

寫(xiě)了一個(gè)函數(shù)用來(lái)展示訓(xùn)練過(guò)程:

def show_train_history(H):
    # plot the training loss and accuracy
    plt.style.use("ggplot")
    plt.figure()
    N = EPOCHS  # 訓(xùn)練周期數(shù)
    plt.plot(np.arange(0, N), H.history["loss"], label="train_loss")
    plt.plot(np.arange(0, N), H.history["val_loss"], label="val_loss")
    plt.plot(np.arange(0, N), H.history["acc"], label="train_acc")
    plt.plot(np.arange(0, N), H.history["val_acc"], label="val_acc")
    plt.title("Training Loss and Accuracy on traffic-sign classifier")
    plt.xlabel("Epoch #")
    plt.ylabel("Loss/Accuracy")
    plt.legend(loc="lower left")
    plt.savefig('plot.png')
    plt.show()
20180709214035215.png

數(shù)據(jù)集中給出了15個(gè)樣例文件,導(dǎo)入樣例文件進(jìn)行預(yù)測(cè);
需要注意的是:對(duì)預(yù)測(cè)的圖片必須處理和訓(xùn)練時(shí)一樣,并且重新搭建模型,把模型載入,預(yù)測(cè)結(jié)果即可。代碼如下:

import tensorflow as tf
import keras
import lenet_model
import os
from keras.preprocessing.image import ImageDataGenerator
from keras.utils import plot_model
import cv2
import numpy as np
import pandas as pd
from keras.preprocessing.image import img_to_array

def get_file(path):
    test_list = []
    test_name_list = []
    for file in os.listdir(path):
        image = cv2.imread(path+'/'+file )
        image = cv2.resize(image, (norm_size, norm_size))
        image = img_to_array(image)
        test_list.append(image)
        file_list_split = file.split(".")
        test_name_list.append(file_list_split[0])
    test_list = np.array(test_list, dtype="float") / 255.0
    return test_list,test_name_list
norm_size = 32
if __name__ == '__main__':
    name_dic = {'0': 'Black-grass', '1': 'Charlock', '2': 'Cleavers',
                '3': 'Common Chickweed', '4': 'Common wheat',
                '5': 'Fat Hen', '6': 'Loose Silky-bent', '7': 'Maize',
                '8': 'Scentless Mayweed', '9': 'Shepherds Purse',
                '10': 'Small-flowered Cranesbill', '11': 'Sugar beet'}
    path = "../dataset_test/test2"
    test_list, test_name_list=get_file(path)
    model = lenet_model.LeNet.build(width=32, height=32, depth=3, classes=12)
    try:
        model.load_weights('saveModel/traffic_sign_w.model')
        print("加載模型成功!繼續(xù)訓(xùn)練模型")
    except:
        print("加載模型失??!開(kāi)始訓(xùn)練一個(gè)新的模型")
    # 可視化模型
    # plot_model(model, to_file='model.png')
    aug = ImageDataGenerator(rotation_range=30, width_shift_range=0.1,
                             height_shift_range=0.1, shear_range=0.2, zoom_range=0.2,
                             horizontal_flip=True, fill_mode="nearest")
    aug.flow(test_list)
    result = model.predict_classes(test_list)
    name_list = []
    for i in result:
        name_list.append(name_dic[str(i)])
    finally_result = pd.DataFrame({'file':test_name_list,'species':name_list})
    print(finally_result)
    finally_result.to_csv("../dataset_test/result.csv",index=False)
    # print(finally_result1)

最終結(jié)果200次迭代,線下90%,線上86%,效果有待提高。

下面貼出所有代碼:

model.py

# import the necessary packages
from keras.models import Sequential
from keras.layers.convolutional import Conv2D
from keras.layers.convolutional import MaxPooling2D
from keras.layers.core import Dense,Dropout,Activation,Flatten
from keras import backend as K

from keras.preprocessing.image import ImageDataGenerator
from keras.optimizers import Adam
from sklearn.model_selection import train_test_split
from keras.preprocessing.image import img_to_array
from keras.utils import to_categorical # 用于one-hot編碼
import matplotlib.pyplot as plt
import numpy as np
import cv2
import os
import pandas as pd
class LeNet:
    def build(width, height, depth, classes):
        '''參數(shù)分別為:長(zhǎng) 寬 高 分類'''
        # initialize the model
        model = Sequential() # 建立線性堆疊模型
        inputShape = (height, width, depth)
        # if we are using "channels last", update the input shape
        if K.image_data_format() == "channels_first":   #for tensorflow
            inputShape = (depth, height, width)
        # first set of CONV => RELU => POOL layers
        # 卷積1 過(guò)濾器大小為 5 * 5,會(huì)產(chǎn)生20個(gè)圖像,卷積不會(huì)改變圖像大小,起到了濾鏡效果,設(shè)置ReLU激活函數(shù)
        model.add(Conv2D(filters=20,kernel_size=(5, 5),padding="same",input_shape=inputShape,activation='relu'))
        # 添加激活層
        # model.add(Activation("relu"))
        # 加入Dropout避免過(guò)擬合。
        model.add(Dropout(0.25))
        # 最大池化1 過(guò)濾器大小為 2 * 2,長(zhǎng)和寬的步長(zhǎng)均為2,不會(huì)改變圖像的數(shù)量(仍舊是20),會(huì)改變大?。?2*32變成16*16)
        model.add(MaxPooling2D(pool_size=(2, 2), strides=(2, 2)))
        #second set of CONV => RELU => POOL layers
        # 卷積2 過(guò)濾器大小為 5 * 5,會(huì)產(chǎn)生50個(gè)圖像,卷積不會(huì)改變圖像大小,起到了濾鏡效果,設(shè)置ReLU激活函數(shù)
        model.add(Conv2D(filters=50, kernel_size = (5, 5), padding="same",activation='relu'))
        # 激活函數(shù)
        # model.add(Activation("relu"))
        # 加入Dropout避免過(guò)擬合。
        # model.add(Dropout(0.25))
        # 最大池化2 過(guò)濾器大小為2 * 2,長(zhǎng)和寬的步長(zhǎng)均為2,不會(huì)改變圖像的數(shù)量(仍舊是50),會(huì)改變大小(16*16變成8*8)
        model.add(MaxPooling2D(pool_size=(2, 2), strides=(2, 2)))
        # first (and only) set of FC => RELU layers
        # Flatten層用來(lái)將輸入“壓平”
        model.add(Flatten())
        # Dense表示全連接層(500個(gè)神經(jīng)元)
        model.add(Dense(500))
        model.add(Activation("relu"))
        # 加入Dropout避免過(guò)擬合。
        # model.add(Dropout(0.25))

        # softmax classifier
        # 建立輸出層(分類數(shù)個(gè)神經(jīng)元),softmax可以將輸出預(yù)測(cè)為每一個(gè)圖像的概率
        model.add(Dense(classes,activation='softmax'))
        # 多分類
        model.add(Activation("softmax"))
        # 查看模型的摘要
        print(model.summary())
        # return the constructed network architecture
        return model
# 獲取文件路徑和標(biāo)簽
def get_files(file_dir):
    # file_dir: 文件夾路徑
    # return: 亂序后的圖片和標(biāo)簽
    # 直接讀取數(shù)據(jù),會(huì)節(jié)約時(shí)間
    if (os.path.exists('train_image_list1.csv.npy')
        & os.path.exists('test_image_list1.csv.npy')
        & os.path.exists('test_label_list.csv.npy')
        &os.path.exists('train_label_list.csv.npy')
        &os.path.exists('hunxiao.csv.npy')):
        train_image_list_1 =  np.load('train_image_list1.csv.npy')
        train_label_list_1 = np.load('train_label_list.csv.npy')
        test_image_list_1 = np.load('test_image_list1.csv.npy')
        test_label_list_1 = np.load('test_label_list.csv.npy')
        test_label_list = np.load('hunxiao.csv.npy')
        print("訓(xùn)練集一共有%d張圖\n" % len(train_label_list_1))
        print("測(cè)試集一共有%d張圖\n" % len(test_label_list_1))
        return train_image_list_1, train_label_list_1, test_image_list_1, test_label_list_1,test_label_list
    image_list = []
    label_list = []
    name_dic = {'Black-grass': 0, 'Charlock': 1, 'Cleavers': 2, 'Common Chickweed': 3, 'Common wheat': 4,
                'Fat Hen': 5, 'Loose Silky-bent': 6, 'Maize': 7, 'Scentless Mayweed': 8, 'Shepherds Purse': 9,
                'Small-flowered Cranesbill': 10, 'Sugar beet': 11}
    # 載入數(shù)據(jù)路徑并寫(xiě)入標(biāo)簽值
    for file in os.listdir(file_dir):
        name = str(file)
        name_count = 0
        for key in os.listdir(file_dir + file):
            name_count+=1
            image_list.append(file_dir + '\\' + file + '\\' + key)
            label_list.append(name_dic[file])
        print(name+"種類有"+str(name_count)+"張圖片")
    print("一共有%d張圖\n" % len(image_list))
    image_list = np.hstack(image_list)
    label_list = np.hstack(label_list)
    temp = np.array([image_list, label_list])
    temp = temp.transpose()  # 轉(zhuǎn)置
    np.random.shuffle(temp)

    train_img, test_img = train_test_split(temp, train_size=0.7)
    train_image_list = list(train_img[:, 0])
    test_image_list = list(test_img[:, 0])
    train_label_list = list(train_img[:, 1])
    train_label_list = [int(i) for i in train_label_list]
    test_label_list = list(test_img[:, 1])
    test_label_list = [int(i) for i in test_label_list]

    train_image_list1 = []
    test_image_list1 = []
    for m in range(len(train_image_list)):
        image = cv2.imread(train_image_list[m])
        # print(image.shape) # 查看部分圖片的shape
        image = cv2.resize(image, (norm_size, norm_size))
        image = img_to_array(image)
        train_image_list1.append(image)
    for m in range(len(test_image_list)):
        image1 = cv2.imread(test_image_list[m])
        image1 = cv2.resize(image1, (norm_size, norm_size))
        image1 = img_to_array(image1)
        test_image_list1.append(image1)
    # 標(biāo)準(zhǔn)化:提高模型預(yù)測(cè)精準(zhǔn)度,加快收斂
    train_image_list1 = np.array(train_image_list1, dtype="float") / 255.0
    test_image_list1 = np.array(test_image_list1, dtype="float") / 255.0
    # convert the labels from integers to vectors one-hot編碼
    train_label_list1 = to_categorical(train_label_list, num_classes=CLASS_NUM)
    test_label_list1 = to_categorical(test_label_list, num_classes=CLASS_NUM)
    # 第一運(yùn)行 把處理好的數(shù)據(jù)保存下來(lái)
    np.save('train_image_list1.csv',train_image_list1)
    np.save('test_image_list1.csv',test_image_list1)
    np.save('test_label_list.csv',test_label_list1)
    np.save('train_label_list.csv',train_label_list1)
    np.save('hunxiao.csv',test_label_list)
    return  train_image_list1,train_label_list1,test_image_list1,test_label_list1,np.array(test_label_list)

def show_train_history(H):
    # plot the training loss and accuracy
    plt.style.use("ggplot")
    plt.figure()
    N = EPOCHS  # 訓(xùn)練周期數(shù)
    plt.plot(np.arange(0, N), H.history["loss"], label="train_loss")
    plt.plot(np.arange(0, N), H.history["val_loss"], label="val_loss")
    plt.plot(np.arange(0, N), H.history["acc"], label="train_acc")
    plt.plot(np.arange(0, N), H.history["val_acc"], label="val_acc")
    plt.title("Training Loss and Accuracy on traffic-sign classifier")
    plt.xlabel("Epoch #")
    plt.ylabel("Loss/Accuracy")
    plt.legend(loc="lower left")
    plt.savefig('plot.png')
    plt.show()
def train(aug, trainX, trainY, testX, testY,test_label_list):
    # initialize the model
    print("開(kāi)始構(gòu)建模型···")
    model = LeNet.build(width=norm_size, height=norm_size, depth=3, classes=CLASS_NUM)
    # 加載已經(jīng)存在的模型
    try:
        model.load_weights('saveModel/plant_sign.model')
        print("加載模型成功!繼續(xù)訓(xùn)練模型")
    except:
        print("加載模型失??!開(kāi)始訓(xùn)練一個(gè)新的模型")
    print("定義訓(xùn)練方式···")
    # 定義訓(xùn)練方式,三個(gè)參數(shù),分別是loss:設(shè)置損失函數(shù);optimizer:使用adam優(yōu)化器收斂更快,metrics:設(shè)置評(píng)估模型的方式是準(zhǔn)確率
    opt = Adam(lr=INIT_LR, decay=INIT_LR / EPOCHS)
    model.compile(loss="categorical_crossentropy", optimizer=opt,
                  metrics=["accuracy"])

    # train the network,開(kāi)始訓(xùn)練
    print("開(kāi)始訓(xùn)練網(wǎng)絡(luò)···")
    H = model.fit_generator(aug.flow(trainX, trainY, batch_size=BS),
                            validation_data=(testX, testY), steps_per_epoch=len(trainX) // BS,
                            epochs=EPOCHS, verbose=1)
    # 輸入訓(xùn)練數(shù)據(jù)集,劃分方式是0.8+0.2 訓(xùn)練20個(gè)訓(xùn)練周期,每一個(gè)批次128項(xiàng)數(shù)據(jù),verbose=2為顯示訓(xùn)練過(guò)程
    predY = model.predict_classes(testX)
    # print(predY.shape)
    # print(test_label_list.shape)
    # 打印混淆矩陣
    matrix = pd.crosstab(test_label_list,predY, rownames=['label'], colnames=['predict'])
    print(matrix)
    # save the model to disk
    print("[INFO] serializing network...")
    # model.save('saveModel/traffic_sign_result.model') # 保存模型
    # 畫(huà)出準(zhǔn)確率執(zhí)行結(jié)果
    show_train_history(H)
    # prediction_probability = model.predict(True_Train_X) # 預(yù)測(cè)可能性
    # prediction = model.predict_classes(True_Train_X) # 直接預(yù)測(cè)分類結(jié)果


EPOCHS = 32 # 迭代次數(shù)
INIT_LR = 1e-3
BS = 32
CLASS_NUM = 12
norm_size = 32
if __name__=='__main__':
    train_file_path = "../dataset\\"

    trainX,trainY,testX,testY,test_label_list = get_files(train_file_path) # 導(dǎo)入數(shù)據(jù)集
    aug = ImageDataGenerator(rotation_range=30, width_shift_range=0.1,
                             height_shift_range=0.1, shear_range=0.2, zoom_range=0.2,
                             horizontal_flip=True, fill_mode="nearest")
    train(aug, trainX, trainY, testX, testY,test_label_list)

predict.py

import tensorflow as tf
import keras
import lenet_model
import os
from keras.preprocessing.image import ImageDataGenerator
from keras.utils import plot_model
import cv2
import numpy as np
import pandas as pd
from keras.preprocessing.image import img_to_array

def get_file(path):
    test_list = []
    test_name_list = []
    for file in os.listdir(path):
        image = cv2.imread(path+'/'+file )
        image = cv2.resize(image, (norm_size, norm_size))
        image = img_to_array(image)
        test_list.append(image)
        file_list_split = file.split(".")
        test_name_list.append(file_list_split[0])
    test_list = np.array(test_list, dtype="float") / 255.0
    return test_list,test_name_list
norm_size = 32
if __name__ == '__main__':
    name_dic = {'0': 'Black-grass', '1': 'Charlock', '2': 'Cleavers',
                '3': 'Common Chickweed', '4': 'Common wheat',
                '5': 'Fat Hen', '6': 'Loose Silky-bent', '7': 'Maize',
                '8': 'Scentless Mayweed', '9': 'Shepherds Purse',
                '10': 'Small-flowered Cranesbill', '11': 'Sugar beet'}
    path = "../dataset_test/test2"
    test_list, test_name_list=get_file(path)
    model = lenet_model.LeNet.build(width=32, height=32, depth=3, classes=12)
    try:
        model.load_weights('saveModel/traffic_sign_w.model')
        print("加載模型成功!繼續(xù)訓(xùn)練模型")
    except:
        print("加載模型失敗!開(kāi)始訓(xùn)練一個(gè)新的模型")
    # 可視化模型
    # plot_model(model, to_file='model.png')
    aug = ImageDataGenerator(rotation_range=30, width_shift_range=0.1,
                             height_shift_range=0.1, shear_range=0.2, zoom_range=0.2,
                             horizontal_flip=True, fill_mode="nearest")
    aug.flow(test_list)
    result = model.predict_classes(test_list)
    name_list = []
    for i in result:
        name_list.append(name_dic[str(i)])
    finally_result = pd.DataFrame({'file':test_name_list,'species':name_list})
    print(finally_result)
    finally_result.to_csv("../dataset_test/result.csv",index=False)
    # print(finally_result1)
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容