CNN圖像分類入門


CNN圖像分類 入門

本次入門學習的項目是CNN圖像分類的花卉識別

通過使用五種各五百張不同種類的花卉圖片進行模型訓練

訓練結果如下:

預測成功率大概在64%左右(與訓練集過少還是有一些關系的)

image-20210720145709219

預測結果如下:

image-20210720145521625

代碼部分

訓練代碼解釋部分:

  1. 模型導入:
# -*- coding:uft-8

import glob
import os
import cv2
import  tensorflow as tf
from tensorflow.keras import layers, optimizers, datasets, Sequential
import numpy as np
import matplotlib.pyplot as plt
from sklearn.model_selection import train_test_split
#模型導入模塊
from keras.models import load_model

# 數據集的地址  改為你自己的
path = './flower_photos/'

# 縮放圖片大小為100*100
w = 100
h = 100
# c = 3

  1. 定義圖片讀取函數
# 定義函數read_img,用於讀取圖像數據,並且對圖像進行resize格式統一處理
def read_img(path): 
    # 創建層級列表cate,用於對數據存放目錄下面的數據文件夾進行遍歷,os.path.isdir用於判斷文件是否是目錄,然后對是目錄文件的文件進行遍歷
    cate=[path+x for x in os.listdir(path) if os.path.isdir(path+x)]  
     # 創建保存圖像的空列表
    imgs=[]   
    # 創建用於保存圖像標簽的空列表
    labels=[]    
    # enumerate函數用於將一個可遍歷的數據對象組合為一個索引序列,同時列出數據和下標,一般用在for循環當中
    # enumerate(cate) -> (0, './flower_photos/daisy') 索引+路徑
    for idx,folder in enumerate(cate):    
        # 利用glob.glob函數搜索每個層級文件下面符合特定格式“/*.jpg”進行遍歷 匹配
        for im in glob.glob(folder+'/*.jpg'): 
            #opencv讀取圖片,僅僅是讀一下
            # 利用imread函數讀取每一張被遍歷的圖像
            img=cv2.imread(im)      
            # 利用resize函數對每張img圖像進行大小縮放,統一處理為大小為w*h(即100*100)的圖像
            img=cv2.resize(img,(w,h))
            # 將每張經過處理的圖像數據保存在之前創建的imgs空列表當中,最終所有的圖片都塞到了imgs列表中
            imgs.append(img)  
            # 將每張經過處理的圖像的標簽數據保存在之前創建的labels列表當中 ---這里的標簽僅僅是index 0123標號
            labels.append(idx) 
     # 利用np.asarray函數對生成的imgs和labels列表數據進行轉化,之后轉化成數組數據(imgs轉成浮點數型,labels轉成整數型)        
    return np.asarray(imgs,np.float32),np.asarray(labels,np.int32)    
  1. data、label數據填充
# 將read_img函數處理之后的數據定義為樣本數據data和標簽數據label
data,label=read_img(path)    
# 查看樣本數據的大小 
print("shape of data:",data.shape)              
# 查看標簽數據的大小 
print("shape of label:",label.shape)      
# 查看標簽數組的內容,發現數組僅僅是01234的那種標簽 用於區別圖片屬於哪個文件夾
print(label)                                                          
  1. 數據集切分、對數據進行標准化處理
# 保證生成的隨機數具有可預測性,即相同的種子(seed值)所產生的隨機數是相同的,這里是數是可以隨便取的
seed = 785
#生成隨機種子
np.random.seed(seed)

#切分數據集
#train_test_split函數按照用戶指定的比例,隨機將樣本集合划分為訓練集和測試集,並返回划分好的訓練集和測試集數據
#data  待划分的樣本特征集合--數組型的,這就解釋了為啥上面要通np.asarry進行處理
#label 待划分的樣本標簽
#test_size 若在0-1之間,為測試集樣本數目與原始樣本數目之比;若為整數,則是測試集樣本的數目  說白了就是拿出多少的測試集去讓機器自己訓練自己
#x_train 划分出的訓練集數據 x_val 划分出的測試集數據 一般為x_test
#y_train 划分出的訓練集標簽 y_val 划分出的測試集標簽
x_train, x_val, y_train, y_val = train_test_split(data, label, test_size=0.20, random_state=seed)

# 對數據作 標准化處理 
# 由於圖像的RGB色彩是最大不過(255,255,255)所以得除255 歸一到[0,1]之間,由於之間轉換成浮點數了,所以有小數出現 ---歸一化
x_train = x_train / 255 
x_val = x_val / 255
#print("hello",x_train)
  1. 創建圖像標簽列表
#創建圖像標簽列表
flower_dict = {0:'daisy',1:'dandelion',2:'rose',3:'sunflowers',4:'tulips'}
  1. 模型制作(靈魂部分)
