基於Tensorflow + Opencv 實現CNN自定義影像分類

華為雲開發者社群發表於2021-09-22
摘要:本篇文章主要通過Tensorflow+Opencv實現CNN自定義影像分類案例,它能解決我們現實論文或實踐中的影像分類問題,並與機器學習的影像分類演算法進行對比實驗。

本文分享自華為雲社群《Tensorflow+Opencv實現CNN自定義影像分類及與KNN影像分類對比》,作者:eastmount 。

一.影像分類

影像分類(Image Classification)是對影像內容進行分類的問題,它利用計算機對影像進行定量分析,把影像或影像中的區域劃分為若干個類別,以代替人的視覺判斷。影像分類的傳統方法是特徵描述及檢測,這類傳統方法可能對於一些簡單的影像分類是有效的,但由於實際情況非常複雜,傳統的分類方法不堪重負。現在,廣泛使用機器學習和深度學習的方法來處理影像分類問題,其主要任務是給定一堆輸入圖片,將其指派到一個已知的混合類別中的某個標籤。

在下圖中,影像分類模型將獲取單個影像,並將為4個標籤{cat,dog,hat,mug},分別對應概率{0.6, 0.3, 0.05, 0.05},其中0.6表示影像標籤為貓的概率,其餘類比。該影像被表示為一個三維陣列。在這個例子中,貓的影像寬度為248畫素,高度為400畫素,並具有紅綠藍三個顏色通道(通常稱為RGB)。因此,影像由248×400×3個數字組成或總共297600個數字,每個數字是一個從0(黑色)到255(白色)的整數。影像分類的任務是將這接近30萬個數字變成一個單一的標籤,如“貓(cat)”。

那麼,如何編寫一個影像分類的演算法呢?又怎麼從眾多影像中識別出貓呢?這裡所採取的方法和教育小孩看圖識物類似,給出很多影像資料,讓模型不斷去學習每個類的特徵。在訓練之前,首先需要對訓練集的影像進行分類標註,如圖所示,包括cat、dog、mug和hat四類。在實際工程中,可能有成千上萬類別的物體,每個類別都會有上百萬張影像。

影像分類是輸入一堆影像的畫素值陣列,然後給它分配一個分類標籤,通過訓練學習來建立演算法模型,接著使用該模型進行影像分類預測,具體流程如下:

  • 輸入: 輸入包含N個影像的集合,每個影像的標籤是K種分類標籤中的一種,這個集合稱為訓練集。
  • 學習: 第二步任務是使用訓練集來學習每個類的特徵,構建訓練分類器或者分類模型。
  • 評價: 通過分類器來預測新輸入影像的分類標籤,並以此來評價分類器的質量。通過分類器預測的標籤和影像真正的分類標籤對比,從而評價分類演算法的好壞。如果分類器預測的分類標籤和影像真正的分類標籤一致,表示預測正確,否則預測錯誤。

常見的分類演算法包括樸素貝葉斯分類器、決策樹、K最近鄰分類演算法、支援向量機、神經網路和基於規則的分類演算法等,同時還有用於組合單一類方法的整合學習演算法,如Bagging和Boosting等。

二.基於KNN演算法的影像分類

1.KNN演算法

K最近鄰分類(K-Nearest Neighbor Classifier)演算法是一種基於例項的分類方法,是資料探勘分類技術中最簡單常用的方法之一。該演算法的核心思想是從訓練樣本中尋找所有訓練樣本X中與測試樣本距離(歐氏距離)最近的前K個樣本(作為相似度),再選擇與待分類樣本距離最小的K個樣本作為X的K個最鄰近,並檢測這K個樣本大部分屬於哪一類樣本,則認為這個測試樣本類別屬於這一類樣本。

假設現在需要判斷下圖中的圓形圖案屬於三角形還是正方形類別,採用KNN演算法分析如下:

  • 當K=3時,圖中第一個圈包含了三個圖形,其中三角形2個,正方形一個,該圓的則分類結果為三角形。
  • 當K=5時,第二個圈中包含了5個圖形,三角形2個,正方形3個,則以3:2的投票結果預測圓為正方形類標。設定不同的K值,可能預測得到不同的結果。

