|
| 1 | +# Builtin operator code extension in TensorFlow Lite |
| 2 | + |
| 3 | +Status | Accepted |
| 4 | +:------------ | :---------------------------------- |
| 5 | +**Author(s) ** | Jae sung Chung ( [email protected]) |
| 6 | +**Sponsor ** | Jared Duke ( [email protected]) |
| 7 | +**Updated** | 2020-09-01 |
| 8 | + |
| 9 | +## Objective |
| 10 | + |
| 11 | +This RFC proposes a FlatBuffer schema change in TensorFlow Lite, which extends |
| 12 | +the builtin operator code in the `OperatorCode` table section to resolve the |
| 13 | +shortage problem in the builtin operator code. Even though Flatbuffer is an |
| 14 | +internal details of the TensorFlow Lite, some advanced users, who write |
| 15 | +visualization tools or third-party delegates, depend on reading Flatbuffer |
| 16 | +schema directly via C++ API. This RFC focuses on the solution for the shortage |
| 17 | +problem and its compatibility issues. |
| 18 | + |
| 19 | +### Goals: |
| 20 | + |
| 21 | +* Discuss how to resolve the builtin operator code shortage problem in |
| 22 | + TensorFlow Lite Flatbuffer schema without breaking backward-compatibility of |
| 23 | + the existing TensorFlow Lite Flatbuffer schema version 3 files in future |
| 24 | + versions of the TensorFlow Lite library. |
| 25 | + |
| 26 | +## Background |
| 27 | + |
| 28 | +A byte type is used for builtin operator code in Flatbuffer, so only 127 builtin |
| 29 | +operators can be added. TensorFlow Lite builtin operator library already reached |
| 30 | +126 operators. |
| 31 | + |
| 32 | +``` |
| 33 | +enum BuiltinOperator : byte { |
| 34 | + ... |
| 35 | +} |
| 36 | +``` |
| 37 | + |
| 38 | +TensorFlow Lite keeps extending to support more domains in ML and users require |
| 39 | +more builtin operators to increase ML model coverage on device, for example Hash |
| 40 | +table support and so on. |
| 41 | + |
| 42 | +### Compatibility issues |
| 43 | + |
| 44 | +TensorFlow Lite has used its Flatbuffer schema version 3 since 2018 and a lot of |
| 45 | +TensorFlow Lite models can be found over the TensorFlow project sites. |
| 46 | +Supporting the schema version 3 is critical for future versions of TensorFlow |
| 47 | +Lite. |
| 48 | + |
| 49 | +The compatibility issue of newly generated models, that will contain new builtin |
| 50 | +operators after this proposal's change, from old TensorFlow Lite libraries won't |
| 51 | +be a problem because the TensorFlow Lite library's version always should be the |
| 52 | +same or the newer than the TensorFlow Lite Converter API's version for builtin |
| 53 | +operator availability. |
| 54 | + |
| 55 | +## Proposal |
| 56 | + |
| 57 | +### Schema changes |
| 58 | + |
| 59 | +For resolving compatibility issues easily, the old builtin operator code will |
| 60 | +remain and the new builtin operator code will be added into the last position as |
| 61 | +follows. |
| 62 | + |
| 63 | +``` |
| 64 | +enum BuiltinOperator : int32 { |
| 65 | + ... // Existing builtin operator codes |
| 66 | + PLACEHOLDER_FOR_GREATER_OP_CODES = 127, |
| 67 | + // New builtin operators will be added in here. |
| 68 | + BROADCAST_TO = 128 |
| 69 | +} |
| 70 | +
|
| 71 | +// An OperatorCode can be an enum value (BuiltinOperator) if the operator is a |
| 72 | +// builtin, or a string if the operator is custom. |
| 73 | +table OperatorCode { |
| 74 | + // This field is for backward compatibility. This field will be used when |
| 75 | + // the value of the extended builtin_code field has less than |
| 76 | + // BulitinOperator_PLACEHOLDER_FOR_GREATER_OP_CODES. |
| 77 | + deprecated_builtin_code:byte; |
| 78 | + custom_code:string; |
| 79 | +
|
| 80 | + // The version of the operator. The version need to be bumped whenever new |
| 81 | + // parameters are introduced into an op. |
| 82 | + version:int = 1; |
| 83 | +
|
| 84 | + // This field is introduced for resolving op builtin operator code shortage problem |
| 85 | + // (the original BuiltinOperator enum field was represented as a byte). |
| 86 | + // This field will be used when the value of the extended builtin_code field |
| 87 | + // is greater than BuiltinOperator_PLACEHOLDER_FOR_GREATER_OP_CODES. |
| 88 | + builtin_code:BuiltinOperator; |
| 89 | +} |
| 90 | +
|
| 91 | +``` |
| 92 | + |
| 93 | +### How to Read `builtin_code` |
| 94 | + |
| 95 | +The builtin operator code value will co-exist in the two fields. Old schema |
| 96 | +version 3 models will have the default value in the new field. For those old |
| 97 | +schema models, the `deprecated_builtin_code` field should be read when the |
| 98 | +default value is set in the new `builtin_code` field. |
| 99 | + |
| 100 | +``` |
| 101 | + BuiltinOperator builtin_code = (op_code->builtin_code ? op_code->builtin_code |
| 102 | + : static_cast<BuiltinOperator>(op_code->deprecated_builtin_code)); |
| 103 | +``` |
| 104 | + |
| 105 | +#### Handling compatibility issues |
| 106 | + |
| 107 | +| | Old .tflite model | New .tflite model | |
| 108 | +| ------------------ | ---------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- | |
| 109 | +| Old TFLite runtime | OK<br>(Current behavior) | OK<br>(As long as the model is not using<br>any *new* builtin ops. If it is, it will<br>fail as it would when using any<br> other unsupported op.) | |
| 110 | +| New TFLite runtime | OK<br>(Uses the legacy builtin op field) | OK<br>(Uses the new builtin op field) | |
| 111 | + |
| 112 | +The details will be described in the following sections. |
| 113 | + |
| 114 | +##### Supporting schema version 3 models |
| 115 | + |
| 116 | +The TensorFlow Lite library built after the proposal will read the existing |
| 117 | +`deprecated_builtin_code` field from old model files. |
| 118 | + |
| 119 | +The new `builtin_code` field is not available in the version 3 models. Flatbuffer |
| 120 | +library will feed zero value, which is the default value in the version 3a schema. The |
| 121 | +actual builtin operator code value will exist in the deprecated, renamed |
| 122 | +`deprecated_builtin_code` field. |
| 123 | + |
| 124 | +##### Compatibility with old TensorFlow Lite libraries |
| 125 | + |
| 126 | +The TensorFlow Lite library built before the proposal will read the existing |
| 127 | +`deprecated_builtin_code` field. However, it will not handle new operations |
| 128 | +declared above 127. This is intended behavior, as it doesn't have a definition |
| 129 | +for the higher op values anyway. |
| 130 | + |
| 131 | +### Schema version |
| 132 | + |
| 133 | +Schema version will remain as before since we are not going to break backward |
| 134 | +compatibility in the schema level. However, in the viewpoint of the Flatbuffer |
| 135 | +tool, renaming a field is treated as compatibility breakage even though the |
| 136 | +runtime code could handle schema formats before and after both. The schema |
| 137 | +change can be regarded as a minor version. The schema version "3a" can be an |
| 138 | +option. |
| 139 | + |
| 140 | +### New utils for schema manipulation |
| 141 | + |
| 142 | +After the proposal is landed, the codes, that read builtin operator code from |
| 143 | +TensorFlow Lite Flatbuffer, requires accessing both `builtin_code` and |
| 144 | +`deprecated_builtin_code` fields. |
| 145 | + |
| 146 | +To avoid redundant logics in a lot of places, the RFC proposes the following |
| 147 | +helper functions in the new C++ library, `tensorflow/lite/schema:schema_utils`. |
| 148 | + |
| 149 | +``` |
| 150 | +BuiltinOperator GetBuiltinCode(const OperatorCode *op_code); |
| 151 | +BuiltinOperator GetBuiltinCode(const OperatorCodeT *op_code); |
| 152 | +``` |
| 153 | + |
| 154 | +The above library also provides the following `OperatorCode` table object |
| 155 | +creation methods for backward compatibility. |
| 156 | + |
| 157 | +These are manually copied from the flatbuffer generated code from schema version 3. |
| 158 | +They serve as overloads for the version 3a's CreateOperatorCode functions in |
| 159 | +schema_generated.h and enable code that still assumes flatbuffer schema version 3 to be |
| 160 | +unchanged with the inclusion of the schema_utils header. |
| 161 | + |
| 162 | +``` |
| 163 | +flatbuffersf:Offset<OperatorCode> CreateOperatorCode( |
| 164 | + flatbuffers::FlatBufferBuilder &_fbb, |
| 165 | + tflite::BuiltinOperator builtin_code = tflite::BuiltinOperator_ADD, |
| 166 | + flatbuffers::Offset<flatbuffers::String> custom_code = 0, |
| 167 | + int32_t version = 1); |
| 168 | +
|
| 169 | +flatbuffers::Offset<OperatorCode> CreateOperatorCodeDirect( |
| 170 | + flatbuffers::FlatBufferBuilder &_fbb, |
| 171 | + tflite::BuiltinOperator builtin_code = tflite::BuiltinOperator_ADD, |
| 172 | + const char *custom_code = nullptr, int32_t version = 1); |
| 173 | +``` |
0 commit comments