PowerSensor AI教程1.1 - 數字識別 - tf模型訓練

寫在前面

  1. 需要懂一點機器學習
  2. 需要懂一點常用的shell指令
  3. 最好電腦配有顯卡,訓練過程會快很多
  4. 模型訓練不要使用虛擬機,會非常慢
  5. windows和linux都可以用來訓練模型,不要歧視windows
  6. 本章的例程在下面的百度網盤可以下載到:
    鏈接:https://pan.baidu.com/s/1-ThiQVzCazyY-5ZnqMYfXw
    提取碼:bg7c

背景知識

  1. 人工神經網絡
    人工神經網絡(Artificial Neural Network,即ANN ),是20世紀80 年代以來人工智能領域興起的研究熱點。它從信息處理角度對人腦神經元網絡進行抽象, 建立某種簡單模型,按不同的連接方式組成不同的網絡。在工程與學術界也常直接簡稱爲神經網絡或類神經網絡。神經網絡是一種運算模型,由大量的節點(或稱神經元)之間相互聯接構成。每個節點代表一種特定的輸出函數,稱爲激勵函數(activation function)。每兩個節點間的連接都代表一個對於通過該連接信號的加權值,稱之爲權重,這相當於人工神經網絡的記憶。網絡的輸出則依網絡的連接方式,權重值和激勵函數的不同而不同。而網絡自身通常都是對自然界某種算法或者函數的逼近,也可能是對一種邏輯策略的表達。

  2. 深度學習
    深度學習是機器學習中一種基於對數據進行表徵學習的算法。目前常說的深度學習技術主要指利用深層(5層或以上)的神經網絡對一個模型(比如手寫數字識別)進行逼近的技術。深度學習可以看成一個萬能逼近器,只要提供足夠多的樣本和標籤,通過訓練,深度學習就可以代替人類,完成生活中的分類、跟蹤等任務。

  3. 深度學習的模型、結構、權值
    我們常說的深度學習的模型主要包含神經網絡的結構和權值兩個部分,爲什麼要把這兩個參數分開呢?因爲一般網絡結構具有較好的泛化能力,就是說識別水果和識別數字可以使用同一個網絡結構,但是權值往往具有針對性,同一個結構的神經網絡通過載入不同的權值來實現不同的識別任務。下文所說的模型編譯指:當網絡訓練好要對模型進行部署時,把網絡權值固化到網絡結構裏,並編譯成powersensor能夠運行的機器碼。

  4. tensorflow
    tensorflow是google開發的一個機器學習庫,支持python等多種編程語言。雖然深度學習本身的結構複雜,很難從零開始編程實現,但是通過tensorflow,哪怕一個是中學生也可以通過幾十行代碼完成簡單的深度學習網絡的實現和訓練。

  5. dpu和dnndk
    DPU是xilinx推出的一個適用於FPGA的用於深度學習加速的IP核,爲了方便用戶將自己訓練的模型部署到DPU中,xilinx推出了dnndk來完成模型編譯(將用戶模型編譯成DPU能運行的模型)。可以說,DPU是硬件IP核,而dnndk是軟件管理的函數庫。由於dnndk只能在linux平臺下運行, 爲了方便大家,我們準備了一個按照好dnndk的vmware虛擬機,這樣用戶就可以在windows下通過虛擬機完成模型編譯。

  6. Anaconda
    Anaconda是一個免費開源[5]PythonR語言的發行版本,用於計算科學數據科學機器學習大數據處理預測分析),Anaconda致力於簡化包管理和部署。Anaconda的包使用軟件包管理系統Conda[6]進行管理。(來自wiki百科)。
    簡單的說,Anaconda是一個python的包提供平臺,軟件倉庫,同時配有conda管理工具。

  7. conda
    conda是一個開源跨平臺的包管理和環境管理系統,常用於python虛擬環境的搭建與管理。conda允許用戶從不同的源下載和升級軟件包。conda使得用戶可以同時安裝多個python虛擬環境,通過激活的方式,在不同的任務中使用不同的虛擬環境。

