PowerSensorAI教程2-石頭剪刀布-彩色數據預處理

要什麼openmv,來試試我們的powersensor吧,深度學習-剪刀石頭布!!

介紹

上一期的minist案例是爲了讓大家熟悉一下powersensor ai的整個流程,使用的數據集是google提供好的minist數據集,是灰度的、尺寸統一的。然而,實際的識別任務往往需要從不同的來源獲取不同尺寸的原始圖像,且一般是彩色。爲了解決這個問題,本章主要介紹如何對收集的數據進行預處理,以及生成適合tensorflow訓練的訓練集,以及如何調用powersensor獲取彩色的圖片並進行識別。

這次的案例是一個大家熟知的小遊戲,“石頭剪刀布。。”,這個布要拉長音。

PC訓練模型

數據集準備與預處理

本節使用數據集是google的石頭剪刀布數據集,已經包含在我們的百度網盤下載包裏。下載解壓完在dataset目錄下,可以看到:
在這裏插入圖片描述
除了minist這種封裝好的數據集,更普遍的機器學習數據集(如本節的數據集)一般是以原圖像文件分文件夾存放的形式提供,這樣有兩個優勢,一個是便於壓縮和分享,避免傳輸大體積文件;另一個是用戶較容易添加自己的數據集,以及便於使用網絡上下載的不同來源數據集的合併。但是,它也存在問題,深度網絡訓練時使用的數據集輸入往往是尺寸一致並經過標準化、歸一化操作的。因此,我們在獲取完數據集後,需要對數據集進行預處理,主要解決以下問題

  • 原圖像的存在分辨率的差異
  • 圖像的標籤和樣本沒有有機綁定(往往是一個標籤的圖片放一個文件夾)
  • 圖像沒有隨機排列,不利於訓練

爲了解決上述問題,我們使用tensorflow的tf recorder來完成數據集的準備和預處理工作。

  1. 首先,包含一些重要的頭文件和定義參數:
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 = 128
# 訓練的batch大小
batch_size = 32
# 數據庫路徑
dataset_path = '../dataset/'
# 存放過程和結果的路徑
run_path = './run/'
if not os.path.exists(run_path):
    os.mkdir(run_path)
wordlist = ['布', '石頭', '剪刀']
sorts_pathes = ['paper', 'rock', 'scissors']

# 存放轉換後的tf數據集的路徑
dataset_tf_path_train = run_path + 'datasetTfTrain.tfrecords'
dataset_tf_path_test = run_path + 'datasetTfTest.tfrecords'

# # 存放轉換後的tf數據集的路徑
# dataset_tf_path = run_path + 'flowersTf.tfrecords'
dataset_nums = 2520
testSet_nums = 372
  1. 使用tf-recorder生成數據集
def generate_dataset(raw_data_path, dataset_path):
    tick_begin = time.time()
    img_cnt = int(0)
    label_cnt = int(0)
    with tf.io.TFRecordWriter(dataset_path) as writer:
        for sort_path in sorts_pathes:    
            exp_list = os.listdir(raw_data_path + sort_path)
            for img_name in exp_list:
                img_path = raw_data_path + sort_path + "/" + img_name
                img = cv2.imread(img_path)  
                img_scale = cv2.resize(img,(img_size_net, img_size_net), interpolation = cv2.INTER_CUBIC)
                if not img is None:
                    feature = {
                        'img1':tf.train.Feature(bytes_list=tf.train.BytesList(value=[img_scale.tostring()])),
                        'label':tf.train.Feature(int64_list=tf.train.Int64List(value=[label_cnt]))
    #                     'label':tf.train.Feature(int64_list=tf.train.Int64List(value=[label_cnt]))
                    }
                    example = tf.train.Example(features=tf.train.Features(feature=feature))
                    writer.write(example.SerializeToString())
                    # 每隔50張打印一張圖片
                    if img_cnt % 100 == 0:
                        print('The ', str(img_cnt), ' image')
                        plt.imshow(cv2.cvtColor(img_scale, cv2.COLOR_BGR2RGB))
                        plt.show()
                    img_cnt += 1
            label_cnt = label_cnt + 1
        writer.close()   
    tick_end = time.time()
    print('Generate the dataset complete! Experied ', str(tick_end - tick_begin), 'The count of example is ', str(img_cnt))
    print('The dataset is ', dataset_path)

