Skip to main content
LMain.1
Associate II
May 15, 2023
Solved

How can I handle multiple outputs networks?

  • May 15, 2023
  • 8 replies
  • 2812 views

The approach described on the official XCUBE-AI documentation is not working for my case.

    This topic has been closed for replies.
    Best answer by jean-michel.d

    Hello,

    According the shared code, I think that you have an issue when you initalize the ai_buffer handlers before to call the ai_network_run() function. The passed @ are not correct, "in_data" and "out_data" are the array of pointer, code should be modified as follow:

     aiConvertInputFloat_2_Int8(input, in_data);
    	/* ai_inputs[0].data = AI_HANDLE_PTR(&in_data[0]); */
     ai_inputs[0].data = AI_HANDLE_PTR(in_data[0]);
     
    	/* Update the AI output handlers */
    	for (int i=0; i < AI_NETWORK_OUT_NUM; i++) {
     /* ai_outputs[i].data = AI_HANDLE_PTR(&out_data[i]); */
     ai_outputs[i].data = AI_HANDLE_PTR(out_data[i]);
     }

    Note that is also recommended to align the IO buffer:

    /* C-table to store the @ of the input buffer */
    AI_ALIGNED(32)
    static ai_i8 in_data[AI_NETWORK_IN_1_SIZE];
     
    /* AI input/output handlers */
    static ai_buffer *ai_inputs;
    static ai_buffer *ai_outputs;
     
    /* data buffer for the output buffers */
    AI_ALIGNED(32)
    static ai_i8 out_1_data[AI_NETWORK_OUT_1_SIZE];
    AI_ALIGNED(32)
    static ai_i8 out_2_data[AI_NETWORK_OUT_2_SIZE];

    Best,

    Jean-Michel

    8 replies

    fauvarque.daniel
    ST Employee
    May 15, 2023

    can you describe what is not working in your case ?

    Regards

    Daniel

    LMain.1
    LMain.1Author
    Associate II
    May 15, 2023

    I am not able to correctly initialize the output buffer. When I try to access its size or meta_info to dequantize the output, I get random values...

    LMain.1
    LMain.1Author
    Associate II
    May 16, 2023

    Now the code is working, but I always get 0 as output.

    Here is my code:

    /* Global handle to reference the instance of the NN */
    static ai_handle network = AI_HANDLE_NULL;
    static ai_u8 activations[AI_NETWORK_DATA_ACTIVATIONS_SIZE];
     
    /* C-table to store the @ of the input buffer */
    static ai_i8 in_data[AI_NETWORK_IN_1_SIZE];
     
    /* AI input/output handlers */
    static ai_buffer *ai_inputs;
    static ai_buffer *ai_outputs;
     
    /* data buffer for the output buffers */
    static ai_i8 out_1_data[AI_NETWORK_OUT_1_SIZE];
    static ai_i8 out_2_data[AI_NETWORK_OUT_2_SIZE];
     
    /* C-table to store the @ of the output buffers */
    static ai_i8* out_data[AI_NETWORK_OUT_NUM] = {
     &out_1_data[0],
     &out_2_data[0]
    };
     
    /*
     * Init function to create and initialize a NN.
     */
    void aiInit(void) {
    	/* 1 - Create and initialize network */
    	ai_error err;
     
    	const ai_handle act_addr[] = { activations };
     
    	err = ai_network_create_and_init(&network, act_addr, NULL);
     
    	if (err.type != AI_ERROR_NONE) {
    		printf("ai_network_create error - type=%d code=%d\r\n", err.type, err.code);
    	}
     
    	/* 2 - Retrieve IO network infos */
    	ai_inputs = ai_network_inputs_get(network, NULL);
    	ai_outputs = ai_network_outputs_get(network, NULL);
    }
     
     
    void aiConvertInputFloat_2_Int8(ai_float *in_f32, ai_i8 *out_int8)
    {
    	ai_buffer_format format = ai_inputs->format;
    	ai_size size = ai_inputs->size;
     
    	ai_float scale = 0.0;
    	ai_float zero_point = 0;
     
    	if (AI_BUFFER_FMT_TYPE_Q != AI_BUFFER_FMT_GET_TYPE(format) &&\
    			! AI_BUFFER_FMT_GET_SIGN(format) && 8 != AI_BUFFER_FMT_GET_BITS(format))
    	{
    		printf("E: expected signed integer 8 bits\r\n");
    		return;
    	}
     
    	if (AI_BUFFER_META_INFO_INTQ(ai_inputs->meta_info))
    	{
    		scale = AI_BUFFER_META_INFO_INTQ_GET_SCALE(ai_inputs->meta_info, 0);
    		if (scale != 0.0F) {
    			scale= 1.0F/scale ;
    		}
    		else {
    			printf("E: division by zero\r\n");
    			return;
    		}
    		zero_point = AI_BUFFER_META_INFO_INTQ_GET_ZEROPOINT(ai_inputs->meta_info, 0);
    	}
    	else {
    		printf("E: no meta info\r\n");
    		return;
    	}
     
    	for (uint32_t i = 0; i < size ; i++) {
    		out_int8[i] = __SSAT((int32_t) roundf(zero_point + in_f32[i]*scale), 8);
    	}
    }
     
     
    void aiConvertOutputInt8_2_Float(ai_i8 *in_int8, ai_float *out_f32)
    {
    	ai_buffer_format format = ai_outputs->format;
    	ai_size size = ai_outputs->size;
     
    	ai_float scale = 0.0;
    	ai_float zero_point = 0;
     
    	if (AI_BUFFER_FMT_TYPE_Q != AI_BUFFER_FMT_GET_TYPE(format) &&\
    			! AI_BUFFER_FMT_GET_SIGN(format) && 8 != AI_BUFFER_FMT_GET_BITS(format))
    	{
    		printf("E: expected signed integer 8 bits\r\n");
    		return;
    	}
     
    	if (AI_BUFFER_META_INFO_INTQ(ai_outputs->meta_info))
    	{
    		scale = AI_BUFFER_META_INFO_INTQ_GET_SCALE(ai_outputs->meta_info, 0);
    		zero_point = AI_BUFFER_META_INFO_INTQ_GET_ZEROPOINT(ai_outputs->meta_info, 0);
    	}
    	else {
    		printf("E: no meta info\r\n");
    		return;
    	}
     
    	for (uint32_t i = 0; i < size ; i++) {
    		out_f32[i] = scale * ((ai_float)(in_int8[i]) - zero_point);
    	}
    }
     
     
    uint8_t roundInt(float32_t cnnOutput)
    {
    	if (cnnOutput > 0.5) {
    		return 1;
    	}
    	else {
    		return 0;
    	}
    }
     
     
    uint8_t aiArgmax(ai_i8 *cnnOutput)
    {
    	/* ArgMax to associate NN output with the most likely classification label */
    	uint8_t prediction = 0;
    	ai_i8 max_out = cnnOutput[0];
     
    	for (uint8_t i = 1; i < AI_NETWORK_OUT_2_SIZE; i++) {
    		if (cnnOutput[i] > max_out) {
    			max_out = cnnOutput[i];
    			prediction = i;
    		}
    	}
     
    	return prediction;
    }
     
     
    void multi_task_network(float32_t *input, uint8_t *detectPrediction, uint8_t *classPrediction)
    {
     ai_i32 nbatch;
     ai_error err;
     float32_t detectProb = 0.0;
     
     aiConvertInputFloat_2_Int8(input, in_data);
    	ai_inputs[0].data = AI_HANDLE_PTR(&in_data[0]);
     
    	/* Update the AI output handlers */
    	for (int i=0; i < AI_NETWORK_OUT_NUM; i++) {
     ai_outputs[i].data = AI_HANDLE_PTR(&out_data[i]);
     }
     
     nbatch = ai_network_run(network, ai_inputs, ai_outputs);
     
     if (nbatch != 1) {
     	err = ai_network_get_error(network);
     	printf("AI ai_network_run error - type=%d code=%d\r\n", err.type, err.code);
     }
     
    	// Detection: convert back to float (probability)
    	aiConvertOutputInt8_2_Float(out_1_data, &detectProb);
    	*detectPrediction = roundInt(detectProb);
     
    	// Classification: take the argmax of the int8 output
    	*classPrediction = aiArgmax(out_2_data);
    }

    fauvarque.daniel
    ST Employee
    May 17, 2023

    When you generated the network with X-Cube-AI or the stm32ai command line did you use the allocate input and output in the activation buffer option ?

    I see in your case that you allocate the input / output outside, in that case you should not use the --allocate-inputs or --allocate-output options. This option is set by default in STM32CubeMX / X-Cube-AI and can be deselected in the advanced settings of the network (toothed wheel icon on the top right of the network parameters)

    Regards

    Daniel

    LMain.1
    LMain.1Author
    Associate II
    May 17, 2023

    Even trying without the allocate input and output in the activation buffer option does not solve the issue. I keep getting 0 outputs for both the output layers

    jean-michel.dBest answer
    ST Employee
    May 17, 2023

    Hello,

    According the shared code, I think that you have an issue when you initalize the ai_buffer handlers before to call the ai_network_run() function. The passed @ are not correct, "in_data" and "out_data" are the array of pointer, code should be modified as follow:

     aiConvertInputFloat_2_Int8(input, in_data);
    	/* ai_inputs[0].data = AI_HANDLE_PTR(&in_data[0]); */
     ai_inputs[0].data = AI_HANDLE_PTR(in_data[0]);
     
    	/* Update the AI output handlers */
    	for (int i=0; i < AI_NETWORK_OUT_NUM; i++) {
     /* ai_outputs[i].data = AI_HANDLE_PTR(&out_data[i]); */
     ai_outputs[i].data = AI_HANDLE_PTR(out_data[i]);
     }

    Note that is also recommended to align the IO buffer:

    /* C-table to store the @ of the input buffer */
    AI_ALIGNED(32)
    static ai_i8 in_data[AI_NETWORK_IN_1_SIZE];
     
    /* AI input/output handlers */
    static ai_buffer *ai_inputs;
    static ai_buffer *ai_outputs;
     
    /* data buffer for the output buffers */
    AI_ALIGNED(32)
    static ai_i8 out_1_data[AI_NETWORK_OUT_1_SIZE];
    AI_ALIGNED(32)
    static ai_i8 out_2_data[AI_NETWORK_OUT_2_SIZE];

    Best,

    Jean-Michel

    ST Employee
    May 17, 2023

    Oops, for the ai_inputs, code was correct.

     aiConvertInputFloat_2_Int8(input, in_data);
     ai_inputs[0].data = AI_HANDLE_PTR(&in_data[0]);

    LMain.1
    LMain.1Author
    Associate II
    May 17, 2023

    Hello,

    Thank you so much! This solved the issue!

    I definitely got confused with pointers.