Error[8]: Undefined offset: 702, File: /www/wwwroot/outofmemory.cn/tmp/plugin_ss_superseo_model_superseo.php, Line: 121
File: /www/wwwroot/outofmemory.cn/tmp/plugin_ss_superseo_model_superseo.php, Line: 473, decode(

文章目录

1 获取onnx模型

我是使用pytorch训练得到的pth模型,转成onnx模型,这一步之前写过博客,可参考:pytorch分类模型导出onnx模型并验证

2 启动docker容器

如果之前在开发机上已经安装过地平线给的docker开发环境,可直接执行指令:

docker start horizon			# 启动horizon,horizon是我的容器container 别名
docker attach horizon			# 进入horizon

如果你之前没安装过,也没学过docker的相关知识,可参考我的另一篇博客:配置地平线提供的docker开发环境。

3 onnx模型检查 3.1 为什么要检查?

官方回答: 验证模型中所使用的算子需要符合地平线平台的算子约束。

翻译回答: onnx模型中,不是所有的函数“我”都支持,你得用“我”支持的。怎么知道“我”是否支持呢?给你个excel表让你查太费事,写个函数让程序查吧,这样快!

3.2 如何 *** 作

把在第一步得到的resnet34.onnx模型,放到开发机的指定位置:

/data/wyx/horizon/horizon_xj3_open_explorer_v1.8.5_20211224/ddk/samples/ai_toolchain/horizon_model_convert_sample/03_classification/05_efficientnet_lite0_onnx/resnet_x3/output/

没有哪过文件夹,就新建它,如下图所示:

为什么要这样放?往下看,你会明白的。

然后在resnet_x3文件夹下,新建一个01_check.sh文件,内容如下:

#!/usr/bin/env sh

set -e -v
cd $(dirname hb_mapper checker) || exit

# 模型类型,本文以onnx为例
model_type="onnx"
# 要检查的onnx模型位置
onnx_model="./output/resnet34.onnx"   
# 检查输出日志,放到哪里去
# 虽然它还是放到了与01_check.sh同级目录下(感觉像小bug)
output="./model_output/resnet34_checker.log"    
# 用的什么架构,不用改
march="bernoulli2"    

hb_mapper checker --model-type ${model_type} \
                  --model ${onnx_model} \
                  --output ${output} --march ${march}

解释一下:resnet_x3就是地平线专属命令了,俗称“工具”。

cd到sh 01_check.sh 文件夹下,执行命令:

resnet_x3/data/image_origin/

输出比较重要的部分如下,第二列是BPU,说明算子运行在BPU上,同理,也可能运行在CPU上。

多数算子bpu都支持,包括卷积conv、池化pool、全连接fc等 *** 作。
对于不支持的算子,算了,我们就先让它在CPU上跑。

4 图像数据预处理

本来以为模型检查完,一行命令加载onnx模型,一行命令输出能在地平线开发板上运行的模型。

总体看,确实是,但是,呵,天真。

4.1 一些问题的思考 4.2 图片挑选与放置

从5个类别中,每个类别随机挑了20张图片,放在image_converted_rgb_f32文件夹下,同时新建一个文件夹flower_data_preprocess.py,用于存放处理后的图像,如图所示:

下面就是对挑选的图片进行处理了。这里我提供两种方法,一种是根据地平线提供的模板进行图像预处理,但它跳来跳去的,而且包括的内容太多,我自己又写了一个resnet_x3,实现同样的功能,下面分别介绍。

4.2 使用地平线提供的模板进行图像预处理

如下图所示,在02_preprocess.sh文件夹下需要有这三个文件,下面分别介绍其作用及内容:

内容如下:

data_preprocess.py

很明显告诉我们运行那个文件,往里传哪些参数,至于preprocess.py里面的内容就不再介绍了,不然跳来跳去,讲不完了。反正data_preprocess.py是地平线给的,用就完事了。

data中会用到resnet_x3文件中的一个函数,如下图所示。至于里面的内容,就不展开了,不然又得跳了。

里面放着挑选出来的图片,处理后的图片也会放在这里面。

flower_data_preprocess.py文件夹下,执行命令:

resnet_x3

运行界面及结果如下图所示:

4.3 使用自己写的图像预处理函数

自己写的图像预处理函数放在import文件中,文件放在import文件夹下,实现效果与4.2节一致,其内容如下:
对于关注的归一化 *** 作,预训练权重的均值与方差怎么办问题,详看代码注释。

import cv2
as os
## ------------------------------------------------------------# numpy #   src_dir:从数据集中每个类别各挑选15张图片放到一起 np


#   dst_dir:处理后的图片存放的路径
#   pic_ext:处理后的图片后缀名(影响不大,只是为了说明它的通道顺序)
## ------------------------------------------------------------#
=
'./data/image_origin'
src_dir = './data/image_converted_rgb_f32'
dst_dir = '.rgb'
pic_ext ## ---------------------------------------# #   一次只 *** 作一张图片        

## ---------------------------------------#
for
in
sorted src_name ( .(os)listdir)src_dir:## -----------------------------##   把图片路径拼出来
    ## -----------------------------#
    =
    .
    src_file . os(path,join)src_dir## -----------------------------# src_name##  opencv实现预处理  
 
    ## -----------------------------#
    =
    .
    img ( cv2)imread=src_file.
    img ( cv2.resize(np)array,img(224 ,224) ,=. interpolation)cv2.INTER_CUBIC(.astype)np## -----------------------------------------------------#float32#   PC端网络训练时,数据需要归一化,为何在这儿不做?
    #   答:模型转换时,需要的图像输入分为是0~255,故不要归一化。
    ## -----------------------------------------------------#
    # img /= 255.0        
    # ---------------------------------------#
    #   常规 *** 作是:先转成RGB,再减均值,除方差

    # ---------------------------------------#
    =
    .
    img ( cv2,cvtColor.img) cv2## -----------------------------------------------------------------------------------------#COLOR_BGR2RGB#   问题1:PC端网络训练时,数据需要减均值,除方差,为何在这儿不做?	
    #   答:为了和yaml中data_mean_and_scale下的mean_value与scale_value参数配合
    #       在yaml文件中设置即可
    #   问题2:是否在这儿减均值,除方差,在yaml中data_mean_and_scale参数设置为no_preprocess即可?
    #   答:按道理是的,但要注意,下方的减去均值,除以方差,是针对ImageNet归一化后的数据,
    #       那没有归一化的数据,其均值方差和下面的数据大小又有着怎么的关系呢?欢迎查看下一小节
    ## -----------------------------------------------------------------------------------------#
    # img -= [0.485, 0.456, 0.406]
    # img /= [0.229, 0.224, 0.225]
    ## -----------------------------------------------------------------------------------------#
    #   从HWC,变为CHW。用的是Pytorch框架,其输入是NCHW,故需要这一步。

    #   对于更多NCHW还是NHWC问题,可参考https://blog.csdn.net/weixin_45377629/article/details/124040681
    ## -----------------------------------------------------------------------------------------#
    =
    .
    img ( img2transpose,0, 1) # ---------------------------------------##   添加batch维度        
    #   至此,图像预处理完毕
    # ---------------------------------------#
    =
    .
    img ( np,expand_dims0img) # -----------------------------------------------------##   os.path.basename:返回最后的 文件名,也就是src_image

    #   例如:os.path.basename("./src/1.jpg"),返回:1.jpg
    # -----------------------------------------------------#
    =
    .
    filename . os(path)basename# print(src_file)src_file# -----------------------------------------------------#
    #   os.path.splitext: 把图片名和图片扩展名分开,

    #   例如:1.jpg,short_name=1, ext=.jpg
    # -----------------------------------------------------#
    ,
    =
    short_name. ext . os(path)splitext# ---------------------------------------#filename#   新的图片名
    # ---------------------------------------#
    =
    .
    pic_name . os(path,join+dst_dir) short_name = pic_ext.
    dtype . np(float32
    img)astype.dtype()tofileprintpic_name(
    "write:%s"%) if pic_name'coco'

运行效果如下图所示:

4.4 归一化前后 图像数据 均值与方差怎么变 ?

对于在服务器端使用Pytorch训练网络是,通常会归一化图像数据,然后减去均值,除以方差,再传入网络。

在训练网络模型时,我们大多会使用Imagenet数据集或者COCO数据集的预训练权重,用它们的均值和方差,例如:

in . : args=dataset[
    mean_vals 0.471 ,0.448, 0.408] =[
    std_vals 0.234 ,0.239, 0.242] elif'imagenet'
in . : args=dataset[
    mean_vals 0.485 ,0.456, 0.406] =[
    std_vals 0.229 ,0.224, 0.225] importas

这些都是归一化后的均值与方差。那归一化前后数据的均值与方差又有着怎么的关系呢?用个例子说明:

= numpy . np

a ( np[array4,8,12,16])=/
b 255.0 a = .
mean_a ( np)mean=a.
mean_b ( np)mean=b.
std_a ( np)std=a.
std_b ( np)stdprintb(

"mean_a",)print mean_a(
"mean_b",)print mean_b(
"std_a",)print std_a(
"std_b",)mean_b = mean_a / 255 std_bstd_b = std_a / 255

运行输出:

PS D:\DeepLearning\classification> python .\1.py
mean_a 10.0
mean_b 0.0392156862745098
std_a 4.47213595499958
std_b 0.01753778805882188

可以发现 1 255 \frac{1}{255} ,也就是说对于归一化后的数据其均值与方差均是归一化前的数据的2551.bin.bin

5 获取.bin模型

准备好处理后的图像数据,下面就是获取能够在开发板上运行的模型了,地平线在开发板上运行的模型后缀为03_build.sh,故在此称为resnet34_config.yaml模型。

这一步需要准备两个文件,一个是resnet_x3,一个是resnet34_config.yaml,两个文件均放于#!/bin/bash set -e -v cd $(dirname # 模型转化相关的参数 model_parameters: # ONNX浮点网络数据模型文件 onnx_model: './output/resnet34.onnx' # 适用BPU架构 march: "bernoulli2" # 指定模型转换过程中是否输出各层的中间结果,如果为True,则输出所有层的中间输出结果, layer_out_dump: False # 日志文件的输出控制参数, # debug输出模型转换的详细信息 # info只输出关键信息 # warn输出警告和错误级别以上的信息 log_level: 'debug' # 模型转换输出的结果的存放目录 working_dir: 'model_output' # 模型转换输出的用于上板执行的模型文件的名称前缀 output_model_file_prefix: 'resnet34_224x224_rgb' # 模型输入相关参数, 若输入多个节点, 则应使用';'进行分隔, 使用默认缺省设置则写None input_parameters: # (选填) 模型输入的节点名称, 此名称应与模型文件中的名称一致, 否则会报错, 不填则会使用模型文件中的节点名称 input_name: "" # 网络实际执行时,输入给网络的数据格式,包括 nv12/rgb/bgr/yuv444/gray/featuremap, # pytorch模型一般是rgb input_type_rt: 'rgb' # 网络实际执行时输入的数据排布, 可选值为 NHWC/NCHW # 若input_type_rt配置为nv12,则此处参数不需要配置 # pytorch模型一般是NCHW input_layout_rt: 'NCHW' # 网络训练时输入的数据格式,可选的值为rgb/bgr/gray/featuremap/yuv444 # pytorch模型一般是rgb input_type_train: 'rgb' # 网络训练时输入的数据排布, 可选值为 NHWC/NCHW # pytorch模型一般是rgb input_layout_train: 'NCHW' # (选填) 模型网络的输入大小, 以'x'分隔, 不填则会使用模型文件中的网络输入大小,否则会覆盖模型文件中输入大小 input_shape: '' # 网络实际执行时,输入给网络的batch_size, 默认值为1 #input_batch: 1 # 网络输入的预处理方法,主要有以下几种: # no_preprocess 不做任何 *** 作 # data_mean 减去通道均值mean_value # data_scale 对图像像素乘以data_scale系数 # data_mean_and_scale 减去通道均值后再乘以scale系数 # 注意:此处不是减去均值,除以方差! norm_type: 'data_mean_and_scale' # 图像减去的均值, 如果是通道均值,value之间必须用空格分隔 # 注意:此处的均值是没有归一化的均值,例如ImageNet的R通道,应该是: 124.16 = 0.485x255 mean_value: 124.16 116.28 103.53 # 图像预处理缩放比例,如果是通道缩放比例,value之间必须用空格分隔 # 注意:此处的scale是乘以,以前的方差是除以。且是没有归一化数据 # 例如ImageNet的R通道,应该是: 0.0171248 = 1 / (0.229x255) scale_value: 0.0171248 0.0175070 0.0174292 # 模型量化相关参数 calibration_parameters: # 模型量化的参考图像的存放目录,图片格式支持Jpeg、Bmp等格式,输入的图片 # 应该是使用的典型场景,一般是从测试集中选择20~100张图片,另外输入 # 的图片要覆盖典型场景,不要是偏僻场景,如过曝光、饱和、模糊、纯黑、纯白等图片 # 若有多个输入节点, 则应使用';'进行分隔 # 预处理后的图片所在路径 cal_data_dir: './data/image_converted_rgb_f32' # 如果输入的图片文件尺寸和模型训练的尺寸不一致时,并且preprocess_on为true, # 则将采用默认预处理方法(skimage resize), # 将输入图片缩放或者裁减到指定尺寸,否则,需要用户提前把图片处理为训练时的尺寸 # preprocess_on: False # 模型量化的算法类型,支持kl、max、default、load,通常采用default即可满足要求, 若为QAT导出的模型, 则应选择load calibration_type: 'default' # 编译器相关参数 compiler_parameters: # 编译策略,支持bandwidth和latency两种优化模式; # bandwidth以优化ddr的访问带宽为目标; # latency以优化推理时间为目标 compile_mode: 'latency' # 设置debug为True将打开编译器的debug模式,能够输出性能仿真的相关信息,如帧率、DDR带宽占用等 debug: False # 编译模型指定核数,不指定默认编译单核模型, 若编译双核模型,将下边注释打开即可 # core_num: 2 # 优化等级可选范围为O0~O3 # O0不做任何优化, 编译速度最快,优化程度最低, # O1-O3随着优化等级提高,预期编译后的模型的执行速度会更快,但是所需编译时间也会变长。 # 推荐用O2做最快验证 optimize_level: 'O3' ) config_file="./resnet34_config.yaml" model_type="onnx" # build model hb_mapper makertbin --config ${config_file} \ --model-type ${model_type} 文件夹下。下面分别介绍这两个文件里的内容:

5.1 03_build.sh

加载sh 03_build.sh 文件,使用hb_mapper中的makertbin工具去 *** 作即可。

Cosine Similarity
5.2 resnet34_config.yaml

重点关注网络输入的预处理方法:norm_type: ‘data_mean_and_scale’,需要了解的内容已在注释中给出。

hb_mapper_makertbin.log

运行下方指令:

resnet34_224x224_rgb_original_float_model.onnx

重点关注第5列的预先相似度resnet34_224x224_rgb_optimized_float_model.onnx,都在0.9以上,说明转换的还不错。

转换成功后,得到几个文件中比较重要的有:

6 在开发机上验证转换过程中生成的两个onnx模型

resnet_34/用在import构建得到as模型之后,用于验证过程产物------两个onnx模型,存放在import文件夹下。
处理思路很简单:数据预处理–加载模型–模型推理–模型输出后处理,其内容如下:

from numpy import np
import os
. PIL as Image
import matplotlibimportpyplot from plt
import json
def cv2
softmax_2D horizon_tc_ui ( HB_ONNXRuntime


) :"""
    针对二维numpy矩阵每一行进行softmax *** 作
    X: np.array. Probably should be floats.
    return: 二维矩阵
    """X# looping through rows of X#   循环遍历X的行
    =
    .
    (
    ps . np)emptyforXinshaperange
    ( i . [0X]shape):[,:
        ps]i=.(  [ np,exp:X]i)[,:
        ps]i/=.sum ( np[,:ps]i)returndefcheck_onnx
    ( ps


, ,,onnx_model) img: json_path## --------------------------------------------# input_shape##  opencv实现预处理方式## --------------------------------------------#
    =
    .
    (
    img . cv2(resize)np,array,img=. input_shape) interpolation.cv2(INTER_CUBIC.)astype## --------------------------------------------#np#   使用编译过程中生成的onnx模型是不需要归一化的float32#   模型需要的数值范围0~255,ncv读取的刚好是0~255
    ## --------------------------------------------#
    # img /= 255.0
    ## --------------------------------------------#
    #   网络训练输入一般是RGB的图片,故在此也转一下
    #   为了省时间,似乎这儿可以和yaml中参数配合,去掉这一步

    ## --------------------------------------------#
    =
    .
    (
    img , cv2.cvtColor)img## ------------------------------------------------------------------# cv2#   常规 *** 作是:减去均值,除以方差COLOR_BGR2RGB#   为了和yaml中data_mean_and_scale下的mean_value与scale_value参数配合
    #   注意:此处是针对0~255的通道顺序RGB的图像进行减去均值,乘以方差倒数
    ## ------------------------------------------------------------------#
    -=
    [
    124.16
    img , 116.28,103.53 ]*= [0.0171248
    img , 0.0175070,0.0174292 ]## --------------------------------------------# #   opencv读取的img是HWC格式#   quantized_onnx_model输入格式NHWC
    #   optimized_float_onnx_model输入格式NCHW
    ## --------------------------------------------#
    if
    "optimized"
    in
    : = . onnx_model(
        img 2 img,transpose0,1 )# 从HWC,变为CHW ## -------------------------------###  添加batch维度    ## -------------------------------#
    =
    .
    (
    img , np0expand_dims)img# --------------------------------# #   class_indict用于可视化类别# --------------------------------#
    with
    open
    (
    , "r")json_pathas := . f(
        class_indict ) json## -------------------------------#load##  加载onnx模型f## -------------------------------#
    =
    (
    =
    ort_session ) HB_ONNXRuntime## 下面这两行有啥用?我注释掉也没什么影响model_file#ort_session_2.set_dim_param(0, 0, '?')     onnx_model#ort_session_2.set_providers(['CPUExecutionProvider'])
    # -----------------------------------#
    #   onnx模型推理
    #   初始化数据,注意此时 img 是numpy格式
    
    # -----------------------------------#
    =
    .
    [
    input_name 0 ort_session]input_names=.(
    ort_outs None ort_session,run:}) {input_name# 推理得到输出 img# print(ort_outs)     # [array([[-4.290639  , -2.267056  ,  7.666328  , -1.4162455 ,  0.57391334]], dtype=float32)]# -----------------------------------#        #   经过softmax转化为概率
    #   softmax_2D按行转化,一行一个样本
    
    #   测试时,softmax可不要!
    # -----------------------------------#
    =
    (
    [
    predict_probability 0 softmax_2D]ort_outs)# print(predict_probability)  # array([[0.1],[0.2],[0.3],[0.3],[0.1]])           # ----------------------------------------##   argmax得到最大概率索引,也就是类别对应索引        
    # ----------------------------------------#
    
    =
    .
    (
    predict_cla , np=argmax-predict_probability1 axis)# print(predict_cla)        # array([2])="class: {}   prob: {:.3}"
    .

    print_res format ([str(class_indict[0]predict_cla)],[0]
                                                 predict_probability[[0]predict_cla])print()
    .(print_res)
    pltfortitleinprint_resrange
    ( i len ([0]predict_probability)):print("class: {:10}   prob: {:.3}"
        .format([str(class_indict)],i[0]
                                                  predict_probability[]))i.("./result.jpg"
    plt)savefig.()
    pltifshow=='__main__'

: __name__ ## ----------------------------------------------------------------# #   注意,quantized_onnx_model的输入格式为:NHWC#   optimized_float_onnx_model的输入格式为:NCHW
    #   这儿的命名一定要以quantized和optimized进行区分,后面的代码中有用到
    ## ----------------------------------------------------------------#
    =
    './model_output/resnet34_224x224_rgb_quantized_model.onnx'
    =
    quantized_onnx_model './model_output/resnet34_224x224_rgb_optimized_float_model.onnx' # ----------------------------------------#
    optimized_float_onnx_model #   输入图像被resize到什么尺寸 #   分类网络输入尺寸

    # ----------------------------------------#
    =
    (
    224
    input_shape , 224)= "./data/tulip.jpg"assert
    img_path . .
    ( os)path,exists"file: '{}' dose not exist."img_path.format ()# 下面两行,用它显示图片而已=img_path.
    open
    img ( Image).(img_path)
    plt=imshow.img(     

    img ) cv2# read class_indictimread=img_path'./class_indices.json'
    # -----------------------------------------------------------------------#
    json_path #   关键部分 #   第一个参数用于切换:quantized_onnx_model 或 optimized_float_onnx_model
    # -----------------------------------------------------------------------#
    (
    ,
    ,
    check_onnx,quantized_onnx_model) imgprint json_path( input_shape"onnx model check finsh."
    )rose.jpgoptimized_float_onnx_modeltulip.jpg

运行结果:

7 使用hb_perf工具估计在开发板上运行的性能

拿到了hb_perf_result文件,在上开发板运行前,预估一下能运行多少FPS等。
cd到与hb_perf.log同级,运行命令:

[+++]

生成[+++]文件夹和[+++],打开输出的文件自己看两眼就明白了。

8 接下来工作

将转换得到的.bin模型在x3开发板上跑起来。

)
File: /www/wwwroot/outofmemory.cn/tmp/route_read.php, Line: 126, InsideLink()
File: /www/wwwroot/outofmemory.cn/tmp/index.inc.php, Line: 165, include(/www/wwwroot/outofmemory.cn/tmp/route_read.php)
File: /www/wwwroot/outofmemory.cn/index.php, Line: 30, include(/www/wwwroot/outofmemory.cn/tmp/index.inc.php)
Error[8]: Undefined offset: 703, File: /www/wwwroot/outofmemory.cn/tmp/plugin_ss_superseo_model_superseo.php, Line: 121
File: /www/wwwroot/outofmemory.cn/tmp/plugin_ss_superseo_model_superseo.php, Line: 473, decode(

文章目录

1 获取onnx模型

我是使用pytorch训练得到的pth模型,转成onnx模型,这一步之前写过博客,可参考:pytorch分类模型导出onnx模型并验证

2 启动docker容器

如果之前在开发机上已经安装过地平线给的docker开发环境,可直接执行指令:

docker start horizon			# 启动horizon,horizon是我的容器container 别名
docker attach horizon			# 进入horizon

如果你之前没安装过,也没学过docker的相关知识,可参考我的另一篇博客:配置地平线提供的docker开发环境。

3 onnx模型检查 3.1 为什么要检查?

官方回答: 验证模型中所使用的算子需要符合地平线平台的算子约束。

翻译回答: onnx模型中,不是所有的函数“我”都支持,你得用“我”支持的。怎么知道“我”是否支持呢?给你个excel表让你查太费事,写个函数让程序查吧,这样快!

3.2 如何 *** 作

把在第一步得到的resnet34.onnx模型,放到开发机的指定位置:

/data/wyx/horizon/horizon_xj3_open_explorer_v1.8.5_20211224/ddk/samples/ai_toolchain/horizon_model_convert_sample/03_classification/05_efficientnet_lite0_onnx/resnet_x3/output/

没有哪过文件夹,就新建它,如下图所示:

为什么要这样放?往下看,你会明白的。

然后在resnet_x3文件夹下,新建一个01_check.sh文件,内容如下:

#!/usr/bin/env sh

set -e -v
cd $(dirname hb_mapper checker) || exit

# 模型类型,本文以onnx为例
model_type="onnx"
# 要检查的onnx模型位置
onnx_model="./output/resnet34.onnx"   
# 检查输出日志,放到哪里去
# 虽然它还是放到了与01_check.sh同级目录下(感觉像小bug)
output="./model_output/resnet34_checker.log"    
# 用的什么架构,不用改
march="bernoulli2"    

hb_mapper checker --model-type ${model_type} \
                  --model ${onnx_model} \
                  --output ${output} --march ${march}

解释一下:resnet_x3就是地平线专属命令了,俗称“工具”。

cd到sh 01_check.sh 文件夹下,执行命令:

resnet_x3/data/image_origin/

输出比较重要的部分如下,第二列是BPU,说明算子运行在BPU上,同理,也可能运行在CPU上。

多数算子bpu都支持,包括卷积conv、池化pool、全连接fc等 *** 作。
对于不支持的算子,算了,我们就先让它在CPU上跑。

4 图像数据预处理

本来以为模型检查完,一行命令加载onnx模型,一行命令输出能在地平线开发板上运行的模型。

总体看,确实是,但是,呵,天真。

4.1 一些问题的思考 4.2 图片挑选与放置

从5个类别中,每个类别随机挑了20张图片,放在image_converted_rgb_f32文件夹下,同时新建一个文件夹flower_data_preprocess.py,用于存放处理后的图像,如图所示:

下面就是对挑选的图片进行处理了。这里我提供两种方法,一种是根据地平线提供的模板进行图像预处理,但它跳来跳去的,而且包括的内容太多,我自己又写了一个resnet_x3,实现同样的功能,下面分别介绍。

4.2 使用地平线提供的模板进行图像预处理

如下图所示,在02_preprocess.sh文件夹下需要有这三个文件,下面分别介绍其作用及内容:

内容如下:

data_preprocess.py

很明显告诉我们运行那个文件,往里传哪些参数,至于preprocess.py里面的内容就不再介绍了,不然跳来跳去,讲不完了。反正data_preprocess.py是地平线给的,用就完事了。

data中会用到resnet_x3文件中的一个函数,如下图所示。至于里面的内容,就不展开了,不然又得跳了。

里面放着挑选出来的图片,处理后的图片也会放在这里面。

flower_data_preprocess.py文件夹下,执行命令:

resnet_x3

运行界面及结果如下图所示:

4.3 使用自己写的图像预处理函数

自己写的图像预处理函数放在import文件中,文件放在import文件夹下,实现效果与4.2节一致,其内容如下:
对于关注的归一化 *** 作,预训练权重的均值与方差怎么办问题,详看代码注释。

import cv2
as os
## ------------------------------------------------------------# numpy #   src_dir:从数据集中每个类别各挑选15张图片放到一起 np


#   dst_dir:处理后的图片存放的路径
#   pic_ext:处理后的图片后缀名(影响不大,只是为了说明它的通道顺序)
## ------------------------------------------------------------#
=
'./data/image_origin'
src_dir = './data/image_converted_rgb_f32'
dst_dir = '.rgb'
pic_ext ## ---------------------------------------# #   一次只 *** 作一张图片        

## ---------------------------------------#
for
in
sorted src_name ( .(os)listdir)src_dir:## -----------------------------##   把图片路径拼出来
    ## -----------------------------#
    =
    .
    src_file . os(path,join)src_dir## -----------------------------# src_name##  opencv实现预处理  
 
    ## -----------------------------#
    =
    .
    img ( cv2)imread=src_file.
    img ( cv2.resize(np)array,img(224 ,224) ,=. interpolation)cv2.INTER_CUBIC(.astype)np## -----------------------------------------------------#float32#   PC端网络训练时,数据需要归一化,为何在这儿不做?
    #   答:模型转换时,需要的图像输入分为是0~255,故不要归一化。
    ## -----------------------------------------------------#
    # img /= 255.0        
    # ---------------------------------------#
    #   常规 *** 作是:先转成RGB,再减均值,除方差

    # ---------------------------------------#
    =
    .
    img ( cv2,cvtColor.img) cv2## -----------------------------------------------------------------------------------------#COLOR_BGR2RGB#   问题1:PC端网络训练时,数据需要减均值,除方差,为何在这儿不做?	
    #   答:为了和yaml中data_mean_and_scale下的mean_value与scale_value参数配合
    #       在yaml文件中设置即可
    #   问题2:是否在这儿减均值,除方差,在yaml中data_mean_and_scale参数设置为no_preprocess即可?
    #   答:按道理是的,但要注意,下方的减去均值,除以方差,是针对ImageNet归一化后的数据,
    #       那没有归一化的数据,其均值方差和下面的数据大小又有着怎么的关系呢?欢迎查看下一小节
    ## -----------------------------------------------------------------------------------------#
    # img -= [0.485, 0.456, 0.406]
    # img /= [0.229, 0.224, 0.225]
    ## -----------------------------------------------------------------------------------------#
    #   从HWC,变为CHW。用的是Pytorch框架,其输入是NCHW,故需要这一步。

    #   对于更多NCHW还是NHWC问题,可参考https://blog.csdn.net/weixin_45377629/article/details/124040681
    ## -----------------------------------------------------------------------------------------#
    =
    .
    img ( img2transpose,0, 1) # ---------------------------------------##   添加batch维度        
    #   至此,图像预处理完毕
    # ---------------------------------------#
    =
    .
    img ( np,expand_dims0img) # -----------------------------------------------------##   os.path.basename:返回最后的 文件名,也就是src_image

    #   例如:os.path.basename("./src/1.jpg"),返回:1.jpg
    # -----------------------------------------------------#
    =
    .
    filename . os(path)basename# print(src_file)src_file# -----------------------------------------------------#
    #   os.path.splitext: 把图片名和图片扩展名分开,

    #   例如:1.jpg,short_name=1, ext=.jpg
    # -----------------------------------------------------#
    ,
    =
    short_name. ext . os(path)splitext# ---------------------------------------#filename#   新的图片名
    # ---------------------------------------#
    =
    .
    pic_name . os(path,join+dst_dir) short_name = pic_ext.
    dtype . np(float32
    img)astype.dtype()tofileprintpic_name(
    "write:%s"%) if pic_name'coco'

运行效果如下图所示:

4.4 归一化前后 图像数据 均值与方差怎么变 ?

对于在服务器端使用Pytorch训练网络是,通常会归一化图像数据,然后减去均值,除以方差,再传入网络。

在训练网络模型时,我们大多会使用Imagenet数据集或者COCO数据集的预训练权重,用它们的均值和方差,例如:

in . : args=dataset[
    mean_vals 0.471 ,0.448, 0.408] =[
    std_vals 0.234 ,0.239, 0.242] elif'imagenet'
in . : args=dataset[
    mean_vals 0.485 ,0.456, 0.406] =[
    std_vals 0.229 ,0.224, 0.225] importas

这些都是归一化后的均值与方差。那归一化前后数据的均值与方差又有着怎么的关系呢?用个例子说明:

= numpy . np

a ( np[array4,8,12,16])=/
b 255.0 a = .
mean_a ( np)mean=a.
mean_b ( np)mean=b.
std_a ( np)std=a.
std_b ( np)stdprintb(

"mean_a",)print mean_a(
"mean_b",)print mean_b(
"std_a",)print std_a(
"std_b",)mean_b = mean_a / 255 std_bstd_b = std_a / 255

运行输出:

PS D:\DeepLearning\classification> python .\1.py
mean_a 10.0
mean_b 0.0392156862745098
std_a 4.47213595499958
std_b 0.01753778805882188

可以发现 1 255 \frac{1}{255} ,也就是说对于归一化后的数据其均值与方差均是归一化前的数据的2551.bin.bin

5 获取.bin模型

准备好处理后的图像数据,下面就是获取能够在开发板上运行的模型了,地平线在开发板上运行的模型后缀为03_build.sh,故在此称为resnet34_config.yaml模型。

这一步需要准备两个文件,一个是resnet_x3,一个是resnet34_config.yaml,两个文件均放于#!/bin/bash set -e -v cd $(dirname # 模型转化相关的参数 model_parameters: # ONNX浮点网络数据模型文件 onnx_model: './output/resnet34.onnx' # 适用BPU架构 march: "bernoulli2" # 指定模型转换过程中是否输出各层的中间结果,如果为True,则输出所有层的中间输出结果, layer_out_dump: False # 日志文件的输出控制参数, # debug输出模型转换的详细信息 # info只输出关键信息 # warn输出警告和错误级别以上的信息 log_level: 'debug' # 模型转换输出的结果的存放目录 working_dir: 'model_output' # 模型转换输出的用于上板执行的模型文件的名称前缀 output_model_file_prefix: 'resnet34_224x224_rgb' # 模型输入相关参数, 若输入多个节点, 则应使用';'进行分隔, 使用默认缺省设置则写None input_parameters: # (选填) 模型输入的节点名称, 此名称应与模型文件中的名称一致, 否则会报错, 不填则会使用模型文件中的节点名称 input_name: "" # 网络实际执行时,输入给网络的数据格式,包括 nv12/rgb/bgr/yuv444/gray/featuremap, # pytorch模型一般是rgb input_type_rt: 'rgb' # 网络实际执行时输入的数据排布, 可选值为 NHWC/NCHW # 若input_type_rt配置为nv12,则此处参数不需要配置 # pytorch模型一般是NCHW input_layout_rt: 'NCHW' # 网络训练时输入的数据格式,可选的值为rgb/bgr/gray/featuremap/yuv444 # pytorch模型一般是rgb input_type_train: 'rgb' # 网络训练时输入的数据排布, 可选值为 NHWC/NCHW # pytorch模型一般是rgb input_layout_train: 'NCHW' # (选填) 模型网络的输入大小, 以'x'分隔, 不填则会使用模型文件中的网络输入大小,否则会覆盖模型文件中输入大小 input_shape: '' # 网络实际执行时,输入给网络的batch_size, 默认值为1 #input_batch: 1 # 网络输入的预处理方法,主要有以下几种: # no_preprocess 不做任何 *** 作 # data_mean 减去通道均值mean_value # data_scale 对图像像素乘以data_scale系数 # data_mean_and_scale 减去通道均值后再乘以scale系数 # 注意:此处不是减去均值,除以方差! norm_type: 'data_mean_and_scale' # 图像减去的均值, 如果是通道均值,value之间必须用空格分隔 # 注意:此处的均值是没有归一化的均值,例如ImageNet的R通道,应该是: 124.16 = 0.485x255 mean_value: 124.16 116.28 103.53 # 图像预处理缩放比例,如果是通道缩放比例,value之间必须用空格分隔 # 注意:此处的scale是乘以,以前的方差是除以。且是没有归一化数据 # 例如ImageNet的R通道,应该是: 0.0171248 = 1 / (0.229x255) scale_value: 0.0171248 0.0175070 0.0174292 # 模型量化相关参数 calibration_parameters: # 模型量化的参考图像的存放目录,图片格式支持Jpeg、Bmp等格式,输入的图片 # 应该是使用的典型场景,一般是从测试集中选择20~100张图片,另外输入 # 的图片要覆盖典型场景,不要是偏僻场景,如过曝光、饱和、模糊、纯黑、纯白等图片 # 若有多个输入节点, 则应使用';'进行分隔 # 预处理后的图片所在路径 cal_data_dir: './data/image_converted_rgb_f32' # 如果输入的图片文件尺寸和模型训练的尺寸不一致时,并且preprocess_on为true, # 则将采用默认预处理方法(skimage resize), # 将输入图片缩放或者裁减到指定尺寸,否则,需要用户提前把图片处理为训练时的尺寸 # preprocess_on: False # 模型量化的算法类型,支持kl、max、default、load,通常采用default即可满足要求, 若为QAT导出的模型, 则应选择load calibration_type: 'default' # 编译器相关参数 compiler_parameters: # 编译策略,支持bandwidth和latency两种优化模式; # bandwidth以优化ddr的访问带宽为目标; # latency以优化推理时间为目标 compile_mode: 'latency' # 设置debug为True将打开编译器的debug模式,能够输出性能仿真的相关信息,如帧率、DDR带宽占用等 debug: False # 编译模型指定核数,不指定默认编译单核模型, 若编译双核模型,将下边注释打开即可 # core_num: 2 # 优化等级可选范围为O0~O3 # O0不做任何优化, 编译速度最快,优化程度最低, # O1-O3随着优化等级提高,预期编译后的模型的执行速度会更快,但是所需编译时间也会变长。 # 推荐用O2做最快验证 optimize_level: 'O3' ) config_file="./resnet34_config.yaml" model_type="onnx" # build model hb_mapper makertbin --config ${config_file} \ --model-type ${model_type} 文件夹下。下面分别介绍这两个文件里的内容:

5.1 03_build.sh

加载sh 03_build.sh 文件,使用hb_mapper中的makertbin工具去 *** 作即可。

Cosine Similarity
5.2 resnet34_config.yaml

重点关注网络输入的预处理方法:norm_type: ‘data_mean_and_scale’,需要了解的内容已在注释中给出。

hb_mapper_makertbin.log

运行下方指令:

resnet34_224x224_rgb_original_float_model.onnx

重点关注第5列的预先相似度resnet34_224x224_rgb_optimized_float_model.onnx,都在0.9以上,说明转换的还不错。

转换成功后,得到几个文件中比较重要的有:

6 在开发机上验证转换过程中生成的两个onnx模型

resnet_34/用在import构建得到as模型之后,用于验证过程产物------两个onnx模型,存放在import文件夹下。
处理思路很简单:数据预处理–加载模型–模型推理–模型输出后处理,其内容如下:

from numpy import np
import os
. PIL as Image
import matplotlibimportpyplot from plt
import json
def cv2
softmax_2D horizon_tc_ui ( HB_ONNXRuntime


) :"""
    针对二维numpy矩阵每一行进行softmax *** 作
    X: np.array. Probably should be floats.
    return: 二维矩阵
    """X# looping through rows of X#   循环遍历X的行
    =
    .
    (
    ps . np)emptyforXinshaperange
    ( i . [0X]shape):[,:
        ps]i=.(  [ np,exp:X]i)[,:
        ps]i/=.sum ( np[,:ps]i)returndefcheck_onnx
    ( ps


, ,,onnx_model) img: json_path## --------------------------------------------# input_shape##  opencv实现预处理方式## --------------------------------------------#
    =
    .
    (
    img . cv2(resize)np,array,img=. input_shape) interpolation.cv2(INTER_CUBIC.)astype## --------------------------------------------#np#   使用编译过程中生成的onnx模型是不需要归一化的float32#   模型需要的数值范围0~255,ncv读取的刚好是0~255
    ## --------------------------------------------#
    # img /= 255.0
    ## --------------------------------------------#
    #   网络训练输入一般是RGB的图片,故在此也转一下
    #   为了省时间,似乎这儿可以和yaml中参数配合,去掉这一步

    ## --------------------------------------------#
    =
    .
    (
    img , cv2.cvtColor)img## ------------------------------------------------------------------# cv2#   常规 *** 作是:减去均值,除以方差COLOR_BGR2RGB#   为了和yaml中data_mean_and_scale下的mean_value与scale_value参数配合
    #   注意:此处是针对0~255的通道顺序RGB的图像进行减去均值,乘以方差倒数
    ## ------------------------------------------------------------------#
    -=
    [
    124.16
    img , 116.28,103.53 ]*= [0.0171248
    img , 0.0175070,0.0174292 ]## --------------------------------------------# #   opencv读取的img是HWC格式#   quantized_onnx_model输入格式NHWC
    #   optimized_float_onnx_model输入格式NCHW
    ## --------------------------------------------#
    if
    "optimized"
    in
    : = . onnx_model(
        img 2 img,transpose0,1 )# 从HWC,变为CHW ## -------------------------------###  添加batch维度    ## -------------------------------#
    =
    .
    (
    img , np0expand_dims)img# --------------------------------# #   class_indict用于可视化类别# --------------------------------#
    with
    open
    (
    , "r")json_pathas := . f(
        class_indict ) json## -------------------------------#load##  加载onnx模型f## -------------------------------#
    =
    (
    =
    ort_session ) HB_ONNXRuntime## 下面这两行有啥用?我注释掉也没什么影响model_file#ort_session_2.set_dim_param(0, 0, '?')     onnx_model#ort_session_2.set_providers(['CPUExecutionProvider'])
    # -----------------------------------#
    #   onnx模型推理
    #   初始化数据,注意此时 img 是numpy格式
    
    # -----------------------------------#
    =
    .
    [
    input_name 0 ort_session]input_names=.(
    ort_outs None ort_session,run:}) {input_name# 推理得到输出 img# print(ort_outs)     # [array([[-4.290639  , -2.267056  ,  7.666328  , -1.4162455 ,  0.57391334]], dtype=float32)]# -----------------------------------#        #   经过softmax转化为概率
    #   softmax_2D按行转化,一行一个样本
    
    #   测试时,softmax可不要!
    # -----------------------------------#
    =
    (
    [
    predict_probability 0 softmax_2D]ort_outs)# print(predict_probability)  # array([[0.1],[0.2],[0.3],[0.3],[0.1]])           # ----------------------------------------##   argmax得到最大概率索引,也就是类别对应索引        
    # ----------------------------------------#
    
    =
    .
    (
    predict_cla , np=argmax-predict_probability1 axis)# print(predict_cla)        # array([2])="class: {}   prob: {:.3}"
    .

    print_res format ([str(class_indict[0]predict_cla)],[0]
                                                 predict_probability[[0]predict_cla])print()
    .(print_res)
    pltfortitleinprint_resrange
    ( i len ([0]predict_probability)):print("class: {:10}   prob: {:.3}"
        .format([str(class_indict)],i[0]
                                                  predict_probability[]))i.("./result.jpg"
    plt)savefig.()
    pltifshow=='__main__'

: __name__ ## ----------------------------------------------------------------# #   注意,quantized_onnx_model的输入格式为:NHWC#   optimized_float_onnx_model的输入格式为:NCHW
    #   这儿的命名一定要以quantized和optimized进行区分,后面的代码中有用到
    ## ----------------------------------------------------------------#
    =
    './model_output/resnet34_224x224_rgb_quantized_model.onnx'
    =
    quantized_onnx_model './model_output/resnet34_224x224_rgb_optimized_float_model.onnx' # ----------------------------------------#
    optimized_float_onnx_model #   输入图像被resize到什么尺寸 #   分类网络输入尺寸

    # ----------------------------------------#
    =
    (
    224
    input_shape , 224)= "./data/tulip.jpg"assert
    img_path . .
    ( os)path,exists"file: '{}' dose not exist."img_path.format ()# 下面两行,用它显示图片而已=img_path.
    open
    img ( Image).(img_path)
    plt=imshow.img(     

    img ) cv2# read class_indictimread=img_path'./class_indices.json'
    # -----------------------------------------------------------------------#
    json_path #   关键部分 #   第一个参数用于切换:quantized_onnx_model 或 optimized_float_onnx_model
    # -----------------------------------------------------------------------#
    (
    ,
    ,
    check_onnx,quantized_onnx_model) imgprint json_path( input_shape"onnx model check finsh."
    )rose.jpgoptimized_float_onnx_modeltulip.jpg

运行结果:

7 使用hb_perf工具估计在开发板上运行的性能

拿到了hb_perf_result文件,在上开发板运行前,预估一下能运行多少FPS等。
cd到与hb_perf.log同级,运行命令:

 

生成[+++]文件夹和[+++],打开输出的文件自己看两眼就明白了。

8 接下来工作

将转换得到的.bin模型在x3开发板上跑起来。

)
File: /www/wwwroot/outofmemory.cn/tmp/route_read.php, Line: 126, InsideLink()
File: /www/wwwroot/outofmemory.cn/tmp/index.inc.php, Line: 165, include(/www/wwwroot/outofmemory.cn/tmp/route_read.php)
File: /www/wwwroot/outofmemory.cn/index.php, Line: 30, include(/www/wwwroot/outofmemory.cn/tmp/index.inc.php)
Error[8]: Undefined offset: 704, File: /www/wwwroot/outofmemory.cn/tmp/plugin_ss_superseo_model_superseo.php, Line: 121
File: /www/wwwroot/outofmemory.cn/tmp/plugin_ss_superseo_model_superseo.php, Line: 473, decode(

文章目录

1 获取onnx模型

我是使用pytorch训练得到的pth模型,转成onnx模型,这一步之前写过博客,可参考:pytorch分类模型导出onnx模型并验证

2 启动docker容器

如果之前在开发机上已经安装过地平线给的docker开发环境,可直接执行指令:

docker start horizon			# 启动horizon,horizon是我的容器container 别名
docker attach horizon			# 进入horizon

如果你之前没安装过,也没学过docker的相关知识,可参考我的另一篇博客:配置地平线提供的docker开发环境。

3 onnx模型检查 3.1 为什么要检查?

官方回答: 验证模型中所使用的算子需要符合地平线平台的算子约束。

翻译回答: onnx模型中,不是所有的函数“我”都支持,你得用“我”支持的。怎么知道“我”是否支持呢?给你个excel表让你查太费事,写个函数让程序查吧,这样快!

3.2 如何 *** 作

把在第一步得到的resnet34.onnx模型,放到开发机的指定位置:

/data/wyx/horizon/horizon_xj3_open_explorer_v1.8.5_20211224/ddk/samples/ai_toolchain/horizon_model_convert_sample/03_classification/05_efficientnet_lite0_onnx/resnet_x3/output/

没有哪过文件夹,就新建它,如下图所示:

为什么要这样放?往下看,你会明白的。

然后在resnet_x3文件夹下,新建一个01_check.sh文件,内容如下:

#!/usr/bin/env sh

set -e -v
cd $(dirname hb_mapper checker) || exit

# 模型类型,本文以onnx为例
model_type="onnx"
# 要检查的onnx模型位置
onnx_model="./output/resnet34.onnx"   
# 检查输出日志,放到哪里去
# 虽然它还是放到了与01_check.sh同级目录下(感觉像小bug)
output="./model_output/resnet34_checker.log"    
# 用的什么架构,不用改
march="bernoulli2"    

hb_mapper checker --model-type ${model_type} \
                  --model ${onnx_model} \
                  --output ${output} --march ${march}

解释一下:resnet_x3就是地平线专属命令了,俗称“工具”。

cd到sh 01_check.sh 文件夹下,执行命令:

resnet_x3/data/image_origin/

输出比较重要的部分如下,第二列是BPU,说明算子运行在BPU上,同理,也可能运行在CPU上。

多数算子bpu都支持,包括卷积conv、池化pool、全连接fc等 *** 作。
对于不支持的算子,算了,我们就先让它在CPU上跑。

4 图像数据预处理

本来以为模型检查完,一行命令加载onnx模型,一行命令输出能在地平线开发板上运行的模型。

总体看,确实是,但是,呵,天真。

4.1 一些问题的思考 4.2 图片挑选与放置

从5个类别中,每个类别随机挑了20张图片,放在image_converted_rgb_f32文件夹下,同时新建一个文件夹flower_data_preprocess.py,用于存放处理后的图像,如图所示:

下面就是对挑选的图片进行处理了。这里我提供两种方法,一种是根据地平线提供的模板进行图像预处理,但它跳来跳去的,而且包括的内容太多,我自己又写了一个resnet_x3,实现同样的功能,下面分别介绍。

4.2 使用地平线提供的模板进行图像预处理

如下图所示,在02_preprocess.sh文件夹下需要有这三个文件,下面分别介绍其作用及内容:

内容如下:

data_preprocess.py

很明显告诉我们运行那个文件,往里传哪些参数,至于preprocess.py里面的内容就不再介绍了,不然跳来跳去,讲不完了。反正data_preprocess.py是地平线给的,用就完事了。

data中会用到resnet_x3文件中的一个函数,如下图所示。至于里面的内容,就不展开了,不然又得跳了。

里面放着挑选出来的图片,处理后的图片也会放在这里面。

flower_data_preprocess.py文件夹下,执行命令:

resnet_x3

运行界面及结果如下图所示:

4.3 使用自己写的图像预处理函数

自己写的图像预处理函数放在import文件中,文件放在import文件夹下,实现效果与4.2节一致,其内容如下:
对于关注的归一化 *** 作,预训练权重的均值与方差怎么办问题,详看代码注释。

import cv2
as os
## ------------------------------------------------------------# numpy #   src_dir:从数据集中每个类别各挑选15张图片放到一起 np


#   dst_dir:处理后的图片存放的路径
#   pic_ext:处理后的图片后缀名(影响不大,只是为了说明它的通道顺序)
## ------------------------------------------------------------#
=
'./data/image_origin'
src_dir = './data/image_converted_rgb_f32'
dst_dir = '.rgb'
pic_ext ## ---------------------------------------# #   一次只 *** 作一张图片        

## ---------------------------------------#
for
in
sorted src_name ( .(os)listdir)src_dir:## -----------------------------##   把图片路径拼出来
    ## -----------------------------#
    =
    .
    src_file . os(path,join)src_dir## -----------------------------# src_name##  opencv实现预处理  
 
    ## -----------------------------#
    =
    .
    img ( cv2)imread=src_file.
    img ( cv2.resize(np)array,img(224 ,224) ,=. interpolation)cv2.INTER_CUBIC(.astype)np## -----------------------------------------------------#float32#   PC端网络训练时,数据需要归一化,为何在这儿不做?
    #   答:模型转换时,需要的图像输入分为是0~255,故不要归一化。
    ## -----------------------------------------------------#
    # img /= 255.0        
    # ---------------------------------------#
    #   常规 *** 作是:先转成RGB,再减均值,除方差

    # ---------------------------------------#
    =
    .
    img ( cv2,cvtColor.img) cv2## -----------------------------------------------------------------------------------------#COLOR_BGR2RGB#   问题1:PC端网络训练时,数据需要减均值,除方差,为何在这儿不做?	
    #   答:为了和yaml中data_mean_and_scale下的mean_value与scale_value参数配合
    #       在yaml文件中设置即可
    #   问题2:是否在这儿减均值,除方差,在yaml中data_mean_and_scale参数设置为no_preprocess即可?
    #   答:按道理是的,但要注意,下方的减去均值,除以方差,是针对ImageNet归一化后的数据,
    #       那没有归一化的数据,其均值方差和下面的数据大小又有着怎么的关系呢?欢迎查看下一小节
    ## -----------------------------------------------------------------------------------------#
    # img -= [0.485, 0.456, 0.406]
    # img /= [0.229, 0.224, 0.225]
    ## -----------------------------------------------------------------------------------------#
    #   从HWC,变为CHW。用的是Pytorch框架,其输入是NCHW,故需要这一步。

    #   对于更多NCHW还是NHWC问题,可参考https://blog.csdn.net/weixin_45377629/article/details/124040681
    ## -----------------------------------------------------------------------------------------#
    =
    .
    img ( img2transpose,0, 1) # ---------------------------------------##   添加batch维度        
    #   至此,图像预处理完毕
    # ---------------------------------------#
    =
    .
    img ( np,expand_dims0img) # -----------------------------------------------------##   os.path.basename:返回最后的 文件名,也就是src_image

    #   例如:os.path.basename("./src/1.jpg"),返回:1.jpg
    # -----------------------------------------------------#
    =
    .
    filename . os(path)basename# print(src_file)src_file# -----------------------------------------------------#
    #   os.path.splitext: 把图片名和图片扩展名分开,

    #   例如:1.jpg,short_name=1, ext=.jpg
    # -----------------------------------------------------#
    ,
    =
    short_name. ext . os(path)splitext# ---------------------------------------#filename#   新的图片名
    # ---------------------------------------#
    =
    .
    pic_name . os(path,join+dst_dir) short_name = pic_ext.
    dtype . np(float32
    img)astype.dtype()tofileprintpic_name(
    "write:%s"%) if pic_name'coco'

运行效果如下图所示:

4.4 归一化前后 图像数据 均值与方差怎么变 ?

对于在服务器端使用Pytorch训练网络是,通常会归一化图像数据,然后减去均值,除以方差,再传入网络。

在训练网络模型时,我们大多会使用Imagenet数据集或者COCO数据集的预训练权重,用它们的均值和方差,例如:

in . : args=dataset[
    mean_vals 0.471 ,0.448, 0.408] =[
    std_vals 0.234 ,0.239, 0.242] elif'imagenet'
in . : args=dataset[
    mean_vals 0.485 ,0.456, 0.406] =[
    std_vals 0.229 ,0.224, 0.225] importas

这些都是归一化后的均值与方差。那归一化前后数据的均值与方差又有着怎么的关系呢?用个例子说明:

= numpy . np

a ( np[array4,8,12,16])=/
b 255.0 a = .
mean_a ( np)mean=a.
mean_b ( np)mean=b.
std_a ( np)std=a.
std_b ( np)stdprintb(

"mean_a",)print mean_a(
"mean_b",)print mean_b(
"std_a",)print std_a(
"std_b",)mean_b = mean_a / 255 std_bstd_b = std_a / 255

运行输出:

PS D:\DeepLearning\classification> python .\1.py
mean_a 10.0
mean_b 0.0392156862745098
std_a 4.47213595499958
std_b 0.01753778805882188

可以发现 1 255 \frac{1}{255} ,也就是说对于归一化后的数据其均值与方差均是归一化前的数据的2551.bin.bin

5 获取.bin模型

准备好处理后的图像数据,下面就是获取能够在开发板上运行的模型了,地平线在开发板上运行的模型后缀为03_build.sh,故在此称为resnet34_config.yaml模型。

这一步需要准备两个文件,一个是resnet_x3,一个是resnet34_config.yaml,两个文件均放于#!/bin/bash set -e -v cd $(dirname # 模型转化相关的参数 model_parameters: # ONNX浮点网络数据模型文件 onnx_model: './output/resnet34.onnx' # 适用BPU架构 march: "bernoulli2" # 指定模型转换过程中是否输出各层的中间结果,如果为True,则输出所有层的中间输出结果, layer_out_dump: False # 日志文件的输出控制参数, # debug输出模型转换的详细信息 # info只输出关键信息 # warn输出警告和错误级别以上的信息 log_level: 'debug' # 模型转换输出的结果的存放目录 working_dir: 'model_output' # 模型转换输出的用于上板执行的模型文件的名称前缀 output_model_file_prefix: 'resnet34_224x224_rgb' # 模型输入相关参数, 若输入多个节点, 则应使用';'进行分隔, 使用默认缺省设置则写None input_parameters: # (选填) 模型输入的节点名称, 此名称应与模型文件中的名称一致, 否则会报错, 不填则会使用模型文件中的节点名称 input_name: "" # 网络实际执行时,输入给网络的数据格式,包括 nv12/rgb/bgr/yuv444/gray/featuremap, # pytorch模型一般是rgb input_type_rt: 'rgb' # 网络实际执行时输入的数据排布, 可选值为 NHWC/NCHW # 若input_type_rt配置为nv12,则此处参数不需要配置 # pytorch模型一般是NCHW input_layout_rt: 'NCHW' # 网络训练时输入的数据格式,可选的值为rgb/bgr/gray/featuremap/yuv444 # pytorch模型一般是rgb input_type_train: 'rgb' # 网络训练时输入的数据排布, 可选值为 NHWC/NCHW # pytorch模型一般是rgb input_layout_train: 'NCHW' # (选填) 模型网络的输入大小, 以'x'分隔, 不填则会使用模型文件中的网络输入大小,否则会覆盖模型文件中输入大小 input_shape: '' # 网络实际执行时,输入给网络的batch_size, 默认值为1 #input_batch: 1 # 网络输入的预处理方法,主要有以下几种: # no_preprocess 不做任何 *** 作 # data_mean 减去通道均值mean_value # data_scale 对图像像素乘以data_scale系数 # data_mean_and_scale 减去通道均值后再乘以scale系数 # 注意:此处不是减去均值,除以方差! norm_type: 'data_mean_and_scale' # 图像减去的均值, 如果是通道均值,value之间必须用空格分隔 # 注意:此处的均值是没有归一化的均值,例如ImageNet的R通道,应该是: 124.16 = 0.485x255 mean_value: 124.16 116.28 103.53 # 图像预处理缩放比例,如果是通道缩放比例,value之间必须用空格分隔 # 注意:此处的scale是乘以,以前的方差是除以。且是没有归一化数据 # 例如ImageNet的R通道,应该是: 0.0171248 = 1 / (0.229x255) scale_value: 0.0171248 0.0175070 0.0174292 # 模型量化相关参数 calibration_parameters: # 模型量化的参考图像的存放目录,图片格式支持Jpeg、Bmp等格式,输入的图片 # 应该是使用的典型场景,一般是从测试集中选择20~100张图片,另外输入 # 的图片要覆盖典型场景,不要是偏僻场景,如过曝光、饱和、模糊、纯黑、纯白等图片 # 若有多个输入节点, 则应使用';'进行分隔 # 预处理后的图片所在路径 cal_data_dir: './data/image_converted_rgb_f32' # 如果输入的图片文件尺寸和模型训练的尺寸不一致时,并且preprocess_on为true, # 则将采用默认预处理方法(skimage resize), # 将输入图片缩放或者裁减到指定尺寸,否则,需要用户提前把图片处理为训练时的尺寸 # preprocess_on: False # 模型量化的算法类型,支持kl、max、default、load,通常采用default即可满足要求, 若为QAT导出的模型, 则应选择load calibration_type: 'default' # 编译器相关参数 compiler_parameters: # 编译策略,支持bandwidth和latency两种优化模式; # bandwidth以优化ddr的访问带宽为目标; # latency以优化推理时间为目标 compile_mode: 'latency' # 设置debug为True将打开编译器的debug模式,能够输出性能仿真的相关信息,如帧率、DDR带宽占用等 debug: False # 编译模型指定核数,不指定默认编译单核模型, 若编译双核模型,将下边注释打开即可 # core_num: 2 # 优化等级可选范围为O0~O3 # O0不做任何优化, 编译速度最快,优化程度最低, # O1-O3随着优化等级提高,预期编译后的模型的执行速度会更快,但是所需编译时间也会变长。 # 推荐用O2做最快验证 optimize_level: 'O3' ) config_file="./resnet34_config.yaml" model_type="onnx" # build model hb_mapper makertbin --config ${config_file} \ --model-type ${model_type} 文件夹下。下面分别介绍这两个文件里的内容:

5.1 03_build.sh

加载sh 03_build.sh 文件,使用hb_mapper中的makertbin工具去 *** 作即可。

Cosine Similarity
5.2 resnet34_config.yaml

重点关注网络输入的预处理方法:norm_type: ‘data_mean_and_scale’,需要了解的内容已在注释中给出。

hb_mapper_makertbin.log

运行下方指令:

resnet34_224x224_rgb_original_float_model.onnx

重点关注第5列的预先相似度resnet34_224x224_rgb_optimized_float_model.onnx,都在0.9以上,说明转换的还不错。

转换成功后,得到几个文件中比较重要的有:

6 在开发机上验证转换过程中生成的两个onnx模型

resnet_34/用在import构建得到as模型之后,用于验证过程产物------两个onnx模型,存放在import文件夹下。
处理思路很简单:数据预处理–加载模型–模型推理–模型输出后处理,其内容如下:

from numpy import np
import os
. PIL as Image
import matplotlibimportpyplot from plt
import json
def cv2
softmax_2D horizon_tc_ui ( HB_ONNXRuntime


) :"""
    针对二维numpy矩阵每一行进行softmax *** 作
    X: np.array. Probably should be floats.
    return: 二维矩阵
    """X# looping through rows of X#   循环遍历X的行
    =
    .
    (
    ps . np)emptyforXinshaperange
    ( i . [0X]shape):[,:
        ps]i=.(  [ np,exp:X]i)[,:
        ps]i/=.sum ( np[,:ps]i)returndefcheck_onnx
    ( ps


, ,,onnx_model) img: json_path## --------------------------------------------# input_shape##  opencv实现预处理方式## --------------------------------------------#
    =
    .
    (
    img . cv2(resize)np,array,img=. input_shape) interpolation.cv2(INTER_CUBIC.)astype## --------------------------------------------#np#   使用编译过程中生成的onnx模型是不需要归一化的float32#   模型需要的数值范围0~255,ncv读取的刚好是0~255
    ## --------------------------------------------#
    # img /= 255.0
    ## --------------------------------------------#
    #   网络训练输入一般是RGB的图片,故在此也转一下
    #   为了省时间,似乎这儿可以和yaml中参数配合,去掉这一步

    ## --------------------------------------------#
    =
    .
    (
    img , cv2.cvtColor)img## ------------------------------------------------------------------# cv2#   常规 *** 作是:减去均值,除以方差COLOR_BGR2RGB#   为了和yaml中data_mean_and_scale下的mean_value与scale_value参数配合
    #   注意:此处是针对0~255的通道顺序RGB的图像进行减去均值,乘以方差倒数
    ## ------------------------------------------------------------------#
    -=
    [
    124.16
    img , 116.28,103.53 ]*= [0.0171248
    img , 0.0175070,0.0174292 ]## --------------------------------------------# #   opencv读取的img是HWC格式#   quantized_onnx_model输入格式NHWC
    #   optimized_float_onnx_model输入格式NCHW
    ## --------------------------------------------#
    if
    "optimized"
    in
    : = . onnx_model(
        img 2 img,transpose0,1 )# 从HWC,变为CHW ## -------------------------------###  添加batch维度    ## -------------------------------#
    =
    .
    (
    img , np0expand_dims)img# --------------------------------# #   class_indict用于可视化类别# --------------------------------#
    with
    open
    (
    , "r")json_pathas := . f(
        class_indict ) json## -------------------------------#load##  加载onnx模型f## -------------------------------#
    =
    (
    =
    ort_session ) HB_ONNXRuntime## 下面这两行有啥用?我注释掉也没什么影响model_file#ort_session_2.set_dim_param(0, 0, '?')     onnx_model#ort_session_2.set_providers(['CPUExecutionProvider'])
    # -----------------------------------#
    #   onnx模型推理
    #   初始化数据,注意此时 img 是numpy格式
    
    # -----------------------------------#
    =
    .
    [
    input_name 0 ort_session]input_names=.(
    ort_outs None ort_session,run:}) {input_name# 推理得到输出 img# print(ort_outs)     # [array([[-4.290639  , -2.267056  ,  7.666328  , -1.4162455 ,  0.57391334]], dtype=float32)]# -----------------------------------#        #   经过softmax转化为概率
    #   softmax_2D按行转化,一行一个样本
    
    #   测试时,softmax可不要!
    # -----------------------------------#
    =
    (
    [
    predict_probability 0 softmax_2D]ort_outs)# print(predict_probability)  # array([[0.1],[0.2],[0.3],[0.3],[0.1]])           # ----------------------------------------##   argmax得到最大概率索引,也就是类别对应索引        
    # ----------------------------------------#
    
    =
    .
    (
    predict_cla , np=argmax-predict_probability1 axis)# print(predict_cla)        # array([2])="class: {}   prob: {:.3}"
    .

    print_res format ([str(class_indict[0]predict_cla)],[0]
                                                 predict_probability[[0]predict_cla])print()
    .(print_res)
    pltfortitleinprint_resrange
    ( i len ([0]predict_probability)):print("class: {:10}   prob: {:.3}"
        .format([str(class_indict)],i[0]
                                                  predict_probability[]))i.("./result.jpg"
    plt)savefig.()
    pltifshow=='__main__'

: __name__ ## ----------------------------------------------------------------# #   注意,quantized_onnx_model的输入格式为:NHWC#   optimized_float_onnx_model的输入格式为:NCHW
    #   这儿的命名一定要以quantized和optimized进行区分,后面的代码中有用到
    ## ----------------------------------------------------------------#
    =
    './model_output/resnet34_224x224_rgb_quantized_model.onnx'
    =
    quantized_onnx_model './model_output/resnet34_224x224_rgb_optimized_float_model.onnx' # ----------------------------------------#
    optimized_float_onnx_model #   输入图像被resize到什么尺寸 #   分类网络输入尺寸

    # ----------------------------------------#
    =
    (
    224
    input_shape , 224)= "./data/tulip.jpg"assert
    img_path . .
    ( os)path,exists"file: '{}' dose not exist."img_path.format ()# 下面两行,用它显示图片而已=img_path.
    open
    img ( Image).(img_path)
    plt=imshow.img(     

    img ) cv2# read class_indictimread=img_path'./class_indices.json'
    # -----------------------------------------------------------------------#
    json_path #   关键部分 #   第一个参数用于切换:quantized_onnx_model 或 optimized_float_onnx_model
    # -----------------------------------------------------------------------#
    (
    ,
    ,
    check_onnx,quantized_onnx_model) imgprint json_path( input_shape"onnx model check finsh."
    )rose.jpgoptimized_float_onnx_modeltulip.jpg

运行结果:

7 使用hb_perf工具估计在开发板上运行的性能

拿到了hb_perf_result文件,在上开发板运行前,预估一下能运行多少FPS等。
cd到与hb_perf.log同级,运行命令:

 

生成文件夹和[+++],打开输出的文件自己看两眼就明白了。

8 接下来工作

将转换得到的.bin模型在x3开发板上跑起来。

)
File: /www/wwwroot/outofmemory.cn/tmp/route_read.php, Line: 126, InsideLink()
File: /www/wwwroot/outofmemory.cn/tmp/index.inc.php, Line: 165, include(/www/wwwroot/outofmemory.cn/tmp/route_read.php)
File: /www/wwwroot/outofmemory.cn/index.php, Line: 30, include(/www/wwwroot/outofmemory.cn/tmp/index.inc.php)
【地平线开发板 模型转换】将pytorch生成的onnx模型转换成.bin模型_python_内存溢出

