Skip to content

附录. 前处理和配置文件注意要点

1. 前处理文件Preprocess.py

由于不同网络模型的前处理方式不尽相同,为了能够在转换网络时尽可能减小精度的丢失,应该使用与训练相同的图片前处理方式,每种前处理方式需独立编写python文件。 编写图片前处理文件时,编写图片处理函数(函数名称不限),返回np.array 格式的图片数据,函数必须包含2个参数:

  • 图片路径

  • 归一化标记(norm=True)

归一化标记是为了区分网络模型是否是浮点模型。因为在浮点网络模型阶段,图片的归一化需要在送进网络前处理好。但是定点网络模型和离线网络模型已经包含了input_config.ini文件的设置信息,能够将图片数据自行做归一化处理,因此送进网络模型的数据不需要做归一化,这与在SigmaStar硬件上处理方式相同。

根据input_config.ini文件中training_input_formats 和 input_formats设置的不同,下面举例前处理文件的写法。

1.1. training_input_formats和input_formats均为RGB或者均为BGR

PC上转换模型和simulator模型示例的preprocess.py如下。
如果在每个channel上有不同的std值,需要在对应channel上除以std,如 std= [57.375, 57.12, 58.395]。 同时,input_config.ini文件中std_value每个通道对应的std_value值,以英文冒号( : )分隔,顺序为RGB。

training_input_formats input_formats 板上运行时数据对齐方式
RGB/BGR RGB/BGR 不用对齐
RGB/BGR RGBA/BGRA W = ALIGN_UP(W * 4, xrgb_h_pitch_alignment) / 4
import cv2
import numpy as np

def get_image(img_path, resizeH=224, resizeW=224, norm=True, meanB=103.53, meanG=116.28, meanR=123.68, std=57.375, rgb=False, nchw=False):
    img = cv2.imread(img_path)
    if img is None:
        raise FileNotFoundError('No such image: {}'.format(img_path))

    img_norm = cv2.resize(img, (resizeW, resizeH), interpolation=cv2.INTER_LINEAR)

    if norm:
        img_norm = (img_norm - [meanB, meanG, meanR]) / std
        img_norm = img_norm.astype('float32')
    else:
        img_norm = np.round(img_norm).astype('uint8')

    if rgb:
        img_norm = cv2.cvtColor(img_norm, cv2.COLOR_BGR2RGB)

    if nchw:
        img_norm = np.transpose(img_norm, axes=(2, 0, 1))

    return np.expand_dims(img_norm, 0)

def image_preprocess(img_path, norm=True):
    return get_image(img_path, norm=norm)

1.2. training_input_formats为RGB或BGR和input_formats为GRAY

PC上转换模型和simulator模型示例的preprocess.py如下。
灰度模型,指输入是单通道图片的模型,即输入C维度上为1的模型。input_config.ini灰度图片请按如下方式配置:training_input_formats=RGB;input_formats=GRAY;

training_input_formats input_formats 板上运行时数据对齐方式
RGB/BGR GRAY H = ALIGN_UP(H, yuv420_v_pitch_alignment)
W = ALIGN_UP(W, yuv420_h_pitch_alignment)
import cv2
import numpy as np

def get_image(img_path, resizeH=28, resizeW=28, norm=True, meanR=33.318, std=1.0, rgb=False, nchw=False):
    img = cv2.imread(img_path, flags=-1)
    if img is None:
        raise FileNotFoundError('No such image: {}'.format(img_path))
    try:
        img_dim = img.shape[2]
    except IndexError:
        img_dim = 1
    if img_dim == 3:
        img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    elif img_dim == 4:
        img = cv2.cvtColor(img, cv2.COLOR_BGRA2GRAY)
    img_norm = cv2.resize(img, (resizeW, resizeH), interpolation=cv2.INTER_LINEAR)

    if norm:
        img_norm = (img_norm - meanR) / std
        img_norm = np.expand_dims(img_norm, axis=2)
        dummy = np.zeros((28,28,2))
        img_norm = np.concatenate((img_norm,dummy),axis=2)
        img_norm = img_norm.astype('float32')
    else:
        img_norm = np.expand_dims(img_norm, 2)
        img_norm = np.round(img_norm).astype('uint8')

    if rgb:
        img_norm = cv2.cvtColor(img_norm, cv2.COLOR_BGR2RGB)

    if nchw:
        # NCHW
        img_norm = np.transpose(img_norm, axes=(2, 0, 1))

    return np.expand_dims(img_norm, 0)

def image_preprocess(img_path, norm=True):
    return get_image(img_path, norm=norm)

1.3. training_input_formats为RGB或BGR和input_formats为YUV_NV12

PC上转换模型和simulator模型示例的preprocess.py如下。

