[Diao Ye aprende a programar] MicroPython práctico (10) - red neuronal MaixPy de aprendizaje basado en cero KPU2

La arquitectura básica de la KPU
Repasemos las operaciones básicas de la red neuronal clásica:
Convolución: convolución 1x1, convolución 3x3, convolución 5x5 y superior
Normalización por lotes
(Activar)
piscina Cálculo de
matriz de agrupación (Matrix Calculate): multiplicación de matriz, suma
Para el estructura de red neuronal básica, solo están disponibles cuatro operaciones 1, 2, 3, 4;
para nuevas estructuras de red, como ResNet, después del resultado de convolución se agregará una variable, debe usar la quinta operación, operación de matriz.
Para el chip de control principal K210 de MAIX, tiene aceleración de hardware incorporada para las cuatro operaciones básicas de convolución, normalización por lotes, activación y agrupación, pero no implementa operaciones de matriz generales, por lo que en la estructura de red implementada Hay restricciones.
Para las estructuras de red que requieren operaciones adicionales, los usuarios deben insertar manualmente las implementaciones de la capa de procesamiento intervenida por la CPU después de que el hardware complete las operaciones básicas, lo que reducirá la cantidad de marcos, por lo que se recomienda a los usuarios que optimicen sus estructuras de red a la forma de red básica.
Afortunadamente, la segunda generación del chip admitirá cálculos matriciales de propósito general y solidificará más tipos de estructuras de red.
En la KPU, las cuatro operaciones básicas mencionadas anteriormente no son módulos de aceleración separados, sino un módulo de aceleración integrado, que efectivamente evita la pérdida causada por la intervención de la CPU, pero también pierde algo de flexibilidad operativa.
El diagrama de bloques esquemático del módulo de aceleración KPU analizado desde el sdk/demo independiente y Model Compiler es el siguiente, puede entenderlo mirando la imagen.

inserte la descripción de la imagen aquí

#MicroPython práctico (10)——Aprendizaje básico de la red neuronal KPU de MaixPy
#Uno de los procedimientos experimentales: ejecución de demostración de reconocimiento facial (demostración simple)
#Dirección de descarga del modelo: http://dl.sipeed.com/MAIX/MaixPy
Hay dos archivos en la carpeta del modelo después de descargar /model/face_model_at_0x300000.kfpkg

inserte la descripción de la imagen aquí
inserte la descripción de la imagen aquí

Abra kflash_gui
y use kfpkg para empaquetar los dos archivos modelo y el firmware maixpy ​​y descárguelos a flash

inserte la descripción de la imagen aquí
Ocurrió un error al empaquetar kfpkg, parece que el rango de direcciones del archivo es diferente...

inserte la descripción de la imagen aquí

Lo he intentado muchas veces y sigue sin funcionar, los dos no son compatibles. Más tarde, no lo empaqueté en absoluto, y solo quemé el archivo del modelo kfpkg (antes se quemó el firmware MaixPy V0.4.0), pero no esperaba que fuera posible. Ahora entiendo que el firmware y el modelo se puede quemar por separado.

inserte la descripción de la imagen aquí

#MicroPython práctico (10)——Aprendizaje básico de la red neuronal KPU de MaixPy
#Uno de los procedimientos experimentales: ejecución de demostración de reconocimiento facial (demostración simple)
#Dirección de descarga del modelo: http://dl.sipeed.com/MAIX/MaixPy ... l_at_0x300000.kfpkg

#MicroPython动手做(10)——零基础学MaixPy之神经网络KPU
#实验程序之一:运行人脸识别demo(简单演示)
#模型下载地址:http://dl.sipeed.com/MAIX/MaixPy ... l_at_0x300000.kfpkg

import sensor
import image
import lcd
import KPU as kpu

lcd.init()
sensor.reset()
sensor.set_pixformat(sensor.RGB565)
sensor.set_framesize(sensor.QVGA)
sensor.run(1)
task = kpu.load(0x300000) #使用kfpkg将 kmodel 与 maixpy 固件打包下载到 flash
anchor = (1.889, 2.5245, 2.9465, 3.94056, 3.99987, 5.3658, 5.155437, 6.92275, 6.718375, 9.01025)
a = kpu.init_yolo2(task, 0.5, 0.3, 5, anchor)
while(True):
    img = sensor.snapshot()
    code = kpu.run_yolo2(task, img)
    if code:
        for i in code:
            print(i)
            a = img.draw_rectangle(i.rect())
    a = lcd.display(img)
