Update quantize weights to use hybrid per-channel quantization and asymmetric quantization by default
PiperOrigin-RevId: 317783008
Change-Id: I3c0f026ded030407c434bca70fe15d5fe723c744
diff --git a/tensorflow/lite/kernels/register.cc b/tensorflow/lite/kernels/register.cc
index c3a4aaa..452ce35 100644
--- a/tensorflow/lite/kernels/register.cc
+++ b/tensorflow/lite/kernels/register.cc
@@ -54,24 +54,24 @@
AddBuiltin(BuiltinOperator_L2_POOL_2D, Register_L2_POOL_2D());
AddBuiltin(BuiltinOperator_CONV_2D, Register_CONV_2D(),
/* min_version = */ 1,
- /* max_version = */ 4);
+ /* max_version = */ 5);
AddBuiltin(BuiltinOperator_DEPTHWISE_CONV_2D, Register_DEPTHWISE_CONV_2D(),
/* min_version = */ 1,
- /* max_version = */ 5);
+ /* max_version = */ 6);
AddBuiltin(BuiltinOperator_SVDF, Register_SVDF(),
/* min_version = */ 1,
- /* max_version = */ 3);
+ /* max_version = */ 4);
AddBuiltin(BuiltinOperator_RNN, Register_RNN(),
/* min_version = */ 1,
- /* max_version = */ 2);
+ /* max_version = */ 3);
AddBuiltin(BuiltinOperator_BIDIRECTIONAL_SEQUENCE_RNN,
Register_BIDIRECTIONAL_SEQUENCE_RNN(),
/* min_version = */ 1,
- /* max_version = */ 2);
+ /* max_version = */ 3);
AddBuiltin(BuiltinOperator_UNIDIRECTIONAL_SEQUENCE_RNN,
Register_UNIDIRECTIONAL_SEQUENCE_RNN(),
/* min_version = */ 1,
- /* max_version = */ 2);
+ /* max_version = */ 3);
AddBuiltin(BuiltinOperator_EMBEDDING_LOOKUP, Register_EMBEDDING_LOOKUP(),
/* min_version = */ 1,
/* max_version = */ 3);
@@ -79,7 +79,7 @@
Register_EMBEDDING_LOOKUP_SPARSE());
AddBuiltin(BuiltinOperator_FULLY_CONNECTED, Register_FULLY_CONNECTED(),
/* min_version = */ 1,
- /* max_version = */ 8);
+ /* max_version = */ 9);
AddBuiltin(BuiltinOperator_LSH_PROJECTION, Register_LSH_PROJECTION());
AddBuiltin(BuiltinOperator_HASHTABLE_LOOKUP, Register_HASHTABLE_LOOKUP());
AddBuiltin(BuiltinOperator_SOFTMAX, Register_SOFTMAX(),
@@ -105,13 +105,13 @@
AddBuiltin(BuiltinOperator_LOCAL_RESPONSE_NORMALIZATION,
Register_LOCAL_RESPONSE_NORMALIZATION());
AddBuiltin(BuiltinOperator_LSTM, Register_LSTM(), /* min_version = */ 1,
- /* max_version = */ 3);
+ /* max_version = */ 4);
AddBuiltin(BuiltinOperator_BIDIRECTIONAL_SEQUENCE_LSTM,
Register_BIDIRECTIONAL_SEQUENCE_LSTM(), /* min_version = */ 1,
/* max_version = */ 3);
AddBuiltin(BuiltinOperator_UNIDIRECTIONAL_SEQUENCE_LSTM,
Register_UNIDIRECTIONAL_SEQUENCE_LSTM(), /* min_version = */ 1,
- /* max_version = */ 2);
+ /* max_version = */ 3);
AddBuiltin(BuiltinOperator_PAD, Register_PAD(), /* min_version = */ 1,
/* max_version = */ 2);
AddBuiltin(BuiltinOperator_PADV2, Register_PADV2(), /* min_version = */ 1,
diff --git a/tensorflow/lite/tools/optimize/quantize_weights.cc b/tensorflow/lite/tools/optimize/quantize_weights.cc
index 7e3853c..8bef019 100644
--- a/tensorflow/lite/tools/optimize/quantize_weights.cc
+++ b/tensorflow/lite/tools/optimize/quantize_weights.cc
@@ -43,6 +43,12 @@
int32_t op_input_idx;
} ConsumerOpInfo;
+typedef struct {
+ TensorT* t;
+ bool is_per_channel;
+ int channel_dim;
+} TensorPerChannel;
+
// The default minimum number of elements a weights array must have to be
// quantized by this transformation.
const int kWeightsMinNumElementsDefault = 1024;
@@ -138,6 +144,7 @@
}
} else if (builtin_op_code == BuiltinOperator_FULLY_CONNECTED ||
builtin_op_code == BuiltinOperator_CONV_2D ||
+ builtin_op_code == BuiltinOperator_DEPTHWISE_CONV_2D ||
builtin_op_code == BuiltinOperator_SVDF ||
builtin_op_code == BuiltinOperator_RNN ||
builtin_op_code == BuiltinOperator_BIDIRECTIONAL_SEQUENCE_LSTM ||
@@ -181,9 +188,10 @@
// Inserts Tensors for each input tensor of op that should be
// quantized into tensor_map.
TfLiteStatus InsertQuantizableInputTensorsFromOperator(
- const ModelT* model, const OperatorT* op, uint64_t weights_min_num_elements,
+ const ModelT* model, OperatorT* op, uint64_t weights_min_num_elements,
const CustomOpMap& custom_op_map,
- absl::flat_hash_map<int32_t, TensorT*>* tensor_map, int subgraph_index) {
+ absl::flat_hash_map<int32_t, TensorPerChannel>* tensor_map,
+ int subgraph_index) {
SubGraphT* subgraph = model->subgraphs.at(subgraph_index).get();
const OperatorCodeT* op_code = model->operator_codes[op->opcode_index].get();
@@ -222,7 +230,50 @@
continue;
}
- tensor_map->insert({tensor_idx, tensor});
+ if (op_code->builtin_code == BuiltinOperator_DEPTHWISE_CONV_2D) {
+ tensor_map->insert(
+ {tensor_idx, {tensor, /*is_per_channel=*/true, /*dim=*/3}});
+ } else if (op_code->builtin_code == BuiltinOperator_CONV_2D) {
+ tensor_map->insert(
+ {tensor_idx, {tensor, /*is_per_channel=*/true, /*dim=*/0}});
+ } else {
+ switch (op_code->builtin_code) {
+ case BuiltinOperator_BIDIRECTIONAL_SEQUENCE_LSTM:
+ op->builtin_options.AsBidirectionalSequenceLSTMOptions()
+ ->asymmetric_quantize_inputs = true;
+ break;
+ case BuiltinOperator_BIDIRECTIONAL_SEQUENCE_RNN:
+ op->builtin_options.AsBidirectionalSequenceRNNOptions()
+ ->asymmetric_quantize_inputs = true;
+ break;
+ case BuiltinOperator_FULLY_CONNECTED:
+ op->builtin_options.AsFullyConnectedOptions()
+ ->asymmetric_quantize_inputs = true;
+ break;
+ case BuiltinOperator_LSTM:
+ op->builtin_options.AsLSTMOptions()->asymmetric_quantize_inputs =
+ true;
+ break;
+ case BuiltinOperator_RNN:
+ op->builtin_options.AsRNNOptions()->asymmetric_quantize_inputs = true;
+ break;
+ case BuiltinOperator_SVDF:
+ op->builtin_options.AsSVDFOptions()->asymmetric_quantize_inputs =
+ true;
+ break;
+ case BuiltinOperator_UNIDIRECTIONAL_SEQUENCE_LSTM:
+ op->builtin_options.AsUnidirectionalSequenceLSTMOptions()
+ ->asymmetric_quantize_inputs = true;
+ break;
+ case BuiltinOperator_UNIDIRECTIONAL_SEQUENCE_RNN:
+ op->builtin_options.AsSequenceRNNOptions()
+ ->asymmetric_quantize_inputs = true;
+ break;
+ default:
+ break;
+ }
+ tensor_map->insert({tensor_idx, {tensor, /*is_per_channel=*/false}});
+ }
}
return kTfLiteOk;
@@ -275,17 +326,22 @@
void UpdateInt8OperatorVersions(ModelT* model) {
for (int i = 0; i < model->operator_codes.size(); ++i) {
const BuiltinOperator& op_code = model->operator_codes[i]->builtin_code;
- if (op_code == BuiltinOperator_CONV_2D || op_code == BuiltinOperator_SVDF ||
- op_code == BuiltinOperator_RNN ||
+ if (op_code == BuiltinOperator_BIDIRECTIONAL_SEQUENCE_LSTM ||
op_code == BuiltinOperator_BIDIRECTIONAL_SEQUENCE_RNN ||
+ op_code == BuiltinOperator_EMBEDDING_LOOKUP ||
+ op_code == BuiltinOperator_RNN ||
op_code == BuiltinOperator_UNIDIRECTIONAL_SEQUENCE_LSTM ||
op_code == BuiltinOperator_UNIDIRECTIONAL_SEQUENCE_RNN) {
- model->operator_codes[i]->version = 2;
- } else if (op_code == BuiltinOperator_FULLY_CONNECTED ||
- op_code == BuiltinOperator_BIDIRECTIONAL_SEQUENCE_LSTM ||
- op_code == BuiltinOperator_EMBEDDING_LOOKUP ||
- op_code == BuiltinOperator_LSTM) {
model->operator_codes[i]->version = 3;
+ } else if (op_code == BuiltinOperator_LSTM ||
+ op_code == BuiltinOperator_SVDF) {
+ model->operator_codes[i]->version = 4;
+ } else if (op_code == BuiltinOperator_CONV_2D) {
+ model->operator_codes[i]->version = 5;
+ } else if (op_code == BuiltinOperator_DEPTHWISE_CONV_2D) {
+ model->operator_codes[i]->version = 6;
+ } else if (op_code == BuiltinOperator_FULLY_CONNECTED) {
+ model->operator_codes[i]->version = 9;
}
}
}
@@ -354,7 +410,7 @@
++subgraph_index) {
SubGraphT* subgraph = model->subgraphs.at(subgraph_index).get();
- absl::flat_hash_map<int32_t, TensorT*> tensor_map;
+ absl::flat_hash_map<int32_t, TensorPerChannel> tensor_map;
for (int i = 0; i < subgraph->operators.size(); ++i) {
OperatorT* op = subgraph->operators[i].get();
TF_LITE_ENSURE_STATUS(InsertQuantizableInputTensorsFromOperator(
@@ -362,16 +418,22 @@
subgraph_index));
}
- for (std::pair<int32_t, TensorT*> tensor_pair : tensor_map) {
+ for (std::pair<int32_t, TensorPerChannel> tensor_pair : tensor_map) {
// Quantize the tensor.
- TF_LITE_ENSURE_STATUS(
- utils::SymmetricQuantizeTensor(model.get(), tensor_pair.second));
+ if (tensor_pair.second.is_per_channel) {
+ TF_LITE_ENSURE_STATUS(utils::SymmetricQuantizeTensorPerChannel(
+ model.get(), tensor_pair.second.t, tensor_pair.second.channel_dim,
+ nullptr));
+ } else {
+ TF_LITE_ENSURE_STATUS(
+ utils::SymmetricQuantizeTensor(model.get(), tensor_pair.second.t));
+ }
}
// Examine the tensor consumers to determine which require dequantize ops.
for (const auto& tensor_pair : tensor_map) {
int32_t tensor_idx = tensor_pair.first;
- TensorT* tensor = tensor_pair.second;
+ TensorT* tensor = tensor_pair.second.t;
std::vector<ConsumerOpInfo> consumer_op_infos =
GetTensorConsumers(model.get(), subgraph, tensor_idx);
if (IsQuantizationPassThroughOps(model.get(), consumer_op_infos)) {
diff --git a/tensorflow/lite/tools/optimize/quantize_weights_test.cc b/tensorflow/lite/tools/optimize/quantize_weights_test.cc
index 76f2815..2f92a9a 100644
--- a/tensorflow/lite/tools/optimize/quantize_weights_test.cc
+++ b/tensorflow/lite/tools/optimize/quantize_weights_test.cc
@@ -215,6 +215,8 @@
} else if (quant_tensor->buffer() != 0) {
EXPECT_EQ(quant_tensor->type(), TensorType_INT8)
<< quant_tensor->name()->str();
+ auto shape = GetAsVector(quant_tensor->shape());
+ EXPECT_EQ(quant_tensor->quantization()->scale()->size(), shape[0]);
} else {
EXPECT_EQ(quant_tensor->type(), TensorType_FLOAT32);
}
diff --git a/tensorflow/lite/tools/versioning/op_version.cc b/tensorflow/lite/tools/versioning/op_version.cc
index a97b9da..a339976 100644
--- a/tensorflow/lite/tools/versioning/op_version.cc
+++ b/tensorflow/lite/tools/versioning/op_version.cc
@@ -70,10 +70,13 @@
return 3;
}
// If the op is a signed int8 hybrid operation, we need to return
- // version 2.
+ // version 2 or 5 if per channel.
if (op_sig.input_types.at(0) == TensorType_FLOAT32 &&
op_sig.input_types.at(1) == TensorType_INT8 &&
op_sig.output_types.at(0) == TensorType_FLOAT32) {
+ if (op_sig.options.conv_2d.is_per_channel_quantized) {
+ return 5;
+ }
return 2;
}
return 1;
@@ -87,10 +90,13 @@
}
// If the op is a signed int8 hybrid operation, we need to return
- // version 4.
+ // version 4 or 6 if per-channel.
if (op_sig.input_types.at(0) == TensorType_FLOAT32 &&
op_sig.input_types.at(1) == TensorType_INT8 &&
op_sig.output_types.at(0) == TensorType_FLOAT32) {
+ if (op_sig.options.depthwise_conv_2d.is_per_channel_quantized) {
+ return 6;
+ }
return 4;
}
// If the op has signed int8 op_sig.inputs and op_sig.outputs, its
@@ -154,6 +160,10 @@
if (op_sig.input_types.at(0) == TensorType_FLOAT32 &&
op_sig.input_types.at(1) == TensorType_INT8 &&
op_sig.output_types.at(0) == TensorType_FLOAT32) {
+ if (op_sig.options.fully_connected.asymmetric_quantize_inputs) {
+ // This is to use the updated quantization scheme.
+ return 9;
+ }
return 3;
}
// For float and uint8 fixed point kernels, if the weight is
@@ -185,6 +195,10 @@
if (op_sig.input_types.at(0) == TensorType_FLOAT32 &&
op_sig.input_types.at(1) == TensorType_INT8 &&
op_sig.output_types.at(0) == TensorType_FLOAT32) {
+ // This is to use the updated quantization scheme
+ if (op_sig.options.input_quantization.asymmetric_quantize_inputs) {
+ return 4;
+ }
return 2;
}
return 1;
@@ -251,6 +265,9 @@
op_sig.input_types.at(0) == TensorType_FLOAT32 &&
op_sig.input_types.at(2) == TensorType_INT8 &&
op_sig.output_types.at(0) == TensorType_FLOAT32) {
+ if (op_sig.options.lstm.asymmetric_quantize_inputs) {
+ return 4;
+ }
return 3;
}
// KERNEL_BASIC was added in version 2.
@@ -265,6 +282,9 @@
if (op_sig.input_types.at(0) == TensorType_FLOAT32 &&
op_sig.input_types.at(2) == TensorType_INT8 &&
op_sig.output_types.at(0) == TensorType_FLOAT32) {
+ if (op_sig.options.lstm.asymmetric_quantize_inputs) {
+ return 3;
+ }
return 2;
}
return 1;
@@ -450,7 +470,6 @@
return 2;
}
return 1;
-
case BuiltinOperator_TANH:
case BuiltinOperator_LOGISTIC:
if (op_sig.input_types.at(0) == TensorType_INT16 &&
@@ -500,6 +519,19 @@
}
return 1;
+ case BuiltinOperator_RNN:
+ case BuiltinOperator_UNIDIRECTIONAL_SEQUENCE_RNN:
+ case BuiltinOperator_BIDIRECTIONAL_SEQUENCE_RNN:
+ case BuiltinOperator_BIDIRECTIONAL_SEQUENCE_LSTM:
+ if (op_sig.input_types.at(1) == TensorType_INT8 &&
+ op_sig.output_types.at(0) == TensorType_FLOAT32) {
+ if (op_sig.options.input_quantization.asymmetric_quantize_inputs) {
+ return 3;
+ } else {
+ return 2;
+ }
+ }
+ return 1;
case BuiltinOperator_ADD:
case BuiltinOperator_PAD:
case BuiltinOperator_PADV2:
@@ -566,6 +598,16 @@
op_sig.options.depthwise_conv_2d.dilation_h_factor =
conv_option->dilation_h_factor();
}
+ const Tensor* filter_tensor =
+ subgraph->tensors()->Get(op->inputs()->Get(1));
+ const QuantizationParameters* filter_quant =
+ filter_tensor->quantization();
+ int num_channels = filter_tensor->shape()->Get(3);
+ if (filter_quant && filter_quant->scale() &&
+ filter_quant->scale()->Length() &&
+ filter_quant->scale()->Length() == num_channels) {
+ op_sig.options.depthwise_conv_2d.is_per_channel_quantized = true;
+ }
} break;
case BuiltinOperator_FAKE_QUANT: {
@@ -584,6 +626,8 @@
fully_connected_option->keep_num_dims();
op_sig.options.fully_connected.weights_format =
fully_connected_option->weights_format();
+ op_sig.options.fully_connected.asymmetric_quantize_inputs =
+ fully_connected_option->asymmetric_quantize_inputs();
}
const Tensor* weight_tensor =
@@ -644,6 +688,18 @@
op_sig.options.resize.align_corners = resize_nn_option->align_corners();
}
} break;
+ case BuiltinOperator_CONV_2D: {
+ const Tensor* filter_tensor =
+ subgraph->tensors()->Get(op->inputs()->Get(1));
+ const QuantizationParameters* filter_quant =
+ filter_tensor->quantization();
+ int num_channels = filter_tensor->shape()->Get(0);
+ if (filter_quant && filter_quant->scale() &&
+ filter_quant->scale()->Length() &&
+ filter_quant->scale()->Length() == num_channels) {
+ op_sig.options.conv_2d.is_per_channel_quantized = true;
+ }
+ } break;
// TODO(b/150176627): Add tests for GetOpSignature.
case BuiltinOperator_STRIDED_SLICE:
case BuiltinOperator_SPACE_TO_BATCH_ND:
@@ -651,7 +707,6 @@
case BuiltinOperator_TRANSPOSE: {
op_sig.options.single_input_op.num_dims = GetNumDims(subgraph, op, 0);
} break;
-
case BuiltinOperator_SUB:
case BuiltinOperator_DIV:
case BuiltinOperator_MAXIMUM:
diff --git a/tensorflow/lite/tools/versioning/op_version.h b/tensorflow/lite/tools/versioning/op_version.h
index df74ffa..7136200 100644
--- a/tensorflow/lite/tools/versioning/op_version.h
+++ b/tensorflow/lite/tools/versioning/op_version.h
@@ -30,6 +30,7 @@
struct {
int32_t dilation_w_factor;
int32_t dilation_h_factor;
+ bool is_per_channel_quantized;
} depthwise_conv_2d;
struct {
bool narrow_range;
@@ -40,6 +41,7 @@
// TODO(b/156530611): Make this global when more ops support sparse
// computation.
bool sparse_weight;
+ bool asymmetric_quantize_inputs;
} fully_connected;
struct {
float input1_scale;
@@ -48,6 +50,7 @@
} mul;
struct {
LSTMKernelType kernel_type;
+ bool asymmetric_quantize_inputs;
} lstm;
struct {
bool half_pixel_centers;
@@ -60,6 +63,12 @@
int32_t num_dims;
bool need_broadcast;
} broadcast;
+ struct {
+ bool is_per_channel_quantized;
+ } conv_2d;
+ struct {
+ bool asymmetric_quantize_inputs;
+ } input_quantization;
} options;
} OpSignature;
diff --git a/tensorflow/lite/tools/versioning/op_version_test.cc b/tensorflow/lite/tools/versioning/op_version_test.cc
index 4017fc3..e9fd857 100644
--- a/tensorflow/lite/tools/versioning/op_version_test.cc
+++ b/tensorflow/lite/tools/versioning/op_version_test.cc
@@ -361,6 +361,19 @@
fake_op_sig.options.fully_connected = {
false, FullyConnectedOptionsWeightsFormat_DEFAULT, true};
EXPECT_EQ(GetBuiltinOperatorVersion(fake_op_sig), 8);
+
+ fake_op_sig = {
+ .op = BuiltinOperator_FULLY_CONNECTED,
+ .input_types =
+ std::vector<TensorType>{TensorType_FLOAT32, TensorType_INT8,
+ TensorType_FLOAT32},
+ .output_types = std::vector<TensorType>{TensorType_FLOAT32},
+ };
+ fake_op_sig.options.fully_connected = {
+ false, FullyConnectedOptionsWeightsFormat_DEFAULT, false, false};
+ EXPECT_EQ(GetBuiltinOperatorVersion(fake_op_sig), 3);
+ fake_op_sig.options.fully_connected.asymmetric_quantize_inputs = true;
+ EXPECT_EQ(GetBuiltinOperatorVersion(fake_op_sig), 9);
}
TEST(OpVersionTest, VersioningDequantizeTest) {
@@ -412,6 +425,15 @@
.output_types = std::vector<TensorType>{TensorType_FLOAT32},
};
EXPECT_EQ(GetBuiltinOperatorVersion(fake_op_sig), 2);
+
+ fake_op_sig = {
+ .op = BuiltinOperator_CONV_2D,
+ .input_types =
+ std::vector<TensorType>{TensorType_FLOAT32, TensorType_INT8},
+ .output_types = std::vector<TensorType>{TensorType_FLOAT32},
+ };
+ fake_op_sig.options.conv_2d.is_per_channel_quantized = true;
+ EXPECT_EQ(GetBuiltinOperatorVersion(fake_op_sig), 5);
}
TEST(OpVersionTest, VersioningFloorDivOperatorTest) {
@@ -479,6 +501,8 @@
.output_types = std::vector<TensorType>{TensorType_FLOAT32},
};
EXPECT_EQ(GetBuiltinOperatorVersion(fake_op_sig), 2);
+ fake_op_sig.options.input_quantization.asymmetric_quantize_inputs = true;
+ EXPECT_EQ(GetBuiltinOperatorVersion(fake_op_sig), 4);
fake_op_sig = {
.op = BuiltinOperator_SVDF,
@@ -489,6 +513,7 @@
};
EXPECT_EQ(GetBuiltinOperatorVersion(fake_op_sig), 3);
}
+
TEST(OpVersionTest, VersioningDepthwiseConv2DTest) {
OpSignature fake_op_sig = {
.op = BuiltinOperator_DEPTHWISE_CONV_2D,
@@ -497,6 +522,8 @@
.output_types = std::vector<TensorType>{TensorType_FLOAT32},
};
EXPECT_EQ(GetBuiltinOperatorVersion(fake_op_sig), 4);
+ fake_op_sig.options.depthwise_conv_2d.is_per_channel_quantized = true;
+ EXPECT_EQ(GetBuiltinOperatorVersion(fake_op_sig), 6);
fake_op_sig = {
.op = BuiltinOperator_DEPTHWISE_CONV_2D,
diff --git a/tensorflow/lite/tools/versioning/runtime_version.cc b/tensorflow/lite/tools/versioning/runtime_version.cc
index 3697635..efec5a7 100644
--- a/tensorflow/lite/tools/versioning/runtime_version.cc
+++ b/tensorflow/lite/tools/versioning/runtime_version.cc
@@ -63,11 +63,13 @@
{{BuiltinOperator_CONV_2D, 2}, "1.14.0"},
{{BuiltinOperator_CONV_2D, 3}, "1.14.0"},
{{BuiltinOperator_CONV_2D, 4}, kPendingReleaseVersion},
+ {{BuiltinOperator_CONV_2D, 5}, kPendingReleaseVersion},
{{BuiltinOperator_DEPTHWISE_CONV_2D, 1}, "1.5.0"},
{{BuiltinOperator_DEPTHWISE_CONV_2D, 2}, "1.12.0"},
{{BuiltinOperator_DEPTHWISE_CONV_2D, 3}, "1.14.0"},
{{BuiltinOperator_DEPTHWISE_CONV_2D, 4}, "2.2.0"},
{{BuiltinOperator_DEPTHWISE_CONV_2D, 5}, kPendingReleaseVersion},
+ {{BuiltinOperator_DEPTHWISE_CONV_2D, 6}, kPendingReleaseVersion},
{{BuiltinOperator_ADD, 1}, "1.5.0"},
{{BuiltinOperator_ADD, 2}, "1.14.0"},
{{BuiltinOperator_ADD_N, 1}, "1.14.0"},
@@ -102,6 +104,7 @@
{{BuiltinOperator_FULLY_CONNECTED, 6}, "2.1.0"},
{{BuiltinOperator_FULLY_CONNECTED, 7}, kPendingReleaseVersion},
{{BuiltinOperator_FULLY_CONNECTED, 8}, kPendingReleaseVersion},
+ {{BuiltinOperator_FULLY_CONNECTED, 9}, kPendingReleaseVersion},
{{BuiltinOperator_GATHER, 1}, "1.6.0"},
{{BuiltinOperator_GATHER, 2}, "1.14.0"},
{{BuiltinOperator_GATHER, 3}, "1.15.0"},
@@ -111,6 +114,7 @@
{{BuiltinOperator_SVDF, 1}, "1.5.0"},
{{BuiltinOperator_SVDF, 2}, "1.14.0"},
{{BuiltinOperator_SVDF, 3}, "2.2.0"},
+ {{BuiltinOperator_SVDF, 4}, kPendingReleaseVersion},
{{BuiltinOperator_L2_NORMALIZATION, 1}, "1.5.0"},
{{BuiltinOperator_L2_NORMALIZATION, 2}, "1.14.0"},
{{BuiltinOperator_L2_POOL_2D, 1}, "1.5.0"},
@@ -151,13 +155,18 @@
{{BuiltinOperator_LSTM, 1}, "1.7.0"},
{{BuiltinOperator_LSTM, 2}, "1.10.0"},
{{BuiltinOperator_LSTM, 3}, "1.14.0"},
+ {{BuiltinOperator_LSTM, 4}, kPendingReleaseVersion},
{{BuiltinOperator_UNIDIRECTIONAL_SEQUENCE_LSTM, 1}, "1.13.1"},
{{BuiltinOperator_UNIDIRECTIONAL_SEQUENCE_LSTM, 2}, "1.14.0"},
+ {{BuiltinOperator_UNIDIRECTIONAL_SEQUENCE_LSTM, 3},
+ kPendingReleaseVersion},
{{BuiltinOperator_BIDIRECTIONAL_SEQUENCE_LSTM, 1}, "1.14.0"},
{{BuiltinOperator_BIDIRECTIONAL_SEQUENCE_LSTM, 2}, "1.14.0"},
{{BuiltinOperator_BIDIRECTIONAL_SEQUENCE_LSTM, 3}, "1.14.0"},
{{BuiltinOperator_BIDIRECTIONAL_SEQUENCE_RNN, 1}, "1.14.0"},
{{BuiltinOperator_BIDIRECTIONAL_SEQUENCE_RNN, 2}, "1.14.0"},
+ {{BuiltinOperator_BIDIRECTIONAL_SEQUENCE_RNN, 3},
+ kPendingReleaseVersion},
{{BuiltinOperator_MEAN, 1}, "1.6.0"},
{{BuiltinOperator_MEAN, 2}, "1.14.0"},
{{BuiltinOperator_SUM, 1}, "1.10.0"},
@@ -179,6 +188,7 @@
kPendingReleaseVersion},
{{BuiltinOperator_RNN, 1}, "1.5.0"},
{{BuiltinOperator_RNN, 2}, "1.14.0"},
+ {{BuiltinOperator_RNN, 3}, kPendingReleaseVersion},
{{BuiltinOperator_SKIP_GRAM, 1}, "1.5.0"},
{{BuiltinOperator_SQUEEZE, 1}, "1.6.0"},
{{BuiltinOperator_SPLIT, 1}, "1.5.0"},
@@ -233,6 +243,8 @@
{{BuiltinOperator_UNIQUE, 1}, "1.14.0"},
{{BuiltinOperator_UNIDIRECTIONAL_SEQUENCE_RNN, 1}, "1.14.0"},
{{BuiltinOperator_UNIDIRECTIONAL_SEQUENCE_RNN, 2}, "1.14.0"},
+ {{BuiltinOperator_UNIDIRECTIONAL_SEQUENCE_RNN, 3},
+ kPendingReleaseVersion},
{{BuiltinOperator_WHERE, 1}, "1.14.0"},
{{BuiltinOperator_DEQUANTIZE, 1}, "1.13.1"},
{{BuiltinOperator_DEQUANTIZE, 2}, "1.14.0"},