簡而言之,一個樣本與資料集中的k個最相鄰樣本中的大多數的類別相同。由其思想可以看出,KNN是通過測量不同特徵值之間的距離進行分類,而且在決策樣本類別時,只參考樣本週圍k個“鄰居”樣本的所屬類別。因此比較適合處理樣本集存在較多重疊的場景,主要用於預測分析、文字分類、降維等處理。

KNN在Sklearn機器學習包中,實現的類是neighbors.KNeighborsClassifier,簡稱KNN演算法。構造方法為:

KNeighborsClassifier(algorithm='ball_tree', 
    leaf_size=30, 
    metric='minkowski',
    metric_params=None, 
    n_jobs=1, 
    n_neighbors=3, 
    p=2, 
    weights='uniform')

KNeighborsClassifier可以設定3種演算法:brute、kd_tree、ball_tree,設定K值引數為n_neighbors=3。呼叫方法如下:

  • from sklearn.neighbors import KNeighborsClassifier
  • knn = KNeighborsClassifier(n_neighbors=3, algorithm=“ball_tree”)

它包括兩個步驟:

  • 訓練:nbrs.fit(data, target)
  • 預測:pre = clf.predict(data)

2.資料集

該部分主要使用Scikit-Learn包進行Python影像分類處理。Scikit-Learn擴充套件包是用於Python資料探勘和資料分析的經典、實用擴充套件包,通常縮寫為Sklearn。Scikit-Learn中的機器學習模型是非常豐富的,包括線性迴歸、決策樹、SVM、KMeans、KNN、PCA等等,使用者可以根據具體分析問題的型別選擇該擴充套件包的合適模型,從而進行資料分析,其安裝過程主要通過“pip install scikit-learn”實現。

實驗所採用的資料集為Sort_1000pics資料集,該資料集包含了1000張圖片,總共分為10大類,分別是人(第0類)、沙灘(第1類)、建築(第2類)、大卡車(第3類)、恐龍(第4類)、大象(第5類)、花朵(第6類)、馬(第7類)、山峰(第8類)和食品(第9類),每類100張。如圖所示。

接著將所有各類影像按照對應的類標劃分至“0”至“9”命名的資料夾中,如圖所示,每個資料夾中均包含了100張影像,對應同一類別。

比如,資料夾名稱為“6”中包含了100張花的影像,如下圖所示。

3.KNN影像分類

下面是呼叫KNN演算法進行影像分類的完整程式碼,它將1000張影像按照訓練集為70%,測試集為30%的比例隨機劃分,再獲取每張影像的畫素直方圖,根據畫素的特徵分佈情況進行影像分類分析。KNeighborsClassifier()核心程式碼如下:

* from sklearn.neighbors import KNeighborsClassifier

* clf = KNeighborsClassifier(n_neighbors=11).fit(XX_train, y_train)

* predictions_labels = clf.predict(XX_test)
完整程式碼及註釋如下:

# -*- coding: utf-8 -*-
import os
import cv2
import numpy as np
from sklearn.cross_validation import train_test_split
from sklearn.metrics import confusion_matrix, classification_report

#----------------------------------------------------------------------------------
# 第一步 切分訓練集和測試集
#----------------------------------------------------------------------------------

X = [] #定義影像名稱
Y = [] #定義影像分類類標
Z = [] #定義影像畫素

for i in range(0, 10):
    #遍歷資料夾,讀取圖片
    for f in os.listdir("photo/%s" % i):
        #獲取影像名稱
        X.append("photo//" +str(i) + "//" + str(f))
        #獲取影像類標即為資料夾名稱
        Y.append(i)

X = np.array(X)
Y = np.array(Y)

#隨機率為100% 選取其中的30%作為測試集
X_train, X_test, y_train, y_test = train_test_split(X, Y,
                                                    test_size=0.3, random_state=1)

print len(X_train), len(X_test), len(y_train), len(y_test)

#----------------------------------------------------------------------------------
# 第二步 影像讀取及轉換為畫素直方圖
#----------------------------------------------------------------------------------

