3 #include "caffe2/core/logging.h" 4 #include "caffe2/opt/converter.h" 6 #include "nomnigraph/Graph/Algorithms.h" 8 #include "nomnigraph/Support/Casting.h" 9 #include "nomnigraph/Support/Pointer.h" 15 std::vector<int> getStrides(std::map<std::string, caffe2::Argument> argMap) {
16 std::vector<int> strides;
19 if (argMap.count(
"stride")) {
20 CAFFE_ENFORCE(argMap[
"stride"].has_i(),
"Invalid stride argument");
21 int stride =
static_cast<int>(argMap[
"stride"].i());
22 strides = {stride, stride};
27 std::vector<int> getPads(std::map<std::string, caffe2::Argument> argMap) {
28 std::vector<int> pads;
29 if (argMap.count(
"pad")) {
30 CAFFE_ENFORCE(argMap[
"pad"].has_i(),
"Invalid pad argument");
31 int pad =
static_cast<int>(argMap[
"pad"].i());
32 pads = {pad, pad, pad, pad};
37 std::vector<int> getDilations(std::map<std::string, caffe2::Argument> argMap) {
38 std::vector<int> dilations;
39 if (argMap.count(
"dilation")) {
40 CAFFE_ENFORCE(argMap[
"dilation"].has_i(),
"Invalid dilation argument");
41 int dilation =
static_cast<int>(argMap[
"dilation"].i());
42 dilations = {dilation, dilation};
47 int getGroup(std::map<std::string, caffe2::Argument>& argMap) {
48 if (argMap.count(
"group")) {
49 CAFFE_ENFORCE(argMap[
"group"].has_i() &&
"Invalid group argument");
50 return static_cast<int>(argMap[
"group"].i());
59 C10_DEFINE_REGISTRY(ConverterRegistry, Converter);
61 std::map<std::string, caffe2::Argument> Converter::getArgumentsFromOperator(
62 caffe2::OperatorDef op) {
63 std::map<std::string, caffe2::Argument> argMap;
64 for (
auto arg : op.arg()) {
65 argMap[arg.name()] = arg;
71 std::map<std::string, caffe2::Argument> argMap) {
72 auto arg = argMap.find(
"order");
73 if (arg != argMap.end()) {
74 auto order = argMap[
"order"].s();
75 if (order ==
"NCHW" || order ==
"nchw") {
76 return repr::NeuralNetOperator::NNLayout::NCHW;
77 }
else if (order ==
"NHWC" || order ==
"nhwc") {
78 return repr::NeuralNetOperator::NNLayout::NHWC;
81 return repr::NeuralNetOperator::NNLayout::Undefined;
84 OperatorDef Converter::convertToOperatorDef(
86 auto* annotation = nnOp->getAnnotation();
88 if (annotation && isa<Caffe2Annotation>(annotation)) {
89 return dyn_cast<Caffe2Annotation>(annotation)->getOperatorDef();
92 <<
"Cannot instantiate this OperatorDef from nomnigraph, falling back";
93 caffe2::OperatorDef op;
94 op.set_type(nnOp->getName());
98 std::vector<int> getKernelShape(
99 std::map<std::string, caffe2::Argument> argMap) {
101 std::vector<int> kernelShape;
102 if (argMap.count(
"kernel")) {
103 CAFFE_ENFORCE(argMap[
"kernel"].has_i(),
"Invalid kernel argument");
104 int kernel =
static_cast<int>(argMap[
"kernel"].i());
105 kernelShape = {kernel, kernel};
106 }
else if (argMap.count(
"kernels")) {
107 for (
auto i : argMap[
"kernels"].ints()) {
108 kernelShape.push_back(static_cast<int>(i));
110 }
else if (argMap.count(
"kernel_h") && argMap.count(
"kernel_w")) {
111 CAFFE_ENFORCE(argMap[
"kernel_h"].has_i(),
"Invalid kernel argument");
112 CAFFE_ENFORCE(argMap[
"kernel_w"].has_i(),
"Invalid kernel argument");
113 int kernelH =
static_cast<int>(argMap[
"kernel_h"].i());
114 int kernelW =
static_cast<int>(argMap[
"kernel_w"].i());
115 kernelShape = {kernelH, kernelW};
122 class ConvConverter :
public Converter {
123 std::unique_ptr<nom::repr::NeuralNetOperator> convertToNeuralNetOperator(
124 const OperatorDef& op)
override {
125 std::unique_ptr<repr::NeuralNetOperator> nnOp;
126 auto argMap = getArgumentsFromOperator(op);
127 auto kernelShape = getKernelShape(argMap);
128 nnOp = util::make_unique<repr::Conv>(kernelShape);
129 auto c = dyn_cast<repr::Conv>(nnOp.get());
131 c->setStrides(getStrides(argMap));
132 c->setPads(getPads(argMap));
133 c->setDilations(getDilations(argMap));
134 c->setGroup(getGroup(argMap));
140 ~ConvConverter()
override {}
143 REGISTER_CONVERTER(
Conv, ConvConverter);
145 TRIVIAL_CONVERTER(
Relu);
146 REGISTER_CONVERTER(
Relu, ReluConverter);
148 TRIVIAL_CONVERTER(
Sum);
149 REGISTER_CONVERTER(
Sum, SumConverter);
152 REGISTER_CONVERTER(SpatialBN, BatchNormalizationConverter);
155 REGISTER_CONVERTER(
Flatten, FlattenConverter);
157 class ClipConverter :
public Converter {
158 std::unique_ptr<nom::repr::NeuralNetOperator> convertToNeuralNetOperator(
159 const OperatorDef& op)
override {
160 auto argMap = getArgumentsFromOperator(op);
161 float min = std::numeric_limits<float>::lowest();
162 float max = std::numeric_limits<float>::max();
164 if (argMap.count(
"min")) {
165 CAFFE_ENFORCE(argMap[
"min"].has_f(),
"Invalid 'min' argument");
166 min =
static_cast<float>(argMap[
"min"].f());
169 if (argMap.count(
"max")) {
170 CAFFE_ENFORCE(argMap[
"max"].has_f(),
"Invalid 'max' argument");
171 max =
static_cast<float>(argMap[
"max"].f());
174 return util::make_unique<repr::Clip>(min, max);
178 ~ClipConverter()
override {}
180 REGISTER_CONVERTER(
Clip, ClipConverter);
182 class AveragePoolConverter :
public Converter {
183 std::unique_ptr<nom::repr::NeuralNetOperator> convertToNeuralNetOperator(
184 const OperatorDef& op)
override {
185 std::unique_ptr<repr::NeuralNetOperator> nnOp;
186 auto argMap = getArgumentsFromOperator(op);
187 auto kernelShape = getKernelShape(argMap);
188 nnOp = util::make_unique<repr::AveragePool>(kernelShape);
193 ~AveragePoolConverter()
override {}
195 REGISTER_CONVERTER(
AveragePool, AveragePoolConverter);
197 class MaxPoolConverter :
public Converter {
198 std::unique_ptr<nom::repr::NeuralNetOperator> convertToNeuralNetOperator(
199 const OperatorDef& op)
override {
200 std::unique_ptr<repr::NeuralNetOperator> nnOp;
201 auto argMap = getArgumentsFromOperator(op);
202 auto kernelShape = getKernelShape(argMap);
203 nnOp = util::make_unique<repr::MaxPool>(kernelShape);
208 ~MaxPoolConverter()
override {}
210 REGISTER_CONVERTER(
MaxPool, MaxPoolConverter);
212 class ConcatConverter :
public Converter {
213 std::unique_ptr<nom::repr::NeuralNetOperator> convertToNeuralNetOperator(
214 const OperatorDef& op)
override {
215 std::unique_ptr<repr::NeuralNetOperator> nnOp =
216 util::make_unique<repr::Concat>();
217 auto argMap = getArgumentsFromOperator(op);
219 auto c = dyn_cast<repr::Concat>(nnOp.get());
220 if (argMap.count(
"axis")) {
221 CAFFE_ENFORCE(argMap[
"axis"].has_i(),
"Invalid axis argument");
222 int axis =
static_cast<int>(argMap[
"axis"].i());
225 if (argMap.count(
"add_axis")) {
226 CAFFE_ENFORCE(argMap[
"add_axis"].has_i(),
"Invalid add_axis argument");
227 int add_axis =
static_cast<int>(argMap[
"add_axis"].i());
228 c->setAddAxis(!!add_axis);
234 ~ConcatConverter()
override {}
236 REGISTER_CONVERTER(
Concat, ConcatConverter);
238 class FCConverter :
public Converter {
239 std::unique_ptr<nom::repr::NeuralNetOperator> convertToNeuralNetOperator(
240 const OperatorDef& op)
override {
241 std::unique_ptr<repr::NeuralNetOperator> nnOp =
242 util::make_unique<repr::FC>();
243 auto argMap = getArgumentsFromOperator(op);
245 auto c = dyn_cast<repr::FC>(nnOp.get());
246 if (argMap.count(
"axis")) {
247 CAFFE_ENFORCE(argMap[
"axis"].has_i(),
"Invalid axis argument");
248 int axis =
static_cast<int>(argMap[
"axis"].i());
251 if (argMap.count(
"axis_w")) {
252 CAFFE_ENFORCE(argMap[
"axis_w"].has_i(),
"Invalid axis_w argument");
253 int axis_w =
static_cast<int>(argMap[
"axis_w"].i());
261 ~FCConverter()
override {}
263 REGISTER_CONVERTER(
FC, FCConverter);
267 std::unique_ptr<repr::NeuralNetOperator> convertToNeuralNetOperator(
268 const caffe2::OperatorDef& op) {
269 auto argMap = Converter::getArgumentsFromOperator(op);
271 std::unique_ptr<repr::NeuralNetOperator> nnOp;
273 if (ConverterRegistry()->Has(op.type())) {
275 ConverterRegistry()->Create(op.type())->convertToNeuralNetOperator(op);
279 nnOp = util::make_unique<repr::GenericOperator>(op.type());
283 nnOp->setLayout(getLayout(argMap));
285 auto annotation = util::make_unique<Caffe2Annotation>();
286 annotation->setOperatorDef(op);
288 auto device_name = op.device_option().node_name();
289 if (device_name !=
"") {
290 annotation->setDevice(device_name);
292 annotation->setDeviceType(op.device_option().device_type());
294 nnOp->setAnnotation(std::move(annotation));
302 const caffe2::NetDef& net,
304 std::vector<repr::NNGraph::NodeRef>* opNodeVec) {
314 std::unordered_map<std::string, repr::NNGraph::NodeRef> blobMap;
316 std::unordered_set<std::string> externalInputNames;
317 for (
const auto& inputName : net.external_input()) {
318 externalInputNames.insert(inputName);
324 auto bbNode = cfg.createNamedFunction(
"main");
326 for (
const auto& op : net.op()) {
329 for (
const auto& input : op.input()) {
331 if (!blobMap.count(input)) {
332 auto tensor = util::make_unique<repr::Tensor>(input);
334 dfg.
createNode(unique_dyn_cast<repr::NeuralNetData>(tensor));
335 if (externalInputNames.count(input)) {
336 module.inputs.insert(blobMap[input]);
337 externalInputNames.erase(input);
341 auto tensorNode = blobMap[input];
346 for (
const auto& output : op.output()) {
347 auto tensor = util::make_unique<repr::Tensor>(output);
349 dfg.
createNode(unique_dyn_cast<repr::NeuralNetData>(tensor));
351 blobMap[output] = tensorNode;
354 opNode->resetData(convertToNeuralNetOperator(op));
356 opNodeVec->emplace_back(opNode);
358 auto currentBasicBlock = bbNode->mutableData();
359 currentBasicBlock->pushInstructionNode(opNode);
362 if (externalInputNames.size()) {
365 std::ostringstream os;
366 for (
const auto& inputName : externalInputNames) {
367 os <<
"\"" << inputName <<
"\" ";
371 externalInputNames.size() == 0,
372 "Attempting to convert an ill-formed network: ",
373 "external_input contains ",
374 externalInputNames.size(),
379 for (
const auto& input : externalInputNames) {
380 blobMap[input] = dfg.
createNode(util::make_unique<repr::Tensor>(input));
385 for (
const auto& outputName : net.external_output()) {
387 !strict || blobMap.count(outputName),
388 "NetDef has ill-formed external_output:",
390 if (!blobMap.count(outputName)) {
391 LOG(ERROR) <<
"NetDef has ill-formed external_output: " << outputName;
394 module.outputs.insert(blobMap[outputName]);
400 caffe2::OperatorDef convertToOperatorDef(
402 auto* nnOp = repr::nn::get<repr::NeuralNetOperator>(instrNode);
403 auto op_type = nnOp->getName();
404 auto* annotation = nnOp->getAnnotation();
405 caffe2::OperatorDef op;
407 if (ConverterRegistry()->Has(op_type)) {
408 op = ConverterRegistry()->Create(op_type)->convertToOperatorDef(nnOp);
409 }
else if (!annotation) {
410 op.set_type(op_type);
412 if (isa<Caffe2Annotation>(annotation)) {
414 op = c2_annotation->getOperatorDef();
415 op.mutable_device_option()->set_device_type(
416 c2_annotation->getDeviceType());
419 "Couldn't convert operator annotation to Caffe2 operator def");
431 auto* nnOp = repr::nn::get<repr::NeuralNetOperator>(instrNode);
432 auto* annotation = nnOp->getMutableAnnotation();
434 auto new_annot = util::make_unique<Caffe2Annotation>();
435 new_annot->setOperatorDef(convertToOperatorDef(instrNode));
436 nnOp->setAnnotation(std::move(new_annot));
437 annotation = nnOp->getMutableAnnotation();
439 CAFFE_ENFORCE(isa<Caffe2Annotation>(annotation));
441 return c2_annotation;
445 auto predictNet = caffe2::NetDef();
446 return convertToCaffe2Proto(m, predictNet);
449 std::vector<std::string> mergeExternalTensors(
450 const std::unordered_set<repr::NNGraph::NodeRef>& currExternal,
451 const std::vector<std::string>& oldExternal) {
452 std::vector<std::string> out;
455 std::unordered_set<std::string> newExternal;
456 for (
const auto& tensorNode : currExternal) {
458 repr::nn::is<repr::NeuralNetData>(tensorNode),
459 "A non-tensor node was added to external inputs/outputs of the NNModule");
460 auto name = repr::nn::get<repr::NeuralNetData>(tensorNode)->getName();
461 newExternal.insert(name);
464 for (
const auto& tensorName : oldExternal) {
465 if (newExternal.count(tensorName)) {
466 out.emplace_back(tensorName);
467 newExternal.erase(tensorName);
470 for (
const auto& tensorName : newExternal) {
471 out.emplace_back(tensorName);
477 caffe2::NetDef convertToCaffe2Proto(
479 const caffe2::NetDef& oldNet) {
480 auto predictNet = caffe2::NetDef();
482 predictNet.CopyFrom(oldNet);
483 predictNet.mutable_op()->Clear();
485 repr::nn::coalesceInsertedDataDependencies(&m);
489 for (
const auto& bbNode : m.controlFlow.getMutableNodes()) {
490 if (bbNode->getOutEdges().size() > 1) {
491 CAFFE_THROW(
"Control flow not yet supported in Caffe2 converter.");
493 auto& bb = bbNode->data();
494 for (
const auto& instrNode : bb.getInstructions()) {
495 caffe2::OperatorDef op = convertToOperatorDef(instrNode);
497 for (
const auto& inEdge : instrNode->getInEdges()) {
500 *op.add_input() = tensorNode->getName();
502 for (
const auto& outEdge : instrNode->getOutEdges()) {
505 *op.add_output() = tensorNode->getName();
508 auto* nnOp = repr::nn::get<repr::NeuralNetOperator>(instrNode);
509 if (nnOp->getLayout() != repr::NeuralNetOperator::NNLayout::Undefined) {
511 for (
int i = 0; i < op.arg_size(); ++i) {
512 auto arg_ = op.mutable_arg(i);
513 if (arg_->name() ==
"order") {
521 arg->set_name(
"order");
524 auto layout = nnOp->getLayout();
525 if (layout == repr::NeuralNetOperator::NNLayout::NCHW) {
528 if (layout == repr::NeuralNetOperator::NNLayout::NHWC) {
534 *predictNet.add_op() = op;
539 std::vector<std::string> oldExternalInputs;
540 std::vector<std::string> oldExternalOutputs;
542 for (
const auto& inputName : predictNet.external_input()) {
543 oldExternalInputs.emplace_back(inputName);
545 for (
const auto& outputName : predictNet.external_output()) {
546 oldExternalOutputs.emplace_back(outputName);
549 auto newExternalInputs = mergeExternalTensors(m.inputs, oldExternalInputs);
550 auto newExternalOutputs = mergeExternalTensors(m.outputs, oldExternalOutputs);
552 predictNet.clear_external_input();
553 predictNet.clear_external_output();
555 for (
const auto& inputName : newExternalInputs) {
556 predictNet.add_external_input(inputName);
559 for (
const auto& outputName : newExternalOutputs) {
560 predictNet.add_external_output(outputName);
566 void pushOpToFront(caffe2::OperatorDef& op, caffe2::NetDef* net) {
568 google::protobuf::RepeatedPtrField<caffe2::OperatorDef>* op_list(
571 for (
int i(net->op_size() - 1); i > 0; --i) {
572 op_list->SwapElements(i, i - 1);
576 void injectDataEdgeIndicators(caffe2::NetDef* net) {
577 for (
const auto& input : net->external_input()) {
578 caffe2::OperatorDef op;
579 op.set_type(
"Declare");
580 op.add_output(input);
581 pushOpToFront(op, net);
583 for (
const auto& output : net->external_output()) {
584 caffe2::OperatorDef op;
585 op.set_type(
"Export");
586 op.add_input(output);
589 net->clear_external_input();
590 net->clear_external_output();
593 void removeDataEdgeIndicators(caffe2::NetDef* net) {
594 google::protobuf::RepeatedPtrField<caffe2::OperatorDef>* op_list(
596 for (
auto i = 0; i < net->op_size(); ++i) {
597 auto op = net->op(i);
598 if (op.type() ==
"Declare") {
599 net->add_external_input(op.output(0));
600 }
else if (op.type() ==
"Export") {
601 net->add_external_output(op.input(0));
606 op_list->DeleteSubrange(i--, 1);
NodeRef createNode(T &&data)
Creates a node and retains ownership of it.
NNLayout
An optional tensor-type specifier.
A global dictionary that holds information about what Caffe2 modules have been loaded in the current ...
repr::NNModule convertToNNModule(const caffe2::NetDef &net, bool strict, std::vector< repr::NNGraph::NodeRef > *opNodeVec)
Ingest a caffe2 protobuf model and output an NNModule.
A simple graph implementation.
EdgeRef createEdge(NodeRef tail, NodeRef head, U...data)
Creates a directed edge and retains ownership of it.