#https://www.cnblogs.com/wj-1314/p/9579490.html 參考博客
#Sequential是Keras中的序貫模型 
#序貫模型是函數式模型的簡略版,為最簡單的線性、從頭到尾的結構順序,不分叉,是多個網絡層的線性堆疊
#Keras實現了很多層,包括core核心層,Convolution卷積層、Pooling池化層等非常豐富有趣的網絡結構
#用法:可以將層的列表傳遞給Sequential的構造函數,來創建一個Sequential模型
#個人預測---所以這個方法可以套用別人的模型進行訓練
#創建模型。模型包括3個卷積層和三個RELU激活函數,兩個池化層
model = Sequential([ 
    #調用layer.Con2D()創建了一個卷積層。32表示kernel的數量。padding=“same”表示填充輸入以使輸出具有與原始輸入相同的長度,使用RELU函數
    #tf.keras.layers.Conv2D()參數解析
    #第一個參數 filters 過濾器個數/卷積核個數 與卷積后的輸出通道數一樣,這里的通道數就是32
    #第二個參數 kernel_size卷積核尺寸 一般為3*3或5*5 這里的尺寸是height*width,若長寬一樣也可以直接寫一個整數3或者5
    #卷積后:卷積后的height,width的計算公式如下
    #滑動步長為strides,卷積核的尺寸為S,輸入的尺寸為P,padding = "valid"
    # height =width = (P-S)/strides +1,
    # 輸入形狀為20×20,卷積核為3×3,滑動步長為1,所以輸出為(20-3)/1 +1 =18
    #第三個參數 滑動步長strides 默認為(1,1) 橫向和縱向滑動均為1 可以結合上述式子進行計算輸出形狀
    #第四個參數 默認padding="valid" 邊緣不填充 另一個取值為"same"表示邊緣用0填充,如果為"same"則輸出的形狀為height=width=P/strides
    #第五個參數 data_format='channels_first'/'channels_last'輸入數據的格式 ,設置輸入數據中通道數是第一個還是最后一個,默認為最后一個
    #第六個參數 activation = "relu" 激活函數 ,相當於經過卷積輸出后,再經過一次激活函數,常見的激活函數有relu,softmax,selu等
    layers.Conv2D(32, kernel_size=[5, 5], padding="same", activation=tf.nn.relu),
    
    #調用layers.MaxPool2D()創建最大池化層,步長為2,padding=“same”表示填充輸入以使輸出具有與原始輸入相同的長度。
    layers.MaxPool2D(pool_size=[2, 2], strides=2, padding='same'),
    
    #利用dropout隨機丟棄25%的神經元,防止過擬合,默認為0.5
    layers.Dropout(0.35),

    #繼續添加兩個卷積層和一個最大池化層 卷積核數64 尺寸為3*3
    layers.Conv2D(64, kernel_size=[3, 3], padding="same", activation=tf.nn.relu),
    layers.Conv2D(64, kernel_size=[3, 3], padding="same", activation=tf.nn.relu),
    #最大池化層
    layers.MaxPool2D(pool_size=[2, 2], strides=2, padding='same'),
    #利用dropout隨機丟棄神經元
    layers.Dropout(0.35),

    # 繼續添加兩個卷積層和一個最大池化層
    layers.Conv2D(128, kernel_size=[3, 3], padding="same", activation=tf.nn.relu),
    layers.MaxPool2D(pool_size=[2, 2], strides=2, padding='same'),
    
    #利用dropout隨機丟棄25%的神經元
    layers.Dropout(0.35),
    
    #Flatten層用來將輸入“壓平”,即把多維的輸入一維化,常用在從卷積層到全連接層的過渡 
    layers.Flatten(),
    
    #調用layers.Dense()創建全連接層
    #輸出節點數為512 256 激活函數為relu 常用的激活函數罷了,ReLU處理了它的sigmoid、tanh中常見的梯度消失問題
    layers.Dense(512, activation=tf.nn.relu),
    layers.Dense(256, activation=tf.nn.relu),
    
    #添加全連接層,最后輸出每個分類(5)的數值
    #softmax用於多分類過程中,它將多個神經元的輸出,映射到(0,1)區間內,由於只有五個類,所以輸出結點為5
    #我估計這里的輸出節點數(維數)和激活函數是有關系的
    layers.Dense(5, activation='softmax')
    ])
#使用Adam優化器,優化模型參數。lr(learning rate, 學習率) 老師給的參考=0.00001
#https://keras.io/zh/optimizers/ 優化器參考
opt = optimizers.Adam(learning_rate=0.001)
#編譯模型以供訓練。metrics=['accuracy']即評估模型在訓練和測試時的性能的指標。
#optimizer 優化器
#loss 損失函數 模型試圖最小化的目標函數 損失函數可以根據最后添加全連接層的激活函數進行修改
#metrics 評估標准 默認為metrics = ['accuracy']
model.compile(optimizer=opt, 
              loss='sparse_categorical_crossentropy',
              metrics=['accuracy'])
  1. 模型訓練