【地平线开发板 模型转换】将pytorch生成的onnx模型转换成.bin模型

【地平线开发板 模型转换】将pytorch生成的onnx模型转换成.bin模型,第1张

文章目录
  • 1 获取onnx模型
  • 2 启动docker容器
  • 3 onnx模型检查
    • 3.1 为什么要检查?
    • 3.2 如何 *** 作
  • 4 图像数据预处理
    • 4.1 一些问题的思考
    • 4.2 图片挑选与放置
    • 4.2 使用地平线提供的模板进行图像预处理
    • 4.3 使用自己写的图像预处理函数
    • 4.4 归一化前后 图像数据 均值与方差怎么变 ?
  • 5 获取.bin模型
    • 5.1 03_build.sh
    • 5.2 resnet34_config.yaml
  • 6 在开发机上验证转换过程中生成的两个onnx模型
  • 7 使用hb_perf工具估计在开发板上运行的性能
  • 8 接下来工作

1 获取onnx模型

我是使用pytorch训练得到的pth模型,转成onnx模型,这一步之前写过博客,可参考:pytorch分类模型导出onnx模型并验证

2 启动docker容器

如果之前在开发机上已经安装过地平线给的docker开发环境,可直接执行指令:

docker start horizon			# 启动horizon,horizon是我的容器container 别名
docker attach horizon			# 进入horizon