generate_dataset(dataset_path + "rps/", dataset_tf_path_train)
generate_dataset(dataset_path + "rps-test-set/", dataset_tf_path_test)

在這裏插入圖片描述
3. 讀取和測試數據集,生成數據集只需要運行一次就會自動保存到相應的目錄。讀取數據集在每次重啓jupyter的時候都需要加載一次。

def read_and_decode(example_proto):
    '''
    從TFrecord格式文件中讀取數據
    
    '''
    image_feature_description  = {
        'img1':tf.io.FixedLenFeature([],tf.string),
        'label':tf.io.FixedLenFeature([1], tf.int64),
    }
    feature_dict = tf.io.parse_single_example(example_proto, image_feature_description)
    img1 = tf.io.decode_raw(feature_dict['img1'], tf.uint8)
    label = feature_dict['label']
    return img1, label
dataset_train = tf.data.TFRecordDataset(dataset_tf_path_train)
dataset_train = dataset_train.map(read_and_decode)
dataset_test = tf.data.TFRecordDataset(dataset_tf_path_test)
dataset_test = dataset_test.map(read_and_decode)

# 2. 隨機打印8個訓練集測試圖像
dataset = dataset_train.shuffle(buffer_size=dataset_nums)
dataSet = np.array([x1 for x1 in dataset.take(10)])
dataSet_img = np.array([x1[0].numpy() for x1 in dataSet])
dataSet_img = dataSet_img.reshape((-1,img_size_net,img_size_net, 3)) / ((np.float32)(255.))
dataSet_label = np.array([x1[1].numpy()[0] for x1 in dataSet]) 

fig, ax = plt.subplots(3, 2)
fig.set_size_inches(9,15)
l = 0
for i in range(3):
    for j in range(2):
        ax[i, j].imshow(cv2.cvtColor(dataSet_img[l], cv2.COLOR_BGR2RGB))
        ax[i, j].set_title(wordlist[dataSet_label[l]])
        ax[i, j].grid(False)
        l += 1
plt.tight_layout()

在這裏插入圖片描述

模型訓練和保存

  1. 訓練集和測試集的打亂和重排,打亂和重排數據集可以打打減小訓練不穩定的風險。
num_train_eg = dataset_nums
num_test_eg = testSet_nums
# 1. 打亂數據集
dataset_train = dataset_train.shuffle(buffer_size=num_train_eg)
# 3. 分離訓練集和測試集
trainSet = np.array([x1 for x1 in dataset_train.take(dataset_nums)])
trainSet_img = np.array([x1[0].numpy() for x1 in trainSet])
trainSet_img = trainSet_img.reshape((-1,img_size_net,img_size_net, 3)) / ((np.float32)(255.))
trainSet_label = np.array([x1[1].numpy()[0] for x1 in trainSet]) 

testSet = np.array([x1 for x1 in dataset_test.take(num_test_eg)])
testSet_img = np.array([x1[0].numpy() for x1 in testSet])
testSet_img = testSet_img.reshape((-1,img_size_net,img_size_net, 3)) / ((np.float32)(255.))
testSet_label = np.array([x1[1].numpy()[0] for x1 in testSet]) 
  1. 深度學習網絡模型設計,這次的模型比minist的稍微複雜一丟丟哈,也就10層:
model = keras.Sequential([
    #keras.layers.Flatten(input_shape=(128, 128, 3)),
    keras.layers.Conv2D(32, (3,3), padding="same", input_shape=(img_size_net, img_size_net, 3), name='x_input', activation=tf.nn.relu),
#     keras.layers.BatchNormalization(),
#     keras.layers.Activation('relu'),
    keras.layers.MaxPooling2D(pool_size=(2,2)),
    keras.layers.Conv2D(64, (3,3), padding="same", activation=tf.nn.relu),
    keras.layers.MaxPooling2D(pool_size=(2,2)),
    keras.layers.Conv2D(128, (3,3), padding="same", activation=tf.nn.relu),
    keras.layers.MaxPooling2D(pool_size=(2,2)),
    keras.layers.Conv2D(128, (3,3), padding="same", activation=tf.nn.relu),
    keras.layers.MaxPooling2D(pool_size=(2,2)),
    keras.layers.Flatten(),
    keras.layers.Dense(30, activation=tf.nn.relu),
    keras.layers.Dropout(0.5),
    # 最後一個層決定輸出類別的數量
    keras.layers.Dense(3, 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(trainSet_img, trainSet_label, batch_size=batch_size, epochs=15, validation_data=(testSet_img, testSet_label))
tick_end = time.time()
print("Tring completed. Experied ", str(tick_end - tick_start))

在這裏插入圖片描述

  1. 減小學習率,再訓練一會兒
from tensorflow.keras.optimizers import SGD
model.compile(optimizer=SGD(lr=0.0001, momentum=0.9), loss=tf.keras.losses.SparseCategoricalCrossentropy(), metrics=['accuracy'])
tick_start = time.time()
history = model.fit(trainSet_img, trainSet_label, batch_size=batch_size, epochs=30, validation_data=(testSet_img, testSet_label))
tick_end = time.time()
print("Tring completed. Experied ", str(tick_end - tick_start))

別看精度沒有提高,只要誤差下降就能提升性能。

  1. 測試訓練效果
# 使用tensorflow的函數評估精度
res = model.evaluate(testSet_img, testSet_label)
print('test set: ', res)
res = model.evaluate(trainSet_img, trainSet_label)
print('train set: ', res)

#========================================
#372/372 [==============================] - 0s 445us/sample - #loss: 0.4589 - accuracy: 0.9355
#test set:  [0.45888313828211436, 0.9354839]
#2520/2520 [==============================] - 1s 398us/sample - #loss: 4.8881e-06 - accuracy: 1.0000
#train set:  [4.888053711369814e-06, 1.0]
  1. 保存模型
model.save_weights(run_path + "model_weight.h5")
json_config = model.to_json()
with open(run_path + 'model_config.json', 'w') as json_file:
    json_file.write(json_config)

dnndk編譯

  1. 打開虛擬機,登錄密碼是123
  2. 在虛擬機的/home/xiaobo/powersensor目錄下新建rockPaperScissors文件夾,並把案例目錄下的dnndk文件夾和dataset_valid文件夾複製到新建的文件夾下面。如果要使用自己新訓練的模型,需要把自己的模型(在案例目錄/pc/run下面的2個模型文件)替換掉我們準備好的文件。
  3. 使用下面指令固化模型
./1_vitisAI_keras2frozon.sh
  1. 第二步,量化,注意把2_vitisAI_tf_quantize.sh中的input_nodesinput_shapesoutput_nodes改成與第一步打印的節點名稱一致。
./2_vitisAI_tf_quantize.sh
  1. 第三步,編譯模型
./3_vitisAI_tf_compile.sh 

edge調用

  1. 進入powersenosr的jupyter文件管理頁面,在/powersensor_workspace/powersensor_ai下面新建ministNumber目錄(隨教程發佈的案例已經準備好文件了)。在新建的ministNumber目錄下新建dataset_validedge文件夾
    edge_dir
  2. 通過jupyter的上傳功能,把案例目錄下的edge文件夾下的兩個文件上傳到powersensor的edge目錄;驗證集dataset_valid下的文件也同理上傳到相應目錄(可能需要新建paperrockscissros文件夾)。
  • 如果要使用自己新訓練的模型,可以把edge下的elf換成虛擬機裏的compileResult下的elf文件。
  • 注意虛擬機裏的文件不能直接上傳(找不到),要先拷貝到自己的電腦裏才能上傳。
  1. 打開powersensor的edge下的powersensor_ministNumer.ipynb文件,按照notebook裏面的指導逐個運行程序。

  2. 首先也是加載頭文件和重要的參數,其中DPU網絡參數應該與DPU的編譯結果輸出保持一致,否則會導致DPU崩潰。

from dnndk import n2cube
import numpy as np
from numpy import float32
import os
import cv2
import matplotlib.pyplot as plt
import random
import time
import matplotlib as mpl
from matplotlib import font_manager
import PowerSensor as ps
from IPython.display import clear_output

mpl.rcParams['axes.unicode_minus']=False     # 正常顯示負號
font = font_manager.FontProperties(fname="/usr/share/fonts/truetype/droid/DroidSansFallbackFull.ttf")

wordlist = ['布', '石頭', '剪刀']

# DPU網絡參數
# KERNEL_CONV="testModel"
ELF_NAME = "dpu_testModel_0.elf"
CONV_INPUT_NODE = "x_input_Conv2D"
CONV_OUTPUT_NODE = "y_out_MatMul"
  1. 測試集讀取與測試
dataset_path = '../dataset_valid/'
(validSet_images, validSet_lables) = load_valid_data(dataset_path)

# 2. 圖像預處理
# test_images = test_images.reshape((-1,28,28,1)) / 255.
validSet_images = np.array(validSet_images, dtype='float32')

# 3. 隨機打印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(validSet_lables))
        ax[i, j].imshow(cv2.cvtColor(validSet_images[l], cv2.COLOR_BGR2RGB))
        title = wordlist[validSet_lables[l]]
        title_utf8 = title.decode('utf8')
        ax[i, j].set_title(title_utf8, fontproperties=font)
plt.tight_layout()

在這裏插入圖片描述

  1. 加載DPU
dpu1 = ps.DpuHelper()
dpu1.load_kernel(ELF_NAME, input_node_name=CONV_INPUT_NODE, output_node_name=CONV_OUTPUT_NODE)
  1. 測試驗證集
tick_start = time.time()
test_num = len(validSet_lables)
right_eg_cnt = 0
for i in range(test_num):
    img1_scale = validSet_images[i]
    softmax = dpu1.predit_softmax(img1_scale)
    pdt= np.argmax(softmax, axis=0)
    if pdt == validSet_lables[i]:
        right_eg_cnt += 1
tick_end = time.time()
print('精度: ' + str((right_eg_cnt*1.) / test_num))
print('測試 ' + str(test_num) + ' 個樣本。耗時 ' + str(tick_end - tick_start) + '秒!')

###########################################
# 精度: 1.0
# 測試 30 個樣本。耗時 5.25601506233秒!
  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)-1)
        img1_scale = validSet_images[l]
        softmax = dpu1.predit_softmax(img1_scale)
        pdt= np.argmax(softmax, axis=0)
        ax[i, j].imshow(cv2.cvtColor(validSet_images[l], cv2.COLOR_BGR2RGB))
#         title = "預測:" + str(wordlist[pdt]) + "\n" + "真實:" + str(wordlist[test_labels[l]])
        title = "預測:" + wordlist[pdt] + "\n" + "真實:" + wordlist[validSet_lables[l]]
        title_utf8 = title.decode('utf8')
        ax[i, j].set_title(title_utf8, fontproperties=font)
plt.tight_layout()

在這裏插入圖片描述
8. 加載相機對象

# 這個對象用於操作攝像頭
cam1 = ps.ImageSensor()
cv_font = cv2.freetype.createFreeType2()
cv_font.loadFontData(fontFileName='/usr/share/fonts/truetype/droid/DroidSansFallbackFull.ttf', id=0)
  1. 相機讀取圖像並識別測試
for i in range(100):
    # 記錄時間
    start = time.time()
    # 清空顯示區
    clear_output(wait=True)
    # 讀取圖像
    imgMat = cam1.read_img_ori()
    imgShow = cv2.resize(imgMat, (320,240))
#     imgMat_cali = grey_world2(imgMat)
    # 圖像縮放,太大的圖像顯示非常浪費資源
    tempImg = cv2.resize(imgMat, (128,128))
    img_scale = tempImg / 255.
    img_scale = np.array(img_scale, dtype=np.float32)
    softmax = dpu1.predit_softmax(img_scale)
    pdt= np.argmax(softmax, axis=0)
    cv_font.putText(imgShow,wordlist[pdt], (10,10), fontHeight=30, color=(0,0,0), thickness=-1, line_type=cv2.LINE_4, bottomLeftOrigin=False)
#     tempImg = imgMat
    # 顯示圖像
    img = ps.CommonFunction.show_img_jupyter(imgShow)
    # 記錄運行時間
    end = time.time()
    # 打印運行時間
    print(end - start)
    # 因爲網絡傳輸的延時,需要稍息一下
#     time.sleep(0.1)

在這裏插入圖片描述

本章小結

本章主要通過石頭剪刀布的例子,讓大家瞭解如何處理比較通用的數據集,以及進一步熟悉dpu的操作流程。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章