#fix 函數 https://keras.io/zh/models/sequential/
#訓練模型,決定訓練集和驗證集,batch size:進行梯度下降時每個batch包含的樣本數(以多少個樣本為一個batch進行一個迭代)。
#verbose:日志顯示,0為不在標准輸出流輸出日志信息,1為輸出進度條記錄,2為每個epoch輸出一行記錄
#validation_data=(x_val, y_val) 用來評估損失,以及在每輪結束時的任何模型度量指標
model.fit(x_train, y_train, epochs=40, validation_data=(x_val, y_val),batch_size=200, verbose=2)
#輸出模型的結構和參數量
model.summary()
  1. 輸出日志
    image-20210720151538565

  2. 模型保存/載入函數(當你的模型訓練完的時候,可以保存起來,到時候可以直接載入直接使用,省的每次都需要重新訓練一遍)

#保存模型
model.save('./Model/flower_fin.h5')
#載入模型
model = load_model("./Model/flower_fin.h5")

預測代碼解釋部分:

  1. 讀取圖像
# 測試圖像的地址 (改為自己的)
path_test = './TestImages/'
#
# 利用glob.glob函數搜索每個層級文件下面符合特定格式“/*.jpg”進行遍歷
# glob.glob函數並不是按順序從1-n來掃描的,是一種亂序掃描,第一個掃到1.jpg,第二個就掃到了10.jpg,並不像常人想的那種從1-2-3順序掃描,
# 因此下面順序輸出的時候會出現圖片和預測結果對不上的問題
# 創建保存圖像的空列表
print(glob.glob(path_test+'/*.jpg')) 
correction = glob.glob(path_test+'/*.jpg')
print(correction[0])
imgs=[]        
for im in glob.glob(path_test+'/*.jpg'):                # 利用glob.glob函數搜索每個層級文件下面符合特定格式“/*.jpg”進行遍歷
    img=cv2.imread(im)                                  
    img=cv2.resize(img,(w,h))                           
    imgs.append(img)                                     # 將每張經過處理的圖像數據保存在之前創建的imgs空列表當中
imgs = np.asarray(imgs,np.float32)                      # 列表轉換為數組 和上面的一樣

print("shape of data:",imgs.shape) 
  1. 導入模型,進行預測
#將圖像導入模型進行預測     導入測試集圖像
prediction = model.predict_classes(imgs)
# prediction = np.argmax(model.predict(imgs), axis=-1)

#繪制預測圖像
print(prediction)
print(np.size(prediction))
for i in range(np.size(prediction)):
    #打印每張圖像的預測結果
    print("第",i+1,"朵花預測:"+flower_dict[prediction[i]]) #預測返回的01234的標簽,通過列表將他變成花名    
#     img = plt.imread(path_test+"test"+str(i+1)+".jpg")     #讀取正確圖像
    #由於glob函數讀圖是亂序讀的,所以得修正一下
    img = plt.imread(correction[i]) 
    plt.imshow(img)
    plt.show()                                             #繪制

個人總結

1.通過loss、accuracy、val_loss、val_accruacy分析來優化--入門版

  1. loss:訓練集損失值
  2. accuracy:訓練集准確率
  3. val_loss:測試集損失值
  4. val_accruacy:測試集准確率

分析准則:

​ train loss 不斷下降,test loss不斷下降,說明網絡仍在學習;(最好的)

​ train loss 不斷下降,test loss趨於不變,說明網絡過擬合;(max pool或者正則化)

​ train loss 趨於不變,test loss不斷下降,說明數據集100%有問題;(檢查dataset)

​ train loss 趨於不變,test loss趨於不變,說明學習遇到瓶頸,需要減小學習率或批量數目;(減少學習率)

​ train loss 不斷上升,test loss不斷上升,說明網絡結構設計不當,訓練超參數設置不當,數據集經過清洗等問題。(最不好的情況)

​ 參考博客 https://www.cnblogs.com/Timeouting-Study/p/12591448.html

2.提高圖像分類准確率----進階版

image-20210720152957355

由於原博客的作者已經寫的很清楚了,這里就只貼出鏈接以供學習參考

(PS:我個人覺得,還是通過增大數據集來訓練的方法對新手還是友好一些,例如這個花卉識別由於圖片每類就五六百張圖片,導致准確率不大高。但是我去用類似的模型拿各有上萬張圖片的貓狗圖片的時候,准確率能夠達到百分之八十多,所以通過增大數據集還是一個可以用的方法)

​ 參考博客 https://blog.csdn.net/weixin_38208741/article/details/79285632

3.個人認為較好的一些CNN概念介紹博客

  1. https://zhuanlan.zhihu.com/p/38681805
  2. http://www.woshipm.com/ai/3884563.html

4.TensoFlow-gpu版本安裝(個人留坑)

在訓練貓狗識別的時候,發現模型在迭代一次的時候就需要三分鍾,如果迭代次數過多的話,就會需要很長時間,如果以后需要嘗試其他項目的時候,可能會有一些不方便,決定在有時間了裝上GPU版本的TensoFlow。

安裝參考博客:https://blog.csdn.net/qq_43529415/article/details/100847887

CNN貓狗識別參考:https://blog.csdn.net/Einstellung/article/details/82773170

花卉識別項目:https://github.com/1103270775/Machine-learning_project


免責聲明!

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



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