powersensor ai案例的開發流程

在瞭解了以上的基礎知識後,如果要把一個深度學習模型部署到powersensor上運行需要進行以下流程。

  • 首先,PC環節,使用tensorflow(本文使用的是tensorflow2.0)完成深度學習模型的設計和訓練,並將結構和權值分開存儲。
  • 然後,dnndk環節,在虛擬機下完成模型權值固化,並使用dnndk將tensorflow模型編譯成dpu能夠運行的模型(elf模型)。
  • 最後,edge環節,在powersensor下編寫調度程序,完成最終的深度學習任務。
    本章主要介紹第一步,pc環節。

軟件安裝與啓動

軟件安裝(只需要進行一次)

  1. 安裝anaconda,下載地址(去我們的百度網盤下載):https://www.anaconda.com/products/individual

  2. 啓動anaconda prompt,這個是終端,大部分包的安裝等操作會在終端進行

  3. 新建一個虛擬的Anaconda環境。命令過程需要輸入y確認,這裏都把這個步驟略過。

# 新建一個虛擬環境,名爲ps
conda create -n ps
# 激活ps環境,每次啓動prompt都需要激活,激活後左側的標識會變成ps
(base) C:\Users\xxx>conda activate ps

(ps) C:\Users\xxx>
  1. 逐條輸入以下指令完成python軟件包的安裝

熟悉的用戶可以通過ananconda換源來提升在國內的下載速度,教程見https://mirror.tuna.tsinghua.edu.cn/help/anaconda/

conda install opencv
conda install matplotlib
conda install jupyter

# 沒有顯卡的安裝這個tf
conda install tensorflow
# 有顯卡的安裝這個tf
conda install tensorflow-gpu

啓動jupyter(每次啓動需要進行一次)

# 首先激活環境
conda activate ps
# 啓動jupyter,路徑填powersensor_flowerclassfication所在盤的盤符(如e:)
jupyter-notebook --notebook-dir 路徑

模型訓練與保存

模型訓練

  1. 解壓下載到的ministNumber_classificaiton壓縮包(或者從Github上獲取,從Github上可以獲取到最新的程序,但是數據集需要額外下載),可以看到以下目錄結構

dir
其中,dataset中存放的是用於訓練和測試的數據集,dataset_valid存放的是用於驗證的數據集。pc存放的是tensorflow相關的訓練文件,用於訓練模型。dnndk存放的是需要拷貝到虛擬機編譯配置文件。edge存放的是在powersensor運行的調度文件。

  1. 啓動anaconda,激活上面建好的環境,啓動jupyter。進入pc文件夾,打開modelTrain.ipython,按照記事本的提示逐個運行。

  2. 首先是包含頭文件和重要的參數初始化,如果沒有GPU,請把GPU下面的4行註釋掉

import cv2
import numpy as np
import os
import tensorflow as tf
from tensorflow import keras
import random
import time
import matplotlib.pyplot as plt

# 有GPU才能打印
gpus = tf.config.experimental.list_physical_devices(device_type='GPU')
cpus = tf.config.experimental.list_physical_devices(device_type='CPU')
print(gpus, cpus)
for gpu in gpus:
    tf.config.experimental.set_memory_growth(gpu, True)
    
# 讓Matplotlib正確顯示中文
import matplotlib as mpl
mpl.style.use('seaborn')
mpl.rcParams['font.sans-serif']=['SimHei']   # 用黑體顯示中文
mpl.rcParams['axes.unicode_minus']=False     # 正常顯示負號

# 訓練用的圖像尺寸
img_size_net = 28
# 訓練的batch大小
batch_size = 32
# 數據庫路徑
dataset_path = '../dataset/minist-number/'
# 存放過程和結果的路徑
run_path = './run/'
if not os.path.exists(run_path):
    os.mkdir(run_path)