#訓練集
XX_train = []
for i in X_train:
    #讀取影像
    #print i
    image = cv2.imread(i)
    
    #影像畫素大小一致
    img = cv2.resize(image, (256,256),
                     interpolation=cv2.INTER_CUBIC)

    #計算影像直方圖並儲存至X陣列
    hist = cv2.calcHist([img], [0,1], None,
                            [256,256], [0.0,255.0,0.0,255.0])

    XX_train.append(((hist/255).flatten()))

#測試集
XX_test = []
for i in X_test:
    #讀取影像
    #print i
    image = cv2.imread(i)
    
    #影像畫素大小一致
    img = cv2.resize(image, (256,256),
                     interpolation=cv2.INTER_CUBIC)

    #計算影像直方圖並儲存至X陣列
    hist = cv2.calcHist([img], [0,1], None,
                            [256,256], [0.0,255.0,0.0,255.0])

    XX_test.append(((hist/255).flatten()))

#----------------------------------------------------------------------------------
# 第三步 基於KNN的影像分類處理
#----------------------------------------------------------------------------------

from sklearn.neighbors import KNeighborsClassifier
clf = KNeighborsClassifier(n_neighbors=11).fit(XX_train, y_train)
predictions_labels = clf.predict(XX_test)

print u'預測結果:'
print predictions_labels

print u'演算法評價:'
print (classification_report(y_test, predictions_labels))

#輸出前10張圖片及預測結果
k = 0
while k<10:
    #讀取影像
    print X_test[k]
    image = cv2.imread(X_test[k])
    print predictions_labels[k]
    #顯示影像
    cv2.imshow("img", image)
    cv2.waitKey(0)
    cv2.destroyAllWindows()
    k = k + 1

程式碼中對預測集的前十張影像進行了顯示,其中“818.jpg”影像如圖所示,其分類預測的類標結果為“8”,表示第8類山峰,預測結果正確。

下圖展示了“452.jpg”影像,其分類預測的類標結果為“4”,表示第4類恐龍,預測結果正確。

下圖展示了“507.jpg”影像,其分類預測的類標結果為“7”,錯誤地預測為第7類恐龍,其真實結果應該是第5類大象。

使用KNN演算法進行影像分類實驗,最後演算法評價的準確率(Precision)、召回率(Recall)和F值(F1-score)如圖所示,其中平均準確率為0.64,平均召回率為0.55,平均F值為0.50,其結果不是非常理想。那麼,如果採用CNN卷積神經網路進行分類,通過不斷學習細節是否能提高準確度呢?

三.Tensorflow+Opencv實現CNN影像分類

首先,我們需要在TensorFlow環境下安裝OpenCV擴充套件包;其次需要通過TensorFlow環境搭建CNN神經網路;最後通過不斷學實現影像分類實驗。

1.OpenCV庫安裝

第一步,開啟Anaconda程式,並選擇已經安裝好的“TensorFlow”環境,執行Spyder。

第二步,我們需要在TensorFlow環境中安裝opencv-python擴充套件包,否則會提示錯誤“ModuleNotFoundError: No module named ‘cv2’”。呼叫Anaconda Prompt安裝即可,如下圖所示:

activate tensorflow
pip install opencv-python

安裝成功如下圖所示。

但是,由於anaconda的.org伺服器在國外,下載速度很慢,提示錯誤“Anaconda An HTTP error occurred when trying to retrieve this URL.HTTP errors are often intermittent”。

  • 解決方法一:從國內清華的映象下載
  • 解決方法二:從PYPI網站下載對應版本的opencv-python,在再安裝本地下載的.whl檔案。下載地址:https://www.lfd.uci.edu/~gohlke/pythonlibs/#OpenCV
    由於第一種方法一直失敗,這裡推薦讀者嘗試第二種方法,同時作者會將“opencv_python-4.1.2-cp36-cp36m-win_amd64.whl”檔案上傳供大家直接使用。(4.1.2代表opencv的版本,cp36代表用的python3.6,並且是64位)。

第三步,呼叫PIP安裝本地opencv擴充套件包。

activate tensorflow
pip install C:\Users\xiuzhang\Desktop\TensorFlow\opencv_python-4.1.2-cp36-cp36m-win_amd64.whl

這種方法非常迅速,推薦大家使用。安裝成功之後,開始編寫我們的程式碼吧!

2.讀取資料夾影像