如果你之前没安装过,也没学过docker的相关知识,可参考我的另一篇博客:配置地平线提供的docker开发环境。

3 onnx模型检查 3.1 为什么要检查?

官方回答: 验证模型中所使用的算子需要符合地平线平台的算子约束。

翻译回答: onnx模型中,不是所有的函数“我”都支持,你得用“我”支持的。怎么知道“我”是否支持呢?给你个excel表让你查太费事,写个函数让程序查吧,这样快!

3.2 如何 *** 作

把在第一步得到的resnet34.onnx模型,放到开发机的指定位置:

/data/wyx/horizon/horizon_xj3_open_explorer_v1.8.5_20211224/ddk/samples/ai_toolchain/horizon_model_convert_sample/03_classification/05_efficientnet_lite0_onnx/resnet_x3/output/

没有哪过文件夹,就新建它,如下图所示:

为什么要这样放?往下看,你会明白的。

然后在resnet_x3文件夹下,新建一个01_check.sh文件,内容如下:

#!/usr/bin/env sh

set -e -v
cd $(dirname hb_mapper checker) || exit

# 模型类型,本文以onnx为例
model_type="onnx"
# 要检查的onnx模型位置
onnx_model="./output/resnet34.onnx"   
# 检查输出日志,放到哪里去
# 虽然它还是放到了与01_check.sh同级目录下(感觉像小bug)
output="./model_output/resnet34_checker.log"    
# 用的什么架构,不用改
march="bernoulli2"    

