1 /* Copyright 2019 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/lite/delegates/coreml/builders/op_builder.h"
16
17 #include <functional>
18 #include <memory>
19 #include <string>
20
21 #include "tensorflow/lite/builtin_ops.h"
22 #include "tensorflow/lite/c/builtin_op_data.h"
23 #include "tensorflow/lite/delegates/coreml/builders/op_factory.h"
24 #include "tensorflow/lite/kernels/kernel_util.h"
25
26 namespace tflite {
27 namespace delegates {
28 namespace coreml {
29
ToString() const30 std::string TensorID::ToString() const {
31 return std::to_string(node_) + "_" + std::to_string(output_id_);
32 }
33
NodeID() const34 int TensorID::NodeID() const { return node_; }
35
OutputID() const36 int TensorID::OutputID() const { return output_id_; }
37
AddBuilder(int builtin_code,const TfLiteNode * node)38 OpBuilder* GraphBuilder::AddBuilder(int builtin_code, const TfLiteNode* node) {
39 switch (builtin_code) {
40 case kTfLiteBuiltinAdd:
41 return AddBuilder(CreateAddOpBuilder, node);
42 case kTfLiteBuiltinAveragePool2d:
43 return AddBuilder(CreateAveragePool2dOpBuilder, node);
44 case kTfLiteBuiltinConcatenation:
45 return AddBuilder(CreateConcatenationOpBuilder, node);
46 case kTfLiteBuiltinConv2d:
47 return AddBuilder(CreateConvolutionOpBuilder, node);
48 case kTfLiteBuiltinDepthwiseConv2d:
49 return AddBuilder(CreateDepthwiseConvolutionOpBuilder, node);
50 // TODO(b/141490853): Add proper dequantize OpBuilder for int8/uint8 inputs.
51 case kTfLiteBuiltinDequantize:
52 // FP16 dequantize is claimed by the delegate to prevent them from running
53 // on CPU, but don't need to be excuted on the Core ML delegate either.
54 return AddBuilder(CreateDummyOpBuilder, node);
55 case kTfLiteBuiltinFullyConnected:
56 return AddBuilder(CreateFullyConnectedOpBuilder, node);
57 case kTfLiteBuiltinLogistic:
58 return AddBuilder(CreateLogisticOpBuilder, node);
59 case kTfLiteBuiltinMaxPool2d:
60 return AddBuilder(CreateMaxPool2dOpBuilder, node);
61 case kTfLiteBuiltinMean:
62 return AddBuilder(CreateMeanOpBuilder, node);
63 case kTfLiteBuiltinMirrorPad:
64 return AddBuilder(CreateMirrorPadOpBuilder, node);
65 case kTfLiteBuiltinMul:
66 return AddBuilder(CreateMulOpBuilder, node);
67 case kTfLiteBuiltinPad:
68 case kTfLiteBuiltinPadv2:
69 return AddBuilder(CreatePadOpBuilder, node);
70 case kTfLiteBuiltinRelu:
71 return AddBuilder(CreateReluOpBuilder, node);
72 case kTfLiteBuiltinReluN1To1:
73 return AddBuilder(CreateReluN1To1OpBuilder, node);
74 case kTfLiteBuiltinRelu6:
75 return AddBuilder(CreateRelu6OpBuilder, node);
76 case kTfLiteBuiltinReshape:
77 return AddBuilder(CreateReshapeOpBuilder, node);
78 case kTfLiteBuiltinResizeBilinear:
79 return AddBuilder(CreateResizeBilinearOpBuilder, node);
80 case kTfLiteBuiltinSoftmax:
81 return AddBuilder(CreateSoftmaxOpBuilder, node);
82 case kTfLiteBuiltinTanh:
83 return AddBuilder(CreateTanhOpBuilder, node);
84 case kTfLiteBuiltinTransposeConv:
85 return AddBuilder(CreateTransposeConvolutionOpBuilder, node);
86 case kTfLiteBuiltinHardSwish:
87 return AddBuilder(CreateHardSwishOpBuilder, node);
88 default:
89 return nullptr;
90 }
91 }
92
AddBuilder(const std::function<OpBuilder * (GraphBuilder *)> & builder,const TfLiteNode * node)93 OpBuilder* GraphBuilder::AddBuilder(
94 const std::function<OpBuilder*(GraphBuilder*)>& builder,
95 const TfLiteNode* node) {
96 if (builder == nullptr) {
97 fprintf(stderr, "builder should be set.\n");
98 return nullptr;
99 }
100 OpBuilder* op = builder(this);
101
102 builders_.emplace_back(op);
103 op->SetNodeID(builders_.size());
104 if (node != nullptr) {
105 op->SetBuiltinData(node->builtin_data);
106 op->SetTfLiteNode(node);
107 }
108 return builders_.back().get();
109 }
110
BuildModel()111 CoreML::Specification::Model* GraphBuilder::BuildModel() {
112 CoreML::Specification::Model* model = new CoreML::Specification::Model();
113 if (coreml_version_ == 2) { // Core ML 2, iOS >= 12.0
114 model->set_specificationversion(3);
115 } else if (coreml_version_ == 3) { // Core ML 3, iOS >= 13.0
116 model->set_specificationversion(4);
117 model->mutable_neuralnetwork()->set_arrayinputshapemapping(
118 CoreML::Specification::EXACT_ARRAY_MAPPING);
119 } else {
120 fprintf(stderr, "Unsupported Core ML version: %d\n", coreml_version_);
121 delete model;
122 return nullptr;
123 }
124 auto* neural_network = model->mutable_neuralnetwork();
125 for (auto& builder : builders_) {
126 CoreML::Specification::NeuralNetworkLayer* layer = builder->Build();
127 if (layer == nullptr) {
128 fprintf(stderr, "Null layer returned from builder: %s\n",
129 builder->DebugName().c_str());
130 continue;
131 }
132 neural_network->mutable_layers()->AddAllocated(layer);
133 }
134 return model;
135 }
136
AddTensorWithID(int tf_tensor_id,const TensorID & tensor_id)137 void GraphBuilder::AddTensorWithID(int tf_tensor_id,
138 const TensorID& tensor_id) {
139 if (tensors_.size() <= tf_tensor_id) {
140 tensors_.resize(tf_tensor_id + 1);
141 used_tensor_.resize(tf_tensor_id + 1);
142 }
143 tensors_[tf_tensor_id] = tensor_id;
144 }
145
GetTensorName(int tensor_id)146 std::string GraphBuilder::GetTensorName(int tensor_id) {
147 return GetTensorID(tensor_id).ToString();
148 }
149
GetTensorID(int tensor_id)150 const TensorID GraphBuilder::GetTensorID(int tensor_id) {
151 if (!HasTensor(tensor_id)) {
152 // TODO(karimnosseir): Double check if this happened, if we are
153 // adding in execution order it shouldn't happen.
154 fprintf(stderr, "index out of range...!!! Requested index %d , size %d\n",
155 tensor_id, static_cast<int>(tensors_.size()));
156 // Return invalid ID.
157 return TensorID(-1, -1);
158 }
159 used_tensor_[tensor_id] = true;
160 return tensors_[tensor_id];
161 }
162
HasTensor(int tflite_tensor_index)163 bool GraphBuilder::HasTensor(int tflite_tensor_index) {
164 if (tensors_.size() <= tflite_tensor_index) {
165 return false;
166 }
167 return tensors_[tflite_tensor_index].NodeID() != -1;
168 }
169
IsTensorUsed(int tflite_tensor_index)170 bool GraphBuilder::IsTensorUsed(int tflite_tensor_index) {
171 if (!HasTensor(tflite_tensor_index)) return false;
172 return used_tensor_[tflite_tensor_index];
173 }
174
Build()175 CoreML::Specification::NeuralNetworkLayer* OpBuilder::Build() {
176 layer_->set_name(DebugName());
177 return layer_.release();
178 }
179
PopulateSubgraph(TfLiteContext * context)180 TfLiteStatus OpBuilder::PopulateSubgraph(TfLiteContext* context) {
181 builder_output_ = AddOutput();
182 return kTfLiteOk;
183 }
184
SetBuiltinData(void * builtin_data)185 void OpBuilder::SetBuiltinData(void* builtin_data) {
186 builtin_data_ = builtin_data;
187 }
188
SetNodeID(int id)189 void OpBuilder::SetNodeID(int id) { node_id_ = id; }
190
SetTfLiteNode(const TfLiteNode * node)191 void OpBuilder::SetTfLiteNode(const TfLiteNode* node) { tflite_node_ = node; }
192
GetID() const193 int OpBuilder::GetID() const { return node_id_; }
194
GetOutput(TfLiteContext * context)195 TensorID OpBuilder::GetOutput(TfLiteContext* context) {
196 if (builder_output_.NodeID() != -1) {
197 return builder_output_;
198 }
199 // builder_output_ is not set when PopulateSubgraph is not called.
200 builder_output_ = AddOutput();
201 return builder_output_;
202 }
203
AddInput(const std::string & input_name)204 void OpBuilder::AddInput(const std::string& input_name) {
205 if (layer_ == nullptr) {
206 layer_ = std::make_unique<CoreML::Specification::NeuralNetworkLayer>();
207 }
208 *layer_->mutable_input()->Add() = input_name;
209 }
210
AddInput(const TensorID & input_id)211 void OpBuilder::AddInput(const TensorID& input_id) {
212 AddInput(input_id.ToString());
213 }
214
AddInput(int tf_input_id)215 void OpBuilder::AddInput(int tf_input_id) {
216 AddInput(graph_builder_->GetTensorName(tf_input_id));
217 }
218
AddOutput()219 TensorID OpBuilder::AddOutput() {
220 auto tensor_id = TensorID(GetID(), num_outputs_++);
221 *layer_->mutable_output()->Add() = tensor_id.ToString();
222 return tensor_id;
223 }
224
SetDebugName(const char * name,int id)225 void OpBuilder::SetDebugName(const char* name, int id) {
226 debug_name_ = std::string(name) + "_" + std::to_string(id);
227 }
228
229 } // namespace coreml
230 } // namespace delegates
231 } // namespace tflite
232