該部分具體步驟如下:

  • 定義函式read_img(),讀取資料夾“photo”中“0”到“9”的影像
  • 呼叫cv2.imread()函式迴圈獲取每張圖片的所有畫素值,並通過
    cv2.resize()統一修改為32*32大小
  • 依次獲取影像畫素、影像類標和影像路徑名稱:fpaths, data, label = read_img(path)
  • 將影像的順序隨機調整,並按照2-8比例劃分資料集,其中80%的資料用於訓練,20%的資料用於測試
#---------------------------------第一步 讀取影像-----------------------------------
def read_img(path):
    cate = [path + x for x in os.listdir(path) if os.path.isdir(path + x)]
    imgs = []
    labels = []
    fpath = []
    for idx, folder in enumerate(cate):
        # 遍歷整個目錄判斷每個檔案是不是符合
        for im in glob.glob(folder + '/*.jpg'):
            #print('reading the images:%s' % (im))
            img = cv2.imread(im)             #呼叫opencv庫讀取畫素點
            img = cv2.resize(img, (32, 32))  #影像畫素大小一致
            imgs.append(img)                 #影像資料
            labels.append(idx)               #影像類標
            fpath.append(path+im)            #影像路徑名
            #print(path+im, idx)
            
    return np.asarray(fpath, np.string_), np.asarray(imgs, np.float32), np.asarray(labels, np.int32)

# 讀取影像
fpaths, data, label = read_img(path)
print(data.shape)  # (1000, 256, 256, 3)
# 計算有多少類圖片
num_classes = len(set(label))
print(num_classes)

# 生成等差數列隨機調整影像順序
num_example = data.shape[0]
arr = np.arange(num_example)
np.random.shuffle(arr)
data = data[arr]
label = label[arr]
fpaths = fpaths[arr]

# 拆分訓練集和測試集 80%訓練集 20%測試集
ratio = 0.8
s = np.int(num_example * ratio)
x_train = data[:s]
y_train = label[:s]
fpaths_train = fpaths[:s] 
x_val = data[s:]
y_val = label[s:]
fpaths_test = fpaths[s:] 
print(len(x_train),len(y_train),len(x_val),len(y_val)) #800 800 200 200
print(y_val)

3.搭建CNN

該部分具體步驟如下:

  • 首先定義Placeholder,用於傳入輸入值,xs表示圖片3232畫素點,並且包含RGB三個圖層,故大小設定為32 32 * 3;ys表示每張圖片最終預測的類標值。
  • 呼叫tf.layers.conv2d()函式定義卷積層,包括20個卷積核,卷積核大小為5,激勵函式為Relu;呼叫tf.layers.max_pooling2d()函式定義池化處理,步長為2,縮小一倍。
  • 接著定義第二個卷積層和池化層,現共有conv0, pool0和conv1, pool1。
  • 通過tf.layers.dense()函式定義全連線層,轉換為長度為400的特徵向量,加上DropOut防止過擬合。
  • 輸出層為logits,包括10個數字,最終預測結果為predicted_labels,即為tf.arg_max(logits, 1)。
#---------------------------------第二步 建立神經網路-----------------------------------
# 定義Placeholder
xs = tf.placeholder(tf.float32, [None, 32, 32, 3])  #每張圖片32*32*3個點
ys = tf.placeholder(tf.int32, [None])               #每個樣本有1個輸出
# 存放DropOut引數的容器 
drop = tf.placeholder(tf.float32)                   #訓練時為0.25 測試時為0

# 定義卷積層 conv0
conv0 = tf.layers.conv2d(xs, 20, 5, activation=tf.nn.relu)    #20個卷積核 卷積核大小為5 Relu啟用
# 定義max-pooling層 pool0
pool0 = tf.layers.max_pooling2d(conv0, [2, 2], [2, 2])        #pooling視窗為2x2 步長為2x2
print("Layer0:\n", conv0, pool0)
 
# 定義卷積層 conv1
conv1 = tf.layers.conv2d(pool0, 40, 4, activation=tf.nn.relu) #40個卷積核 卷積核大小為4 Relu啟用
# 定義max-pooling層 pool1
pool1 = tf.layers.max_pooling2d(conv1, [2, 2], [2, 2])        #pooling視窗為2x2 步長為2x2
print("Layer1:\n", conv1, pool1)

