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,
|
||||
take a look at the question on
|
||||
[missing operators](faq.md#why-are-some-operations-not-implemented-in-tensorflow-lite).
|
||||
Unsupported operators include embeddings and LSTM/RNNs. For conversion issues
|
||||
not related to missing operations, search our
|
||||
Unsupported operators include embeddings and LSTM/RNNs. For models with
|
||||
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+)
|
||||
or file a [new one](https://github.com/tensorflow/tensorflow/issues).
|
||||
|
||||
|
@ -16,6 +16,7 @@ limitations under the License.
|
||||
|
||||
#include "flatbuffers/flexbuffers.h"
|
||||
#include "absl/strings/str_join.h"
|
||||
#include "tensorflow/core/lib/core/errors.h"
|
||||
#include "tensorflow/lite/context.h"
|
||||
#include "tensorflow/lite/schema/schema_generated.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);
|
||||
}
|
||||
|
||||
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(
|
||||
const Model& model, string* output_file_contents,
|
||||
const ExportParams& params,
|
||||
@ -483,6 +509,18 @@ tensorflow::Status Export(
|
||||
|
||||
if (!custom_ops.empty()) {
|
||||
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
|
||||
// compose the list. Both ops are removed during graph transformations.
|
||||
// However, if an op is unimplemented earlier in the model, the graph
|
||||
@ -499,62 +537,45 @@ tensorflow::Status Export(
|
||||
custom_ops_final = 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";
|
||||
};
|
||||
|
||||
if (params.enable_select_tf_ops) {
|
||||
return tensorflow::errors::InvalidArgument(absl::StrCat(
|
||||
please_report_bug_message(),
|
||||
"Some 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: ",
|
||||
absl::StrJoin(builtin_ops, ", "),
|
||||
". Here is a list "
|
||||
"of operators for which you will need custom implementations: ",
|
||||
absl::StrJoin(custom_ops_final, ", "), "."));
|
||||
} else {
|
||||
return tensorflow::errors::InvalidArgument(absl::StrCat(
|
||||
please_report_bug_message(),
|
||||
"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: ",
|
||||
absl::StrJoin(builtin_ops, ", "),
|
||||
". Here is a list "
|
||||
"of operators for which you will need custom implementations: ",
|
||||
absl::StrJoin(custom_ops_final, ", "), "."));
|
||||
if (!custom_ops_final.empty()) {
|
||||
if (params.enable_select_tf_ops) {
|
||||
error_msgs.push_back(absl::StrCat(
|
||||
"Some 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: ",
|
||||
absl::StrJoin(builtin_ops, ", "),
|
||||
". Here is a list "
|
||||
"of operators for which you will need custom implementations: ",
|
||||
absl::StrJoin(custom_ops_final, ", "), "."));
|
||||
} else {
|
||||
error_msgs.push_back(absl::StrCat(
|
||||
"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: ",
|
||||
absl::StrJoin(builtin_ops, ", "),
|
||||
". Here is a list "
|
||||
"of operators for which you will need custom implementations: ",
|
||||
absl::StrJoin(custom_ops_final, ", "), "."));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 (!error_msgs.empty()) {
|
||||
return tensorflow::errors::InvalidArgument(absl::StrCat(
|
||||
please_report_bug_message(), absl::StrJoin(error_msgs, " ")));
|
||||
}
|
||||
}
|
||||
if (!unsupported_control_flow_ops.empty()) {
|
||||
return tensorflow::errors::InvalidArgument(absl::StrCat(
|
||||
"TensorFlow Lite currently doesn't support control flow ops: ",
|
||||
absl::StrJoin(unsupported_control_flow_ops, ", "), "."));
|
||||
}
|
||||
}
|
||||
|
||||
if (!unsupported_flex_ops.empty()) {
|
||||
|
@ -227,6 +227,123 @@ TEST_F(ExportTest, Export) {
|
||||
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) {
|
||||
// Sanity check for quantize_weights parameter.
|
||||
BuildQuantizableTestModel();
|
||||
@ -335,7 +452,7 @@ TEST_F(OpSetsTest, BuiltinsAndTfSelect) {
|
||||
|
||||
// This test is based on a hypothetical scenario that dilation is supported
|
||||
// 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
|
||||
: public BuiltinOperator<ConvOperator, ::tflite::Conv2DOptions,
|
||||
::tflite::BuiltinOptions_Conv2DOptions> {
|
||||
|
Loading…
Reference in New Issue
Block a user