hb_mapper checker --model-type ${model_type} \
                  --model ${onnx_model} \
                  --output ${output} --march ${march}

解释一下:resnet_x3就是地平线专属命令了,俗称“工具”。

cd到sh 01_check.sh 文件夹下,执行命令:

resnet_x3/data/image_origin/

输出比较重要的部分如下,第二列是BPU,说明算子运行在BPU上,同理,也可能运行在CPU上。

多数算子bpu都支持,包括卷积conv、池化pool、全连接fc等 *** 作。
对于不支持的算子,算了,我们就先让它在CPU上跑。

4 图像数据预处理

本来以为模型检查完,一行命令加载onnx模型,一行命令输出能在地平线开发板上运行的模型。

总体看,确实是,但是,呵,天真。

4.1 一些问题的思考
  • 问题1: 模型转换是什么?
    回答: 模型转换,是指模型经过量化(例如,网络参数从float32转换成int8)、算子重组、结构优化等一系列 *** 作,得到新的模型,这个新的模型体积更小,推理速度更快。
  • 问题2: 为什么要进行模型转换?
    回答: 服务器端训练得到的模型,想跑到各家开发板上,就得适应各家开发板的架构,学底层太费事,故用一套工具链,帮助你快速把服务器端的模型转换得到能在开发板上跑的模型。
  • 问题3: 为什么要进行图像数据预处理?
    回答: 地平线的量化方案属于“数据依赖型”,也就是说,在进行模型量化时,需要有 典型图片 输入作为支撑。
  • 问题4: 什么样的图片能称为典型呢?
    回答: 比如分类数据集,每一类都挑到,随机挑就好。一共挑50~100张即可。