a = kpu.deinit(task)

inserte la descripción de la imagen aquí
El puerto serial genera una gran cantidad de datos

inserte la descripción de la imagen aquí

{“x”:0, “y”:31, “w”:107, “h”:145, “valor”:0.611305, “classid”:0, “index”:0, “objnum”:1}
{ “x”:0, “y”:31, “w”:107, “h”:145, “valor”:0.500000, “classid”:0, “index”:0, “objnum”:1} {
“ x”:1, “y”:31, “w”:107, “h”:144, “valor”:0.500000, “classid”:0, “index”:0, “objnum”:1} {“
x ”:0, “y”:31, “w”:107, “h”:145, “valor”:0.556360, “classid”:0, “index”:0, “objnum”:1} {“x
” :13, “y”:34, “w”:83, “h”:139, “valor”:0.556360, “classid”:0, “index”:0, “objnum”:1} {“x”
: 0, “y”:31, “w”:107, “h”:145, “valor”:0.500000, “classid”:0, “index”:0, “objnum”:1} {“x”:
0 , “y”:31, “w”:107, “h”:145, “valor”:0.500000, “classid”:0, “index”:0, “objnum”:1} {“x”:0
, “y”:31, “w”:107, “h”:145, “valor”:0.556360, “classid”:0, “index”:0, “objnum”:1}
{“x”:0, “y”:31, “w”:107, “h”:145, “valor”:0.500000, “classid”:0, “index”:0, “objnum”:1}
{ “x”:15, “y”:36, “w”:83, “h”:111, “valor”:0.556360, “classid”:0, “index”:0, “objnum”:1} {
“ x”:13, “y”:33, “w”:83, “h”:139, “valor”:0.556360, “classid”:0, “índice”:0, “objnum”:1} {“
x ”:14, “y”:47, “w”:83, “h”:111, “valor”:0.500000, “classid”:0, “index”:0, “objnum”:1} {“x
” :0, “y”:31, “w”:107, “h”:144, “valor”:0.556360, “classid”:0, “index”:0, “objnum”:1} {“x”
: 13, “y”:32, “w”:83, “h”:139, “valor”:0.500000, “classid”:0, “index”:0, “objnum”:1} {“x”:
14 , “y”:32, “w”:83, “h”:139, “valor”:0.611305, “classid”:0, “index”:0, “objnum”:1} {“x”:0
, “y”:31, “w”:107, “h”:144, “valor”:0.556360, “classid”:0, “index”:0, “objnum”:1}

KPU es un procesador de redes neuronales de propósito general, que puede realizar cálculos de redes neuronales convolucionales con bajo consumo de energía, obtener el tamaño, las coordenadas y los tipos de objetivos detectados de vez en cuando, y detectar y clasificar rostros u objetos. Método del módulo KPU:

  1. Cargar modelo
    Cargar modelo desde flash o sistema de archivos
    importar KPU como
    tarea kpu = kpu.load(offset o file_path)

Compensación de parámetros
: el tamaño de compensación del modelo en flash, como 0xd00000, significa que el modelo se quema en el punto de inicio de 13M
ruta_de_archivo: el nombre de archivo del modelo en el sistema de archivos, como "/sd/xxx.kmodel"

volver
kpu_net: objeto de red kpu

  1. Inicialice la red yolo2
    y pase los parámetros de inicialización para el modelo de red yolo2
    import KPU as kpu
    task = kpu.load(offset or file_path)
    ancla = (1.889, 2.5245, 2.9465, 3.94056, 3.99987, 5.3658, 5.155437, 6.92275, 6.718 375 , 9.01025)
    kpu.init_yolo2(tarea, 0.5, 0.3, 5, ancla)

Parámetros
kpu_net:
umbral de objeto de red kpu: umbral de probabilidad
nms_value: umbral box_iou
ancla_num: número de puntos de anclaje
ancla: los parámetros del punto de anclaje son consistentes con los parámetros del modelo

  1. Desinicialización
    importar KPU como
    tarea kpu = kpu.load(offset o file_path)
    kpu.deinit(tarea)

