Improve error logs when converting model with control flow. Also update the g3doc to clearly convey that 'control flow' is not currently supported.
PiperOrigin-RevId: 248773536
This commit is contained in:
parent
39e91628f5
commit
5260e5f979
@ -30,8 +30,16 @@ Since the number of TensorFlow Lite operations is smaller than TensorFlow's,
|
|||||||
some inference models may not be able to convert. For unimplemented operations,
|
some inference models may not be able to convert. For unimplemented operations,
|
||||||
take a look at the question on
|
take a look at the question on
|
||||||
[missing operators](faq.md#why-are-some-operations-not-implemented-in-tensorflow-lite).
|
[missing operators](faq.md#why-are-some-operations-not-implemented-in-tensorflow-lite).
|
||||||
Unsupported operators include embeddings and LSTM/RNNs. For conversion issues
|
Unsupported operators include embeddings and LSTM/RNNs. For models with
|
||||||
not related to missing operations, search our
|
LSTM/RNNs, you can also try the experimental API
|
||||||
|
[OpHint](https://www.tensorflow.org/api_docs/python/tf/lite/OpHint) to convert.
|
||||||
|
Models with control flow ops (Switch, Merge, etc) are not convertible at the
|
||||||
|
moment, but we are working on adding support for control flow in Tensorflow
|
||||||
|
Lite, please see
|
||||||
|
[GitHub issues](https://github.com/tensorflow/tensorflow/issues/28485).
|
||||||
|
|
||||||
|
For conversion issues not related to missing operations or control flow ops,
|
||||||
|
search our
|
||||||
[GitHub issues](https://github.com/tensorflow/tensorflow/issues?q=label%3Acomp%3Alite+)
|
[GitHub issues](https://github.com/tensorflow/tensorflow/issues?q=label%3Acomp%3Alite+)
|
||||||
or file a [new one](https://github.com/tensorflow/tensorflow/issues).
|
or file a [new one](https://github.com/tensorflow/tensorflow/issues).
|
||||||
|
|
||||||
|
@ -16,6 +16,7 @@ limitations under the License.
|
|||||||
|
|
||||||
#include "flatbuffers/flexbuffers.h"
|
#include "flatbuffers/flexbuffers.h"
|
||||||
#include "absl/strings/str_join.h"
|
#include "absl/strings/str_join.h"
|
||||||
|
#include "tensorflow/core/lib/core/errors.h"
|
||||||
#include "tensorflow/lite/context.h"
|
#include "tensorflow/lite/context.h"
|
||||||
#include "tensorflow/lite/schema/schema_generated.h"
|
#include "tensorflow/lite/schema/schema_generated.h"
|
||||||
#include "tensorflow/lite/toco/tflite/operator.h"
|
#include "tensorflow/lite/toco/tflite/operator.h"
|
||||||
@ -431,6 +432,31 @@ tensorflow::Status Export(const Model& model, string* output_file_contents,
|
|||||||
return Export(model, output_file_contents, params, ops_by_type);
|
return Export(model, output_file_contents, params, ops_by_type);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ParseControlFlowErrors(std::set<string>* custom_ops,
|
||||||
|
std::vector<string>* error_msgs) {
|
||||||
|
std::set<string> unsupported_control_flow_ops;
|
||||||
|
// Check if unsupported ops contains control flow ops. It's impossible
|
||||||
|
// to implement these ops as custom ops at the moment.
|
||||||
|
for (const auto& op : *custom_ops) {
|
||||||
|
if (IsControlFlowOp(op)) {
|
||||||
|
unsupported_control_flow_ops.insert(op);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!unsupported_control_flow_ops.empty()) {
|
||||||
|
error_msgs->push_back(absl::StrCat(
|
||||||
|
"TensorFlow Lite currently doesn't support control flow ops: ",
|
||||||
|
absl::StrJoin(unsupported_control_flow_ops, ", "), ".",
|
||||||
|
" We are working on supporting control flow ops, please see github "
|
||||||
|
"issue at "
|
||||||
|
"https://github.com/tensorflow/tensorflow/issues/28485."));
|
||||||
|
}
|
||||||
|
// Remove control flow ops from `custom_ops` set so that they won't be
|
||||||
|
// reported again in later messages.
|
||||||
|
for (const auto& op : unsupported_control_flow_ops) {
|
||||||
|
custom_ops->erase(op);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
tensorflow::Status Export(
|
tensorflow::Status Export(
|
||||||
const Model& model, string* output_file_contents,
|
const Model& model, string* output_file_contents,
|
||||||
const ExportParams& params,
|
const ExportParams& params,
|
||||||
@ -483,6 +509,18 @@ tensorflow::Status Export(
|
|||||||
|
|
||||||
if (!custom_ops.empty()) {
|
if (!custom_ops.empty()) {
|
||||||
if (!params.allow_custom_ops) {
|
if (!params.allow_custom_ops) {
|
||||||
|
auto please_report_bug_message = []() {
|
||||||
|
return "We are continually in the process of adding support to "
|
||||||
|
"TensorFlow Lite for more ops. It would be helpful if you could "
|
||||||
|
"inform us of how this conversion went by opening a github "
|
||||||
|
"issue at "
|
||||||
|
"https://github.com/tensorflow/tensorflow/issues/new?template="
|
||||||
|
"40-tflite-op-request.md\n and pasting the following:\n\n";
|
||||||
|
};
|
||||||
|
|
||||||
|
std::vector<string> error_msgs;
|
||||||
|
ParseControlFlowErrors(&custom_ops, &error_msgs);
|
||||||
|
|
||||||
// Remove ExpandDims and ReorderAxes from unimplemented list unless they
|
// Remove ExpandDims and ReorderAxes from unimplemented list unless they
|
||||||
// compose the list. Both ops are removed during graph transformations.
|
// compose the list. Both ops are removed during graph transformations.
|
||||||
// However, if an op is unimplemented earlier in the model, the graph
|
// However, if an op is unimplemented earlier in the model, the graph
|
||||||
@ -499,20 +537,12 @@ tensorflow::Status Export(
|
|||||||
custom_ops_final = custom_ops;
|
custom_ops_final = custom_ops;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto please_report_bug_message = []() {
|
if (!custom_ops_final.empty()) {
|
||||||
return "We are continually in the process of adding support to "
|
|
||||||
"TensorFlow Lite for more ops. It would be helpful if you could "
|
|
||||||
"inform us of how this conversion went by opening a github "
|
|
||||||
"issue at "
|
|
||||||
"https://github.com/tensorflow/tensorflow/issues/new?template="
|
|
||||||
"40-tflite-op-request.md\n and pasting the following:\n\n";
|
|
||||||
};
|
|
||||||
|
|
||||||
if (params.enable_select_tf_ops) {
|
if (params.enable_select_tf_ops) {
|
||||||
return tensorflow::errors::InvalidArgument(absl::StrCat(
|
error_msgs.push_back(absl::StrCat(
|
||||||
please_report_bug_message(),
|
|
||||||
"Some of the operators in the model are not supported by "
|
"Some of the operators in the model are not supported by "
|
||||||
"the standard TensorFlow Lite runtime and are not recognized by "
|
"the standard TensorFlow Lite runtime and are not recognized "
|
||||||
|
"by "
|
||||||
"TensorFlow. If you have a custom "
|
"TensorFlow. If you have a custom "
|
||||||
"implementation for them you can disable this error with "
|
"implementation for them you can disable this error with "
|
||||||
"--allow_custom_ops, or by setting allow_custom_ops=True "
|
"--allow_custom_ops, or by setting allow_custom_ops=True "
|
||||||
@ -523,15 +553,15 @@ tensorflow::Status Export(
|
|||||||
"of operators for which you will need custom implementations: ",
|
"of operators for which you will need custom implementations: ",
|
||||||
absl::StrJoin(custom_ops_final, ", "), "."));
|
absl::StrJoin(custom_ops_final, ", "), "."));
|
||||||
} else {
|
} else {
|
||||||
return tensorflow::errors::InvalidArgument(absl::StrCat(
|
error_msgs.push_back(absl::StrCat(
|
||||||
please_report_bug_message(),
|
|
||||||
"Some of the operators in the model are not supported by "
|
"Some of the operators in the model are not supported by "
|
||||||
"the standard TensorFlow Lite runtime. If those are native "
|
"the standard TensorFlow Lite runtime. If those are native "
|
||||||
"TensorFlow operators, you might be able to use the extended "
|
"TensorFlow operators, you might be able to use the extended "
|
||||||
"runtime by passing --enable_select_tf_ops, or by setting "
|
"runtime by passing --enable_select_tf_ops, or by setting "
|
||||||
"target_ops=TFLITE_BUILTINS,SELECT_TF_OPS when calling "
|
"target_ops=TFLITE_BUILTINS,SELECT_TF_OPS when calling "
|
||||||
"tf.lite.TFLiteConverter(). Otherwise, if you have a "
|
"tf.lite.TFLiteConverter(). Otherwise, if you have a "
|
||||||
"custom implementation for them you can disable this error with "
|
"custom implementation for them you can disable this error "
|
||||||
|
"with "
|
||||||
"--allow_custom_ops, or by setting allow_custom_ops=True "
|
"--allow_custom_ops, or by setting allow_custom_ops=True "
|
||||||
"when calling tf.lite.TFLiteConverter(). Here is a list "
|
"when calling tf.lite.TFLiteConverter(). Here is a list "
|
||||||
"of builtin operators you are using: ",
|
"of builtin operators you are using: ",
|
||||||
@ -541,19 +571,10 @@ tensorflow::Status Export(
|
|||||||
absl::StrJoin(custom_ops_final, ", "), "."));
|
absl::StrJoin(custom_ops_final, ", "), "."));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (!error_msgs.empty()) {
|
||||||
std::set<string> unsupported_control_flow_ops;
|
|
||||||
// Check if unsupported ops contains control flow ops. It's impossible
|
|
||||||
// to implement these ops as custom ops at the moment.
|
|
||||||
for (const auto& op : custom_ops) {
|
|
||||||
if (IsControlFlowOp(op)) {
|
|
||||||
unsupported_control_flow_ops.insert(op);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!unsupported_control_flow_ops.empty()) {
|
|
||||||
return tensorflow::errors::InvalidArgument(absl::StrCat(
|
return tensorflow::errors::InvalidArgument(absl::StrCat(
|
||||||
"TensorFlow Lite currently doesn't support control flow ops: ",
|
please_report_bug_message(), absl::StrJoin(error_msgs, " ")));
|
||||||
absl::StrJoin(unsupported_control_flow_ops, ", "), "."));
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -227,6 +227,123 @@ TEST_F(ExportTest, Export) {
|
|||||||
EXPECT_THAT(ExportAndGetOperatorIndices(params), ElementsAre(1, 0, 2, 3));
|
EXPECT_THAT(ExportAndGetOperatorIndices(params), ElementsAre(1, 0, 2, 3));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(ExportTest, UnsupportedControlFlowErrors) {
|
||||||
|
AddOperatorsByName({"Conv", "Add", "Switch", "Merge"});
|
||||||
|
|
||||||
|
ExportParams params;
|
||||||
|
params.allow_custom_ops = false;
|
||||||
|
|
||||||
|
// The model contains control flow ops which are not convertible, so we should
|
||||||
|
// check the returned error message.
|
||||||
|
|
||||||
|
string output;
|
||||||
|
const auto ops_by_type = BuildOperatorByTypeMap();
|
||||||
|
auto status = Export(input_model_, &output, params, ops_by_type);
|
||||||
|
EXPECT_EQ(status.error_message(),
|
||||||
|
"We are continually in the process of adding support to TensorFlow "
|
||||||
|
"Lite for more ops. It would be helpful if you could inform us of "
|
||||||
|
"how this conversion went by opening a github issue at "
|
||||||
|
"https://github.com/tensorflow/tensorflow/issues/"
|
||||||
|
"new?template=40-tflite-op-request.md\n and pasting the "
|
||||||
|
"following:\n\nTensorFlow Lite currently doesn't support control "
|
||||||
|
"flow ops: Merge, Switch. We are working on supporting control "
|
||||||
|
"flow ops, please see github issue at "
|
||||||
|
"https://github.com/tensorflow/tensorflow/issues/28485.");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(ExportTest, UnsupportedOpsAndNeedEnableFlex) {
|
||||||
|
AddOperatorsByName({"Conv", "Add", "BatchNormWithGlobalNormalization"});
|
||||||
|
|
||||||
|
ExportParams params;
|
||||||
|
params.allow_custom_ops = false;
|
||||||
|
params.enable_select_tf_ops = false;
|
||||||
|
|
||||||
|
string output;
|
||||||
|
const auto ops_by_type = BuildOperatorByTypeMap();
|
||||||
|
auto status = Export(input_model_, &output, params, ops_by_type);
|
||||||
|
EXPECT_EQ(
|
||||||
|
status.error_message(),
|
||||||
|
"We are continually in the process of adding support to TensorFlow Lite "
|
||||||
|
"for more ops. It would be helpful if you could inform us of how this "
|
||||||
|
"conversion went by opening a github issue at "
|
||||||
|
"https://github.com/tensorflow/tensorflow/issues/"
|
||||||
|
"new?template=40-tflite-op-request.md\n and pasting the "
|
||||||
|
"following:\n\nSome of the operators in the model are not supported by "
|
||||||
|
"the standard TensorFlow Lite runtime. If those are native TensorFlow "
|
||||||
|
"operators, you might be able to use the extended runtime by passing "
|
||||||
|
"--enable_select_tf_ops, or by setting "
|
||||||
|
"target_ops=TFLITE_BUILTINS,SELECT_TF_OPS when calling "
|
||||||
|
"tf.lite.TFLiteConverter(). Otherwise, if you have a custom "
|
||||||
|
"implementation for them you can disable this error with "
|
||||||
|
"--allow_custom_ops, or by setting allow_custom_ops=True when calling "
|
||||||
|
"tf.lite.TFLiteConverter(). Here is a list of builtin operators you are "
|
||||||
|
"using: ADD, CONV_2D. Here is a list of operators for which you will "
|
||||||
|
"need custom implementations: BatchNormWithGlobalNormalization.");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(ExportTest, UnsupportedOpsNeedCustomImplementation) {
|
||||||
|
AddOperatorsByName({"Conv", "Add", "MyCustomOp1", "MyCustomOp2"});
|
||||||
|
|
||||||
|
ExportParams params;
|
||||||
|
params.allow_custom_ops = false;
|
||||||
|
params.enable_select_tf_ops = true;
|
||||||
|
|
||||||
|
string output;
|
||||||
|
const auto ops_by_type = BuildOperatorByTypeMap();
|
||||||
|
auto status = Export(input_model_, &output, params, ops_by_type);
|
||||||
|
EXPECT_EQ(
|
||||||
|
status.error_message(),
|
||||||
|
"We are continually in the process of adding support to TensorFlow Lite "
|
||||||
|
"for more ops. It would be helpful if you could inform us of how this "
|
||||||
|
"conversion went by opening a github issue at "
|
||||||
|
"https://github.com/tensorflow/tensorflow/issues/"
|
||||||
|
"new?template=40-tflite-op-request.md\n and pasting the "
|
||||||
|
"following:\n\nSome of the operators in the model are not supported by "
|
||||||
|
"the standard TensorFlow Lite runtime and are not recognized by "
|
||||||
|
"TensorFlow. If you have a custom implementation for them you can "
|
||||||
|
"disable this error with --allow_custom_ops, or by setting "
|
||||||
|
"allow_custom_ops=True when calling tf.lite.TFLiteConverter(). Here is a "
|
||||||
|
"list of builtin operators you are using: ADD, CONV_2D. Here is a list "
|
||||||
|
"of operators for which you will need custom implementations: "
|
||||||
|
"MyCustomOp1, MyCustomOp2.");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(ExportTest, UnsupportedControlFlowAndCustomOpsErrors) {
|
||||||
|
AddOperatorsByName(
|
||||||
|
{"Conv", "Add", "Switch", "Merge", "MyCustomOp1", "MyCustomOp2"});
|
||||||
|
|
||||||
|
ExportParams params;
|
||||||
|
params.allow_custom_ops = false;
|
||||||
|
|
||||||
|
// The model contains control flow ops which are not convertible, so we should
|
||||||
|
// check the returned error message.
|
||||||
|
|
||||||
|
string output;
|
||||||
|
const auto ops_by_type = BuildOperatorByTypeMap();
|
||||||
|
auto status = Export(input_model_, &output, params, ops_by_type);
|
||||||
|
EXPECT_EQ(
|
||||||
|
status.error_message(),
|
||||||
|
"We are continually in the process of adding support to TensorFlow Lite "
|
||||||
|
"for more ops. It would be helpful if you could inform us of how this "
|
||||||
|
"conversion went by opening a github issue at "
|
||||||
|
"https://github.com/tensorflow/tensorflow/issues/"
|
||||||
|
"new?template=40-tflite-op-request.md\n and pasting the "
|
||||||
|
"following:\n\nTensorFlow Lite currently doesn't support control flow "
|
||||||
|
"ops: Merge, Switch. We are working on supporting control flow ops, "
|
||||||
|
"please see github issue at "
|
||||||
|
"https://github.com/tensorflow/tensorflow/issues/28485. Some of the "
|
||||||
|
"operators in the model are not supported by the standard TensorFlow "
|
||||||
|
"Lite runtime. If those are native TensorFlow operators, you might be "
|
||||||
|
"able to use the extended runtime by passing --enable_select_tf_ops, or "
|
||||||
|
"by setting target_ops=TFLITE_BUILTINS,SELECT_TF_OPS when calling "
|
||||||
|
"tf.lite.TFLiteConverter(). Otherwise, if you have a custom "
|
||||||
|
"implementation for them you can disable this error with "
|
||||||
|
"--allow_custom_ops, or by setting allow_custom_ops=True when calling "
|
||||||
|
"tf.lite.TFLiteConverter(). Here is a list of builtin operators you are "
|
||||||
|
"using: ADD, CONV_2D. Here is a list of operators for which you will "
|
||||||
|
"need custom implementations: MyCustomOp1, MyCustomOp2.");
|
||||||
|
}
|
||||||
|
|
||||||
TEST_F(ExportTest, QuantizeWeights) {
|
TEST_F(ExportTest, QuantizeWeights) {
|
||||||
// Sanity check for quantize_weights parameter.
|
// Sanity check for quantize_weights parameter.
|
||||||
BuildQuantizableTestModel();
|
BuildQuantizableTestModel();
|
||||||
@ -335,7 +452,7 @@ TEST_F(OpSetsTest, BuiltinsAndTfSelect) {
|
|||||||
|
|
||||||
// This test is based on a hypothetical scenario that dilation is supported
|
// This test is based on a hypothetical scenario that dilation is supported
|
||||||
// only in Conv version 2. So Toco populates version=1 when dialation
|
// only in Conv version 2. So Toco populates version=1 when dialation
|
||||||
// parameters are all 1, and version=2 otehrwise.
|
// parameters are all 1, and version=2 otherwise.
|
||||||
class FakeConvolutionOperator
|
class FakeConvolutionOperator
|
||||||
: public BuiltinOperator<ConvOperator, ::tflite::Conv2DOptions,
|
: public BuiltinOperator<ConvOperator, ::tflite::Conv2DOptions,
|
||||||
::tflite::BuiltinOptions_Conv2DOptions> {
|
::tflite::BuiltinOptions_Conv2DOptions> {
|
||||||
|
Loading…
Reference in New Issue
Block a user