Skip to content
This repository was archived by the owner on Dec 21, 2023. It is now read-only.

Commit 10bb08b

Browse files
authored
Migrate Object Detection export to CoreML to new Checkpoint type (#3022)
1 parent a16d9ad commit 10bb08b

16 files changed

+444
-296
lines changed

src/ml/neural_net/model_spec.cpp

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,17 +20,18 @@ namespace neural_net {
2020

2121
namespace {
2222

23-
using CoreML::Specification::BorderAmounts_EdgeSizes;
2423
using CoreML::Specification::BatchnormLayerParams;
24+
using CoreML::Specification::BorderAmounts_EdgeSizes;
2525
using CoreML::Specification::ConvolutionLayerParams;
2626
using CoreML::Specification::InnerProductLayerParams;
2727
using CoreML::Specification::Model;
2828
using CoreML::Specification::NeuralNetwork;
2929
using CoreML::Specification::NeuralNetworkImageScaler;
3030
using CoreML::Specification::NeuralNetworkLayer;
3131
using CoreML::Specification::NeuralNetworkPreprocessing;
32-
using CoreML::Specification::PoolingLayerParams;
3332
using CoreML::Specification::PaddingLayerParams;
33+
using CoreML::Specification::Pipeline;
34+
using CoreML::Specification::PoolingLayerParams;
3435
using CoreML::Specification::SamePadding;
3536
using CoreML::Specification::UniDirectionalLSTMLayerParams;
3637
using CoreML::Specification::UpsampleLayerParams;
@@ -974,5 +975,16 @@ void model_spec::add_preprocessing(const std::string& feature_name,
974975
image_scaler->set_channelscale(image_scale);
975976
}
976977

978+
pipeline_spec::pipeline_spec(std::unique_ptr<Pipeline> impl)
979+
: impl_(std::move(impl)) {}
980+
981+
pipeline_spec::pipeline_spec(pipeline_spec&&) = default;
982+
pipeline_spec& pipeline_spec::operator=(pipeline_spec&&) = default;
983+
pipeline_spec::~pipeline_spec() = default;
984+
985+
std::unique_ptr<Pipeline> pipeline_spec::move_coreml_spec() && {
986+
return std::move(impl_);
987+
}
988+
977989
} // neural_net
978990
} // turi

src/ml/neural_net/model_spec.hpp

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
namespace CoreML {
2323
namespace Specification {
2424
class NeuralNetwork;
25+
class Pipeline;
2526
class WeightParams;
2627
}
2728
}
@@ -400,6 +401,43 @@ class model_spec {
400401
std::unique_ptr<CoreML::Specification::NeuralNetwork> impl_;
401402
};
402403

404+
/**
405+
* Simple wrapper around CoreML::Specification::Pipeline that allows client code
406+
* to pass around instances without importing full protobuf headers.
407+
*
408+
* \todo As needed, elaborate this class and move into its own file.
409+
*/
410+
class pipeline_spec {
411+
public:
412+
pipeline_spec(std::unique_ptr<CoreML::Specification::Pipeline> impl);
413+
414+
pipeline_spec(pipeline_spec&&);
415+
pipeline_spec& operator=(pipeline_spec&&);
416+
417+
// Declared here and defined in the .cpp file just to prevent the implicit
418+
// default destructor from attempting (and failing) to instantiate
419+
// std::unique_ptr<Pipeline>::~unique_ptr()
420+
~pipeline_spec();
421+
422+
/**
423+
* Exposes the underlying CoreML proto.
424+
*/
425+
const CoreML::Specification::Pipeline& get_coreml_spec() const {
426+
return *impl_;
427+
}
428+
429+
/**
430+
* Transfer ownership of the underlying CoreML proto, invalidating the current
431+
* instance (leaving it in a "moved-from" state).
432+
*
433+
* (Note that this method may only be invoked from a pipeline_spec&&)
434+
*/
435+
std::unique_ptr<CoreML::Specification::Pipeline> move_coreml_spec() &&;
436+
437+
private:
438+
std::unique_ptr<CoreML::Specification::Pipeline> impl_;
439+
};
440+
403441
} // neural_net
404442
} // turi
405443

src/toolkits/coreml_export/neural_net_models_exporter.cpp

Lines changed: 13 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -167,86 +167,30 @@ ImageFeatureType* set_image_feature(
167167
} //namespace
168168