Parámetro
kpu_net: objeto kpu_net devuelto por kpu_load

  1. 运行yolo2网络
    importar KPU como tarea
    de imagen de importación
    kpu = kpu.load(offset o file_path)
    ancla = (1.889, 2.5245, 2.9465, 3.94056, 3.99987, 5.3658, 5.155437, 6.92275, 6.718375, 9. 01025)
    kpu.init_yolo2(tarea, 0.5, 0.3, 5, ancla)
    img = imagen.Imagen()
    kpu.run_yolo2(tarea, img)

Parámetro
kpu_net: el objeto kpu_net devuelto por kpu_load
image_t: la imagen recopilada del sensor

Lista de retorno
: lista de kpu_yolo2_find

  1. El cálculo directo de la red (forward)
    calcula el modelo de red cargado en el número especificado de capas y genera el mapa de características de la capa de destino
    importar KPU como kpu
    task = kpu.load(offset or file_path)
     …
    fmap=kpu.forward(task, imagen, 3)

Parámetros
kpu_net: objeto kpu_net
image_t: la imagen recopilada del sensor
int: especifica qué capa de la red calcular

Devolver
fmap: objeto de mapa de características, que contiene mapas de características de todos los canales de la capa actual

  1. mapa de funciones fmap
    Lleva los datos del canal especificado del mapa de funciones al objeto de imagen
    img=kpu.fmap(fmap,1)

Parámetro
fmap: objeto de mapa de características
int: número de canal del mapa de características especificado]

Devolver
img_t: la imagen en escala de grises generada por el canal correspondiente del mapa de características

  1. fmap_free liberar mapa de funciones
    liberar mapa de funciones objeto
    kpu.fmap_free(fmap)

Parámetro
fmap: objeto de mapa de características

devolver
ninguno

  1. netinfo
    obtiene la información de la estructura de red del modelo
    info=kpu.netinfo(task)
    layer0=info[0]

Parámetro
kpu_net: objeto kpu_net

Devolver
netinfo list: lista de información de todas las capas, incluida la información:
index: número de capas de la capa actual en la red
wi: ancho de entrada
hi: altura de entrada
wo: ancho de salida
ho: altura de salida
chi: número de canales de entrada
cho: número de canales de salida
dw: si se trata de una capa de profundidad
kernel_type: tipo de kernel de convolución, 0 es 1x1, 1 es 3x3
pool_type: tipo de agrupación, 0 no es agrupación; 1: 2x2 agrupación máxima; 2:...
para_size: palabra de parámetro de convolución de la capa actual Número de sección

