Skip to content

5. Simulator

5.1. 使用方法

Simulator工具的位置在 SGS_IPU_SDK/Scripts/calibrator/simulator.py。 该工具具有以下功能:

  • 对分类网络数据集验证;
  • 对检测网络数据集验证(支持COCO数据集);
  • 对单张图片预测;
  • 绘制单张图片目标检测结果;

在SGS_IPU_SDK ⽬录下运⾏以下脚本,输出Library的路径(已经做过该步骤可忽略):

cd ~/SGS_IPU_SDK 
source cfg_env.sh

进入到该工具目录,工具使用示例:

  • 测试ILSVRC数据集,并返回模型精度结果:
python3 simulator.py \ 
-i ~/SGS_Models/resource/classify/ilsvrc2012_val_set100/ \ 
-m ~/SGS_Models/tensorflow/mobilenet_v2/mobilenet_v2_float.sim \ 
-l ~/SGS_Models/resource/classify/tensorflow_labels.txt \ 
-c Classification \ 
-t Float \ 
-n mobilenet_v2 \ 
--num_process 20

或者可以使用传入 指定图片路径列表文件 的形式:

python3 simulator.py \ 
-i ~/SGS_Models/resource/classify/ilsvrc2012_val_set100/file.list \ 
-m ~/SGS_Models/tensorflow/mobilenet_v2/mobilenet_v2_float.sim \ 
-l ~/SGS_Models/resource/classify/tensorflow_labels.txt \ 
-c Classification \ 
-t Float \ 
-n mobilenet_v2 \ 
--num_process 20
  • 测试COCO2017数据集,并返回模型检测结果:
python3 simulator.py \ 
-i ~/SGS_Models/resource/detection/coco2017_val_set100 \ 
-m ~/SGS_Models/tensorflow/ssd_mobilenet_v1/ssd_mobilenet_v1_float.sim \ 
-l ~/SGS_Models/resource/detection/coco2017_val_set100.json \ 
-c Detection \ 
-t Float \ 
-n ssd_mobilenet_v1 \ 
--num_process 10

或者可以使用传入 指定图片路径列表文件 的形式:

python3 simulator.py \ 
-i ~/SGS_Models/resource/detection/coco2017_val_set100/file.list \ 
-m ~/SGS_Models/tensorflow/ssd_mobilenet_v1/ssd_mobilenet_v1_float.sim \ 
-l ~/SGS_Models/resource/detection/coco2017_val_set100.json \ 
-c Detection \ 
-t Float \ 
-n ssd_mobilenet_v1 \ 
--num_process 10
  • 测试单张图片,并将检测结果绘制在图片上,保存到 ./results/ 文件夹下:
python3 simulator.py \ 
-i ~/SGS_Models/resource/detection/coco2017_val_set100/000000567877.jpg \ 
-m ~/SGS_Models/tensorflow/ssd_mobilenet_v1/ssd_mobilenet_v1_float.sim \ 
-c Detection \ 
-t Float \ 
-n ssd_mobilenet_v1 \ 
--draw_result ./results

对该工具参数的具体说明如下。


5.1.1 必选参数

-i, --image: 图片文件 / 图片文件夹路径 / 指定图片路径列表文件

-m, --model: 网络模型文件路径。

-c, --category: 模型的类别,主要有Classification / Detection / Unknown。

  • Classification: 模型有1个输出,会根据输出排序输出分数由高至低的前5个。
  • Detection: 模型有4个输出,会根据输出转换为输入图片的bbox位置以及类别。只支持SigmaStar后处理算子 [SigmaStar后处理模块]。其他后处理请使用Unknown。
  • Unknown: 模型输出不属于上述两种类型,会输出全部的Tensor数值。

-t, --type: 模型的数据类型:

  • Float: 浮点模型。
  • Fixed: 定点在线模型。
  • Offline: 定点离线模型。

Please Note:

  • 不加-n/--preprocess参数时,-i/--image不能给图片或图片文件夹路径。
  • -i/--image参数传入 指定图片路径列表文件 的形式时:
网络模型为单输入时:
~/SGS_IPU_SDK/image_test/2007000364.jpg
~/SGS_IPU_SDK/image_test/ILSVRC2012_test_00000002.bmp
...


网络模型为多输入时:
~/SGS_IPU_SDK/image_test/2007000364.jpg,~/SGS_IPU_SDK/image_test/ILSVRC2012_test_00000002.bmp
~/SGS_IPU_SDK/image_test/2007000365.jpg,~/SGS_IPU_SDK/image_test/ILSVRC2012_test_00000003.bmp
...