169169
std::shared_ptr<MLModelWrapper> export_object_detector_model(
170-
const neural_net::model_spec& nn_spec, size_t image_width,
171-
size_t image_height, size_t num_classes, size_t num_predictions,
172-
flex_list class_labels, const std::string& input_name,
170+
neural_net::pipeline_spec raw_pipeline, size_t num_classes,
171+
size_t num_predictions, flex_list class_labels,
173172
std::map<std::string, flexible_type> options) {
174173
// Set up Pipeline
175174
CoreML::Specification::Model model_pipeline;
176175
model_pipeline.set_specificationversion(3);
177176
ModelDescription* pipeline_desc = model_pipeline.mutable_description();
178177

179-
// Add NeuralNetwork model to pipeline
180-
auto* model_nn = model_pipeline.mutable_pipeline()->add_models();
181-
182-
// Scale pixel values 0..255 to [0,1]
183-
NeuralNetworkLayer* first_layer =
184-
model_nn->mutable_neuralnetwork()->add_layers();
185-
first_layer->set_name("_divscalar0");
186-
first_layer->add_input(input_name);
187-
first_layer->add_output("_divscalar0");
188-
first_layer->mutable_scale()->add_shapescale(1);
189-
first_layer->mutable_scale()->mutable_scale()->add_floatvalue(1 / 255.f);
190-
191-
// Copy the NeuralNetwork layers from nn_spec.
192-
// TODO: This copies ~60MB at present. Should object_detector be responsible
193-
// for _divscalar0, even though this isn't performed by the cnn_module?
194-
model_nn->mutable_neuralnetwork()->MergeFrom(nn_spec.get_coreml_spec());
195-
ASSERT_GT(model_nn->neuralnetwork().layers_size(), 1);
196-
197-
// Set the name of preprocessing layer to input_name as well.
198-
// Since in the test case somehow the model doesn't have a preprocessing
199-
// layer, so we need to check the size first...
200-
if (model_nn->neuralnetwork().preprocessing_size() == 1) {
201-
NeuralNetworkPreprocessing* preprocessing_layer =
202-
model_nn->mutable_neuralnetwork()->mutable_preprocessing(0);
203-
preprocessing_layer->set_featurename(input_name);
204-
}
205-
206-
// Wire up the input layer from the copied layers to _divscalar0.
207-
// TODO: This assumes that the first copied layer is the (only) one to take
208-
// the input from "image".
209-
NeuralNetworkLayer* second_layer =
210-
model_nn->mutable_neuralnetwork()->mutable_layers(1);
211-
ASSERT_EQ(second_layer->input_size(), 1);
212-
213-
second_layer->set_input(0, "_divscalar0");
214-
215-
// Write the ModelDescription.
216-
ModelDescription* model_desc = model_nn->mutable_description();
217-
218-
// Write FeatureDescription for the image input.
219-
set_image_feature(model_desc->add_input(), image_width, image_height,
220-
input_name);
178+
// Adopt the model pipeline passed to us as input.
179+
std::unique_ptr<CoreML::Specification::Pipeline> raw_pipeline_spec =
180+
std::move(raw_pipeline).move_coreml_spec();
181+
model_pipeline.mutable_pipeline()->Swap(raw_pipeline_spec.get());
221182

222183
if (!options["include_non_maximum_suppression"].to<bool>()){
184+
// Only support this case for models supporting spec version 1, which means
185+
// no pipeline models.
186+
ASSERT_EQ(model_pipeline.pipeline().models_size(), 1);
223187

224-
// Write FeatureDescription for the confidence output.
225-
set_predictions_feature(model_desc->add_output(), "confidence", num_predictions, num_classes,
226-
true, false, CONFIDENCE_STR);
227-
228-
// Write FeatureDescription for the coordinates output.
229-
set_predictions_feature(model_desc->add_output(), "coordinates", num_predictions, 4, true,
230-
false, COORDINATES_STR);
231-
232-
// Set CoreML spec version.
233-
model_nn->set_specificationversion(1);
234188
auto model_wrapper = std::make_shared<MLModelWrapper>(
235-
std::make_shared<CoreML::Model>(*model_nn));
189+
std::make_shared<CoreML::Model>(model_pipeline.pipeline().models(0)));
236190

237191
return model_wrapper;
238192
}
239193

240-
model_nn->set_specificationversion(3);
241-
242-
// Write FeatureDescription for the raw confidence output.
243-
set_predictions_feature(model_desc->add_output(), "raw_confidence", num_predictions, num_classes,
244-
true, true, "");
245-
246-
// Write FeatureDescription for the coordinates output.
247-
set_predictions_feature(model_desc->add_output(), "raw_coordinates", num_predictions, 4, true,
248-
true, "");
249-
250194
// Add Non Maximum Suppression model to pipeline
251195
auto* model_nms = model_pipeline.mutable_pipeline()->add_models();
252196
model_nms->set_specificationversion(3);
@@ -294,9 +238,9 @@ std::shared_ptr<MLModelWrapper> export_object_detector_model(
294238
first_layer_nms->set_confidenceoutputfeaturename("confidence");
295239
first_layer_nms->set_coordinatesoutputfeaturename("coordinates");
296240

297-
// Write FeatureDescription for the image input.
298-
set_image_feature(pipeline_desc->add_input(), image_width, image_height,
299-
input_name, "Input image");
241+
// Copy input feature descriptions from the first model in the pipeline.
242+
*pipeline_desc->mutable_input() =
243+
model_pipeline.pipeline().models(0).description().input();
300244

301245
// Write FeatureDescription for the IOU Threshold input.
302246
FeatureDescription* iou_threshold = pipeline_desc->add_input();

src/toolkits/coreml_export/neural_net_models_exporter.hpp

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,8 @@ namespace turi {
2828
* is responsible for populating the inputs and outputs?
2929
*/
3030
std::shared_ptr<coreml::MLModelWrapper> export_object_detector_model(
31-
const neural_net::model_spec& nn_spec, size_t image_width,
32-
size_t image_height, size_t num_classes, size_t num_predictions,
33-
flex_list class_labels, const std::string& input_name,
31+
neural_net::pipeline_spec pipeline, size_t num_classes,
32+
size_t num_predictions, flex_list class_labels,
3433
std::map<std::string, flexible_type> options);
3534

3635
/** Wraps a trained activity classifier model_spec as a complete MLModel. */

src/toolkits/object_detection/CMakeLists.txt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,10 @@ make_library(unity_object_detection OBJECT
44
SOURCES
55
class_registrations.cpp
66
object_detector.cpp
7-
od_darknet_yolo_model.cpp
7+
od_darknet_yolo_model_trainer.cpp
88
od_data_iterator.cpp
99
od_evaluation.cpp
10-
od_model.cpp
10+
od_model_trainer.cpp
1111
od_serialization.cpp
1212
od_yolo.cpp
1313
REQUIRES

0 commit comments

Comments
 (0)