# 將3維特徵轉換為1維向量
flatten = tf.layers.flatten(pool1)

# 全連線層 轉換為長度為400的特徵向量
fc = tf.layers.dense(flatten, 400, activation=tf.nn.relu)
print("Layer2:\n", fc)

# 加上DropOut防止過擬合
dropout_fc = tf.layers.dropout(fc, drop)

# 未啟用的輸出層
logits = tf.layers.dense(dropout_fc, num_classes)
print("Output:\n", logits)

# 定義輸出結果
predicted_labels = tf.arg_max(logits, 1)

4.定義損失函式和優化器

利用交叉熵定義損失,同時用AdamOptimizer優化器進行深度學習,核心程式碼如下。

one-hot型別資料又稱為一位有效編碼,主要是採用N位狀態暫存器來對N個狀態進行編碼,每個狀態都由他獨立的暫存器位,並且在任意時候只有一位有效。例如[0 0 0 1 0 0 0 0 0 0…] 表示為“動物”。

# 利用交叉熵定義損失
losses = tf.nn.softmax_cross_entropy_with_logits(
        labels = tf.one_hot(ys, num_classes),       #將input轉化為one-hot型別資料輸出
        logits = logits)

# 平均損失
mean_loss = tf.reduce_mean(losses)

# 定義優化器 學習效率設定為0.0001
optimizer = tf.train.AdamOptimizer(learning_rate=1e-4).minimize(losses)

5.模型訓練和預測

定義標記變數train,當它為True時進行訓練操作並儲存訓練模型;當其為False時進行預測,20%預測集進行影像分類預測實驗。

#------------------------------------第四步 模型訓練和預測-----------------------------------
# 用於儲存和載入模型
saver = tf.train.Saver()
# 訓練或預測
train = False
# 模型檔案路徑
model_path = "model/image_model"

with tf.Session() as sess:
    if train:
        print("訓練模式")
        # 訓練初始化引數
        sess.run(tf.global_variables_initializer())
        # 定義輸入和Label以填充容器 訓練時dropout為0.25
        train_feed_dict = {
                xs: x_train,
                ys: y_train,
                drop: 0.25
        }
        # 訓練學習1000次
        for step in range(1000):
            _, mean_loss_val = sess.run([optimizer, mean_loss], feed_dict=train_feed_dict)
            if step % 50 == 0:  #每隔50次輸出一次結果
                print("step = {}\t mean loss = {}".format(step, mean_loss_val))
        # 儲存模型
        saver.save(sess, model_path)
        print("訓練結束,儲存模型到{}".format(model_path))
    else:
        print("測試模式")
        # 測試載入引數
        saver.restore(sess, model_path)
        print("從{}載入模型".format(model_path))
        # label和名稱的對照關係
        label_name_dict = {
            0: "人類",
            1: "沙灘",
            2: "建築",
            3: "公交",
            4: "恐龍",
            5: "大象",
            6: "花朵",
            7: "野馬",
            8: "雪山",
            9: "美食"
        }
        # 定義輸入和Label以填充容器 測試時dropout為0
        test_feed_dict = {
            xs: x_val,
            ys: y_val,
            drop: 0
        }
        
        # 真實label與模型預測label
        predicted_labels_val = sess.run(predicted_labels, feed_dict=test_feed_dict)
        for fpath, real_label, predicted_label in zip(fpaths_test, y_val, predicted_labels_val):
            # 將label id轉換為label名
            real_label_name = label_name_dict[real_label]
            predicted_label_name = label_name_dict[predicted_label]
            print("{}\t{} => {}".format(fpath, real_label_name, predicted_label_name))
        # 評價結果
        print("正確預測個數:", sum(y_val==predicted_labels_val))
        print("準確度為:", 1.0*sum(y_val==predicted_labels_val) / len(y_val))

6.完整程式碼及實驗結果

完整程式碼如下所示,這裡參考了王詩爺老師的部分程式碼,強烈推薦大家學習他的部落格。地址:https://blog.csdn.net/wills798

"""
Created on Sun Dec 29 19:21:08 2019
@author: xiuzhang Eastmount CSDN
"""
import os
import glob
import cv2
import numpy as np
import tensorflow as tf