wordlist = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']

訓練用的圖像尺寸指輸入給神經網絡的圖像的尺寸,決定模型的入口大小,這一講用的數據集是預處理好的,因此原圖像尺寸與訓練用的圖像尺寸是一致的,但在其他問題裏面可能不一樣,需要進行預處理。神經網絡的訓練一次往往使用多個樣本,訓練的batch大小描述的就是這個大小,一般去2的冪次。數據庫的路徑,請確認在dataset\minist-number下有4個訓練數據。run路徑會臨時生成,存放的是訓練完的結果(需要拷貝到虛擬機裏)。wordlist是樣本的標籤,比如數字識別就是0-9,水果識別就是蘋果、梨。。。

  1. 數據集讀取與測試,在訓練前,我們需要加載數據集,並把數據集分成訓練集和測試集,訓練集用於訓練神經網絡,測試集用於測試訓練效果(不參與訓練)。這裏會隨機打印幾張樣本,用來驗證讀取是否正確。
# 1. 讀取數據集
(train_images, train_labels) = load_mnist(dataset_path, 'train')
(test_images, test_labels) = load_mnist(dataset_path, 't10k')

# 2. 圖像預處理
train_images = train_images.reshape((-1,28,28,1)) / 255.
test_images = test_images.reshape((-1,28,28,1)) / 255.

# 3. 統計各種訓練集中各種樣本的數量
print('各個樣本的數量:')
l = []
for x in train_labels:
    l.append(wordlist[x])
plt.hist(l, rwidth=0.5)
plt.show()

# 4. 隨機打印8個測試圖像
fig, ax = plt.subplots(5, 2)
fig.set_size_inches(15,15)
for i in range(5):
    for j in range(2):
        l = random.randint(0, len(test_labels))
        ax[i, j].imshow(test_images[l, :, :, 0])
        ax[i, j].set_title(wordlist[test_labels[l]])
plt.tight_layout()
  1. 網絡結構設計,這裏設計的是一個較爲簡單的6層神經網絡,包含2個卷積層(用於提取特徵),2個池化層(用於抽象特徵),2個全連接層,全連接層用於特徵的合併和類別的預測。因爲我們有0-9共10個類別需要預測,所以輸出層的神經元數量是10。compile是將設計的神經網絡實現處理,這裏我們使用了Adam作爲優化器,這種優化器具有動量的特性,能夠躍出一些常見的極小值點。lr是學習率。
model = keras.Sequential([
    #keras.layers.Flatten(input_shape=(128, 128, 3)),
    keras.layers.Conv2D(32, (3,3), padding="same", activation=tf.nn.relu, input_shape=(img_size_net, img_size_net, 1), name='x_input'),
    keras.layers.MaxPooling2D(pool_size=(2,2)),
    keras.layers.Conv2D(64, (3,3), padding="same", activation=tf.nn.relu, input_shape=(img_size_net, img_size_net, 1)),
    keras.layers.MaxPooling2D(pool_size=(2,2)),
    keras.layers.Flatten(),
    keras.layers.Dense(128, activation=tf.nn.relu),
    keras.layers.Dropout(0.5),
    # 最後一個層決定輸出類別的數量
    keras.layers.Dense(10, activation=tf.nn.softmax, name='y_out')
])
model.compile(optimizer=keras.optimizers.Adam(lr=0.001),
    loss='sparse_categorical_crossentropy',
    metrics=['accuracy'])
model.summary()
  1. 神經網絡訓練,對於這種簡單的數據集,第一次訓練的效果會非常好,一般地訓練集精度會隨着訓練進行逐漸提高,而測試集會呈現一個先上升再下降的趨勢(過擬合)。
tick_start = time.time()
history = model.fit(train_images, train_labels, batch_size=batch_size, epochs=15, validation_data=(test_images, test_labels))
tick_end = time.time()
print("Tring completed. Experied ", str(tick_end - tick_start))
  1. 訓練完成,我們可以打印訓練過程的精度變化