4.2 图片挑选与放置

从5个类别中,每个类别随机挑了20张图片,放在image_converted_rgb_f32文件夹下,同时新建一个文件夹flower_data_preprocess.py,用于存放处理后的图像,如图所示:

下面就是对挑选的图片进行处理了。这里我提供两种方法,一种是根据地平线提供的模板进行图像预处理,但它跳来跳去的,而且包括的内容太多,我自己又写了一个resnet_x3,实现同样的功能,下面分别介绍。

4.2 使用地平线提供的模板进行图像预处理

如下图所示,在02_preprocess.sh文件夹下需要有这三个文件,下面分别介绍其作用及内容:

  • #!/usr/bin/env bash set -e -v cd $(dirname data_preprocess.py) || exit python3 ../../../data_preprocess.py \ # 原图片路径 --src_dir ./data/image_origin \ # 处理后图片存哪 --dst_dir ./data/image_converted_rgb_f32 \ # 处理后图片后缀名 --pic_ext .rgb \ # 读取图片的方式,有opencv和skimage两种 --read_mode opencv

内容如下:

data_preprocess.py

很明显告诉我们运行那个文件,往里传哪些参数,至于preprocess.py里面的内容就不再介绍了,不然跳来跳去,讲不完了。反正data_preprocess.py是地平线给的,用就完事了。

  • preprocess.py

