PowerSensorAI 3 从现有模型迁移训练 - 五花分类

效果展示

要什么openMV,来试试Powersensor吧 - 深度学习 - 五花分类

图文教程

  • 案例资料包:
    链接:https://pan.baidu.com/s/1bqxA2homcCQ1durTJDH2TQ
    提取码:knhd

  • Anaconda安装教程地址
    https://blog.csdn.net/linxiaobo110/article/details/106420769

  • 虚拟机下载地址:
    链接:https://pan.baidu.com/s/1mdZV9jn74RCxKp_pUh4mew
    提取码:623k

  • powersensor镜像下载地址:
    链接:https://pan.baidu.com/s/18CFK2aXonxuFF6-L_ittdw
    提取码:8ve4

对于一些简单的分类问题,如PowerSensor AI 1中的ministFashion,随手设计一个简单的深度网络就可以得到较好的分类效果。然而,这样的手法对于复杂一些的数据集很难保证效果。对于复杂的分类问题,使用已发布的精心设计的神经网络作为基础网络是更明智的选择。同时,从头开始训练深度的神经网络是一个繁重的工作,使用别人在imagenet上预训练好的权值来初始化我们的网络可以起到事半功倍的效果。本节我们将以五花分类问题(虽然这个问题不见得多复杂)为例,给大家介绍如何把tensorflow2.0官方训练好的InceptionV3,通过简单的几步操作,迁移到我们自己的数据集上使用,并在powersensor上验证运行。

经过上一讲的学习,我们已经知道DPU的应用开发流程分为PC模型训练、DNNDK模型编译、EDGE模型部署,三个步骤,本章剩下的内容将逐个介绍。

PC训练模型

首先我们需要包含一些重要的头文件和定义重要的参数

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
# 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/'
# 各个花的路径
flower_pathes = ['flowers/daisy', 'flowers/dandelion', 'flowers/rose', 'flowers/sunflower', 'flowers/tulip']
wordlist = ['雏菊', '蒲公英', '玫瑰', '向日葵', '郁金香']
# 存放过程和结构的路径
run_path = './run/'
if not os.path.exists(run_path):
    os.mkdir(run_path)
# 存放转换后的tf数据集的路径
dataset_tf_path = run_path + 'flowersTf.tfrecords'
dataset_nums = 4300

数据集准备与预处理

本节使用数据集是五花(玫瑰、向日葵、蒲公英、郁金香、雏菊)数据集,可以在我们的百度网盘数据集文件夹里下载到。下载完解压到flower_classification的dataset目录下,如下图所示:
在这里插入图片描述

在数据集预处理上,我们使用了上一章“石头剪刀布”识别的方案,先把原图像处理后转换为tf-recorder,然后再使用tfrecoder完成样本训练。

tick_begin = time.time()
img_cnt = int(0)
label_cnt = int(0)
with tf.io.TFRecordWriter(dataset_tf_path) as writer:
    for sort_path in flower_pathes:    
        flower_list = os.listdir(dataset_path + sort_path)
        for img_name in flower_list:
            img_path = dataset_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))
print('The dataset is ', dataset_tf_path)

在这里插入图片描述

读取和测试数据集,生成数据集只需要运行一次就会自动保存到相应的目录。读取数据集在每次重启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

# 1. 读取数据集
dataset = tf.data.TFRecordDataset(dataset_tf_path)
dataset = dataset.map(read_and_decode)

# 2. 随机打印8个测试图像
dataset = dataset.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(5, 2)
fig.set_size_inches(15,15)
l = 0
for i in range(5):
    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]])
        l += 1
plt.tight_layout()

在这里插入图片描述

网络设计与预训练

  1. 训练集和测试集的打乱和重排,打乱和重排数据集可以打打减小训练不稳定的风险。
# 1. 打乱数据集
dataset = dataset.shuffle(buffer_size=dataset_nums)
# 2. 抓取数据集
dataSet = np.array([x1 for x1 in dataset.take(dataset_nums)])
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]) 
# 3. 分离训练集和测试集
trainSet_num = int(0.75 * dataset_nums)
trainSet_img = dataSet_img[0 : trainSet_num, :, :, :]
testSet_img = dataSet_img[trainSet_num : , :, :, :]
trainSet_label = dataSet_label[0 : trainSet_num]
testSet_label = dataSet_label[trainSet_num : ]

# 3. 统计各种训练集中各种样本的数量
print('数据集中各个样本的数量:')
l = []
for x in dataSet_label:
    l.append(wordlist[x])
plt.hist(l, rwidth=0.5)
plt.show()
  1. 网络设计,对于minist哪种简单的数据集,我们可以随手搭一个自己的网络就可以有不俗的表现,这对于复杂一些的数据集很难保证效果。对于复杂的分类问题,使用已发布的精心设计的神经网络作为基础网络是更明智的选择。同时,从头开始训练深度的神经网络是一个繁重的工作,使用别人在imagenet上预训练好的权值来初始化我们的网络可以起到事半功倍的效果。