Descripción de la configuración del registro KPU
El fabricante del chip no proporciona un manual de registro, analizamos la definición de cada registro de kpu.c, kpu.h y Model Compiler. La configuración de registro de la KPU está escrita en la estructura kpu_layer_argument_t. Tomamos el gencode.c en la demostración de kpu en la demostración independiente para el análisis. (https://github.com/kendryte/kend … pu/gencode_output.c)

//层参数列表,共16层kpu_layer_argument_t la[] __attribute__((aligned(128))) = {
    
    
//0{
    
    
.kernel_offset.data = {
    
    
  .coef_row_offset = 0,                //固定为0
  .coef_column_offset = 0        //固定为0
},
.image_addr.data = {
    
                    //图像输入输出地址,一个在前,一个在后,下一层运算的时候翻过来,可以避免拷贝工作。
  .image_dst_addr = (uint64_t)0x6980,        //图像输出地址,int((0 if idx & 1 else (img_ram_size - img_output_size)) / 64)
  .image_src_addr = (uint64_t)0x0                //图像加载地址
},
.kernel_calc_type_cfg.data = {
    
    
  .load_act = 1,                        //使能激活函数,必须使能(硬件设计如此),不使能则输出全为0
  .active_addr = 0,                        //激活参数加载首地址,在kpu_task_init里初始化为激活折线表
  .row_switch_addr = 0x5,        //图像宽占用的单元数,一个单元64Byte.  ceil(width/64)=ceil(320/64)=5
  .channel_switch_addr = 0x4b0,                        //单通道占用的单元数.  row_switch_addr*height=5*240=1200=0x4b0
  .coef_size = 0,                        //固定为0
  .coef_group = 1                        //一次可以计算的组数,因为一个单元64字节,
                                                        //所以宽度>32,设置为1;宽度17~32,设置为2;宽度<=16,设置为4
},
.interrupt_enabe.data = {
    
    
  .depth_wise_layer = 0,        //常规卷积层,设置为0
  .ram_flag = 0,                        //固定为0
  .int_en = 0,                                //失能中断
  .full_add = 0                                //固定为0
},
.dma_parameter.data = {
    
            //DMA传输参数
  .dma_total_byte = 307199,                //该层输出16通道,即 19200*16=308200
  .send_data_out = 0,                        //使能输出数据
  .channel_byte_num = 19199                //输出单通道的字节数,因为后面是2x2 pooling, 所以大小为160*120=19200
},
.conv_value.data = {
    
                    //卷积参数,y = (x*arg_x)>>shr_x
  .arg_x = 0x809179,                //24bit        乘法参数
  .arg_w = 0x0,
  .shr_x = 8,                                //4bit        移位参数
  .shr_w = 0
},
.conv_value2.data = {
    
                    //arg_add = kernel_size * kernel_size * bw_div_sw * bx_div_sx =3x3x?x?
  .arg_add = 0
},
.write_back_cfg.data = {
    
            //写回配置
  .wb_row_switch_addr = 0x3,                //ceil(160/64)=3
  .wb_channel_switch_addr = 0x168,        //120*3=360=0x168
  .wb_group = 1                                                //输入行宽>32,设置为1
},
.image_size.data = {
    
            //输入320*240,输出160*120
  .o_col_high = 0x77,
  .i_col_high = 0xef,
  .i_row_wid = 0x13f,
  .o_row_wid = 0x9f
},
.kernel_pool_type_cfg.data = {
    
    
  .bypass_conv = 0,                //硬件不能跳过卷积,固定为0
  .pad_value = 0x0,                //边界填充0
  .load_para = 1,                //硬件不能跳过归一化,固定为1
  .pad_type = 0,                //使用填充值
  .kernel_type = 1,                //3x3设置为1, 1x1设置为0
  .pool_type = 1,                //池化类型,步长为2的2x2 max pooling
  .dma_burst_size = 15,        //dma突发传送大小,16字节;脚本中固定为16
  .bwsx_base_addr = 0,        //批归一化首地址,在kpu_task_init中初始化
  .first_stride = 0                //图像高度不超过255;图像高度最大为512},
.image_channel_num.data = {
    
    
  .o_ch_num_coef = 0xf,        //一次性参数加载可计算的通道数,16通道。4K/单通道卷积核数
                                                //o_ch_num_coef = math.floor(weight_buffer_size / o_ch_weights_size_pad)       
  .i_ch_num = 0x2,                //输入通道,3通道 RGB
  .o_ch_num = 0xf                //输出通道,16通道
},
.kernel_load_cfg.data = {
    
    
  .load_time = 0,                //卷积加载次数,不超过72KB,只加载一次
  .para_size = 864,                //卷积参数大小864字节,864=3(RGB)*9(3x3)*2*16
  .para_start_addr = 0,        //起始地址
  .load_coor = 1                //允许加载卷积参数
}
},
   //0层参数结束……
};

上表中还有些结构体内容没有填充,是在KPU初始化函数中填充:```kpu_task_t* kpu_task_init(kpu_task_t* task){
    
    
la[0].kernel_pool_type_cfg.data.bwsx_base_addr = (uint64_t)&bwsx_base_addr_0;        //初始化批归一化表
la[0].kernel_calc_type_cfg.data.active_addr = (uint64_t)&active_addr_0;                //初始化激活表
la[0].kernel_load_cfg.data.para_start_addr = (uint64_t)¶_start_addr_0;         //初始化参数加载
……        //16层参数,逐层计算
task->layers = la;
task->layers_length = sizeof(la)/sizeof(la[0]);        //16层
task->eight_bit_mode = 0;                                        //16bit模式
task->output_scale = 0.12349300010531557;        //输出的缩放,偏置
task->output_bias = -13.528212547302246;
return task;
}```

Supongo que te gusta

Origin blog.csdn.net/weixin_41659040/article/details/131971994
Recomendado
Clasificación