# 定義圖片路徑
path = 'photo/'

#---------------------------------第一步 讀取影像-----------------------------------
def read_img(path):
    cate = [path + x for x in os.listdir(path) if os.path.isdir(path + x)]
    imgs = []
    labels = []
    fpath = []
    for idx, folder in enumerate(cate):
        # 遍歷整個目錄判斷每個檔案是不是符合
        for im in glob.glob(folder + '/*.jpg'):
            #print('reading the images:%s' % (im))
            img = cv2.imread(im)             #呼叫opencv庫讀取畫素點
            img = cv2.resize(img, (32, 32))  #影像畫素大小一致
            imgs.append(img)                 #影像資料
            labels.append(idx)               #影像類標
            fpath.append(path+im)            #影像路徑名
            #print(path+im, idx)
            
    return np.asarray(fpath, np.string_), np.asarray(imgs, np.float32), np.asarray(labels, np.int32)

# 讀取影像
fpaths, data, label = read_img(path)
print(data.shape)  # (1000, 256, 256, 3)
# 計算有多少類圖片
num_classes = len(set(label))
print(num_classes)

# 生成等差數列隨機調整影像順序
num_example = data.shape[0]
arr = np.arange(num_example)
np.random.shuffle(arr)
data = data[arr]
label = label[arr]
fpaths = fpaths[arr]

# 拆分訓練集和測試集 80%訓練集 20%測試集
ratio = 0.8
s = np.int(num_example * ratio)
x_train = data[:s]
y_train = label[:s]
fpaths_train = fpaths[:s] 
x_val = data[s:]
y_val = label[s:]
fpaths_test = fpaths[s:] 
print(len(x_train),len(y_train),len(x_val),len(y_val)) #800 800 200 200
print(y_val)
#---------------------------------第二步 建立神經網路-----------------------------------
# 定義Placeholder
xs = tf.placeholder(tf.float32, [None, 32, 32, 3])  #每張圖片32*32*3個點
ys = tf.placeholder(tf.int32, [None])               #每個樣本有1個輸出
# 存放DropOut引數的容器 
drop = tf.placeholder(tf.float32)                   #訓練時為0.25 測試時為0

# 定義卷積層 conv0
conv0 = tf.layers.conv2d(xs, 20, 5, activation=tf.nn.relu)    #20個卷積核 卷積核大小為5 Relu啟用
# 定義max-pooling層 pool0
pool0 = tf.layers.max_pooling2d(conv0, [2, 2], [2, 2])        #pooling視窗為2x2 步長為2x2
print("Layer0:\n", conv0, pool0)
 
# 定義卷積層 conv1
conv1 = tf.layers.conv2d(pool0, 40, 4, activation=tf.nn.relu) #40個卷積核 卷積核大小為4 Relu啟用
# 定義max-pooling層 pool1
pool1 = tf.layers.max_pooling2d(conv1, [2, 2], [2, 2])        #pooling視窗為2x2 步長為2x2
print("Layer1:\n", conv1, pool1)

# 將3維特徵轉換為1維向量
flatten = tf.layers.flatten(pool1)

# 全連線層 轉換為長度為400的特徵向量
fc = tf.layers.dense(flatten, 400, activation=tf.nn.relu)
print("Layer2:\n", fc)

# 加上DropOut防止過擬合
dropout_fc = tf.layers.dropout(fc, drop)

# 未啟用的輸出層
logits = tf.layers.dense(dropout_fc, num_classes)
print("Output:\n", logits)

# 定義輸出結果
predicted_labels = tf.arg_max(logits, 1)
#---------------------------------第三步 定義損失函式和優化器---------------------------------

# 利用交叉熵定義損失
losses = tf.nn.softmax_cross_entropy_with_logits(
        labels = tf.one_hot(ys, num_classes),       #將input轉化為one-hot型別資料輸出
        logits = logits)

# 平均損失
mean_loss = tf.reduce_mean(losses)

# 定義優化器 學習效率設定為0.0001
optimizer = tf.train.AdamOptimizer(learning_rate=1e-4).minimize(losses)
#------------------------------------第四步 模型訓練和預測-----------------------------------
# 用於儲存和載入模型
saver = tf.train.Saver()
# 訓練或預測
train = False
# 模型檔案路徑
model_path = "model/image_model"

