Enable loading realistic model inputs from external images/files Currently all model inputs are arbitrarily random. We here add support for allowing loading realistic model input from external images/files. Only person_detection and mobilenet are supported for now. Change-Id: Ib189ff9735dd656a63d01d2c5b90123942ae7c60
diff --git a/CMakeLists.txt b/CMakeLists.txt index 201fcab..9c41392 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt
@@ -74,5 +74,6 @@ include($ENV{ROOTDIR}/toolchain/iree/build_tools/cmake/iree_copts.cmake) include(springbok_bytecode_module) +include(iree_model_input) # Add the included directory here. add_subdirectory(samples)
diff --git a/build_tools/gen_mlmodel_input.py b/build_tools/gen_mlmodel_input.py new file mode 100755 index 0000000..5bd8918 --- /dev/null +++ b/build_tools/gen_mlmodel_input.py
@@ -0,0 +1,55 @@ +#!/usr/bin/env python3 +"""Generate ML model inputs from external images.""" +import argparse +import os +import sys +import struct +import urllib.request + +import numpy as np +from PIL import Image + + +parser = argparse.ArgumentParser( + description='Generate inputs for ML models.') +parser.add_argument('--i', dest='input_name', + help='Model input image name', required=True) +parser.add_argument('--o', dest='output_file', + help='Output binary name', required=True) +parser.add_argument('--s', dest='input_shape', + help='Model input shape (example: "1, 224, 224, 3")', required=True) +parser.add_argument('--q', dest='is_quant', action='store_true', + help='Indicate it is quant model (default: False)') +parser.add_argument('--u', dest='img_url', help='Input image URL') +args = parser.parse_args() + + +def write_binary_file(file_path, input, is_quant): + with open(file_path, "wb+") as file: + for d in input: + if is_quant: + file.write(struct.pack("<B", d)) + else: + file.write(struct.pack("<f", d)) + + +def gen_mlmodel_input(input_name, output_file, input_shape, is_quant, img_url): + if not os.path.exists(input_name): + urllib.request.urlretrieve(img_url, input_name) + if len(input_shape) < 3: + raise ValueError("Input shape < 3 dimensions") + resized_img = Image.open(input_name).resize( + (input_shape[1], input_shape[2])) + input = np.array(resized_img).reshape(np.prod(input_shape)) + if not is_quant: + input = 2.0 / 255.0 * input - 1 + write_binary_file(output_file, input, is_quant) + + +if __name__ == '__main__': + # convert input shape to a list + input_shape = [int(x) for x in args.input_shape.split(',')] + # remove whitespace in image URL if any + img_url = args.img_url.replace(' ', '') + gen_mlmodel_input(args.input_name, args.output_file, + input_shape, args.is_quant, img_url)
diff --git a/cmake/iree_model_input.cmake b/cmake/iree_model_input.cmake new file mode 100644 index 0000000..9d66348 --- /dev/null +++ b/cmake/iree_model_input.cmake
@@ -0,0 +1,66 @@ +include(CMakeParseArguments) + +# iree_model_input() +# +# CMake function to load an external model input (an image) +# and convert to the iree_c_embed_data. +# +# Parameters: +# NAME: Name of model input image. +# SHAPE: Input shape. +# SRC: Input image URL. +# QUANT: When added, indicate it's a quant model. +# +# Examples: +# iree_model_input( +# NAME +# person_detection_quant_input +# SHAPE +# "1, 96, 96, 1" +# SRC +# "https://github.com/tensorflow/tflite-micro/raw/aeac6f39e5c7475cea20c54e86d41e3a38312546/ \ +# tensorflow/lite/micro/examples/person_detection/testdata/person.bmp" +# QUANT +# ) +# +function(iree_model_input) + cmake_parse_arguments( + _RULE + "QUANT" + "NAME;SHAPE;SRC" + "" + ${ARGN} + ) + + get_filename_component(EXT_STR "${_RULE_SRC}" LAST_EXT) + set(_GEN_INPUT_SCRIPT "${CMAKE_SOURCE_DIR}/build_tools/gen_mlmodel_input.py") + set(_OUTPUT_BINARY ${_RULE_NAME}.bin) + set(_ARGS) + list(APPEND _ARGS "--i=${_RULE_NAME}${EXT_STR}") + list(APPEND _ARGS "--o=${_OUTPUT_BINARY}") + list(APPEND _ARGS "--s=${_RULE_SHAPE}") + list(APPEND _ARGS "--u=${_RULE_SRC}") + if(_RULE_QUANT) + list(APPEND _ARGS "--q") + endif() + + add_custom_command( + OUTPUT + ${_OUTPUT_BINARY} + COMMAND + ${_GEN_INPUT_SCRIPT} ${_ARGS} + ) + + iree_c_embed_data( + NAME + "${_RULE_NAME}_c" + GENERATED_SRCS + "${_OUTPUT_BINARY}" + C_FILE_OUTPUT + "${_RULE_NAME}_c.c" + H_FILE_OUTPUT + "${_RULE_NAME}_c.h" + FLATTEN + PUBLIC + ) +endfunction()
diff --git a/samples/float_model_embedding/CMakeLists.txt b/samples/float_model_embedding/CMakeLists.txt index 8d3ef0a..fbd36b3 100644 --- a/samples/float_model_embedding/CMakeLists.txt +++ b/samples/float_model_embedding/CMakeLists.txt
@@ -145,6 +145,34 @@ endif(${BUILD_INTERNAL_MODELS}) #------------------------------------------------------------------------------- +# Binaries to execute the IREE model input +#------------------------------------------------------------------------------- + +iree_model_input( + NAME + mobilenet_input + SHAPE + "1, 224, 224, 3" + SRC + "https://storage.googleapis.com/download.tensorflow.org/ \ + example_images/YellowLabradorLooking_new.jpg" +) + +if(${BUILD_INTERNAL_MODELS}) + +iree_model_input( + NAME + person_detection_input + SHAPE + "1, 96, 96, 1" + SRC + "https://github.com/tensorflow/tflite-micro/raw/aeac6f39e5c7475cea20c54e86d41e3a38312546/ \ + tensorflow/lite/micro/examples/person_detection/testdata/person.bmp" +) + +endif(${BUILD_INTERNAL_MODELS}) + +#------------------------------------------------------------------------------- # Binaries to execute the MLIR bytecode modules #------------------------------------------------------------------------------- @@ -165,6 +193,7 @@ "mobilenet_v1.c" DEPS ::mobilenet_v1_bytecode_module_dylib_c + ::mobilenet_input_c samples::util::util LINKOPTS "LINKER:--defsym=__stack_size__=100k" @@ -241,6 +270,7 @@ "person_detection.c" DEPS ::person_detection_bytecode_module_dylib_c + ::person_detection_input_c samples::util::util LINKOPTS "LINKER:--defsym=__stack_size__=100k"
diff --git a/samples/float_model_embedding/mobilenet_v1.c b/samples/float_model_embedding/mobilenet_v1.c index 0472dde..7535395 100644 --- a/samples/float_model_embedding/mobilenet_v1.c +++ b/samples/float_model_embedding/mobilenet_v1.c
@@ -9,6 +9,7 @@ #include "samples/util/util.h" // Compiled module embedded here to avoid file IO: +#include "samples/float_model_embedding/mobilenet_input_c.h" #include "samples/float_model_embedding/mobilenet_v1_bytecode_module_dylib_c.h" const MlModel kModel = { @@ -34,14 +35,8 @@ iree_status_t load_input_data(const MlModel *model, void **buffer) { iree_status_t result = alloc_input_buffer(model, buffer); - // Populate initial value - srand(33333333); - if (iree_status_is_ok(result)) { - for (int i = 0; i < model->input_length[0]; ++i) { - int x = rand(); - ((float *)*buffer)[i] = (float)x / (float)RAND_MAX; - } - } + const struct iree_file_toc_t *input_file_toc = mobilenet_input_c_create(); + memcpy(*buffer, input_file_toc->data, input_file_toc->size); return result; } @@ -56,5 +51,16 @@ } LOG_INFO("Output #%d data length: %d", index_output, mapped_memory->contents.data_length / model->output_size_bytes); + // find the label index with best prediction + float best_out = 0.0; + int best_idx = -1; + for (int i = 0; i < model->output_length[index_output]; ++i) { + float out = ((float *)mapped_memory->contents.data)[i]; + if (out > best_out) { + best_out = out; + best_idx = i; + } + } + LOG_INFO("Image prediction result is: id: %d", best_idx + 1); return result; }
diff --git a/samples/float_model_embedding/person_detection.c b/samples/float_model_embedding/person_detection.c index 19cfb4f..a892093 100644 --- a/samples/float_model_embedding/person_detection.c +++ b/samples/float_model_embedding/person_detection.c
@@ -10,6 +10,7 @@ // Compiled module embedded here to avoid file IO: #include "samples/float_model_embedding/person_detection_bytecode_module_dylib_c.h" +#include "samples/float_model_embedding/person_detection_input_c.h" const MlModel kModel = { .num_input = 1, @@ -34,14 +35,9 @@ iree_status_t load_input_data(const MlModel *model, void **buffer) { iree_status_t result = alloc_input_buffer(model, buffer); - // Populate initial value - srand(44444444); - if (iree_status_is_ok(result)) { - for (int i = 0; i < model->input_length[0]; ++i) { - int x = rand(); - ((float *)*buffer)[i] = (float)x / (float)RAND_MAX; - } - } + const struct iree_file_toc_t *input_file_toc = + person_detection_input_c_create(); + memcpy(*buffer, input_file_toc->data, input_file_toc->size); return result; } @@ -56,5 +52,15 @@ } LOG_INFO("Output #%d data length: %d", index_output, mapped_memory->contents.data_length / model->output_size_bytes); + + float *data = (float *)mapped_memory->contents.data; + char buffer[20]; + int chars_needed = float_to_str(0, NULL, data[0]); + float_to_str(chars_needed, buffer, data[0]); + LOG_INFO("Output: Non-person Score: %s", buffer); + chars_needed = float_to_str(0, NULL, data[1]); + float_to_str(chars_needed, buffer, data[1]); + LOG_INFO("Output: Person Score: %s", buffer); + return result; }
diff --git a/samples/quant_model_embedding/CMakeLists.txt b/samples/quant_model_embedding/CMakeLists.txt index cca899d..f728e9e 100644 --- a/samples/quant_model_embedding/CMakeLists.txt +++ b/samples/quant_model_embedding/CMakeLists.txt
@@ -147,6 +147,32 @@ endif(${BUILD_INTERNAL_MODELS}) #------------------------------------------------------------------------------- +# Binaries to execute the IREE model input +#------------------------------------------------------------------------------- + +iree_model_input( + NAME + mobilenet_quant_input + SHAPE + "1, 224, 224, 3" + SRC + "https://storage.googleapis.com/download.tensorflow.org/ \ + example_images/YellowLabradorLooking_new.jpg" + QUANT +) + +iree_model_input( + NAME + person_detection_quant_input + SHAPE + "1, 96, 96, 1" + SRC + "https://github.com/tensorflow/tflite-micro/raw/aeac6f39e5c7475cea20c54e86d41e3a38312546/ \ + tensorflow/lite/micro/examples/person_detection/testdata/person.bmp" + QUANT +) + +#------------------------------------------------------------------------------- # Binaries to execute the MLIR bytecode modules #------------------------------------------------------------------------------- @@ -179,6 +205,7 @@ "mobilenet_v1.c" DEPS ::mobilenet_v1_bytecode_module_dylib_c + ::mobilenet_quant_input_c samples::util::util LINKOPTS "LINKER:--defsym=__stack_size__=100k" @@ -191,6 +218,7 @@ "person_detection.c" DEPS ::person_detection_bytecode_module_dylib_c + ::person_detection_quant_input_c samples::util::util LINKOPTS "LINKER:--defsym=__stack_size__=128k"
diff --git a/samples/quant_model_embedding/mobilenet_v1.c b/samples/quant_model_embedding/mobilenet_v1.c index 3570a4b..2fe8509 100644 --- a/samples/quant_model_embedding/mobilenet_v1.c +++ b/samples/quant_model_embedding/mobilenet_v1.c
@@ -9,6 +9,7 @@ #include "samples/util/util.h" // Compiled module embedded here to avoid file IO: +#include "samples/quant_model_embedding/mobilenet_quant_input_c.h" #include "samples/quant_model_embedding/mobilenet_v1_bytecode_module_dylib_c.h" const MlModel kModel = { @@ -34,13 +35,9 @@ iree_status_t load_input_data(const MlModel *model, void **buffer) { iree_status_t result = alloc_input_buffer(model, buffer); - // Populate initial value - srand(33333333); - if (iree_status_is_ok(result)) { - for (int i = 0; i < model->input_length[0]; ++i) { - ((uint8_t *)*buffer)[i] = (uint8_t)rand(); - } - } + const struct iree_file_toc_t *input_file_toc = + mobilenet_quant_input_c_create(); + memcpy(*buffer, input_file_toc->data, input_file_toc->size); return result; } @@ -55,5 +52,16 @@ } LOG_INFO("Output #%d data length: %d", index_output, mapped_memory->contents.data_length / model->output_size_bytes); + // find the label index with best prediction + int best_out = 0; + int best_idx = -1; + for (int i = 0; i < model->output_length[index_output]; ++i) { + uint8_t out = ((uint8_t *)mapped_memory->contents.data)[i]; + if (out > best_out) { + best_out = out; + best_idx = i; + } + } + LOG_INFO("Image prediction result is: id: %d", best_idx + 1); return result; }
diff --git a/samples/quant_model_embedding/person_detection.c b/samples/quant_model_embedding/person_detection.c index 0cd1a09..819fc1e 100644 --- a/samples/quant_model_embedding/person_detection.c +++ b/samples/quant_model_embedding/person_detection.c
@@ -10,6 +10,7 @@ // Compiled module embedded here to avoid file IO: #include "samples/quant_model_embedding/person_detection_bytecode_module_dylib_c.h" +#include "samples/quant_model_embedding/person_detection_quant_input_c.h" const MlModel kModel = { .num_input = 1, @@ -34,13 +35,9 @@ iree_status_t load_input_data(const MlModel *model, void **buffer) { iree_status_t result = alloc_input_buffer(model, buffer); - // Populate initial value - srand(44444444); - if (iree_status_is_ok(result)) { - for (int i = 0; i < model->input_length[0]; ++i) { - ((int8_t *)*buffer)[i] = (int8_t)rand(); - } - } + const struct iree_file_toc_t *input_file_toc = + person_detection_quant_input_c_create(); + memcpy(*buffer, input_file_toc->data, input_file_toc->size); return result; } @@ -55,5 +52,7 @@ } LOG_INFO("Output #%d data length: %d", index_output, mapped_memory->contents.data_length / model->output_size_bytes); + int8_t *data = (int8_t *)mapped_memory->contents.data; + LOG_INFO("Output: Non-person Score: %d; Person Score: %d", data[0], data[1]); return result; }