data中会用到resnet_x3文件中的一个函数,如下图所示。至于里面的内容,就不展开了,不然又得跳了。

  • sh 02_preprocess.sh 文件夹

里面放着挑选出来的图片,处理后的图片也会放在这里面。

flower_data_preprocess.py文件夹下,执行命令:

resnet_x3

运行界面及结果如下图所示:

4.3 使用自己写的图像预处理函数

自己写的图像预处理函数放在import文件中,文件放在import文件夹下,实现效果与4.2节一致,其内容如下:
对于关注的归一化 *** 作,预训练权重的均值与方差怎么办问题,详看代码注释。

import cv2
as os
## ------------------------------------------------------------# numpy #   src_dir:从数据集中每个类别各挑选15张图片放到一起 np


#   dst_dir:处理后的图片存放的路径
#   pic_ext:处理后的图片后缀名(影响不大,只是为了说明它的通道顺序)
## ------------------------------------------------------------#
=
'./data/image_origin'
src_dir = './data/image_converted_rgb_f32'
dst_dir = '.rgb'
pic_ext ## ---------------------------------------# #   一次只 *** 作一张图片        

## ---------------------------------------#
for
in
sorted src_name ( .(os)listdir)src_dir:## -----------------------------##   把图片路径拼出来
    ## -----------------------------#
    =
    .
    src_file . os(path,join)src_dir## -----------------------------# src_name##  opencv实现预处理  
 
    ## -----------------------------#
    =
    .
    img ( cv2)imread=src_file.
    img ( cv2.resize(np)array,img(224 ,224) ,=. interpolation)cv2.INTER_CUBIC(.astype)np## -----------------------------------------------------#float32#   PC端网络训练时,数据需要归一化,为何在这儿不做?
    #   答:模型转换时,需要的图像输入分为是0~255,故不要归一化。
    ## -----------------------------------------------------#
    # img /= 255.0        
    # ---------------------------------------#
    #   常规 *** 作是:先转成RGB,再减均值,除方差

    # ---------------------------------------#
    =
    .
    img ( cv2,cvtColor.img) cv2## -----------------------------------------------------------------------------------------#COLOR_BGR2RGB#   问题1:PC端网络训练时,数据需要减均值,除方差,为何在这儿不做?	
    #   答:为了和yaml中data_mean_and_scale下的mean_value与scale_value参数配合
    #       在yaml文件中设置即可
    #   问题2:是否在这儿减均值,除方差,在yaml中data_mean_and_scale参数设置为no_preprocess即可?
    #   答:按道理是的,但要注意,下方的减去均值,除以方差,是针对ImageNet归一化后的数据,
    #       那没有归一化的数据,其均值方差和下面的数据大小又有着怎么的关系呢?欢迎查看下一小节
    ## -----------------------------------------------------------------------------------------#
    # img -= [0.485, 0.456, 0.406]
    # img /= [0.229, 0.224, 0.225]
    ## -----------------------------------------------------------------------------------------#
    #   从HWC,变为CHW。用的是Pytorch框架,其输入是NCHW,故需要这一步。

    #   对于更多NCHW还是NHWC问题,可参考https://blog.csdn.net/weixin_45377629/article/details/124040681
    ## -----------------------------------------------------------------------------------------#
    =
    .
    img ( img2transpose,0, 1) # ---------------------------------------##   添加batch维度        
    #   至此,图像预处理完毕
    # ---------------------------------------#
    =
    .
    img ( np,expand_dims0img) # -----------------------------------------------------##   os.path.basename:返回最后的 文件名,也就是src_image

    #   例如:os.path.basename("./src/1.jpg"),返回:1.jpg
    # -----------------------------------------------------#
    =
    .
    filename . os(path)basename# print(src_file)src_file# -----------------------------------------------------#
    #   os.path.splitext: 把图片名和图片扩展名分开,

    #   例如:1.jpg,short_name=1, ext=.jpg
    # -----------------------------------------------------#
    ,
    =
    short_name. ext . os(path)splitext# ---------------------------------------#filename#   新的图片名
    # ---------------------------------------#
    =
    .
    pic_name . os(path,join+dst_dir) short_name = pic_ext.
    dtype . np(float32
    img)astype.dtype()tofileprintpic_name(
    "write:%s"%) if pic_name'coco'

