參考教材:人工智能導論(第4版) 王萬良 高等教育出版社
實驗環境:Python3.6 + Tensor flow 1.12
人工智能導論實驗導航
實驗一:斑馬問題 https://blog.csdn.net/weixin_46291251/article/details/122246347
實驗二:圖像恢復 https://blog.csdn.net/weixin_46291251/article/details/122561220
實驗三:花卉識別 https://blog.csdn.net/weixin_46291251/article/details/122561505
實驗四:手寫體生成 https://blog.csdn.net/weixin_46291251/article/details/122576478
實驗源碼: xxx
3.1實驗介紹
3.1.1實驗背景
深度學習為人工智能核心技術,本章主要圍繞深度學習涉及的全連接神經網絡、卷積神經網絡和對抗神經網絡而開設的實驗。
卷積神經網絡(Convolutional Neural Networks, CNN)是一類包含卷積計算且具有深度結構的前饋神經網絡(Feedforward Neural Networks),是深度學習(deep learning)的代表算法之一 。卷積神經網絡具有表征學習(representation learning)能力,能夠按其階層結構對輸入信息進行平移不變分類(shift-invariant classification)
3.1.2實驗目的
本章實驗的主要目的是掌握深度學習相關基礎知識點,了解深度學習相關基礎知識,經典全連接神經網絡、卷積神經網絡和對抗神經網絡。掌握不同神經網絡架構的設計原理,熟悉使用Tensorflow 2.1深度學習框架實現深度學習實驗的一般流程。
3.1.3實驗簡介
隨着電子技術的迅速發展,人們使用便攜數碼設備(如手機、相機等)獲取花卉圖像越來越方便,如何自動識別花卉種類受到了廣泛的關注。由於花卉所處背景的復雜性,以及花卉自身的類間相似性和類內多樣性,利用傳統的手工提取特征進行圖像分類的方法,並不能很好地解決花卉圖像分類這一問題。
本實驗基於卷積神經網絡實現的花卉識別實驗與傳統圖像分類方法不同,卷積神經網絡無需人工提取特征,可以根據輸入圖像,自動學習包含豐富語義信息的特征,得到更為全面的花卉圖像特征描述,可以很好地表達圖像不同類別的信息。
3.2概要設計
本實驗將用戶在客戶端選取的花卉圖像作為輸入,運行花卉識別模型,實時返回識別結果作為輸出結果並顯示給用戶。
3.2.1功能結構
花卉識別實驗總體設計如下圖所示,該實驗可以划分為數據處理、模型構建、圖像識別三個主要的子實驗。
其中數據處理子實驗包括數據集划分、圖像預處理兩個部分;
模型構建子實驗主要包括模型定義、模型訓練以及模型部署三個部分;
圖像識別子實驗內容主要包括讀取花卉圖像、運行推斷模型進行圖像特征提取,輸出模型識別結果三個部分。
3.2.2體系結構
按照體系結構划分,整個實驗的體系結構可以划分為三部分,分別為模型訓練、模型保存和模型推理,如圖5-3所示。各層側重點各不相同。
訓練層運行在安裝有tensorflow框架的服務器,最好配置計算加速卡。
推斷層運行於開發環境,能夠支持卷積神經網絡的加速。
展示層運行於客戶端應用程序,能夠完成圖像選擇並實時顯示推斷層的計算結果。
各層之間存在單向依賴關系。推斷層需要的網絡模型由訓練層提供,並根據需要進行必要的格式轉換或加速重構。相應的,展示層要顯示的元數據需要由推斷層計算得到。
3.3詳細設計
3.3.1導入實驗環境
步驟 1導入相應的模塊
skimage包主要用於圖像數據的處理,在該實驗當中, io模塊主要用於圖像數據的讀取(imread)和輸出(imshow)操作,transform模塊主要用於改變圖像的大小(resize函數);
glob包主要用於查找符合特定規則的文件路徑名,跟使用windows下的文件搜索相似;
os模塊主要用於處理文件和目錄,比如:獲取當前目錄下文件,刪除制定文件,改變目錄,查看文件大小等;
tensorflow是目前業界最流行的深度學習框架之一,在圖像,語音,文本,目標檢測等領域都有深入的應用,也是該實驗的核心,主要用於定義占位符,定義變量,創建卷積神經網絡模型;numpy是一個基於python的科學計算包,在該實驗中主要用來處理數值運算;
time模塊主要用於處理時間系列的數據,在該實驗主要用於返回當前時間戳,計算腳本每個epoch運行所需要的時間。
# 導入模塊
# -*- coding:uft-8
#from skimage
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
步驟 2設置初始化環境
# 數據集的地址,改為自己的圖片文件地址
path = './flower_photos/'
# 縮放圖片大小為100*100,C為通道,彩色圖片數值為3
w = 100
h = 100
c = 3
types = 0 # 所有花的種類數,也是標簽的個數
flower = {0: 'bee', 1: 'blackberry', 2: 'blanket', 3: 'bougainvillea', 4: 'bromelia', 5: 'foxglove'}
3.3.2數據准備
這里通過os.listdir判斷給定目錄下的是文件還是文件夾,如果是文件夾,那么就進入讀取其中的所有文件,這些圖像對應的標簽就是他們所屬的文件夾的編號,遍歷完所有子文件夾(這里不限制文件夾數目,可以動態的識別出文件夾)之后便完成了數據集的讀取,函數返回讀取到的所有圖片和圖片對應的標簽。
# 讀取圖片
def read_img(path):
global types
global flower
imgs = []
labels = []
if not os.path.isdir(path):
print("路徑不正確")
exit(0)
for s in os.listdir(path):
print("\nfolder ", s)
f_label = s.split("_")[-1]
flower[types] = s.split("_")[0]
# flower[f_label]=s.split("_")[0]
s = path + s + "/"
if not os.path.isdir(s): # 只讀文件夾
continue
types += 1 # 數據集數目加1
for im in os.listdir(s):
im = s + im
print('\r\tloading :%s' % (im), end="")
img = io.imread(im)
img = transform.resize(img, (w, h))
imgs.append(img)
# labels.append(f_label)
labels.append(types - 1)
return np.asarray(imgs, np.float32), np.asarray(labels, np.int32)
3.3.3構建花卉識別模型
CNN訓練模型
模型尺寸分析:卷積層全都采用了補0,所以經過卷積層長和寬不變,只有深度加深。池化層全都沒有補0,所以經過池化層長和寬均減小,深度不變。
模型尺寸變化:100×100×3->100×100×32->50×50×32->50×50×64->25×25×64->25×25×128->12×12×128->12×12×128->6×6×128
本文的CNN模型比較簡單,后續完全可以通過增大模型復雜度或者改參數調試以及對圖像進行預處理來提高准確率。
下面列出網絡模型的部分:
這里首先用tf.getvariable創建新的tensorflow變量並用tf.tf.constant_initializer
和tf.constant_initializer初始化為(stddev=0.1)和(0.0)
然后用 tf.nn.conv2d創建2維卷積層步長參數為[1,1,1,1],卷積方式為’SAME’
然后用線性整流函數relu進行處理
with tf.variable_scope('layer1-conv1'):
conv1_weights = tf.get_variable("weight",[5,5,3,32],initializer=tf.truncated_normal_initializer(stddev=0.1))
conv1_biases = tf.get_variable("bias", [32], initializer=tf.constant_initializer(0.0))
conv1 = tf.nn.conv2d(input_tensor, conv1_weights, strides=[1, 1, 1, 1], padding='SAME')
relu1 = tf.nn.relu(tf.nn.bias_add(conv1, conv1_biases))
下面類似:
with tf.variable_scope('layer11-fc3'):
fc3_weights = tf.get_variable("weight", [512, 5],
initializer=tf.truncated_normal_initializer(stddev=0.1))
if regularizer != None: tf.add_to_collection('losses', regularizer(fc3_weights))
fc3_biases = tf.get_variable("bias", [5], initializer=tf.constant_initializer(0.1))
logit = tf.matmul(fc2, fc3_weights) + fc3_biases
下面是池化操作:利用tf.nn.max_pool完成,池化窗口大小為[1,2,2,1],填充方式為VALID表示不填充,窗口在每一個維度上滑動的步長為[1,2,2,1]
with tf.name_scope("layer4-pool2"):
pool2 = tf.nn.max_pool(relu2, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding='VALID')
3.3.4訓練並保存模型
下面給出部分訓練模型的代碼:
訓練過程中輸出每一次訓練的train loss 、train acc、val loss、val acc用於分析訓練的效果。
def train():
......
for epoch in range(n_epoch):
start_time = time.time()
# training
train_loss, train_acc, n_batch = 0, 0, 0
for x_train_a, y_train_a in minibatches(x_train, y_train, batch_size, shuffle=True):
_, err, ac = sess.run([train_op, loss, acc], feed_dict={x: x_train_a, y_: y_train_a})
train_loss += err
train_acc += ac
n_batch += 1
print("--第"+str(epoch + 1)+"次訓練:".ljust(10, " "), end="")
print(str((np.sum(train_loss)) / n_batch).ljust(20, " "), (str(np.sum(train_acc) / n_batch)).ljust(20, " "), end="")
# validation
val_loss, val_acc, n_batch = 0, 0, 0
for x_val_a, y_val_a in minibatches(x_val, y_val, batch_size, shuffle=False):
err, ac = sess.run([loss, acc], feed_dict={x: x_val_a, y_: y_val_a})
val_loss += err
val_acc += ac
n_batch += 1
print(str((np.sum(val_loss) / n_batch)).ljust(20, " "), str((np.sum(val_acc) / n_batch)).ljust(20, " "))
3.3.5識別花卉
首先創建用於讀取測試的花卉圖片的函數,該函數讀取測試文件夾中的所有圖片並以np.array的形式返回。
def get_test_img(path):
imgs = []
cv_data = []
for im in os.listdir(path):
im = path + im
print('\rrecognizing :%s' % (im), end="")
img = io.imread(im)
img = transform.resize(img, (w, h))
imgs.append(img)
cv_data.append(cv2.imread(im))
return np.asarray(imgs), cv_data
然后就可以利用上述訓練得到的判別器來判斷所給的圖像的標簽。然后就可以根據得到的標簽輸出花的名字
def recog():
path = './data/TestImages/'
model_path = "./model/"
data, cv_datas = get_test_img(path)
with tf.Session() as sess:
saver = tf.train.import_meta_graph(model_path + 'model.ckpt.meta')
saver.restore(sess, tf.train.latest_checkpoint(model_path))
graph = tf.get_default_graph()
x = graph.get_tensor_by_name("x:0")
feed_dict = {x: data}
logits = graph.get_tensor_by_name("logits_eval:0")
classification_result = sess.run(logits, feed_dict)
# 打印出預測矩陣
print(classification_result)
# 打印出預測矩陣每一行最大值的索引
print(tf.argmax(classification_result, 1).eval())
# 根據索引通過字典對應花的分類
output = tf.argmax(classification_result, 1).eval()
for i in range(len(output)):
print("第", i + 1, "朵花預測: label:", output[i], "\t", flower[output[i]])
font = cv2.FONT_HERSHEY_SIMPLEX
img = cv_datas[i]
cv2.putText(img, flower[output[i]], (20, 20), font, 1, (0, 0, 0), 1)
# cv2.imshow(flower[output[i]],img)
# cv2.waitKey(0) # # 使圖片停留用於觀察,沒有這一行代碼,圖片會在展示瞬間后消失
由於待識別的圖片一共有六張,所以將其排列在一個兩行三列的圖片之中。
使用pyplot的subplot即可實現,遍歷所有圖片並分別將圖片放第i個位置處。
# 下面改用pyplot繪圖以讓結果顯示在一張圖片中
pyplot.subplot(2, 3, 1 + i) # 使用subplot()構建子圖,第一個參數5表示每列有5張圖,第二個參數表示每行有6張圖,1+i表示第(1+i張圖)
pyplot.axis('off') # 關閉坐標軸("on"為顯示坐標軸)
pyplot.title(flower[output[i]])
pyplot.imshow(img, cmap='gray_r') # 使用imshow()函數畫出訓練集相應圖片原始像素數據,參數cmap = gray表示黑底白字圖像,這邊使用"gray_r"表示白底黑字圖
pyplot.savefig('res.png') # 保存圖片
pyplot.show()
3.4運行測試
首先讀取數據集,由下圖可見,程序依次讀取了六個文件夾,並從每個文件夾中讀取了多張圖片。
然后開始訓練,這里輸出了:訓練集損失值、:訓練集准確率、測試集損失值、測試集准確率這幾個指標,通過觀察數值變化可以發現:
train loss不斷減小
Train acc不斷增大直至為1並穩定為1
Val loss 大體上不斷減小,但后半部分稍有波動
Val acc 大體上不斷增大,但后半部分稍有波動
以上說明網絡正在學習,狀態良好。
然后輸出識別出的花卉對應的標簽以及名稱:
第1朵花預測: Label: 0 bee
第2朵花預測: Label: 1 bLackberry
第3朵花預測: Label: 2 bLanket
第4朵花預測: Label: 3 bougainvillea
第5朵花預測: Label: 4 bromelia
第6朵花預測: Label: 5 foxgLove
然后將圖片和名稱顯示在一張圖片內方便觀察。