diff --git a/compiler/circle-inspect/driver/Driver.cpp b/compiler/circle-inspect/driver/Driver.cpp index 6371261db87..465f4171824 100644 --- a/compiler/circle-inspect/driver/Driver.cpp +++ b/compiler/circle-inspect/driver/Driver.cpp @@ -37,6 +37,7 @@ int entry(int argc, char **argv) arser.add_argument("--constants").nargs(0).help("Dump constant tensors name"); arser.add_argument("--op_version").nargs(0).help("Dump versions of the operators in circle file"); arser.add_argument("--tensor_dtype").nargs(0).help("Dump dtype of tensors"); + arser.add_argument("--tensor_shape").nargs(0).help("Dump shape of tensors"); arser.add_argument("circle").help("Circle file to inspect"); try @@ -51,7 +52,7 @@ int entry(int argc, char **argv) } if (!arser["--operators"] && !arser["--conv2d_weight"] && !arser["--op_version"] && - !arser["--tensor_dtype"] && !arser["--constants"]) + !arser["--tensor_dtype"] && !arser["--constants"] && !arser["--tensor_shape"]) { std::cout << "At least one option must be specified" << std::endl; std::cout << arser; @@ -70,6 +71,8 @@ int entry(int argc, char **argv) dumps.push_back(std::make_unique()); if (arser["--constants"]) dumps.push_back(std::make_unique()); + if (arser["--tensor_shape"]) + dumps.push_back(std::make_unique()); std::string model_file = arser.get("circle"); diff --git a/compiler/circle-inspect/src/Dump.cpp b/compiler/circle-inspect/src/Dump.cpp index 9d363e1ffa9..08238736be9 100644 --- a/compiler/circle-inspect/src/Dump.cpp +++ b/compiler/circle-inspect/src/Dump.cpp @@ -240,3 +240,38 @@ void DumpConstants::run(std::ostream &os, const circle::Model *model, const std: } } // namespace circleinspect + +namespace circleinspect +{ + +void DumpTensorShape::run(std::ostream &os, const circle::Model *model, + const std::vector *data) +{ + mio::circle::Reader reader(model, data); + + const uint32_t subgraph_size = reader.num_subgraph(); + + for (uint32_t g = 0; g < subgraph_size; g++) + { + reader.select_subgraph(g); + auto tensors = reader.tensors(); + + for (uint32_t i = 0; i < tensors->size(); ++i) + { + const auto tensor = tensors->Get(i); + auto shape = tensor->shape_signature() ? tensor->shape_signature() : tensor->shape(); + os << reader.tensor_name(tensor) << " ["; + for (uint32_t i = 0; i < shape->size(); i++) + { + os << shape->Get(i); + if (i != shape->size() - 1) + { + os << ","; + } + } + os << "]" << std::endl; + } + } +} + +} // namespace circleinspect diff --git a/compiler/circle-inspect/src/Dump.h b/compiler/circle-inspect/src/Dump.h index 12a43a71001..4959adcf1db 100644 --- a/compiler/circle-inspect/src/Dump.h +++ b/compiler/circle-inspect/src/Dump.h @@ -78,6 +78,15 @@ class DumpConstants final : public DumpInterface void run(std::ostream &os, const circle::Model *model, const std::vector *data); }; +class DumpTensorShape final : public DumpInterface +{ +public: + DumpTensorShape() = default; + +public: + void run(std::ostream &os, const circle::Model *model, const std::vector *data); +}; + } // namespace circleinspect #endif // __DUMP_H__ diff --git a/compiler/circle2circle-dredd-recipe-test/test.lst b/compiler/circle2circle-dredd-recipe-test/test.lst index 0a8d893c43b..0f30bb59c98 100644 --- a/compiler/circle2circle-dredd-recipe-test/test.lst +++ b/compiler/circle2circle-dredd-recipe-test/test.lst @@ -141,3 +141,6 @@ Add(REGRESS_ONNX_Conv_BN_Relu6_001 PASS Add(REGRESS_ONNX_Mul_Mul_000 PASS convert_nchw_to_nhwc) + +# SHAPE INFERENCE test +Add(Inf_Pad_000 PASS) diff --git a/compiler/dredd-rule-lib/README.md b/compiler/dredd-rule-lib/README.md index 348b0aefba9..4f51aa84258 100644 --- a/compiler/dredd-rule-lib/README.md +++ b/compiler/dredd-rule-lib/README.md @@ -21,6 +21,7 @@ Models (input of test) exist in *model repo*, where The following metric functions are provided: - `all_op_count` : the count of operations inside a compiled tflite file - `file_size` : the size of compiled tflite file +- `tensor_shape` : The shape of a specific node in a compiled tflite file. The format looks like `[1,-1,7,2]`(without spaces). - In addition, `op_count`, `conv2d_weight_not_constant`, etc. - Please , refer to [`rule-lib.sh`](rule-lib.sh) for metric functions diff --git a/compiler/dredd-rule-lib/rule-lib.sh b/compiler/dredd-rule-lib/rule-lib.sh index a920e08abc1..3ee94e6881b 100755 --- a/compiler/dredd-rule-lib/rule-lib.sh +++ b/compiler/dredd-rule-lib/rule-lib.sh @@ -252,4 +252,21 @@ const_count() echo ${ACTUAL} } +tensor_shape() +{ + argc_check $# 1 + file_path_check ${COMPILED_FILE} + file_path_check ${INSPECT_PROG_PATH} + + set -o pipefail + + ACTUAL=`init_error_log ; \ + ${INSPECT_PROG_PATH} --tensor_shape ${COMPILED_FILE} | \ + awk -v tensor_name="$1" '{ if ($1 == tensor_name) print $2}'` + + check_success_exit_code $? 0 + + echo ${ACTUAL} +} + # TODO define more qullity test function diff --git a/res/TensorFlowLiteRecipes/Inf_Pad_000/test.recipe b/res/TensorFlowLiteRecipes/Inf_Pad_000/test.recipe new file mode 100644 index 00000000000..f22f13510c0 --- /dev/null +++ b/res/TensorFlowLiteRecipes/Inf_Pad_000/test.recipe @@ -0,0 +1,33 @@ +# padding with dynamic shape, others same as Pad_000 +operand { + name: "ifm" + type: FLOAT32 + shape { dim: 1 dim: 1 dim: 3 dim: 2 } + shape_signature { dim: 1 dim: -1 dim: 3 dim: 2 } +} +operand { + name: "padding" + type: INT64 + shape { dim: 4 dim: 2 } + filler { + tag: "explicit" + arg: "0" arg: "0" + arg: "1" arg: "1" + arg: "2" arg: "2" + arg: "0" arg: "0" + } +} +operand { + name: "ofm" + type: FLOAT32 + shape { dim: 1 dim: 1 dim: 7 dim: 2 } + shape_signature { dim: 1 dim: -1 dim: 7 dim: 2 } +} +operation { + type: "Pad" + input: "ifm" + input: "padding" + output: "ofm" +} +input: "ifm" +output: "ofm" diff --git a/res/TensorFlowLiteRecipes/Inf_Pad_000/test.rule b/res/TensorFlowLiteRecipes/Inf_Pad_000/test.rule new file mode 100644 index 00000000000..dce0a7d5b77 --- /dev/null +++ b/res/TensorFlowLiteRecipes/Inf_Pad_000/test.rule @@ -0,0 +1,5 @@ +# To check if dynamic dimension properly inferred + +RULE "VERIFY_FILE_FORMAT" $(verify_file_format) '=' 1 + +RULE "PAD_SHAPE" $(tensor_shape ofm) '=' [1,-1,7,2]