运行效果如下图所示:

4.4 归一化前后 图像数据 均值与方差怎么变 ?

对于在服务器端使用Pytorch训练网络是,通常会归一化图像数据,然后减去均值,除以方差,再传入网络。

在训练网络模型时,我们大多会使用Imagenet数据集或者COCO数据集的预训练权重,用它们的均值和方差,例如:

in . : args=dataset[
    mean_vals 0.471 ,0.448, 0.408] =[
    std_vals 0.234 ,0.239, 0.242] elif'imagenet'
in . : args=dataset[
    mean_vals 0.485 ,0.456, 0.406] =[
    std_vals 0.229 ,0.224, 0.225] importas

这些都是归一化后的均值与方差。那归一化前后数据的均值与方差又有着怎么的关系呢?用个例子说明:

= numpy . np

a ( np[array4,8,12,16])=/
b 255.0 a = .
mean_a ( np)mean=a.
mean_b ( np)mean=b.
std_a ( np)std=a.
std_b ( np)stdprintb(

"mean_a",)print mean_a(
"mean_b",)print mean_b(
"std_a",)print std_a(
"std_b",)mean_b = mean_a / 255 std_bstd_b = std_a / 255

运行输出:

PS D:\DeepLearning\classification> python .\1.py
mean_a 10.0
mean_b 0.0392156862745098
std_a 4.47213595499958
std_b 0.01753778805882188

可以发现 1 255 \frac{1}{255} ,也就是说对于归一化后的数据其均值与方差均是归一化前的数据的2551.bin.bin

5 获取.bin模型

准备好处理后的图像数据,下面就是获取能够在开发板上运行的模型了,地平线在开发板上运行的模型后缀为03_build.sh,故在此称为resnet34_config.yaml模型。