import matplotlib.pyplot as plt
fig, ax = plt.subplots(1, 1)
fig.set_size_inches(10,5)
ax.plot(history.history['accuracy'][1:])
ax.plot(history.history['val_accuracy'][1:])
ax.legend(['訓練集精度', '驗證集精度'], loc='upper left')
plt.show()

trainProcess

模型保存

使用以下python語句將模型結構與模型權值分開保存,會在run目錄下生成model_weight.h5model_config.json,保存的參數在dnndk的編譯中會使用到這兩個文件。分開保存是爲了更好地適應tensorflow的版本變化。

model_test.save_weights("model_weight.h5")
json_config = model_test.to_json()
with open('model_config.json', 'w') as json_file:
    json_file.write(json_config)

檢查ragged-tensor
使用記事本打開modelconfig.json,如果在輸入裏出現"ragged": false,字眼的字段,請手動把它刪掉,因爲dnndk裏的tensorflow沒有這個參數。沒有就不用處理。

模型驗證

在訓練好模型後,我們一般需要通過一些驗證程序來測試保存的模型。

  1. 加載模型,由於我們把結構和權值分開存儲,所以我們要先解碼模型再加載權值。
# 加載訓練好的模型

with open(run_path + 'model_config.json') as json_file:
    json_config = json_file.read()
    model_test = tf.keras.models.model_from_json(json_config)
# Load weights
model_test.load_weights(run_path + 'model_weight.h5')
# model_test = tf.keras.models.load_model(run_path + "model.h5")
model_test.summary()

pcModelLoad

  1. 加載驗證集,因爲這個案例中測試集沒有參與超參數的選擇,所以直接使用了測試集當驗證集。實際案例的訓練集、測試集、驗證集應該是獨立的。此外,dnndk在進行量化和在powersensor上測試的時候也需要使用驗證集,所以這裏將驗證集放在一個獨立的文件夾dataset_valid裏。
dataset_valid_path = '../dataset_valid//minist-number/'
# 1. 加載數據集
(validSet_images, validSet_lables) = load_mnist(dataset_valid_path, 't10k')

# 2. 圖像預處理
validSet_images = validSet_images.reshape((-1,28,28,1)) / 255.
  1. 使用驗證集評價模型精度
# 使用tensorflow的函數評估精度
res = model_test.evaluate(validSet_images, validSet_lables)
  1. 使用imblance庫評價統計學特性(可選)。從統計學上說,要評價一個模型的可信賴程度,不僅要看它的精度,還有考慮它的召回率、F1參數等。
# 使用imblance庫評估統計學特性
from imblearn.metrics import classification_report_imbalanced
pdt_label_fre = model_test.predict(validSet_images)
pdt_label = np.argmax(pdt_label_fre, axis=1)
print(classification_report_imbalanced(validSet_lables, pdt_label, target_names=wordlist))

imlearn

  1. 使用隨機樣本直觀地觀測預測結果:
fig, ax = plt.subplots(5, 2)
fig.set_size_inches(15,15)
for i in range(5):
    for j in range(2):
        l = random.randint(0, len(validSet_lables))
        pdt_label_fre = model_test.predict(validSet_images[l:l+1])
        pdt_label = np.argmax(pdt_label_fre, axis=1)
        ax[i, j].imshow(validSet_images[l, :, :, 0])
        title = "預測:" + wordlist[pdt_label[0]] + "\n" + "真實:" + wordlist[validSet_lables[l]]
        ax[i, j].set_title(title)
plt.tight_layout()

result1

  1. 到這裏pc階段的任務就結束了。這個階段的主要任務是根據實際問題設計合適的深度網絡結構,通過訓練得到滿足需求的網絡權值,訓練的結果在pc文件夾的run目錄裏:
    modelFile
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章