From 227d7cebd3517783d7669fb2764bc933dbc4f643 Mon Sep 17 00:00:00 2001 From: =?utf8?q?=EC=9C=A4=EC=A7=80=EC=98=81/=EB=8F=99=EC=9E=91=EC=A0=9C?= =?utf8?q?=EC=96=B4Lab=28SR=29/Engineer/=EC=82=BC=EC=84=B1=EC=A0=84?= =?utf8?q?=EC=9E=90?= Date: Tue, 13 Nov 2018 10:51:24 +0900 Subject: [PATCH] Add tflite_convert scripts into tflkit (#3544) * Add tflite_convert scripts into tflkit tflite_convert tool will convert a TensorFlow GraphDef into TfLite model. Signed-off-by: Jiyoung Yun * Add check routine for bazel Signed-off-by: Jiyoung Yun * Change the PB_PATH to GRAPHDEF_PATH and add detail usages Signed-off-by: Jiyoung Yun * Add detail usage to README.md Signed-off-by: Jiyoung Yun * Fixed wrong exitcode Signed-off-by: Jiyoung Yun --- tools/tflkit/README.md | 52 ++++++++++++++ tools/tflkit/info/convert.template | 6 ++ tools/tflkit/tflite_convert.sh | 136 +++++++++++++++++++++++++++++++++++++ 3 files changed, 194 insertions(+) create mode 100644 tools/tflkit/info/convert.template create mode 100755 tools/tflkit/tflite_convert.sh diff --git a/tools/tflkit/README.md b/tools/tflkit/README.md index 2a2769e..c0c7c1a 100644 --- a/tools/tflkit/README.md +++ b/tools/tflkit/README.md @@ -109,3 +109,55 @@ Number of all operator types: 6 SOFTMAX : 1 (instrs: 4,003) Number of all operators : 126 (total instrs: 11,484,469,630) ``` + +## Convert a TensorFlow model into TfLite model + +### TensorFlow + +TensorFlow provides some kinds of converting guideline. In Python, the [TFLiteConverter](https://www.tensorflow.org/api_docs/python/tf/contrib/lite/TFLiteConverter) class will help you to convert a TensorFlow GraphDef or SavedModel into `output_format` using TOCO. The `output_format` can be `TFLITE` or `GRAPHVIZ_DOT` format. The default `output_format` is `TFLITE`. And there is a Python command line interface for running TOCO, and its name is [tflite_convert](https://github.com/tensorflow/tensorflow/blob/master/tensorflow/lite/python/tflite_convert.py). This converts a TensorFlow GraphDef or SavedModel into `TFLITE` or `GRAPHVIZ_DOT` format like [TFLiteConverter](https://www.tensorflow.org/api_docs/python/tf/contrib/lite/TFLiteConverter). These two way also supports to convert a TensorFlow Keras model into `output_format`. Both functions are implemented using a tool called TOCO. + +### with tflkit + +The tflkit uses the [tflite_convert](https://github.com/tensorflow/tensorflow/blob/master/tensorflow/lite/python/tflite_convert.py) python command line interface to convert a TensorFlow model into TfLite model. It only supports to convert a TensorFlow GraphDef file into `TFLITE` format file. This tool supports the creation of individual `TFLITE` files for different input shapes. When converting to multiple `TFLITE` files, it needs to put a string called `NAME` in `TFLITE_PATH`. The string `NAME` will be replaced by what is listed in teh `NAME` environment. This tool requires an information file as a parameter. There is an [example file](info/convert.template) for a convert information. The `--tensorflow_path` and `--tensorflow_version` can change the TensorFlow location. By default, it uses `externals/tensorflow` directory. + +Convert information: + * GRAPHDEF_PATH : Full filepath of file containing frozen TensorFlow GraphDef. + * TFLITE_PATH : Full filepath of the output TfLite model. If `NAME` optional environment is used, it must include `NAME` string in the file name. (ex. `[...]/inception_v3_NAME.tflite`) + * INPUT : Names of the input arrays, comma-separated. + * INPUT_SHAPE : Shapes correspoding to `INPUT`, colon-separated. + For the creation of individual `TFLITE` files for different input shapes, space-separated. + * OUTPUT : Names of the output arrays, comma-seperated. + * NAME(Optional) : Names of the individual `TFLITE` files for different input shapes, space-seperated. + +Usage (for example, [InceptionV3](https://storage.googleapis.com/download.tensorflow.org/models/tflite/model_zoo/upload_20180427/inception_v3_2018_04_27.tgz)): +``` +$ wget https://storage.googleapis.com/download.tensorflow.org/models/tflite/model_zoo/upload_20180427/inception_v3_2018_04_27.tgz +$ tar xzvf inception_v3_2018_04_27.tgz ./inception_v3.pb +$ cat > convert.info << END +GRAPHDEF_PATH="${PWD}/inception_v3.pb" +TFLITE_PATH="${PWD}/inception_v3.tflite" +INPUT="input" +INPUT_SHAPE="1,299,299,3" +OUTPUT="InceptionV3/Predictions/Reshape_1" +#NAME="" +END +$ ./tflite_convert.sh --info=./convert.info --tensorflow_version=1.12 +$ ls *.tflite +inception_v3.tflite +``` + +Usage (for example, multiple `TFLITE` files): +``` +$ cat > convert_multiple.info << END +GRAPHDEF_PATH="${PWD}/inception_v3.pb" +TFLITE_PATH="${PWD}/inception_v3_NAME.tflite" +INPUT="input" +INPUT_SHAPE="1,299,299,3 3,299,299,3" +OUTPUT="InceptionV3/Predictions/Reshape_1" +NAME="batch1 batch3" +END +$ ./tflite_convert.sh --info=./convert_multiple.info --tensorflow_version=1.12 +$ ls *.tflite +inception_v3_batch1.tflite +inception_v3_batch3.tflite +``` diff --git a/tools/tflkit/info/convert.template b/tools/tflkit/info/convert.template new file mode 100644 index 0000000..88637de --- /dev/null +++ b/tools/tflkit/info/convert.template @@ -0,0 +1,6 @@ +GRAPHDEF_PATH= +TFLITE_PATH= +INPUT= +INPUT_SHAPE= +OUTPUT= +#NAME= diff --git a/tools/tflkit/tflite_convert.sh b/tools/tflkit/tflite_convert.sh new file mode 100755 index 0000000..1694c3c --- /dev/null +++ b/tools/tflkit/tflite_convert.sh @@ -0,0 +1,136 @@ +#!/bin/bash + +usage() +{ + echo "usage : $0" + echo " --info=Information file" + echo " --tensorflow_path=TensorFlow path (Use externals/tensorflow by default)" + echo " --tensorflow_version=TensorFlow version (Must be entered)" +} + +SCRIPT_PATH="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" + +TF_DIR="${SCRIPT_PATH}/../../externals/tensorflow" + +for i in "$@" +do + case $i in + --info=*) + INFO=${i#*=} + ;; + --tensorflow_path=*) + TF_DIR=${i#*=} + ;; + --tensorflow_version=*) + TF_VERSION=${i#*=} + ;; + -h|--help) + usage + exit 0 + ;; + *) + usage + exit 1 + ;; + esac + shift +done + +if [ -z "$INFO" ]; then + echo "INFO is unset or set to the empty string" + usage + exit 1 +fi +if [ -z "$TF_DIR" ]; then + echo "tensorflow_path is unset or set to the empty string" + usage + exit 1 +fi +if [ -z "$TF_VERSION" ]; then + echo "tensorflow_version is unset or set to the empty string" + usage + exit 1 +fi + +if [ ! -x "$(command -v bazel)" ]; then + echo "Cannot find bazel. Please install bazel." + exit 1 +fi + +source $INFO + +if [ -z "$GRAPHDEF_PATH" ]; then + echo "GRAPHDEF_PATH is unset or set to the empty string" + echo "Update the $INFO file" + exit 1 +fi +if [ -z "$TFLITE_PATH" ]; then + echo "TFLITE_PATH is unset or set to the empty string" + echo "Update the $INFO file" + exit 1 +fi +if [ -z "$INPUT" ]; then + echo "INPUT is unset or set to the empty string" + echo "Update the $INFO file" + exit 1 +fi +if [ -z "$INPUT_SHAPE" ]; then + echo "INPUT_SHAPE is unset or set to the empty string" + echo "Update the $INFO file" + exit 1 +fi +if [ -z "$OUTPUT" ]; then + echo "OUTPUT is unset or set to the empty string" + echo "Update the $INFO file" + exit 1 +fi + +CUR_DIR=$(pwd) +{ + echo "Enter $TF_DIR" + pushd $TF_DIR > /dev/null + + NAME_LIST=() + INPUT_SHAPE_LIST=() + if [ -z "$NAME" ]; then + NAME_LIST+=("$TFLITE_PATH") + INPUT_SHAPE_LIST+=("$INPUT_SHAPE") + else + for name in $NAME; do + NAME_LIST[${#NAME_LIST[*]}]="${TFLITE_PATH/NAME/$name}" + done + for shape in $INPUT_SHAPE; do + INPUT_SHAPE_LIST[${#INPUT_SHAPE_LIST[*]}]="$shape" + done + if (( ${#NAME_LIST[*]} != ${#INPUT_SHAPE_LIST[*]} )); then + echo "The number of NAME and INPUT_SHAPE are different" + echo "Update the $INFO file" + exit 1 + fi + fi + + for (( i=0; i < ${#NAME_LIST[@]}; ++i )); do + if [ "${TF_VERSION%%.*}" = "2" ]; then + bazel run tensorflow/lite/python:tflite_convert -- \ + --output_file="${NAME_LIST[$i]}" \ + --graph_def_file="$GRAPHDEF_PATH" \ + --input_arrays="$INPUT" \ + --input_shapes="${INPUT_SHAPE_LIST[$i]}" \ + --output_arrays="$OUTPUT" \ + --allow_custom_ops=true + else + bazel run tensorflow/contrib/lite/python:tflite_convert -- \ + --output_file="${NAME_LIST[$i]}" \ + --graph_def_file="$GRAPHDEF_PATH" \ + --input_arrays="$INPUT" \ + --input_shapes="${INPUT_SHAPE_LIST[$i]}" \ + --output_arrays="$OUTPUT" \ + --allow_custom_ops=true + fi + done + popd + + for (( i=0; i < ${#NAME_LIST[@]}; ++i )); do + echo "OUTPUT FILE : ${NAME_LIST[$i]}" + done +} -- 2.7.4