Home | History | Annotate | Download | only in tflite
      1 /* Copyright 2017 The TensorFlow Authors. All Rights Reserved.
      2 
      3 Licensed under the Apache License, Version 2.0 (the "License");
      4 you may not use this file except in compliance with the License.
      5 You may obtain a copy of the License at
      6 
      7     http://www.apache.org/licenses/LICENSE-2.0
      8 
      9 Unless required by applicable law or agreed to in writing, software
     10 distributed under the License is distributed on an "AS IS" BASIS,
     11 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     12 See the License for the specific language governing permissions and
     13 limitations under the License.
     14 ==============================================================================*/
     15 #include "tensorflow/contrib/lite/toco/tflite/export.h"
     16 
     17 #include "flatbuffers/flexbuffers.h"
     18 #include "absl/strings/str_join.h"
     19 #include "tensorflow/contrib/lite/schema/schema_generated.h"
     20 #include "tensorflow/contrib/lite/toco/tflite/operator.h"
     21 #include "tensorflow/contrib/lite/toco/tflite/types.h"
     22 #include "tensorflow/contrib/lite/toco/tooling_util.h"
     23 #include "tensorflow/contrib/lite/version.h"
     24 
     25 namespace toco {
     26 
     27 namespace tflite {
     28 
     29 using flatbuffers::FlatBufferBuilder;
     30 using flatbuffers::Offset;
     31 using flatbuffers::Vector;
     32 using ::tflite::Buffer;
     33 using ::tflite::BuiltinOperator;
     34 using ::tflite::BuiltinOperator_CUSTOM;
     35 using ::tflite::BuiltinOperator_MAX;
     36 using ::tflite::BuiltinOperator_MIN;
     37 using ::tflite::CreateBuffer;
     38 using ::tflite::CreateModel;
     39 using ::tflite::CreateOperator;
     40 using ::tflite::CreateTensor;
     41 using ::tflite::Operator;
     42 using ::tflite::OperatorCode;
     43 using ::tflite::SubGraph;
     44 using ::tflite::Tensor;
     45 
     46 namespace {
     47 
     48 details::OperatorKey GetOperatorKey(const ::toco::Operator& op) {
     49   string custom_code;
     50   if (op.type == OperatorType::kTensorFlowUnsupported) {
     51     const TensorFlowUnsupportedOperator& unsupported_op =
     52         static_cast<const TensorFlowUnsupportedOperator&>(op);
     53     custom_code = unsupported_op.tensorflow_op;
     54   }
     55   return details::OperatorKey(op.type, custom_code);
     56 }
     57 
     58 }  // Anonymous namespace.
     59 
     60 namespace details {
     61 
     62 void LoadTensorsMap(const Model& model, TensorsMap* tensors_map) {
     63   // First find a list of unique array names.
     64   std::set<string> names;
     65   for (const auto& array_pair : model.GetArrayMap()) {
     66     names.insert(array_pair.first);
     67   }
     68 
     69   // Now assign indices to them and fill in the map.
     70   int index = 0;
     71   for (const auto& name : names) {
     72     (*tensors_map)[name] = index;
     73     ++index;
     74   }
     75 }
     76 
     77 void LoadOperatorsMap(const Model& model, OperatorsMap* operators_map) {
     78   // First find a list of unique operator types.
     79   std::set<OperatorKey> keys;
     80   for (const auto& op : model.operators) {
     81     keys.insert(GetOperatorKey(*op));
     82   }
     83   // Now assign indices to them and fill in the map.
     84   int index = 0;
     85   for (const auto& key : keys) {
     86     (*operators_map)[key] = index;
     87     ++index;
     88   }
     89 }
     90 }  // namespace details
     91 
     92 Offset<Vector<Offset<Tensor>>> ExportTensors(
     93     const Model& model, const details::TensorsMap& tensors_map,
     94     FlatBufferBuilder* builder, std::vector<const Array*>* buffers_to_write) {
     95   // In the end we will need to produce a vector sorted by the indices of the
     96   // tensors in the tensors_map.
     97   std::map<int, Offset<Tensor>> ordered_tensors;
     98 
     99   for (const auto& array_pair : model.GetArrayMap()) {
    100     const string& tensor_name = array_pair.first;
    101     const toco::Array& array = *array_pair.second;
    102 
    103     int buffer_index = buffers_to_write->size();
    104     auto type = DataType::Serialize(array.data_type);
    105     buffers_to_write->push_back(&array);
    106 
    107     std::vector<int> shape;
    108     if (array.has_shape()) {
    109       for (int d : array.shape().dims()) {
    110         shape.push_back(d);
    111       }
    112     }
    113 
    114     Offset<Vector<float>> min;
    115     Offset<Vector<float>> max;
    116     Offset<Vector<float>> scale;
    117     Offset<Vector<int64_t>> zero_point;
    118     if (array.minmax) {
    119       min = builder->CreateVector(
    120           std::vector<float>{static_cast<float>(array.minmax->min)});
    121       max = builder->CreateVector(
    122           std::vector<float>{static_cast<float>(array.minmax->max)});
    123     }
    124     if (array.quantization_params) {
    125       scale = builder->CreateVector(std::vector<float>{
    126           static_cast<float>(array.quantization_params->scale)});
    127       zero_point = builder->CreateVector(
    128           std::vector<int64_t>{array.quantization_params->zero_point});
    129     }
    130     auto q_param = ::tflite::CreateQuantizationParameters(*builder, min, max,
    131                                                           scale, zero_point);
    132 
    133     int index = tensors_map.at(tensor_name);
    134     ordered_tensors[index] =
    135         CreateTensor(*builder, builder->CreateVector(shape), type, buffer_index,
    136                      builder->CreateString(tensor_name), q_param);
    137   }
    138 
    139   std::vector<Offset<Tensor>> tensor_vector;
    140   tensor_vector.reserve(ordered_tensors.size());
    141   for (const auto& tensor : ordered_tensors) {
    142     tensor_vector.push_back(tensor.second);
    143   }
    144 
    145   return builder->CreateVector(tensor_vector);
    146 }
    147 
    148 Offset<Vector<int32_t>> ExportInputTensors(
    149     const Model& model, const details::TensorsMap& tensors_map,
    150     FlatBufferBuilder* builder) {
    151   std::vector<int32_t> inputs;
    152   for (const auto& input : model.flags.input_arrays()) {
    153     inputs.push_back(tensors_map.at(input.name()));
    154   }
    155   return builder->CreateVector<int32_t>(inputs);
    156 }
    157 
    158 Offset<Vector<int32_t>> ExportOutputTensors(
    159     const Model& model, const details::TensorsMap& tensors_map,
    160     FlatBufferBuilder* builder) {
    161   std::vector<int32_t> outputs;
    162   for (const string& output : model.flags.output_arrays()) {
    163     outputs.push_back(tensors_map.at(output));
    164   }
    165   return builder->CreateVector<int32_t>(outputs);
    166 }
    167 
    168 Offset<Vector<Offset<OperatorCode>>> ExportOperatorCodes(
    169     const Model& model,
    170     const std::map<OperatorType, std::unique_ptr<BaseOperator>>& ops_by_type,
    171     const details::OperatorsMap& operators_map, FlatBufferBuilder* builder,
    172     std::set<string>* error_summary) {
    173   // Map from operator name to TF Lite enum value, for all builtins.
    174   std::map<string, BuiltinOperator> builtin_ops;
    175   for (int i = BuiltinOperator_MIN; i <= BuiltinOperator_MAX; ++i) {
    176     BuiltinOperator op = static_cast<BuiltinOperator>(i);
    177     string name = EnumNameBuiltinOperator(op);
    178     if (op != BuiltinOperator_CUSTOM && !name.empty()) {
    179       builtin_ops[name] = op;
    180     }
    181   }
    182 
    183   // We will need to produce a vector of codes in the same order as they
    184   // appear in the operators_map.
    185   std::map<int, Offset<OperatorCode>> ordered_opcodes;
    186 
    187   for (const auto& op : model.operators) {
    188     const details::OperatorKey operator_key = GetOperatorKey(*op);
    189     int op_index = operators_map.at(operator_key);
    190 
    191     string name = HelpfulOperatorTypeName(*op);
    192     bool is_builtin = false;
    193     if (ops_by_type.count(op->type) != 0) {
    194       name = ops_by_type.at(op->type)->name();
    195       is_builtin = (builtin_ops.count(name) > 0);
    196     }
    197 
    198     if (is_builtin) {
    199       ordered_opcodes[op_index] =
    200           CreateOperatorCode(*builder, builtin_ops[name], 0);
    201     } else {
    202       // This could be a kTensorFlowUnsupported, in which case we should be
    203       // able to retrieve the original Tensorflow name from the OperatorKey, or
    204       // this could be a proper TOCO operator that is completely unknown to TF
    205       // Lite.
    206       if (!operator_key.custom_code.empty()) {
    207         name = operator_key.custom_code;
    208       }
    209       // Either way, this is an operator that is not supported by TF Lite,
    210       // so we output it as a custom op and add it to the error summary.
    211       if (error_summary) {
    212         error_summary->insert(name);
    213       }
    214       ordered_opcodes[op_index] = CreateOperatorCode(
    215           *builder, BuiltinOperator_CUSTOM, builder->CreateString(name));
    216     }
    217   }
    218 
    219   std::vector<Offset<OperatorCode>> opcode_vector;
    220   opcode_vector.reserve(ordered_opcodes.size());
    221   for (const auto& opcode : ordered_opcodes) {
    222     opcode_vector.push_back(opcode.second);
    223   }
    224 
    225   return builder->CreateVector(opcode_vector);
    226 }
    227 
    228 Offset<Vector<Offset<Operator>>> ExportOperators(
    229     const Model& model,
    230     const std::map<OperatorType, std::unique_ptr<BaseOperator>>& ops_by_type,
    231     const details::OperatorsMap& operators_map,
    232     const details::TensorsMap& tensors_map, FlatBufferBuilder* builder) {
    233   // The operators are in execution order, so we just follow tf.mini order.
    234   std::vector<Offset<Operator>> op_vector;
    235   for (const auto& op : model.operators) {
    236     std::vector<int32_t> inputs;
    237     for (const string& input : op->inputs) {
    238       // -1 is the ID for optional tensor in TFLite output
    239       int id = model.IsOptionalArray(input) ? -1 : tensors_map.at(input);
    240       inputs.push_back(id);
    241     }
    242     std::vector<int32_t> outputs;
    243     for (const string& output : op->outputs) {
    244       outputs.push_back(tensors_map.at(output));
    245     }
    246 
    247     int op_index = operators_map.at(GetOperatorKey(*op));
    248 
    249     // This is a custom op unless we can find it in ops_by_type, and even then
    250     // it could be a custom op (such as kTensorFlowUnsupported).
    251 
    252     auto options = Options::Custom(0);
    253     if (ops_by_type.count(op->type) != 0) {
    254       options = ops_by_type.at(op->type)->Serialize(*op, builder);
    255     }
    256     // The only supported CustomOptionFormat is FLEXBUFFERS now.
    257     op_vector.push_back(CreateOperator(
    258         *builder, op_index, builder->CreateVector(inputs),
    259         builder->CreateVector(outputs), options.type, options.builtin,
    260         options.custom, ::tflite::CustomOptionsFormat_FLEXBUFFERS));
    261   }
    262 
    263   return builder->CreateVector(op_vector);
    264 }
    265 
    266 Offset<Vector<Offset<Buffer>>> ExportBuffers(
    267     const Model& model, const std::vector<const Array*>& buffers_to_write,
    268     FlatBufferBuilder* builder) {
    269   std::vector<Offset<Buffer>> buffer_vector;
    270   size_t index = 0;
    271   for (const Array* array_ptr : buffers_to_write) {
    272     const Array& array = *array_ptr;
    273     Offset<Vector<uint8_t>> data_buffer = DataBuffer::Serialize(array, builder);
    274     buffer_vector.push_back(CreateBuffer(*builder, data_buffer));
    275     index++;
    276   }
    277   return builder->CreateVector(buffer_vector);
    278 }
    279 
    280 void Export(const Model& model, bool allow_custom_ops,
    281             string* output_file_contents) {
    282   flatbuffers::FlatBufferBuilder builder(/*initial_size=*/10240);
    283 
    284   const auto ops_by_type = BuildOperatorByTypeMap();
    285 
    286   details::TensorsMap tensors_map;
    287   details::LoadTensorsMap(model, &tensors_map);
    288 
    289   details::OperatorsMap operators_map;
    290   details::LoadOperatorsMap(model, &operators_map);
    291 
    292   std::vector<const Array*> buffers_to_write;
    293   Array empty_array;
    294   buffers_to_write.push_back(&empty_array);
    295 
    296   auto tensors = ExportTensors(model, tensors_map, &builder, &buffers_to_write);
    297   auto inputs = ExportInputTensors(model, tensors_map, &builder);
    298   auto outputs = ExportOutputTensors(model, tensors_map, &builder);
    299 
    300   std::set<string> error_summary;
    301   auto op_codes = ExportOperatorCodes(model, ops_by_type, operators_map,
    302                                       &builder, &error_summary);
    303   if (!allow_custom_ops && !error_summary.empty()) {
    304     LOG(QFATAL) << "Some of the operators in the model are not supported by "
    305                    "the standard TensorFlow Lite runtime. If you have a custom "
    306                    "implementation for them you can disable this error with "
    307                    "--allow_custom_ops. Here is a list of operators for which "
    308                    "you will need custom implementations: "
    309                 << absl::StrJoin(error_summary, ", ") << ".";
    310   }
    311 
    312   auto ops =
    313       ExportOperators(model, ops_by_type, operators_map, tensors_map, &builder);
    314 
    315   // TODO(aselle): add support to toco for multiple subgraphs.
    316   auto subgraph = CreateSubGraph(builder, tensors, inputs, outputs, ops);
    317   std::vector<flatbuffers::Offset<SubGraph>> subgraphs = {subgraph};
    318 
    319   auto buffers = ExportBuffers(model, buffers_to_write, &builder);
    320   auto description = builder.CreateString("TOCO Converted.");
    321   auto new_model_location =
    322       CreateModel(builder, TFLITE_SCHEMA_VERSION, op_codes,
    323                   builder.CreateVector(subgraphs), description, buffers);
    324   ::tflite::FinishModelBuffer(builder, new_model_location);
    325   const uint8_t* buffer = builder.GetBufferPointer();
    326   int size = builder.GetSize();
    327   *output_file_contents = string(reinterpret_cast<const char*>(buffer), size);
    328 }
    329 
    330 }  // namespace tflite
    331 
    332 }  // namespace toco
    333