add nnom pack and example
This commit is contained in:
413
components/ai/nnom/inc/nnom.h
Normal file
413
components/ai/nnom/inc/nnom.h
Normal file
@@ -0,0 +1,413 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2020
|
||||
* Jianjia Ma
|
||||
* majianjia@live.com
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Change Logs:
|
||||
* Date Author Notes
|
||||
* 2019-02-05 Jianjia Ma The first version
|
||||
* 2019-02-10 Jianjia Ma Compiler supports dense net connection
|
||||
*/
|
||||
|
||||
#ifndef __NNOM_H__
|
||||
#define __NNOM_H__
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdarg.h>
|
||||
#include <math.h>
|
||||
|
||||
#include "nnom_port.h"
|
||||
|
||||
#define NNOM_ALIGN (sizeof(char*)) // alignment when doing memory ops. Equal to size of pointer in byte.
|
||||
#define q7_t int8_t
|
||||
#define q15_t int16_t
|
||||
#define q31_t int32_t
|
||||
#define q63_t int64_t
|
||||
|
||||
/* version */
|
||||
#define NNOM_MAJORVERSION 0 /**< major version number */
|
||||
#define NNOM_SUBVERSION 4 /**< minor version number */
|
||||
#define NNOM_REVISION 3 /**< revise version number */
|
||||
#define NNOM_VERSION ((NNOM_MAJORVERSION * 10000) + (NNOM_SUBVERSION * 100) + NNOM_REVISION)
|
||||
|
||||
#ifdef ARM_NN_TRUNCATE
|
||||
#define NNOM_TRUNCATE
|
||||
#endif
|
||||
|
||||
#ifndef NNOM_TRUNCATE
|
||||
#define NNOM_ROUND(out_shift) ((0x1 << out_shift) >> 1 )
|
||||
#else
|
||||
#define NNOM_ROUND(out_shift) 0
|
||||
#endif
|
||||
|
||||
typedef enum
|
||||
{
|
||||
NN_SUCCESS = 0, /**< No error */
|
||||
NN_ARGUMENT_ERROR = -1, /**< One or more arguments are incorrect */
|
||||
NN_LENGTH_ERROR = -2, /**< Length of data buffer is incorrect */
|
||||
NN_SIZE_MISMATCH = -3, /**< Size of matrices is not compatible with the operation. */
|
||||
NN_NANINF = -4, /**< Not-a-number (NaN) or infinity is generated */
|
||||
NN_SINGULAR = -5, /**< Generated by matrix inversion if the input matrix is singular and cannot be inverted. */
|
||||
NN_TEST_FAILURE = -6, /**< Test Failed */
|
||||
NN_NO_MEMORY = -7,
|
||||
NN_MORE_TODO = -8
|
||||
} nnom_status_t;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
NNOM_INVALID = 0,
|
||||
NNOM_BASE,
|
||||
NNOM_INPUT,
|
||||
NNOM_OUTPUT,
|
||||
NNOM_CONV_2D,
|
||||
NNOM_DW_CONV_2D,
|
||||
NNOM_CONV2D_TRANS,
|
||||
NNOM_BATCHNORM,
|
||||
NNOM_DENSE,
|
||||
NNOM_ZERO_PADDING,
|
||||
NNOM_CROPPING,
|
||||
NNOM_RNN,
|
||||
NNOM_ACTIVATION,
|
||||
NNOM_RELU,
|
||||
NNOM_LEAKY_RELU,
|
||||
NNOM_ADV_RELU,
|
||||
NNOM_SIGMOID,
|
||||
NNOM_TANH,
|
||||
NNOM_SOFTMAX,
|
||||
NNOM_MAXPOOL,
|
||||
NNOM_GLOBAL_MAXPOOL,
|
||||
NNOM_AVGPOOL,
|
||||
NNOM_GLOBAL_AVGPOOL,
|
||||
NNOM_SUMPOOL,
|
||||
NNOM_GLOBAL_SUMPOOL,
|
||||
NNOM_UPSAMPLE,
|
||||
NNOM_FLATTEN,
|
||||
NNOM_LAMBDA,
|
||||
NNOM_CONCAT,
|
||||
NNOM_ADD,
|
||||
NNOM_SUB,
|
||||
NNOM_MULT,
|
||||
NNOM_TYPE_MAX
|
||||
|
||||
} nnom_layer_type_t;
|
||||
|
||||
#define DEFUALT_LAYER_NAMES \
|
||||
{ \
|
||||
"Unknown", \
|
||||
"Base", \
|
||||
"Input", \
|
||||
"Output", \
|
||||
"Conv2D", \
|
||||
"DW_Conv2D", \
|
||||
"Conv2DTrsp", \
|
||||
"BatchNorm", \
|
||||
"Dense", \
|
||||
"ZeroPad", \
|
||||
"Cropping", \
|
||||
"RNN", \
|
||||
"Activation", \
|
||||
"ReLU", \
|
||||
"Leaky_ReLU", \
|
||||
"Adv_ReLU", \
|
||||
"Sigmoid", \
|
||||
"Tanh", \
|
||||
"Softmax", \
|
||||
"MaxPool", \
|
||||
"GL_MaxPool", \
|
||||
"AvgPool", \
|
||||
"GL_AvgPool", \
|
||||
"SumPool", \
|
||||
"GL_SumPool", \
|
||||
"UpSample", \
|
||||
"Flatten", \
|
||||
"Lambda", \
|
||||
"Concat", \
|
||||
"Add", \
|
||||
"Sub", \
|
||||
"Mult", \
|
||||
}
|
||||
extern const char default_layer_names[][12];
|
||||
|
||||
// We dont count softmax an activation here, softmax is instanced as a layer
|
||||
typedef enum
|
||||
{
|
||||
ACT_UNKNOWN = 0,
|
||||
ACT_RELU,
|
||||
ACT_LEAKY_RELU,
|
||||
ACT_ADV_RELU,
|
||||
ACT_TANH,
|
||||
ACT_SIGMOID,
|
||||
ACT_HARD_TANH,
|
||||
ACT_HARD_SIGMOID
|
||||
} nnom_activation_type_t;
|
||||
|
||||
#define ACTIVATION_NAMES \
|
||||
{ \
|
||||
"Unknown", \
|
||||
"ReLU", \
|
||||
"LkyReLU", \
|
||||
"AdvReLU", \
|
||||
"TanH", \
|
||||
"Sigmoid", \
|
||||
"HrdTanH", \
|
||||
"HrdSigd", \
|
||||
}
|
||||
extern const char default_activation_names[][8];
|
||||
|
||||
// RNN cell type
|
||||
typedef enum
|
||||
{
|
||||
NNOM_UNKOWN_CELL = 0,
|
||||
NNOM_SIMPLE_CELL,
|
||||
NNOM_GRU_CELL,
|
||||
NNOM_LSTM_CELL,
|
||||
NNOM_CELL_TYPE_MAX
|
||||
} nnom_rnn_cell_type_t;
|
||||
|
||||
#define DEFUALT_CELL_NAMES \
|
||||
{ \
|
||||
"Unknown", \
|
||||
"Simple", \
|
||||
"GRU", \
|
||||
"LSTM", \
|
||||
}
|
||||
extern const char default_cell_names[][8];
|
||||
|
||||
|
||||
// parameters
|
||||
typedef enum
|
||||
{
|
||||
PADDING_VALID = 0,
|
||||
PADDING_SAME
|
||||
} nnom_padding_t;
|
||||
|
||||
#define NNOM_TENSOR_BUF_NULL (0) // This buffer is not in used
|
||||
#define NNOM_TENSOR_BUF_TEMP (1) // The memory in IO is temporary occupided, can be reused by other layer once the computation is done.
|
||||
#define NNOM_TENSOR_BUF_RESERVED (2) // the mem is reserve for this layer only (not to be reused by other layer.
|
||||
|
||||
// currently used in compiling.
|
||||
#define NNOM_BUF_EMPTY (0)
|
||||
#define NNOM_BUF_FILLED (1)
|
||||
|
||||
// basic types
|
||||
#define nnom_qformat_param_t int32_t // this should match the backend, need a better way to do it.
|
||||
#define nnom_shape_data_t uint16_t
|
||||
|
||||
typedef struct _nnom_3d_shape_t
|
||||
{
|
||||
nnom_shape_data_t h, w, c;
|
||||
} nnom_3d_shape_t;
|
||||
|
||||
typedef struct _nnom_border_t
|
||||
{
|
||||
nnom_shape_data_t top, bottom, left, right;
|
||||
} nnom_border_t;
|
||||
|
||||
// nnom_3d_shape_axis_t type provide the axis[] format access to nnom_3d_shape_t
|
||||
typedef union {
|
||||
nnom_3d_shape_t s;
|
||||
nnom_shape_data_t axis[sizeof(nnom_3d_shape_t) / sizeof(nnom_shape_data_t)];
|
||||
} nnom_3d_shape_axis_t;
|
||||
|
||||
// tensor quantisation types
|
||||
typedef enum
|
||||
{
|
||||
NNOM_QTYPE_PER_TENSOR = 0,
|
||||
NNOM_QTYPE_PER_AXIS = 1
|
||||
} nnom_qtype_t;
|
||||
|
||||
typedef struct _nnom_weights
|
||||
{
|
||||
const void *p_value;
|
||||
nnom_qformat_param_t shift;
|
||||
} nnom_weight_t;
|
||||
|
||||
typedef struct _nnom_bias
|
||||
{
|
||||
const void *p_value;
|
||||
nnom_qformat_param_t shift;
|
||||
} nnom_bias_t;
|
||||
|
||||
// experimental
|
||||
typedef struct _nnom_tensor_t
|
||||
{
|
||||
void* p_data; // value
|
||||
nnom_shape_data_t *dim; // dimension of this tensor
|
||||
nnom_qformat_param_t *q_dec; // number of decimal bit for Q format (scale)
|
||||
nnom_qformat_param_t *q_offset; // offset for each channel
|
||||
nnom_qtype_t qtype; // the quantisation type
|
||||
uint8_t num_dim; // the number of dimension
|
||||
uint8_t bitwidth; // the data bit width, only support 8bit now
|
||||
} nnom_tensor_t;
|
||||
|
||||
// nn wrappers
|
||||
typedef struct _nnom_layer_t nnom_layer_t;
|
||||
typedef struct _nnom_layer_io_t nnom_layer_io_t;
|
||||
typedef struct _nnom_layer_hook_t nnom_layer_hook_t;
|
||||
typedef struct _nnom_mem_block_t nnom_mem_block_t;
|
||||
|
||||
// activation wrapper
|
||||
typedef struct _nnom_activation_t nnom_activation_t;
|
||||
|
||||
typedef struct _nnom_buf
|
||||
{
|
||||
nnom_mem_block_t *mem;
|
||||
size_t size;
|
||||
uint8_t type;
|
||||
} nnom_buf_t;
|
||||
|
||||
// a memory block to store pre-assign memories during compiling. then assigned to each tensor after.
|
||||
struct _nnom_mem_block_t
|
||||
{
|
||||
void *blk; // data block location
|
||||
size_t size; // the maximum size for this block
|
||||
uint8_t owners; // how many layers own this block
|
||||
uint8_t state; // empty? filled? for static nn, currently only used in compiling
|
||||
};
|
||||
|
||||
typedef struct _nnom_stat_t
|
||||
{
|
||||
size_t macc; //num. of mac operation
|
||||
uint32_t time;
|
||||
} nnom_layer_stat_t;
|
||||
|
||||
struct _nnom_layer_hook_t
|
||||
{
|
||||
nnom_layer_io_t *io; // hooked io
|
||||
nnom_layer_hook_t *next; // next hook include secondary hooked layer
|
||||
};
|
||||
|
||||
struct _nnom_layer_io_t
|
||||
{
|
||||
nnom_layer_hook_t hook; // for example: (layer->out)--hook--(layer->in)
|
||||
nnom_layer_io_t *aux; // point to auxilary I/O (multiple I/O layer)
|
||||
nnom_tensor_t *tensor; // experimental
|
||||
nnom_mem_block_t *mem; // memory blocks handles for compiling only. The memory are now pass by tensor. trying to remove it.
|
||||
nnom_layer_t *owner; // which layer owns this io.
|
||||
uint8_t type;
|
||||
};
|
||||
|
||||
// structured configuration base type
|
||||
typedef struct _nnom_layer_config_t
|
||||
{
|
||||
char* name; // the name of the layer prequantiesd model (the model trained by user before converted to nnom)
|
||||
} nnom_layer_config_t;
|
||||
|
||||
// layers base
|
||||
struct _nnom_layer_t
|
||||
{
|
||||
nnom_layer_t *shortcut; // shortcut points to the next layer, applied on compiling
|
||||
|
||||
nnom_status_t (*run)(nnom_layer_t *layer); // run method. required
|
||||
nnom_status_t (*build)(nnom_layer_t *layer); // compute output buffer shape. can be left null, will call default_build()
|
||||
nnom_status_t (*free)(nnom_layer_t *layer); // a callback to free private resources (comp buf not included) can be left null
|
||||
nnom_buf_t *comp; // computational buf
|
||||
nnom_activation_t *actail; // I have an activation, I have a tail, wooo haaaa, act-tail!!!
|
||||
|
||||
nnom_layer_config_t *config; // point to the configuration of the layers. for machine api only.
|
||||
nnom_layer_type_t type; // layer types
|
||||
nnom_layer_io_t *in; // IO buff, last*layer, states
|
||||
nnom_layer_io_t *out; // IO buff, next*layer, states
|
||||
nnom_layer_stat_t stat; // stats, timing, ops
|
||||
};
|
||||
|
||||
// activation base
|
||||
struct _nnom_activation_t
|
||||
{
|
||||
nnom_status_t (*run)(struct _nnom_activation_t *act);
|
||||
nnom_tensor_t *tensor;
|
||||
nnom_activation_type_t type;
|
||||
};
|
||||
|
||||
// local static functions when libc is not available
|
||||
#ifdef NNOM_USING_STATIC_MEMORY
|
||||
void nnom_set_static_buf(void* buf, size_t size);
|
||||
void *nnom_malloc(size_t size);
|
||||
void nnom_free(void* p);
|
||||
#endif //NNOM_USING_STATIC_BUF
|
||||
|
||||
typedef struct _nnom_model nnom_model_t;
|
||||
|
||||
#include "nnom_tensor.h"
|
||||
#include "nnom_layers.h"
|
||||
#include "nnom_utils.h"
|
||||
|
||||
// models, I dont want to make model class as a child of layer class yet
|
||||
struct _nnom_model
|
||||
{
|
||||
nnom_layer_t *head;
|
||||
nnom_layer_t *tail;
|
||||
|
||||
// model constructor
|
||||
nnom_status_t (*add)(struct _nnom_model *m, nnom_layer_t *layer); // has too pass a raw value
|
||||
nnom_layer_t *(*hook)(nnom_layer_t *curr, nnom_layer_t *last); // create hook between 2 layers' primary IO.
|
||||
nnom_layer_t *(*merge)(nnom_layer_t *method, nnom_layer_t *in1, nnom_layer_t *in2); // an older interface of merge 2 inputs.
|
||||
nnom_layer_t *(*mergex)(nnom_layer_t *method, int num, ...); // merge a few layers using mutiple input method (concate, add, ...)
|
||||
nnom_layer_t *(*active)(nnom_activation_t *act, nnom_layer_t *target_layer); // add the activation to the existing layer's tail
|
||||
|
||||
// callback
|
||||
nnom_status_t (*layer_callback)(nnom_model_t *m, nnom_layer_t *layer); // layer callback will be called after each layer(after actail).
|
||||
|
||||
// block memory for layers
|
||||
nnom_mem_block_t blocks[NNOM_BLOCK_NUM];
|
||||
|
||||
size_t total_ops;
|
||||
|
||||
bool is_inited; // is this structure initialized
|
||||
bool is_allocated; // is this structure allocated by nnom (not by user)
|
||||
};
|
||||
|
||||
#define NNOM_NULL_CHECK(p) \
|
||||
if ((p) == NULL) \
|
||||
{ \
|
||||
NNOM_LOG("Error: NULL object.\n"); \
|
||||
return NN_ARGUMENT_ERROR; \
|
||||
}
|
||||
|
||||
|
||||
// utils
|
||||
size_t nnom_alignto(size_t value, uint32_t alignment);
|
||||
size_t nnom_io_length(nnom_layer_io_t *io);
|
||||
size_t nnom_hook_length(nnom_layer_hook_t *hook);
|
||||
|
||||
// memory (malloc + memeset 0)
|
||||
void *nnom_mem(size_t size);
|
||||
|
||||
// get how much memory has been taken
|
||||
size_t nnom_mem_stat(void);
|
||||
|
||||
// Model APIs
|
||||
// create or init a model
|
||||
nnom_model_t *new_model(nnom_model_t *m);
|
||||
// compile as sequencial model
|
||||
nnom_status_t sequencial_compile(nnom_model_t *m);
|
||||
// compile as functional model
|
||||
nnom_status_t model_compile(nnom_model_t *m, nnom_layer_t *input, nnom_layer_t *output);
|
||||
// run a prediction
|
||||
nnom_status_t model_run(nnom_model_t *m);
|
||||
// delete model.
|
||||
void model_delete(nnom_model_t *m);
|
||||
// check version
|
||||
nnom_status_t check_model_version(unsigned long model_version);
|
||||
|
||||
// callback, called after each layer has finished the calculation.
|
||||
// this callback must return NN_SUCCESS for continually run the model. otherwise, model will be returned with the ERROR code.
|
||||
// this function return NN_LENGTH_ERROR if the callback is already set to other.
|
||||
nnom_status_t model_set_callback(nnom_model_t *m, nnom_status_t (*layer_callback)(nnom_model_t *m, nnom_layer_t *layer));
|
||||
// delete callback.
|
||||
void model_delete_callback(nnom_model_t *m);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __NNOM_H__ */
|
Reference in New Issue
Block a user