with tf.Session() as sess:
    if train:
        print("訓練模式")
        # 訓練初始化引數
        sess.run(tf.global_variables_initializer())
        # 定義輸入和Label以填充容器 訓練時dropout為0.25
        train_feed_dict = {
                xs: x_train,
                ys: y_train,
                drop: 0.25
        }
        # 訓練學習1000次
        for step in range(1000):
            _, mean_loss_val = sess.run([optimizer, mean_loss], feed_dict=train_feed_dict)
            if step % 50 == 0:  #每隔50次輸出一次結果
                print("step = {}\t mean loss = {}".format(step, mean_loss_val))
        # 儲存模型
        saver.save(sess, model_path)
        print("訓練結束,儲存模型到{}".format(model_path))
    else:
        print("測試模式")
        # 測試載入引數
        saver.restore(sess, model_path)
        print("從{}載入模型".format(model_path))
        # label和名稱的對照關係
        label_name_dict = {
            0: "人類",
            1: "沙灘",
            2: "建築",
            3: "公交",
            4: "恐龍",
            5: "大象",
            6: "花朵",
            7: "野馬",
            8: "雪山",
            9: "美食"
        }
        # 定義輸入和Label以填充容器 測試時dropout為0
        test_feed_dict = {
            xs: x_val,
            ys: y_val,
            drop: 0
        }
        
        # 真實label與模型預測label
        predicted_labels_val = sess.run(predicted_labels, feed_dict=test_feed_dict)
        for fpath, real_label, predicted_label in zip(fpaths_test, y_val, predicted_labels_val):
            # 將label id轉換為label名
            real_label_name = label_name_dict[real_label]
            predicted_label_name = label_name_dict[predicted_label]
            print("{}\t{} => {}".format(fpath, real_label_name, predicted_label_name))
        # 評價結果
        print("正確預測個數:", sum(y_val==predicted_labels_val))
        print("準確度為:", 1.0*sum(y_val==predicted_labels_val) / len(y_val))

訓練輸出結果如下所示:

(1000, 32, 32, 3)
10
800 800 200 200
[2 8 6 9 9 5 2 2 9 3 7 0 6 0 0 1 3 2 7 3 4 6 9 5 8 6 4 1 1 4 4 8 6 2 6 1 2
 5 0 7 9 5 2 4 6 8 7 5 8 1 6 5 1 4 8 1 9 1 8 8 6 1 0 5 3 3 1 2 9 1 8 7 6 0
 8 1 8 0 2 1 3 5 3 6 9 8 7 5 2 5 2 8 8 8 4 2 2 4 3 5 3 3 9 1 1 5 2 6 7 6 7
 0 7 4 1 7 2 9 4 0 3 8 7 5 3 8 1 9 3 6 8 0 0 1 7 7 9 5 4 0 3 0 4 5 7 2 2 3
 0 8 2 0 2 3 5 1 7 2 1 6 5 8 1 4 6 6 8 6 5 5 1 7 2 8 7 1 3 9 7 1 3 6 0 8 7
 5 8 0 1 2 7 9 6 2 4 7 7 2 8 0]

Layer0: 
Tensor("conv2d_1/Relu:0", shape=(?, 28, 28, 20), dtype=float32) 
Tensor("max_pooling2d_1/MaxPool:0", shape=(?, 14, 14, 20), dtype=float32)
Layer1: 
Tensor("conv2d_2/Relu:0", shape=(?, 11, 11, 40), dtype=float32) 
Tensor("max_pooling2d_2/MaxPool:0", shape=(?, 5, 5, 40), dtype=float32)
Layer2:
 Tensor("dense_1/Relu:0", shape=(?, 400), dtype=float32)
Output:
 Tensor("dense_2/BiasAdd:0", shape=(?, 10), dtype=float32)