5.1.2 可选参数

-l, --label: 数据集的标签文件路径 / 图片文字描述的标签。

-n, --preprocess: 模型名称,与图片前处理方法相关,详见 [3.2. 图片前处理方法]。 也可以完成前处理文件配置后,给定前处理文件路径。不加该参数,图片参数需要给原始数据,可采用--save_input保存图片数据后,根据该格式制定其他的原始数据。

注意事项

若模型为 多输入 时,-n,--preprocess 参数用法需要多个前处理方法,例如 -n preprocess1.py,preprocess2.py 或者 --preprocess preprocess1.py,preprocess2.py

--tool: sgs_simulator文件路径。

--skip_garbage: Fixed和Offline模型输出结果跳过无用数据。

--dump_rawdata: 保存Fixed和Offline模型输入二进制数据,文件名为图片名 + .bin,保存在当前路径。

--num_process: 进程数,运行同时运行的进程数。(可选参数,不加该参数默认为单线程)验证数据集必须加此参数且进程数 > 1。

--draw_result: 绘制目标检测网络选框结果,参数为结果保存文件夹路径(文件夹不存在会自动创建)和画框的阈值,使用逗号( , )分隔保存路径与阈值。输入阈值画出大于阈值的检测结果,不输入阈值则画出全部检测结果。

--continue_run: 接着上次数据集剩下的部分运行。

--save_input: 保存simulator 前处理后的文件,默认不保存。文件为文本文件,会在运行路径下创建文件夹tmp_image,文件保存在 ./tmp_image/imagename 图片名。


5.1.3 注意事项

  • simulator工具会寻找系统变量从而获得对应阶段任务所需工具路径,因此默认情况下参数-t/--tool不用指定相关工具的位置。
  • simulator工具每次运行结束后会在当前目录创建log目录,log目录下的output文件夹,存放模型推演的结果。
  • -i/--image 的参数为单张图片的路径时,simulator只对该图片推演;当 -i/--image 的参数为图片文件夹的路径时,simulator会对文件夹内的图片全部推演,此时增加--num_process(参数 > 1),可以开启多进程推演。
  • -l/--label 的参数为数据集标签文件路径时必须使用--num_process参数,数据集验证支持ImageNet(Top1、Top5)、COCO目标检测(mmAP);当 -l/--label 参数为图片文字描述标签时,不能使用选项--num_process。
  • -c/--category 的参数选择Unknown时,模型输出Tensor的的最后一个维度会向上按8个数对齐:
layer46-conv Tensor:
{
tensor dim:4, Original shape:[1 13 13 255], Alignment shape:[1 13 13 256]
The following tensor data shape is alignment shape.
tensor data:
...

对于Fixed和Offline的模型,得到有用数据可以使用如下Python代码(假设变量Data 是维度为[1, 13, 13, 256]的np.array数据,真正有用的数据Useful_Data维度为[1, 13, 13, 255]):

Useful_Data = Data[:, :, :, :255]

5.2. calibrator_custom.simulator

calibrator_custom.simulator是基于Python的快速量化和转换模型的模块。使用calibrator_custom.simulator可以更方便灵活的对多输入、多段网络进行量化和转换。calibrator_custom.simulator包含3种,用于解析不同阶段的模型。

calibrator_custom.float_simulator 用于解析float模型

calibrator_custom.fixed_simulator 用于解析fixed模型

calibrator_custom.offline_simulator 用于解析offline模型

目前基于的docker环境,提供Python3.7的预编译的Python模块。由于fixed模型和offline模型使用32位运行库,因此需使用32位Python3.7环境运行。Docker中已更新32位Python3.7环境,使用python32即可运行。float模型使用64位Python运行。

下面基于calibrator_custom.float_simulator,说明使用方法和相关API接口:

import calibrator_custom
model_path = './mobilenet_v2_float.sim'
model = calibrator_custom.float_simulator(model_path) 

calibrator_custom.float_simulator

使用calibrator_custom.float_simulator时,需要给定float.sim模型的路径,用于创建float_simulator的实例。参数给定错误,将无法成功创建float_simulator实例,并返回ValueError。


5.2.1 calibrator_custom.simulator方法

get_input_details:

返回网络模型输入信息(list)

Float模型返回如下:

 input_details = model.get_input_details()
 print(input_details)
 [
  {
    'name': 'sub_7', 
    'shape': array([  1, 513, 513, 3], dtype=int32), 
    'dtype': <class 'numpy.float32'>, 
    'index': 0
  }
] 

返回的list中根据模型输入个数包含以下dict信息:

index: 输入Tensor序号

name: 输入Tensor名称

shape: 输入Tensor的形状

dtype: 输入Tensor的数据类型

Fixed和Offline模型返回如下:

 >>> input_details = model.get_input_details()
 >>> print(input_details)
[
  {
    'index': 0, 
    'shape': array([  1, 513, 513, 3]), 
    'dtype': <class 'numpy.uint8'>, 
    'name': 'sub_7' , 
    'input_formats': 'RGB', 
    'training_input_formats': 'RGB'
  }
] 

get_output_detail

返回的list中根据模型输入个数包含以下dict信息:

index: 输入Tensor序号

name: 输入Tensor名称

shape: 输入Tensor的形状

dtype: 输入Tensor的数据类型

input_formats: 网络模型实际运行时的图片输入格式

training_input_formats: 网络模型训练是的图片输入格式

返回网络模型输出信息(list)

Float模型返回如下:

>>> output_details = model.get_output_details()
>>> print(output_details) 
[
  {
    'name': 'MobilenetV2/Conv/Conv2D', 
    'shape': array([  1, 257, 257,  30], dtype=int32),
    'dtype': <class 'numpy.float32'>, 
    'index': 0
  }
] 

返回的list中根据模型输出个数包含以下dict信息:

index: 输出Tensor序号

name: 输出Tensor名称

shape: 输出Tensor的形状

dtype: 输出Tensor的数据类型

Fixed和Offline模型返回如下:

 >>> output_details = model.get_output_details()
 >>> print(output_details)
[
  {
    'index': 0, 
    'shape': array([  1, 257, 257,  30]), 
    'name': 'MobilenetV2/Conv/Conv2D', 
    'dtype': <class 'numpy.int16'>, 
    'quantization': (0.00013832777040079236, 0)
  }
] 

返回的list中根据模型输出个数包含以下dict信息:

index: 输出Tensor序号

name: 输出Tensor名称

shape: 输出Tensor的形状

dtype: 输出Tensor的数据类型

quantization: 输出Tensor的scale和zero_point(需将模型输出Tensor乘scale得到浮点数)


set_input:

设置网络模型输入数据

>>> model.set_input(0, img_data)  

输入数据,0为输入Tensor的index,可以在get_input_details()的返回值里拿到。img_data是与model输入shape和dtype相同的numpy.ndarray格式数据,错误的shape或dtype,将导致set_input返回ValueError。如果模型有多个输入,可以多次调用set_input,根据get_input_details()的返回值里拿到index设置对应Tensor的输入数据。


invoke

模型运行一次

>>> model.invoke()

调用invoke前请先使用set_input设置输入数据,未调用set_input直接调用invoke会返回ValueError。


get_output

获取网络模型输出数据

>>> result = model.get_output(0)

获取输出数据,0为输出Tensor的index,可以在get_output_details()的返回值里拿到,返回numpy.ndarray格式输出数据。如果模型有多个输出,可以多次调用get_output,根据get_output_details()的返回值里拿到index获取对应Tensor的输出数据。

假设模型输出的shape为[1, 257, 257, 30]

Float模型查看结果的shape

>>> print(result.shape)
(1, 257, 257, 30)

Fixed和Offline模型查看结果shape

 >>> print(result.shape)
 (1, 257, 257, 32)

Fixed和Offline模型输出Tensor因为模拟了在硬件的储存方式,Tensor的最后一个维度会向上对齐,因此输出的维度为Tensor对齐的维度。最后对齐的数据是无用数据,可以使用如下方法去除:

 >>> result = result[..., :output_details[0]['shape'][-1]]
 >>> print(result.shape)
 (1, 257, 257, 30)

get_tensor_details

返回网络模型每个Tensor的信息(list)

Float模型返回如下:

 >>> tensor_details = model.get_tensor_details()
 >>> print(tensor_details)
[
  {
    'name': 'MobilenetV2/Conv/Conv2D', 
    'shape': array([  1, 257, 257,  30], dtype=int32), 
    'dtype': 'FLOAT32', 
    'qtype': 'INT16'
  }, 
  {
    'name': 'MobilenetV2/Conv/Conv2D_bias', 
    'shape': array( [ 2, 30], dtype=int32), 
    'dtype': 'FLOAT32', 
    'qtype': 'INT16'
  }, 
  {
    'name': 'MobilenetV2/Conv/weights/read', 
    'shape': array( [30,  3,  3,  3], dtype=int32), 
    'dtype': 'FLOAT32', 
    'qtype':  'INT8'
  }, 
  {
    'name': 'sub_7', 
    'shape': array([  1, 513, 513, 3], dtype=int32), 
    'dtype': 'FLOAT32', 
    'qtype': 'UINT8'
  }
] 

返回的list中根据模型Tensor个数包含以下dict信息:

name: Tensor名称

shape: Tensor的形状

dtype: Tensor的数据类型

qtype: 定点模型该Tensor可能的数据类型(quantization type)

Fixed和Offline模型如果input_config.ini中[OUTPUT_CONFIG]的dequantizations配置为FALSE时返回如下:

 >>> tensor_details = model.get_tensor_details()
 >>> print(tensor_details)
 [
    {
      'shape': array([  1, 257, 257,  30]), 
      'quantization': [(0.00013832777040079236, 0)], 
      'min': [-4.230099201202393], 
      'max': [4.532586097717285], 
      'name': 'MobilenetV2/Conv/Conv2D',
      'dtype': 'INT16'
    }, 
    {
      'shape':  array([ 2, 30]), 
      'quantization': [], 
      'min': [0.0], 
      'max ': [1.0], 
      'name': 'MobilenetV2/Conv/Conv2D_bias', 
      'dtype':'INT16'
    }, 
    {
      'shape': array([30,  3,  3,  3]), 
      'quantization': [(0.004813921172171831, 0)], 
      'min': [-0.5498989820480347], 
      'max': [0.6113680005073547], 
      'name': 'MobilenetV2/Conv/weights/read', 
      'dtype': 'INT8'
    }, 
    {
      'shape': array([  1, 513, 513, 3 ]), 
      'quantization': [(0.007843137718737125, 128)], 
      'min': [-1 .0], 
      'max': [1.0], 
      'name': 'sub_7', 
      'dtype': 'UINT8'
    }
  ] 

返回的list中根据模型Tensor个数包含以下dict信息:

name: Tensor名称

shape: Tensor的形状

dtype: Tensor的数据类型

quantization: Tensor的scale和zero_point

min: Tensor的最小值

max: Tensor的最大值


5.2.2 calibrator_custom.SIM_Simulator

对于多输入、多段网络同时转换时,提供calibrator_custom.SIM_Simulator,方便进行简单定义后,统一运行。

calibrator_custom.SIM_Simulator是已经实现好的class,当中只有forward方法未实现,使用时仅需实现该方法,即可转换完成。

下面以 SGS_IPU_SDK/Scripts/examples/sim_simulator.py 为例,说明calibrator_custom.SIM_Simulator的使用方法:

import calibrator_custom
class Net(calibrator_custom.SIM_Simulator):
    def __init__(self):
        super().__init__() 
        self.model = calibrator_custom.fixed_simulator(model_path)
    def forward(self, x):
        out_details = self.model.get_output_details()
        self.model.set_input(0, x)
        self.model.invoke()
        result_list = []
        for idx in range(len(out_details)):
            result = self.model.get_output(idx)
            # for Fixed and Offline model
            if result.shape[-1] != out_details[idx]['shape'][-1]:
                result = result[..., :out_details[idx]['shape'][-1]]
            if out_details[idx]['dtype'] == np.int16:
                scale, _ = out_details[idx]['quantization']
                result = np.dot(result, scale)
            result_list.append(result)
        return result_list

定义forward方法,forward的参数为模型输入,如有多个输入,可增加forward的参数。


创建calibrator_custom.SIM_Simulator的实例

  net = Net()


调用calibrator_custom.SIM_Simulator实例的方法

  result = net(img_gen, num_process=4)


调用calibrator_custom.SIM_Simulator实例,需要给定输入图片的numpy.ndarray或者图片生成器。

当num_process大于1时,img_gen必须为图片生成器。

  • 图片生成器(img_gen)

为方便多输入、多段网络转换模型,通过生成器方便组织输入图片的序列。如模型有多个输入,生成器应该按照定义forward时的输入顺序,返回有多个numpy.ndarray的list。

calibrator_custom.utils.image_preprocess_func使用预先定义好的前处理方法

preprocess_func = calibrator_custom.utils.image_preprocess_func(model_name)
def image_generator(folder_path, preprocess_func, norm):
    images = [os.path.join(folder_path, img) for img in os.listdir(folder_path)]
    for image in images:
        img = preprocess_func(image, norm)
        yield [img]
img_gen = image_generator('./images', preprocess_func, False)