input_tensor = tf.keras.layers.Input(shape=(img_size_net,img_size_net,3), name="x_input")
base_model = tf.keras.applications.InceptionV3(weights='imagenet', 
                                               include_top=False, 
                                               input_tensor=input_tensor, 
                                               input_shape=(img_size_net,img_size_net,3))

base_model.summary()    #打印网络结构

在这里插入图片描述

  1. 一般来说,为了更高的效率,我们不需要整个网络,我们从输入开始,截取到输出为6x6的最后一个激活层。接上我们自己的全连接层和输出层,生成我们自己的网络:
base_model = tf.keras.Model(inputs=input_tensor, outputs=base_model.get_layer('activation_74').output)

# 添加全局平均池化层
x = base_model.output
x = tf.keras.layers.GlobalAveragePooling2D()(x)

# 添加一个全连接层
x = tf.keras.layers.Dense(64, activation='relu')(x)
x = tf.keras.layers.Dropout(0.5)(x)

# 添加一个分类器,假设我们有200个类
predictions = tf.keras.layers.Dense(5, activation='softmax', name='y_out')(x)

# 构建我们需要训练的完整模型
model = tf.keras.Model(inputs=input_tensor, outputs=predictions)

# 首先,我们只训练顶部的几层(随机初始化的层)
# 锁住所有 InceptionV3 的卷积层
for layer in model.layers[:195]:
    layer.trainable = False
for layer in model.layers[195:]:
    layer.trainable = True

# 编译模型(一定要在锁层以后操作)
model.compile(optimizer='rmsprop', loss=tf.keras.losses.SparseCategoricalCrossentropy(), metrics=['accuracy'])
model.summary()    #打印网络结构
  1. 训练,首先用较大的学习率训练:
history = model.fit(trainSet_img, trainSet_label, 
                    batch_size=batch_size, 
                    epochs=20, 
                    validation_data=(testSet_img, testSet_label)
                    )
  1. 然后,放开更多的层,用较小的学习率训练:
# 我们锁住前面135层,然后放开之后的层。
for layer in model.layers[:135]:
    layer.trainable = False
for layer in model.layers[135:]:
    layer.trainable = True
# 我们需要重新编译模型,才能使上面的修改生效
# 让我们设置一个很低的学习率,使用 SGD 来微调    
from tensorflow.keras.optimizers import SGD
model.compile(optimizer=SGD(lr=0.0001, momentum=0.9), loss=tf.keras.losses.SparseCategoricalCrossentropy(), metrics=['accuracy'])

history = model.fit(trainSet_img, trainSet_label, 
                    batch_size=batch_size, 
                    epochs=20, 
                    validation_data=(testSet_img, testSet_label)
                    )
  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)
  1. 模型验证部分详情见源代码

  2. 使用记事本查看生成的model_config.json,如果在输入里出现"ragged": false,字眼的字段,请手动把它删掉,因为dnndk里的tensorflow没有这个参数。没有就不用处理。
    ragged

DNNDK 编译

同minisFashiont的例子,我们将在DNNDK里将模型编译成DPU能够运行的模型。

  1. 启动虚拟机
  2. 在虚拟机的/home/xiaobo/powersensor目录下新建flowerFive文件夹,并把案例目录下的dnndk文件夹和dataset_valid文件夹复制到新建的文件夹下面。如果要使用自己新训练的模型,需要把自己的模型(在案例目录/pc/run下面的2个模型文件)替换掉我们准备好的文件。
  3. 进入dnndk目录,右击启动shell,使用下面指令固化模型
./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 

编译的结果在compile_result文件夹里,里面的.elf文件就是编译好的模型。

EDGE调用

  1. 进入powersenosr的jupyter文件管理页面,在/powersensor_workspace/powersensor_ai下面新建flowerFive目录(随教程发布的案例已经准备好文件了)。在新建的flowerFive目录下新建dataset_validedge文件夹
    edge_dir
  2. 通过jupyter的上传功能,把案例目录下的edge文件夹下的两个文件上传到powersensor的edge目录;验证集dataset_valid下的文件也同理上传到相应目录。
  • 如果要使用自己新训练的模型,可以把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")

# 训练用的图像尺寸
img_size_net = 128
# 训练的batch大小
batch_size = 32

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
# 测试 50 个样本。耗时 9.1545650959秒!
  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()

在这里插入图片描述

  1. 拍照测试
# 这个对象用于操作摄像头
cam1 = ps.ImageSensor()
cv_font = cv2.freetype.createFreeType2()
cv_font.loadFontData(fontFileName='/usr/share/fonts/truetype/droid/DroidSansFallbackFull.ttf', id=0)
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=(255,255,255), 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)

在这里插入图片描述

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