training_input_formats input_formats 板上运行时数据对齐方式
RGB/BGR YUV_NV12 H = ALIGN_UP(H, yuv420_v_pitch_alignment)
W = ALIGN_UP(W, yuv420_h_pitch_alignment)
import cv2
import numpy as np

def get_image(img_path, resizeH=224, resizeW=224, norm=True, meanB=127.5, meanG=127.5, meanR=127.5, std=128.0, rgb=False, nchw=False):
    img = cv2.imread(img_path)
    if img is None:
        raise FileNotFoundError('No such image: {}'.format(img_path))

    img_norm = cv2.resize(img, (resizeW, resizeH), interpolation=cv2.INTER_LINEAR)

    if norm:
        img_norm = (img_norm - [meanB, meanG, meanR]) / std
        img_norm = img_norm.astype('float32')
    else:
        img_norm = np.round(img_norm).astype('uint8')

    if rgb:
        img_norm = cv2.cvtColor(img_norm, cv2.COLOR_BGR2RGB)

    if nchw:
        img_norm = np.transpose(img_norm, axes=(2, 0, 1))

    return np.expand_dims(img_norm, 0)

def image_preprocess(img_path, norm=True):
    return get_image(img_path, norm=norm)

1.4. training_input_formats和input_formats均为RAWDATA_S16_NHWC

PC上转换模型和simulator模型示例的preprocess.py如下。
在PC上
当input_config.ini中的training_input_formats和input_formats都填写RAWDATA_S16_NHWC时, 此时input_config.ini中mean_red、mean_green、mean_blue和std_value不会再在定点网络模型中生效, 所有前处理过程都将在输入模型前完成。mean_red、mean_green、mean_blue和std_value不要填写。 浮点模型运行时,使用方法与运行图片输入的模型相同。使用simulator.py运行定点模型时,前处理方法应与浮点前处理模型保持一致, 仍然输入norm=True时的方法,所以RAWDATA_S16_NHWC的网络前处理Python文件编写时norm为True和False的实现都应按照norm为True时编写。 simulator.py会读入原浮点数据,进行反量化和对齐排列后输入给定点模型。
在开发板上
在板上运行RAWDATA_S16_NHWC的网络时,也需要完成输入数据的定点化和对齐排列过程。定点化和对齐排列过程需要自行完成。具体详见9.2 RAWDATA_S16_NHWC模型转换要点

training_input_formats input_formats 板上运行时数据对齐方式
RAWDATA_S16_NHWC RAWDATA_S16_NHWC 最后1个维度 = ALIGN_UP(最后1个维度, 8)
import cv2
import numpy as np

def get_image(img_path, resizeH=224, resizeW=224, norm=True, rgb=False, nchw=False):
    img = cv2.imread(img_path)
    if img is None:
        raise FileNotFoundError('No such image: {}'.format(img_path))

    img_norm = cv2.resize(img, (resizeW, resizeH), interpolation=cv2.INTER_LINEAR)
    img_norm = img_norm.astype('float32')

    if rgb:
        img_norm = cv2.cvtColor(img_norm, cv2.COLOR_BGR2RGB)

    if nchw:
        img_norm = np.transpose(img_norm, axes=(2, 0, 1))
    return np.expand_dims(img_norm, 0)

def image_preprocess(img_path, norm=True):
    return get_image(img_path, norm=norm)

2. 模型性能优化规则

对于卷积的性能优化
1. kernel size 3x3 最好,特别对于首层。
2. kernel size 1x1 的时候,input tensor 最内维度shape值对齐到16 最好。

对于DMA算子
1. concatenation算子比pack算子性能更好。
2. split比slice算子性能更好。
3. 尽量减少在最内维上做transpose。
4. 单口elementwise 算子的const 操作数最好是右操作数,即input[1]。

综合部分
1. Tensor的维度为4最好。
2. Tensor最内维的shape值对齐到32最好。
3. Softmax最好只对最内维度操作。
4. ReduceMax、ReduceMin、ReduceSum最好坍塌的纬度是相邻的。

合并规则
1. Pad + Conv2D/DepthwiseConv,pad会被合并到卷积中,pad几乎不占用时间。
2. Conv2D/DepthwiseConv + 单口Mul + 单口Add,Mul和Add算子会和卷积一起运算。我们会把BatchNorm转换成Mul+Add,所以Conv2D/DepthwiseConv + BatchNorm可以合并。
3. Pad + avgPooling 可以合并。
4. 连续的transpose会自动合并为一个。
5. 所有reshape算子都会被跳过。
6. 连续的单口的相同elementwise算子会合并。
7. 单独的单口mul和单口add,会变成MultAdd单一的算子。
注意:这里单口意思为一个数据是constant的情况