diff --git a/caffe2/opt/backend_cutting_test.cc b/caffe2/opt/backend_cutting_test.cc index 9605be3d1827c1..5e16a884011fa4 100644 --- a/caffe2/opt/backend_cutting_test.cc +++ b/caffe2/opt/backend_cutting_test.cc @@ -1,46 +1,46 @@ #include "caffe2/core/common.h" -#include "caffe2/opt/backend_cutting.h" #include "caffe2/core/logging.h" +#include "caffe2/opt/backend_cutting.h" #include "caffe2/utils/string_utils.h" #include namespace { - using caffe2::StartsWith; +using caffe2::StartsWith; - void AddConv(caffe2::NetDef* net, int tick) { - auto* op = net->add_op(); - op->set_type("MyConv"); - op->add_input("N" + c10::to_string(tick)); - op->add_input("W" + c10::to_string(tick)); - op->add_input("b" + c10::to_string(tick)); - op->add_output("N" + c10::to_string(tick + 1)); - } +void AddConv(caffe2::NetDef* net, int tick) { + auto* op = net->add_op(); + op->set_type("MyConv"); + op->add_input("N" + c10::to_string(tick)); + op->add_input("W" + c10::to_string(tick)); + op->add_input("b" + c10::to_string(tick)); + op->add_output("N" + c10::to_string(tick + 1)); +} - bool Supports(const caffe2::OperatorDef& op) { - return StartsWith(op.type(), "MyConv") || StartsWith(op.type(), "MyRelu") || - StartsWith(op.type(), "Concat"); - } +bool Supports(const caffe2::OperatorDef& op) { + return StartsWith(op.type(), "MyConv") || StartsWith(op.type(), "MyRelu") || + StartsWith(op.type(), "Concat"); +} - caffe2::NetDef Transform(const caffe2::NetDef& net) { - caffe2::NetDef net_opt; - auto * op = net_opt.add_op(); - op->set_type("BigOpt"); +caffe2::NetDef Transform(const caffe2::NetDef& net) { + caffe2::NetDef net_opt; + auto* op = net_opt.add_op(); + op->set_type("BigOpt"); - for (const auto& i: net.external_input()) { - // Absorb the weights and bias - if (!StartsWith(i, "W") && !StartsWith(i, "b")) { - net_opt.add_external_input(i); - op->add_input(i); - } - } - for (const auto& i: net.external_output()) { - net_opt.add_external_output(i); - op->add_output(i); + for (const auto& i : net.external_input()) { + // Absorb the weights and bias + if (!StartsWith(i, "W") && !StartsWith(i, "b")) { + net_opt.add_external_input(i); + op->add_input(i); } - return net_opt; } + for (const auto& i : net.external_output()) { + net_opt.add_external_output(i); + op->add_output(i); + } + return net_opt; } +} // namespace // N0 -> MyConv -> N1 TEST(BackendCuttingTest, unit) { @@ -56,7 +56,6 @@ TEST(BackendCuttingTest, unit) { EXPECT_EQ(1, net_opt.external_output_size()); } - // X -> CopyIn -> MyConv -> MyConv -> CopyOut -> Y TEST(BackendCuttingTest, line) { caffe2::NetDef net; diff --git a/caffe2/opt/backend_transformer_base.cc b/caffe2/opt/backend_transformer_base.cc new file mode 100644 index 00000000000000..406e538641e519 --- /dev/null +++ b/caffe2/opt/backend_transformer_base.cc @@ -0,0 +1,122 @@ +#include "caffe2/opt/backend_transformer_base.h" +#include "caffe2/onnx/onnx_exporter.h" +#include "caffe2/utils/proto_utils.h" + +namespace caffe2 { + +namespace { +void AnnotateOpIndex(NetDef* net) { + int i = 0; + for (auto& op : *(net->mutable_op())) { + AddArgument(kNetPos, i++, &op); + } +} +} // namespace + +std::string BackendTransformerBase::getModelId(const NetDef& net) { + static std::atomic seq_id{0}; + auto model_id = + ArgumentHelper(net).GetSingleArgument(kModelId, ""); + if (model_id.empty()) { + model_id = "unnamed_" + c10::to_string(seq_id++); + } + return model_id; +} + +TensorProto BackendTransformerBase::wrapShapeInfoIntoTensorProto( + const std::string& name, + const ShapeInfo& shape_info) { + TensorProto t; + t.set_name(name); + t.set_data_type(shape_info.shape.data_type()); + for (const auto i : shape_info.shape.dims()) { + t.add_dims(i); + } + return t; +} + +std::unordered_map +BackendTransformerBase::ssaRewriteAndMapNames( + Workspace* ws, + NetDef* pred_net, + const std::unordered_set& weights, + const std::unordered_map& input_shape_hints) { + // Make sure weights do not contain output of any op. + for (const auto& op : pred_net->op()) { + for (const auto& output : op.output()) { + CAFFE_ENFORCE_EQ( + weights.count(output), + 0, + "Weight ", + output, + " shouldn't appear in the output"); + } + } + input_mapping_ = onnx::SsaRewrite(nullptr, pred_net, weights); + // Annote the ops with net position + AnnotateOpIndex(pred_net); + + // Need to add mapping for weights. This will be used to create new workspace + // with mapped weights. + for (const auto& w : weights) { + input_mapping_.emplace(w, w); + } + + // Since we are going to create a mapped workspace, we need to make sure that + // the parent workspace has the mapped blob names. If the blobs don't exist + // (usually such blobs are input tensor names), we exclude them from mapping. + std::vector exclude_mapping; + for (const auto kv : input_mapping_) { + reverse_input_mapping_.emplace(kv.second, kv.first); + if (!ws->HasBlob(kv.second)) { + exclude_mapping.emplace_back(kv.first); + } + } + for (const auto& i : exclude_mapping) { + input_mapping_.erase(i); + } + std::unordered_map shape_hints_mapped; + for (const auto& kv : input_shape_hints) { + const auto it = reverse_input_mapping_.find(kv.first); + if (it != reverse_input_mapping_.end()) { + shape_hints_mapped.emplace(it->second, kv.second); + } else { + shape_hints_mapped.emplace(kv.first, kv.second); + } + } + return shape_hints_mapped; +} + +ShapeInfoMap BackendTransformerBase::inferShapes( + Workspace* ws, + NetDef* pred_net, + const std::unordered_map& shape_hints_mapped, + const BoundShapeSpec& spec) { + ShapeInfoMap shape_map; + // Populate shapes from workplace + const std::vector ws_blobs = ws->Blobs(); + for (const auto& s : ws_blobs) { + auto shape_info = getShapeInfoFromBlob(ws->GetBlob(s)); + if (shape_info.dim_type != ShapeInfo::DimType::UNKNOWN) { + shape_map[s] = shape_info; + } + } + for (const auto& kv : shape_hints_mapped) { + shape_map.emplace( + std::piecewise_construct, + std::forward_as_tuple(kv.first), + std::forward_as_tuple(ShapeInfo::DimType::CONSTANT, kv.second)); + } + BoundShapeInferencer eng(spec); + eng.InferBoundShapeAndType(*pred_net, shape_map); + const auto& out_map = eng.shape_info(); + + for (const auto& kv : out_map) { + shape_map.emplace( + std::piecewise_construct, + std::forward_as_tuple(kv.first), + std::forward_as_tuple(kv.second.dim_type, kv.second.shape)); + } + return shape_map; +} +} // namespace caffe2 diff --git a/caffe2/opt/backend_transformer_base.h b/caffe2/opt/backend_transformer_base.h new file mode 100644 index 00000000000000..998005ec05ceab --- /dev/null +++ b/caffe2/opt/backend_transformer_base.h @@ -0,0 +1,70 @@ +#pragma once + +#include "caffe2/core/common.h" +#include "caffe2/core/workspace.h" +#include "caffe2/opt/bound_shape_inferencer.h" +#include "caffe2/proto/caffe2_pb.h" + +#include +#include +#include + +namespace caffe2 { +namespace { +constexpr char kNetPos[] = "net_pos"; +constexpr char kModelId[] = "model_id"; +} // namespace + +// This class contains some common functions for backend lowering and graph +// cutting +class BackendTransformerBase { + public: + BackendTransformerBase() {} + virtual ~BackendTransformerBase() {} + + const std::unordered_map& input_mapping() const { + return input_mapping_; + } + + const std::unordered_map& reverse_input_mapping() + const { + return reverse_input_mapping_; + } + + virtual void transform( + Workspace* ws, + NetDef* pred_net, + const std::vector& weight_names, + const std::unordered_map& shape_hints, + const std::unordered_set& blacklisted_ops) = 0; + + protected: + // get model ID from the NetDef + std::string getModelId(const NetDef& net); + + // SSA rewrite the net and return name mapping + std::unordered_map ssaRewriteAndMapNames( + Workspace* ws, + NetDef* pred_net, + const std::unordered_set& weights, + const std::unordered_map& input_shape_hints); + + // Wrap TensorShape into TensorProto + TensorProto wrapShapeInfoIntoTensorProto( + const std::string& name, + const ShapeInfo& shape_info); + + // Do bound shape inference and collect shape infos + ShapeInfoMap inferShapes( + Workspace* ws, + NetDef* pred_net, + const std::unordered_map& shape_hints_mapped, + const BoundShapeSpec& spec); + + // Input mapping of input name -> original input name + std::unordered_map input_mapping_; + + // Input mapping of orignal input name -> input name + std::unordered_map reverse_input_mapping_; +}; +} // namespace caffe2 diff --git a/caffe2/opt/onnxifi_transformer.cc b/caffe2/opt/onnxifi_transformer.cc index 94e85a0c76bb15..085546a7fff1d7 100644 --- a/caffe2/opt/onnxifi_transformer.cc +++ b/caffe2/opt/onnxifi_transformer.cc @@ -16,31 +16,9 @@ namespace caffe2 { namespace { - -using ShapeInfoMap = std::unordered_map; - -const std::string kNetPos("net_pos"); -const std::string kModelId("model_id"); const std::string kRealBatchSizeBlob("real_batch_size"); constexpr size_t kBufferSize = 64; -void AnnotateOpIndex(NetDef* net) { - int i = 0; - for (auto& op : *(net->mutable_op())) { - AddArgument(kNetPos, i++, &op); - } -} - -std::string GetModelId(const NetDef& net) { - static std::atomic seq_id{0}; - auto model_id = - ArgumentHelper(net).GetSingleArgument("model_id", ""); - if (model_id.empty()) { - model_id = "unnamed_" + c10::to_string(seq_id++); - } - return model_id; -} - // Convert ShapeInfo map to TensorShape map std::unordered_map StripShapeInfoMap( const ShapeInfoMap& info_map) { @@ -51,19 +29,6 @@ std::unordered_map StripShapeInfoMap( return shape_map; } -// Wrap TensorShape into TensorProto -TensorProto WrapShapeInfoIntoTensorProto( - const std::string& name, - const ShapeInfo& shape_info) { - TensorProto t; - t.set_name(name); - t.set_data_type(shape_info.shape.data_type()); - for (const auto i : shape_info.shape.dims()) { - t.add_dims(i); - } - return t; -} - uint64_t OnnxifiDataType(caffe2::TensorProto::DataType t) { #define CAFFE2_TO_ONNXIFI_TYPE(x, y) \ case (caffe2::TensorProto::x): \ @@ -85,38 +50,6 @@ uint64_t OnnxifiDataType(caffe2::TensorProto::DataType t) { #undef CAFFE2_TO_ONNXIFI_TYPE } -ShapeInfoMap InferShapes( - Workspace* ws, - NetDef* pred_net, - std::unordered_map* shape_hints_mapped, - const BoundShapeSpec& spec) { - ShapeInfoMap shape_map; - // Populate shapes from workplace - const std::vector ws_blobs = ws->Blobs(); - for (const auto& s : ws_blobs) { - auto shape_info = getShapeInfoFromBlob(ws->GetBlob(s)); - if (shape_info.dim_type != ShapeInfo::DimType::UNKNOWN) { - shape_map[s] = shape_info; - } - } - for (const auto& kv : *shape_hints_mapped) { - shape_map.emplace( - std::piecewise_construct, - std::forward_as_tuple(kv.first), - std::forward_as_tuple(ShapeInfo::DimType::CONSTANT, kv.second)); - } - BoundShapeInferencer eng(spec); - eng.InferBoundShapeAndType(*pred_net, shape_map); - const auto& out_map = eng.shape_info(); - - for (const auto& kv : out_map) { - shape_map.emplace( - std::piecewise_construct, - std::forward_as_tuple(kv.first), - std::forward_as_tuple(kv.second.dim_type, kv.second.shape)); - } - return shape_map; -} std::vector<::ONNX_NAMESPACE::ValueInfoProto> ConvertToValueInfo( const std::vector& names, @@ -372,7 +305,7 @@ NetDef ComposeResultNet( } // namespace OnnxifiTransformer::OnnxifiTransformer(const OnnxifiTransformerOptions& opts) - : opts_(opts) { + : BackendTransformerBase(), opts_(opts) { lib_ = onnx::initOnnxifiLibrary(); CAFFE_ENFORCE(lib_, "Cannot initialize ONNXIFI library"); CAFFE_ENFORCE_EQ( @@ -516,7 +449,7 @@ NetDef OnnxifiTransformer::SubnetToOnnxifiOpViaC2( } onnxifi_net.add_external_input(input); shape_arg->mutable_tensors()->Add()->CopyFrom( - WrapShapeInfoIntoTensorProto(input, shape_hints.at(i))); + wrapShapeInfoIntoTensorProto(input, shape_hints.at(i))); } // Compute output shape hints @@ -703,58 +636,6 @@ NetDef OnnxifiTransformer::SubnetToOnnxifiOpViaOnnx( return net_opt; } -std::unordered_map -OnnxifiTransformer::SsaRewriteAndMapNames( - Workspace* ws, - NetDef* pred_net, - const std::unordered_set& weights, - const std::unordered_map& input_shape_hints) { - // Make sure weights do not contain output of any op. - for (const auto& op : pred_net->op()) { - for (const auto& output : op.output()) { - CAFFE_ENFORCE_EQ( - weights.count(output), - 0, - "Weight ", - output, - " shouldn't appear in the output"); - } - } - input_mapping_ = onnx::SsaRewrite(nullptr, pred_net, weights); - // Annote the ops with net position - AnnotateOpIndex(pred_net); - - // Need to add mapping for weights. This will be used to create new workspace - // with mapped weights. - for (const auto& w : weights) { - input_mapping_.emplace(w, w); - } - - // Since we are going to create a mapped workspace, we need to make sure that - // the parent workspace has the mapped blob names. If the blobs don't exist - // (usually such blobs are input tensor names), we exclude them from mapping. - std::vector exclude_mapping; - for (const auto kv : input_mapping_) { - reverse_input_mapping_.emplace(kv.second, kv.first); - if (!ws->HasBlob(kv.second)) { - exclude_mapping.emplace_back(kv.first); - } - } - for (const auto& i : exclude_mapping) { - input_mapping_.erase(i); - } - std::unordered_map shape_hints_mapped; - for (const auto& kv : input_shape_hints) { - const auto it = reverse_input_mapping_.find(kv.first); - if (it != reverse_input_mapping_.end()) { - shape_hints_mapped.emplace(it->second, kv.second); - } else { - shape_hints_mapped.emplace(kv.first, kv.second); - } - } - return shape_hints_mapped; -} - NetDef OnnxifiTransformer::TransformViaC2( NetDef* pred_net, const std::unordered_set& weights, @@ -779,7 +660,7 @@ NetDef OnnxifiTransformer::TransformViaC2( } onnxBackendID backend_id = backend_ids_[idx_]; - auto c2_supports = [&shape_hints, &blacklisted_ops, backend, backend_id]( + auto c2_supports = [this, &shape_hints, &blacklisted_ops, backend, backend_id]( const caffe2::OperatorDef& op) { try { int pos = @@ -812,7 +693,7 @@ NetDef OnnxifiTransformer::TransformViaC2( return false; } shape_arg->mutable_tensors()->Add()->CopyFrom( - WrapShapeInfoIntoTensorProto(i, it->second)); + wrapShapeInfoIntoTensorProto(i, it->second)); } shape_arg = net.add_arg(); shape_arg->set_name("output_shape_info"); @@ -822,7 +703,7 @@ NetDef OnnxifiTransformer::TransformViaC2( return false; } shape_arg->mutable_tensors()->Add()->CopyFrom( - WrapShapeInfoIntoTensorProto(i, it->second)); + wrapShapeInfoIntoTensorProto(i, it->second)); } std::string c2_model_str; @@ -978,7 +859,7 @@ NetDef OnnxifiTransformer::TransformViaOnnx( // Cutting off the runnable part and replace with ONNXIFI ops. Asssume the nets // were topologically sorted -void OnnxifiTransformer::Transform( +void OnnxifiTransformer::transform( Workspace* ws, NetDef* pred_net, const std::vector& weight_names, @@ -987,8 +868,8 @@ void OnnxifiTransformer::Transform( CAFFE_ENFORCE(ws); CAFFE_ENFORCE(pred_net, "Predict net cannot be nullptr"); - // Get model id and reset Onnxifi op id to 0 - model_id_ = GetModelId(*pred_net); + // Get model id and reset Onnxifi op id to 0 + model_id_ = getModelId(*pred_net); onnxifi_op_id_ = 0; std::unordered_set weights( @@ -996,12 +877,12 @@ void OnnxifiTransformer::Transform( // SSA Rewrite the net auto shape_hints_mapped = - SsaRewriteAndMapNames(ws, pred_net, weights, input_shape_hints); + ssaRewriteAndMapNames(ws, pred_net, weights, input_shape_hints); // Populate shape info Workspace mapped_ws(ws, input_mapping_); - ShapeInfoMap shape_hints = InferShapes( - &mapped_ws, pred_net, &shape_hints_mapped, opts_.bound_shape_spec); + ShapeInfoMap shape_hints = inferShapes( + &mapped_ws, pred_net, shape_hints_mapped, opts_.bound_shape_spec); // Transform the net NetDef net_opt = opts_.use_onnx diff --git a/caffe2/opt/onnxifi_transformer.h b/caffe2/opt/onnxifi_transformer.h index e037eefe69684b..3877cd843456f0 100644 --- a/caffe2/opt/onnxifi_transformer.h +++ b/caffe2/opt/onnxifi_transformer.h @@ -7,12 +7,9 @@ #include "onnx/onnx_pb.h" -#include "caffe2/core/common.h" #include "caffe2/core/operator.h" -#include "caffe2/core/workspace.h" #include "caffe2/onnx/onnxifi_init.h" -#include "caffe2/opt/bound_shape_inferencer.h" -#include "caffe2/proto/caffe2_pb.h" +#include "caffe2/opt/backend_transformer_base.h" namespace caffe2 { namespace onnx { @@ -31,26 +28,17 @@ struct OnnxifiTransformerOptions { BoundShapeSpec bound_shape_spec; }; -class CAFFE2_API OnnxifiTransformer final { +class CAFFE2_API OnnxifiTransformer final : public BackendTransformerBase { public: explicit OnnxifiTransformer(const OnnxifiTransformerOptions& opts); - ~OnnxifiTransformer(); + ~OnnxifiTransformer() override; - void Transform( + void transform( Workspace* ws, NetDef* pred_net, const std::vector& weight_names, const std::unordered_map& shape_hints, - const std::unordered_set& blacklisted_ops); - - const std::unordered_map& input_mapping() const { - return input_mapping_; - } - - const std::unordered_map& reverse_input_mapping() - const { - return reverse_input_mapping_; - } + const std::unordered_set& blacklisted_ops) override; private: // Since we create new tensors during the conversion process, we actually need @@ -80,12 +68,6 @@ class CAFFE2_API OnnxifiTransformer final { const std::vector& external_inputs, const std::vector& external_outputs); - std::unordered_map SsaRewriteAndMapNames( - Workspace* ws, - NetDef* pred_net, - const std::unordered_set& weights, - const std::unordered_map& input_shape_hints); - // Transform by passing C2 proto to backend NetDef TransformViaC2( NetDef* pred_net, @@ -121,11 +103,5 @@ class CAFFE2_API OnnxifiTransformer final { // Backned IDs std::vector backend_ids_; - - // Input mapping of input name -> original input name - std::unordered_map input_mapping_; - - // Input mapping of orignal input name -> input name - std::unordered_map reverse_input_mapping_; }; } // namespace caffe2 diff --git a/caffe2/python/pybind_state.cc b/caffe2/python/pybind_state.cc index 609ee01e69dd18..aec3551992605f 100644 --- a/caffe2/python/pybind_state.cc +++ b/caffe2/python/pybind_state.cc @@ -1627,7 +1627,7 @@ void addGlobalMethods(py::module& m) { OnnxifiTransformer ts(opts); Workspace* curr_ws = GetCurrentWorkspace(); auto weight_names = curr_ws->Blobs(); - ts.Transform( + ts.transform( curr_ws, &pred_net, weight_names,