Add tflite_convert scripts into tflkit (#3544)
author윤지영/동작제어Lab(SR)/Engineer/삼성전자 <jy910.yun@samsung.com>
Tue, 13 Nov 2018 01:51:24 +0000 (10:51 +0900)
committer박세희/동작제어Lab(SR)/Principal Engineer/삼성전자 <saehie.park@samsung.com>
Tue, 13 Nov 2018 01:51:24 +0000 (10:51 +0900)
* Add tflite_convert scripts into tflkit

tflite_convert tool will convert a TensorFlow GraphDef into TfLite model.

Signed-off-by: Jiyoung Yun <jy910.yun@samsung.com>
* Add check routine for bazel

Signed-off-by: Jiyoung Yun <jy910.yun@samsung.com>
* Change the PB_PATH to GRAPHDEF_PATH and add detail usages

Signed-off-by: Jiyoung Yun <jy910.yun@samsung.com>
* Add detail usage to README.md

Signed-off-by: Jiyoung Yun <jy910.yun@samsung.com>
* Fixed wrong exitcode

Signed-off-by: Jiyoung Yun <jy910.yun@samsung.com>
tools/tflkit/README.md
tools/tflkit/info/convert.template [new file with mode: 0644]
tools/tflkit/tflite_convert.sh [new file with mode: 0755]

index 2a2769e..c0c7c1a 100644 (file)
@@ -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 (file)
index 0000000..88637de
--- /dev/null
@@ -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 (executable)
index 0000000..1694c3c
--- /dev/null
@@ -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
+}