这一步需要准备两个文件,一个是resnet_x3,一个是resnet34_config.yaml,两个文件均放于#!/bin/bash set -e -v cd $(dirname # 模型转化相关的参数 model_parameters: # ONNX浮点网络数据模型文件 onnx_model: './output/resnet34.onnx' # 适用BPU架构 march: "bernoulli2" # 指定模型转换过程中是否输出各层的中间结果,如果为True,则输出所有层的中间输出结果, layer_out_dump: False # 日志文件的输出控制参数, # debug输出模型转换的详细信息 # info只输出关键信息 # warn输出警告和错误级别以上的信息 log_level: 'debug' # 模型转换输出的结果的存放目录 working_dir: 'model_output' # 模型转换输出的用于上板执行的模型文件的名称前缀 output_model_file_prefix: 'resnet34_224x224_rgb' # 模型输入相关参数, 若输入多个节点, 则应使用';'进行分隔, 使用默认缺省设置则写None input_parameters: # (选填) 模型输入的节点名称, 此名称应与模型文件中的名称一致, 否则会报错, 不填则会使用模型文件中的节点名称 input_name: "" # 网络实际执行时,输入给网络的数据格式,包括 nv12/rgb/bgr/yuv444/gray/featuremap, # pytorch模型一般是rgb input_type_rt: 'rgb' # 网络实际执行时输入的数据排布, 可选值为 NHWC/NCHW # 若input_type_rt配置为nv12,则此处参数不需要配置 # pytorch模型一般是NCHW input_layout_rt: 'NCHW' # 网络训练时输入的数据格式,可选的值为rgb/bgr/gray/featuremap/yuv444 # pytorch模型一般是rgb input_type_train: 'rgb' # 网络训练时输入的数据排布, 可选值为 NHWC/NCHW # pytorch模型一般是rgb input_layout_train: 'NCHW' # (选填) 模型网络的输入大小, 以'x'分隔, 不填则会使用模型文件中的网络输入大小,否则会覆盖模型文件中输入大小 input_shape: '' # 网络实际执行时,输入给网络的batch_size, 默认值为1 #input_batch: 1 # 网络输入的预处理方法,主要有以下几种: # no_preprocess 不做任何 *** 作 # data_mean 减去通道均值mean_value # data_scale 对图像像素乘以data_scale系数 # data_mean_and_scale 减去通道均值后再乘以scale系数 # 注意:此处不是减去均值,除以方差! norm_type: 'data_mean_and_scale' # 图像减去的均值, 如果是通道均值,value之间必须用空格分隔 # 注意:此处的均值是没有归一化的均值,例如ImageNet的R通道,应该是: 124.16 = 0.485x255 mean_value: 124.16 116.28 103.53 # 图像预处理缩放比例,如果是通道缩放比例,value之间必须用空格分隔 # 注意:此处的scale是乘以,以前的方差是除以。且是没有归一化数据 # 例如ImageNet的R通道,应该是: 0.0171248 = 1 / (0.229x255) scale_value: 0.0171248 0.0175070 0.0174292 # 模型量化相关参数 calibration_parameters: # 模型量化的参考图像的存放目录,图片格式支持Jpeg、Bmp等格式,输入的图片 # 应该是使用的典型场景,一般是从测试集中选择20~100张图片,另外输入 # 的图片要覆盖典型场景,不要是偏僻场景,如过曝光、饱和、模糊、纯黑、纯白等图片 # 若有多个输入节点, 则应使用';'进行分隔 # 预处理后的图片所在路径 cal_data_dir: './data/image_converted_rgb_f32' # 如果输入的图片文件尺寸和模型训练的尺寸不一致时,并且preprocess_on为true, # 则将采用默认预处理方法(skimage resize), # 将输入图片缩放或者裁减到指定尺寸,否则,需要用户提前把图片处理为训练时的尺寸 # preprocess_on: False # 模型量化的算法类型,支持kl、max、default、load,通常采用default即可满足要求, 若为QAT导出的模型, 则应选择load calibration_type: 'default' # 编译器相关参数 compiler_parameters: # 编译策略,支持bandwidth和latency两种优化模式; # bandwidth以优化ddr的访问带宽为目标; # latency以优化推理时间为目标 compile_mode: 'latency' # 设置debug为True将打开编译器的debug模式,能够输出性能仿真的相关信息,如帧率、DDR带宽占用等 debug: False # 编译模型指定核数,不指定默认编译单核模型, 若编译双核模型,将下边注释打开即可 # core_num: 2 # 优化等级可选范围为O0~O3 # O0不做任何优化, 编译速度最快,优化程度最低, # O1-O3随着优化等级提高,预期编译后的模型的执行速度会更快,但是所需编译时间也会变长。 # 推荐用O2做最快验证 optimize_level: 'O3' ) config_file="./resnet34_config.yaml" model_type="onnx" # build model hb_mapper makertbin --config ${config_file} \ --model-type ${model_type} 文件夹下。下面分别介绍这两个文件里的内容:

5.1 03_build.sh

加载sh 03_build.sh 文件,使用hb_mapper中的makertbin工具去 *** 作即可。

Cosine Similarity
5.2 resnet34_config.yaml

重点关注网络输入的预处理方法:norm_type: ‘data_mean_and_scale’,需要了解的内容已在注释中给出。

hb_mapper_makertbin.log

运行下方指令:

resnet34_224x224_rgb_original_float_model.onnx

重点关注第5列的预先相似度resnet34_224x224_rgb_optimized_float_model.onnx,都在0.9以上,说明转换的还不错。

转换成功后,得到几个文件中比较重要的有:

  • 日志文件-resnet34_224x224_rgb_quantized_model.onnx
  • 量化前原始onnx模型-resnet34_224x224_rgb.bin
  • 量化中间产物:predict_docker_x3_onnx.py
  • 量化后onnx模型-03_build.sh
  • 板端模型-.bin
6 在开发机上验证转换过程中生成的两个onnx模型

resnet_34/用在import构建得到as模型之后,用于验证过程产物------两个onnx模型,存放在import文件夹下。
处理思路很简单:数据预处理–加载模型–模型推理–模型输出后处理,其内容如下:

from numpy import np
import os
. PIL as Image
import matplotlibimportpyplot from plt
import json
def cv2
softmax_2D horizon_tc_ui ( HB_ONNXRuntime


) :"""
    针对二维numpy矩阵每一行进行softmax *** 作
    X: np.array. Probably should be floats.
    return: 二维矩阵
    """X# looping through rows of X#   循环遍历X的行
    =
    .
    (
    ps . np)emptyforXinshaperange
    ( i . [0X]shape):[,:
        ps]i=.(  [ np,exp:X]i)[,:
        ps]i/=.sum ( np[,:ps]i)returndefcheck_onnx
    ( ps


, ,,onnx_model) img: json_path## --------------------------------------------# input_shape##  opencv实现预处理方式## --------------------------------------------#
    =
    .
    (
    img . cv2(resize)np,array,img=. input_shape) interpolation.cv2(INTER_CUBIC.)astype## --------------------------------------------#np#   使用编译过程中生成的onnx模型是不需要归一化的float32#   模型需要的数值范围0~255,ncv读取的刚好是0~255
    ## --------------------------------------------#
    # img /= 255.0
    ## --------------------------------------------#
    #   网络训练输入一般是RGB的图片,故在此也转一下
    #   为了省时间,似乎这儿可以和yaml中参数配合,去掉这一步

    ## --------------------------------------------#
    =
    .
    (
    img , cv2.cvtColor)img## ------------------------------------------------------------------# cv2#   常规 *** 作是:减去均值,除以方差COLOR_BGR2RGB#   为了和yaml中data_mean_and_scale下的mean_value与scale_value参数配合
    #   注意:此处是针对0~255的通道顺序RGB的图像进行减去均值,乘以方差倒数
    ## ------------------------------------------------------------------#
    -=
    [
    124.16
    img , 116.28,103.53 ]*= [0.0171248
    img , 0.0175070,0.0174292 ]## --------------------------------------------# #   opencv读取的img是HWC格式#   quantized_onnx_model输入格式NHWC
    #   optimized_float_onnx_model输入格式NCHW
    ## --------------------------------------------#
    if
    "optimized"
    in
    : = . onnx_model(
        img 2 img,transpose0,1 )# 从HWC,变为CHW ## -------------------------------###  添加batch维度    ## -------------------------------#
    =
    .
    (
    img , np0expand_dims)img# --------------------------------# #   class_indict用于可视化类别# --------------------------------#
    with
    open
    (
    , "r")json_pathas := . f(
        class_indict ) json## -------------------------------#load##  加载onnx模型f## -------------------------------#
    =
    (
    =
    ort_session ) HB_ONNXRuntime## 下面这两行有啥用?我注释掉也没什么影响model_file#ort_session_2.set_dim_param(0, 0, '?')     onnx_model#ort_session_2.set_providers(['CPUExecutionProvider'])
    # -----------------------------------#
    #   onnx模型推理
    #   初始化数据,注意此时 img 是numpy格式
    
    # -----------------------------------#
    =
    .
    [
    input_name 0 ort_session]input_names=.(
    ort_outs None ort_session,run:}) {input_name# 推理得到输出 img# print(ort_outs)     # [array([[-4.290639  , -2.267056  ,  7.666328  , -1.4162455 ,  0.57391334]], dtype=float32)]# -----------------------------------#        #   经过softmax转化为概率
    #   softmax_2D按行转化,一行一个样本
    
    #   测试时,softmax可不要!
    # -----------------------------------#
    =
    (
    [
    predict_probability 0 softmax_2D]ort_outs)# print(predict_probability)  # array([[0.1],[0.2],[0.3],[0.3],[0.1]])           # ----------------------------------------##   argmax得到最大概率索引,也就是类别对应索引        
    # ----------------------------------------#
    
    =
    .
    (
    predict_cla , np=argmax-predict_probability1 axis)# print(predict_cla)        # array([2])="class: {}   prob: {:.3}"
    .

    print_res format ([str(class_indict[0]predict_cla)],[0]
                                                 predict_probability[[0]predict_cla])print()
    .(print_res)
    pltfortitleinprint_resrange
    ( i len ([0]predict_probability)):print("class: {:10}   prob: {:.3}"
        .format([str(class_indict)],i[0]
                                                  predict_probability[]))i.("./result.jpg"
    plt)savefig.()
    pltifshow=='__main__'

: __name__ ## ----------------------------------------------------------------# #   注意,quantized_onnx_model的输入格式为:NHWC#   optimized_float_onnx_model的输入格式为:NCHW
    #   这儿的命名一定要以quantized和optimized进行区分,后面的代码中有用到
    ## ----------------------------------------------------------------#
    =
    './model_output/resnet34_224x224_rgb_quantized_model.onnx'
    =
    quantized_onnx_model './model_output/resnet34_224x224_rgb_optimized_float_model.onnx' # ----------------------------------------#
    optimized_float_onnx_model #   输入图像被resize到什么尺寸 #   分类网络输入尺寸

    # ----------------------------------------#
    =
    (
    224
    input_shape , 224)= "./data/tulip.jpg"assert
    img_path . .
    ( os)path,exists"file: '{}' dose not exist."img_path.format ()# 下面两行,用它显示图片而已=img_path.
    open
    img ( Image).(img_path)
    plt=imshow.img(     

    img ) cv2# read class_indictimread=img_path'./class_indices.json'
    # -----------------------------------------------------------------------#
    json_path #   关键部分 #   第一个参数用于切换:quantized_onnx_model 或 optimized_float_onnx_model
    # -----------------------------------------------------------------------#
    (
    ,
    ,
    check_onnx,quantized_onnx_model) imgprint json_path( input_shape"onnx model check finsh."
    )rose.jpgoptimized_float_onnx_modeltulip.jpg

运行结果:

  • 对于optimized_float_onnx_model的分类预测结果。
    使用quantized_onnx_model模型推理

  • 对于.bin的分类预测结果
    使用resnet34_224x224_rgb.bin模型推理:

    使用hb_perf resnet34_224x224_rgb.bin 模型推理:

    图片结果展示:

7 使用hb_perf工具估计在开发板上运行的性能

拿到了hb_perf_result文件,在上开发板运行前,预估一下能运行多少FPS等。
cd到与hb_perf.log同级,运行命令:

 

生成文件夹和,打开输出的文件自己看两眼就明白了。

8 接下来工作

将转换得到的.bin模型在x3开发板上跑起来。

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/langs/798934.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-06
下一篇 2022-05-06

发表评论

登录后才能评论

评论列表(0条)

保存