訓練模式
step = 0         mean loss = 66.93688201904297
step = 50        mean loss = 3.376957654953003
step = 100       mean loss = 0.5910811424255371
step = 150       mean loss = 0.061084795743227005
step = 200       mean loss = 0.013018212281167507
step = 250       mean loss = 0.006795921362936497
step = 300       mean loss = 0.004505819175392389
step = 350       mean loss = 0.0032660639844834805
step = 400       mean loss = 0.0024683878291398287
step = 450       mean loss = 0.0019308131886646152
step = 500       mean loss = 0.001541870180517435
step = 550       mean loss = 0.0012695763725787401
step = 600       mean loss = 0.0010685999877750874
step = 650       mean loss = 0.0009132082923315465
step = 700       mean loss = 0.0007910516578704119
step = 750       mean loss = 0.0006900889566168189
step = 800       mean loss = 0.0006068988586775959
step = 850       mean loss = 0.0005381597438827157
step = 900       mean loss = 0.0004809059901162982
step = 950       mean loss = 0.0004320790758356452
訓練結束,儲存模型到model/image_model

預測輸出結果如下圖所示,最終預測正確181張圖片,準確度為0.905。相比之前機器學習KNN的0.500有非常高的提升。

測試模式
INFO:tensorflow:Restoring parameters from model/image_model
從model/image_model載入模型
b'photo/photo/3\\335.jpg'       公交 => 公交
b'photo/photo/1\\129.jpg'       沙灘 => 沙灘
b'photo/photo/7\\740.jpg'       野馬 => 野馬
b'photo/photo/5\\564.jpg'       大象 => 大象
...
b'photo/photo/9\\974.jpg'       美食 => 美食
b'photo/photo/2\\220.jpg'       建築 => 公交
b'photo/photo/9\\912.jpg'       美食 => 美食
b'photo/photo/4\\459.jpg'       恐龍 => 恐龍
b'photo/photo/5\\525.jpg'       大象 => 大象
b'photo/photo/0\\44.jpg'        人類 => 人類

正確預測個數: 181
準確度為: 0.905

四.總結

寫到這裡,這篇文章就講解完畢,更多TensorFlow深度學習文章會繼續分享,同時實驗評價、RNN、LSTM、各專業的案例都會進行深入講解。最後,希望這篇基礎性文章對您有所幫助,如果文章中存在錯誤或不足之處,還請海涵~作為人工智慧的菜鳥,我希望自己能不斷進步並深入,後續將它應用於影像識別、網路安全、對抗樣本等領域,指導大家撰寫簡單的學術論文,一起加油!

參考文獻:

[1] 岡薩雷斯著. 數字影像處理(第3版)[M]. 北京:電子工業出版社,2013.
[2] 楊秀璋, 顏娜. Python網路資料爬取及分析從入門到精通(分析篇)[M]. 北京:北京航天航空大學出版社, 2018.
[3] 羅子江等. Python中的影像處理[M]. 科學出版社, 2020.
[4] [python資料探勘課程] 二十.KNN最近鄰分類演算法分析詳解及平衡秤TXT資料集讀取
[5] TensorFlow【極簡】CNN - Yellow_python大神
[6] 基於深度神經網路的定向啟用功能開發相位資訊的聲源定位 - 章子雎Kevin
[7] TensorFlow實戰:Chapter-5(CNN-3-經典卷積神經網路(GoogleNet)) - DFann
[8] https://github.com/siucaan/CN...
[9] 影像處理講解-以CNN對影像進行分類為例 - 冰機靈
[10] 基於CNN的影像缺陷分類 - BellaVita1
[12] tensorflow(六)訓練分類自己的圖片(CNN超詳細入門版)- Missayaa
[13] 詳解tensorflow訓練自己的資料集實現CNN影像分類 - 王詩爺 (強推)
[14] https://github.com/hujunxianl...
[15] tensorflow(三):用CNN進行影像分類 - flowrush
[16] TensorFlow影像識別(物體分類)入門教程 - cococok2
[17] https://github.com/calssion/F...
[18] CNN圖片分類 - 火舞_流沙
[19] CNN圖片單標籤分類(基於TensorFlow實現基礎VGG16網路)
[20] https://github.com/siucaan/CN...
[21] Tensorflow實現CNN用於MNIST識別 - siucaan (強推)
[22] 使用Anaconda3安裝tensorflow,opencv,使其可以在spyder中執行

點選關注,第一時間瞭解華為雲新鮮技術~

相關文章