附录. 前处理和配置文件注意要点
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和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或者均为RAWDATA_F32_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会读入原浮点数据,进行反量化和对齐排列后输入给定点模型。
当input_config.ini中的training_input_formats和input_formats都填写RAWDATA_F32_NHWC时,simulator.py会读入原浮点数据,由模型内部的align算子自动对齐数据,不需要人为操作。
在开发板上
在板上运行RAWDATA_S16_NHWC的网络时,也需要完成输入数据的定点化和对齐排列过程。定点化和对齐排列过程需要自行完成。
在板上运行RAWDATA_F32_NHWC的网络时,不需要输入数据的定点化和对齐排列过程。由模型内部的align算子自动对齐数据。具体详见。9.2 RAWDATA_F32_NHWC模型转换要点
training_input_formats | input_formats | 板上运行时数据对齐方式 |
---|---|---|
RAWDATA_F32_NHWC | RAWDATA_F32_NHWC | 不用对齐 |
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的情况