From: Ivan Vagin/AI Tools Lab /SRR/Engineer/삼성전자 Date: Thu, 15 Nov 2018 16:16:31 +0000 (+0300) Subject: [nnc] Implemented caffe2 importer interface (#2233) X-Git-Tag: nncc_backup~1308 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=0a16d2b26c754d2ef09d90ed760f20533289462e;p=platform%2Fcore%2Fml%2Fnnfw.git [nnc] Implemented caffe2 importer interface (#2233) * Implemented caffe2 importer interface * Minor fixes in driver sanity checks * Inited caffe2 model dumper Signed-off-by: Ivan Vagin --- diff --git a/contrib/nnc/CMakeLists.txt b/contrib/nnc/CMakeLists.txt index 949b3ab..c19d114 100644 --- a/contrib/nnc/CMakeLists.txt +++ b/contrib/nnc/CMakeLists.txt @@ -41,6 +41,15 @@ else() set(NNC_HDF5_SUPPORTED OFF) endif() +# We need protobuf and pytorch sources to generate caffe2.pb.h and caffe2.pb.cc +nncc_find_package(PytorchSource QUIET) +nncc_find_package(Protobuf QUIET) +if (Protobuf_FOUND AND PytorchSource_FOUND) + set(NNC_FRONTEND_CAFFE2_ENABLED ON) +else() + set(NNC_FRONTEND_CAFFE2_ENABLED OFF) +endif() + # Try to get compiled caffe proto and return if not successful # Note: this creates a target called "caffeproto" that contains compiled caffe.proto sources, # and after linking with it caffe.pb.h will be available as "caffe/proto/caffe.pb.h" @@ -100,6 +109,7 @@ endfunction() set(NNC_SOFT_BACKEND_DIR ${CMAKE_CURRENT_SOURCE_DIR}/passes/soft_backend) set(NNC_INTERPRETER_DIR ${CMAKE_CURRENT_SOURCE_DIR}/passes/interpreter) set(NNC_CAFFE_FRONTEND_DIR ${CMAKE_CURRENT_SOURCE_DIR}/passes/caffe_frontend) +set(NNC_CAFFE2_FRONTEND_DIR ${CMAKE_CURRENT_SOURCE_DIR}/passes/caffe2_frontend) set(NNC_TFLITE_FRONTEND_DIR ${CMAKE_CURRENT_SOURCE_DIR}/passes/tflite_frontend) set(NNC_CORE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/core) set(NNC_SUPPORT_DIR ${CMAKE_CURRENT_SOURCE_DIR}/support) diff --git a/contrib/nnc/driver/Driver.cpp b/contrib/nnc/driver/Driver.cpp index e25ff5f..28adfe8 100644 --- a/contrib/nnc/driver/Driver.cpp +++ b/contrib/nnc/driver/Driver.cpp @@ -17,6 +17,7 @@ #include "pass/PassData.h" #include "passes/caffe_frontend/caffe_importer.h" +#include "passes/caffe2_frontend/caffe2_importer.h" #include "passes/tflite_frontend/tflite_importer.h" #include "passes/interpreter/InterpreterPass.h" #include "passes/soft_backend/CPPGenerator.h" @@ -48,6 +49,28 @@ void Driver::runPasses() { } // runPasses +static std::string getFrontendOptionsString() { + std::string res; + +#ifdef NNC_FRONTEND_CAFFE_ENABLED + res += " '" + cli::caffeFrontend.getNames()[0] + "' "; +#endif // NNC_FRONTEND_CAFFE_ENABLED + +#ifdef NNC_FRONTEND_CAFFE2_ENABLED + res += " '" + cli::caffe2Frontend.getNames()[0] + "' "; +#endif // NNC_FRONTEND_CAFFE2_ENABLED + +#ifdef NNC_FRONTEND_ONNX_ENABLED + res += " '" + cli::ONNXFrontend.getNames()[0] + "' "; +#endif // NNC_FRONTEND_ONNX_ENABLED + +#ifdef NNC_FRONTEND_TFLITE_ENABLED + res += " '" + cli::tflFrontend.getNames()[0] + "' "; +#endif // NNC_FRONTEND_TFLITE_ENABLED + + return res; +} + /** * @brief Register frontend pass * @throw DriverException if errors occurred @@ -56,37 +79,34 @@ void Driver::registerFrontendPass() { std::unique_ptr pass; - if (cli::caffeFrontend.isDisabled() && cli::tflFrontend.isDisabled()) { - throw DriverException("frontends are not available"); - } - - if (cli::caffeFrontend && cli::tflFrontend) { - throw DriverException("only one of the following options are allowed" - " to be set in the same time: '" - + cli::caffeFrontend.getNames()[0] + "', '" - + cli::tflFrontend.getNames()[0] + "'"); - } + // For bool, the value false is converted to zero and the value true is converted to one + if (cli::caffeFrontend + cli::caffe2Frontend + cli::tflFrontend + cli::onnxFrontend != 1) + throw DriverException("One and only one of the following options are allowed and have to be set" + " to be set in the same time: " + getFrontendOptionsString()); if (cli::caffeFrontend) { #ifdef NNC_FRONTEND_CAFFE_ENABLED - pass = std::move(std::unique_ptr(new CaffeImporter(cli::inputFiles[0]))); + pass = std::move(std::unique_ptr(new CaffeImporter(cli::inputFile))); #endif // NNC_FRONTEND_CAFFE_ENABLED - } - else if ( cli::onnxFrontend ) - { + } else if (cli::caffe2Frontend) { +#ifdef NNC_FRONTEND_CAFFE2_ENABLED + // FIXME: caffe2 input shapes are not provided by model and must be set from cli + // current 'inputShapes' could provide only one shape, while model could has several inputs + pass = std::move(std::unique_ptr(new Caffe2Importer(cli::inputFile, cli::initNet, + {cli::inputShapes}))); +#endif // NNC_FRONTEND_CAFFE2_ENABLED + } else if ( cli::onnxFrontend ) { #ifdef NNC_FRONTEND_ONNX_ENABLED pass = std::move(std::unique_ptr(new ONNXImporter())); #endif // NNC_FRONTEND_ONNX_ENABLED } - else if ( cli::tflFrontend ) - { + else if ( cli::tflFrontend ) { #ifdef NNC_FRONTEND_TFLITE_ENABLED - pass = std::move(std::unique_ptr(new TfliteImporter(cli::inputFiles[0]))); + pass = std::move(std::unique_ptr(new TfliteImporter(cli::inputFile))); #endif // NNC_FRONTEND_TFLITE_ENABLED } else { - throw DriverException("one of the following options must be defined: '" - + cli::caffeFrontend.getNames()[0] + "', '" - + cli::tflFrontend.getNames()[0] + "'"); + throw DriverException("One of the following options must be defined: '" + + getFrontendOptionsString()); } _passManager.registerPass(std::move(pass)); diff --git a/contrib/nnc/driver/Options.cpp b/contrib/nnc/driver/Options.cpp index 0d6b714..9712223 100644 --- a/contrib/nnc/driver/Options.cpp +++ b/contrib/nnc/driver/Options.cpp @@ -56,6 +56,52 @@ Option onnxFrontend(optname("--onnx"), showopt(false) #endif // NNC_FRONTEND_ONNX_ENABLED ); + +Option caffe2Frontend(optname("--caffe2"), + overview("treat input file as Caffe2 model (predict_net.pb)"), + false, + optional(true), + optvalues(""), + nullptr, + separators(""), +#ifdef NNC_FRONTEND_CAFFE2_ENABLED + showopt(true), +#else + showopt(false), +#endif // NNC_FRONTEND_CAFFE2_ENABLED + IOption::Group::caffe2 + ); + +Option> inputShapes(optname("--input-shape"), + overview("Shape of caffe2 input"), + std::vector{}, + optional(false), + optvalues(""), + nullptr, + separators(""), +#ifdef NNC_FRONTEND_CAFFE2_ENABLED + showopt(true), +#else + showopt(false), +#endif // NNC_FRONTEND_CAFFE2_ENABLED + IOption::Group::caffe2 + ); + +Option initNet(optname("--init-net"), + overview("path to Caffe2 model weights (init_net.pb)"), + std::string(), + optional(false), + optvalues(""), + nullptr, + separators(""), +#ifdef NNC_FRONTEND_CAFFE2_ENABLED + showopt(true), +#else + showopt(false), +#endif // NNC_FRONTEND_CAFFE2_ENABLED + IOption::Group::caffe2 + ); + Option tflFrontend(optname("--tflite"), overview("treat input file as Tensor Flow Lite model"), false, @@ -81,14 +127,12 @@ Option target(optname("--target"), /** * Options for *frontend* */ -Option> inputFiles(optname("--nnmodel, -m"), - overview("specify input files with serialized NN models: " - "single model file must be provided for caffe, tflite and onnx frameworks; " - "two model files must be specified for caffe2 framework (init_net and predict_net)"), - std::vector{}, - optional(false), - optvalues(""), - checkModelFiles); +Option inputFile(optname("--nnmodel, -m"), + overview("specify input file with serialized NN models"), + std::string(), + optional(false), + optvalues(""), + checkInFile); /** * Options for *backend* diff --git a/contrib/nnc/include/Definitions.h.in b/contrib/nnc/include/Definitions.h.in index 84b5388..070cdd2 100644 --- a/contrib/nnc/include/Definitions.h.in +++ b/contrib/nnc/include/Definitions.h.in @@ -32,6 +32,11 @@ #cmakedefine NNC_FRONTEND_CAFFE_ENABLED /** + * @brief define that CAFFE2 frontend is enabled + */ +#cmakedefine NNC_FRONTEND_CAFFE2_ENABLED + +/** * @brief define that ONNX frontend is enabled */ #cmakedefine NNC_FRONTEND_ONNX_ENABLED diff --git a/contrib/nnc/include/option/Options.h b/contrib/nnc/include/option/Options.h index f13ee4a..79ab690 100644 --- a/contrib/nnc/include/option/Options.h +++ b/contrib/nnc/include/option/Options.h @@ -28,9 +28,13 @@ namespace cli /** * Options for compiler driver */ +extern Option caffe2Frontend; // frontend for CAFFE2 AI framework +extern Option> inputShapes; +extern Option initNet; + extern Option caffeFrontend; // frontend for CAFFE AI framework extern Option tflFrontend; // frontend for TensorFlow Lite AI framework -extern Option onnxFrontend; // frontend for ONNX AI framework +extern Option onnxFrontend; // frontend for ONNX AI framework // valid values for target option #define NNC_TARGET_X86_CPP "x86-c++" @@ -41,7 +45,7 @@ extern Option target; // kind of target for which compiler generat /** * Frontend options */ -extern Option> inputFiles; // files contains model of specific AI framework +extern Option inputFile; // files contains model of specific AI framework /** * Options for backend diff --git a/contrib/nnc/include/passes/caffe2_frontend/caffe2_importer.h b/contrib/nnc/include/passes/caffe2_frontend/caffe2_importer.h new file mode 100644 index 0000000..b9fa140 --- /dev/null +++ b/contrib/nnc/include/passes/caffe2_frontend/caffe2_importer.h @@ -0,0 +1,133 @@ + +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef NNCC_CAFFE2_IMPORTER_H +#define NNCC_CAFFE2_IMPORTER_H + +#include +#include +#include + +#include "passes/common_frontend/nn_importer.h" + +#include "pass/Pass.h" +#include "pass/PassData.h" + +// Use forward declarations for non interface classes +namespace caffe2 { +class OperatorDef; +class NetDef; +} +namespace nnc { +// class Caffe2OpCreator; +enum class SupportedCaffe2OpType : uint8_t; +} + +namespace nnc { + +using MIRTensor = nnc::mir::TensorVariant; + +class Caffe2Importer : public NNImporter, public Pass { +public: + explicit Caffe2Importer(std::string predictNet, std::string initNet, + std::vector> inputShapes); + + /** + * @brief Import model from file, must be called before 'createIR' method + * @throw PassException in case, if model couldn't be parsed or NNC doesn't support it + */ + void import() override; + + /** + * @brief Create MIR graph from caffe model, must be called after 'import' method + * @return MIR graph, corresponding to processed caffe model + */ + mir::Graph* createIR() override; + + PassData run(PassData) override; + void cleanup() override; + + ~Caffe2Importer(); + +private: + std::string _predictNet; + std::string _initNet; + mir::Graph* _graph; + std::unique_ptr<::caffe2::NetDef> _net; + // std::unique_ptr _opCreator; + std::vector _inputShapes; + + static const std::map _operatorTypes; + std::set _problemsOpSet; + + // This map maps caffe2 operators names to MIR operators + // that correspond to previous caffe2 operators + std::map _blobNameToIODescriptor; + mir::Operation* _lastNode; + + std::map> _MIRTensors; + + /** + * @brief Pass through caffe2 graph and collect ops unsupported by NNC + * @throw PassException with message, containing detected problems + */ + // void collectUnsupportedOps(); + + /** + * @brief Collecting unsupported parts of caffe2 operator + */ + // void collectUnsupportedOp(const ::caffe2::OperatorDef&); + + /** + * @brief Creating MIR node from single caffe2 operator + */ + // void createMIRNodesFromOp(const ::caffe2::OperatorDef&); + + /** + * @brief Since caffe2 tensor values stored separately (in init_net) - preload them in _MIRTensors + */ + // void preloadAllTensors(); + + /** + * @brief Creates MIR tensor from caffe2 givenTensorFill op + */ + // std::shared_ptr createTensor(const ::caffe2::OperatorDef&); + + /** + * @brief Returns MIR ops, under given caffe2 op + */ + // std::vector getInputMIROps(const ::caffe2::OperatorDef&); + + /** + * @brief create MIR inputs with given names and shapes + */ + // void createGraphInputs(const std::vector&, const std::vector&); + + /** + * @brief Mark output MIR nodes + */ + // void setGraphOutputs(); + + /** + * @brief Set MIR node names + */ + // void setIrNodeNames(); +}; + +} // namespace nnc + +#endif // NNCC_CAFFE2_IMPORTER_H diff --git a/contrib/nnc/include/passes/common_frontend/model_allocation.h b/contrib/nnc/include/passes/common_frontend/model_allocation.h index b4c0e05..061a343 100644 --- a/contrib/nnc/include/passes/common_frontend/model_allocation.h +++ b/contrib/nnc/include/passes/common_frontend/model_allocation.h @@ -20,12 +20,10 @@ #include #include -namespace nnc -{ +namespace nnc { // Class that can be used to memory map a file with NN model -class ModelAllocation -{ +class ModelAllocation { public: explicit ModelAllocation(std::string filename); virtual ~ModelAllocation(); @@ -34,17 +32,16 @@ public: size_t getNumBytes(); private: - enum MmapState - { - MAPPED, - UNMAPPED + enum MmapState { + mapped, + unmapped }; - MmapState mmapState = UNMAPPED; - void *dataPnt = nullptr; - size_t numBytes = 0; + MmapState _mmapState = unmapped; + void* _dataPnt = nullptr; + size_t _numBytes = 0; - int fd = -1; + int _fd = -1; }; } // namespace nnc diff --git a/contrib/nnc/include/passes/common_frontend/proto_helper.h b/contrib/nnc/include/passes/common_frontend/proto_helper.h new file mode 100644 index 0000000..3d402e2 --- /dev/null +++ b/contrib/nnc/include/passes/common_frontend/proto_helper.h @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef NNCC_PROTO_HELPER_H +#define NNCC_PROTO_HELPER_H + +#include +#include +#include +#include + +#include "google/protobuf/io/coded_stream.h" +#include "google/protobuf/io/zero_copy_stream_impl.h" +#include "google/protobuf/text_format.h" + +#include "passes/common_frontend/model_allocation.h" + +namespace nnc { + +const int protoBytesLimit = INT_MAX; +const int protoBytesWarningLimit = 1024 * 1024 * 512; + +template +bool readProtoFromTextFile(const char* filename, protoType* proto) { + std::unique_ptr protoMap(new ModelAllocation{filename}); + + google::protobuf::io::CodedInputStream coded_input( + (const google::protobuf::uint8*)protoMap->getDataPnt(), protoMap->getNumBytes()); + coded_input.SetTotalBytesLimit(protoBytesLimit, protoBytesWarningLimit); + + bool success = google::protobuf::TextFormat::Parse(&coded_input, proto); + + return success; +} + +template +bool readProtoFromBinaryFile(const char* filename, protoType* proto) { + std::unique_ptr protoMap(new ModelAllocation{filename}); + + google::protobuf::io::CodedInputStream coded_input( + (const google::protobuf::uint8*)protoMap->getDataPnt(), protoMap->getNumBytes()); + coded_input.SetTotalBytesLimit(protoBytesLimit, protoBytesWarningLimit); + + bool success = proto->ParseFromCodedStream(&coded_input); + + return success; +} + +} // namespace nnc + +#endif // NNCC_PROTO_HELPER_H diff --git a/contrib/nnc/include/support/CommandLine.h b/contrib/nnc/include/support/CommandLine.h index dabb65d..3c1c5af 100644 --- a/contrib/nnc/include/support/CommandLine.h +++ b/contrib/nnc/include/support/CommandLine.h @@ -194,7 +194,7 @@ public: { none = 0, caffe2 = 1, - onnx = 2 + onnx = 2 // 'onnx' is currently unused }; /** @@ -539,11 +539,12 @@ Option::Option(const std::vector &optnames, _checker = checker; _is_enabled = enabled; - assert((_is_enabled || _is_optional) && "disabled option can't be required"); + assert((_is_enabled || _is_optional || group != IOption::Group::none) && "disabled non-group option can't be required"); _group = group; - _can_have_several_vals = std::is_same>::value; + _can_have_several_vals = std::is_same>::value + || std::is_same>::value; assert(!(_can_have_several_vals && !_seps.empty()) && "option with several values can't have separators"); // register new option for parser @@ -556,7 +557,6 @@ Option::Option(const std::vector &optnames, // prototypes of option checker functions // void checkInFile(const Option &); -void checkModelFiles(const Option> &); void checkOutFile(const Option &); void checkOutDir(const Option &); void checkDebugFile(const Option &); diff --git a/contrib/nnc/passes/CMakeLists.txt b/contrib/nnc/passes/CMakeLists.txt index 8c4340c..0c93a45 100644 --- a/contrib/nnc/passes/CMakeLists.txt +++ b/contrib/nnc/passes/CMakeLists.txt @@ -11,6 +11,7 @@ add_subdirectory(common_frontend) add_subdirectory(tflite_frontend) add_subdirectory(caffe_frontend) add_subdirectory(onnx_frontend) +add_subdirectory(caffe2_frontend) # # BACKENDs diff --git a/contrib/nnc/passes/caffe2_frontend/CMakeLists.txt b/contrib/nnc/passes/caffe2_frontend/CMakeLists.txt new file mode 100644 index 0000000..82dceb3 --- /dev/null +++ b/contrib/nnc/passes/caffe2_frontend/CMakeLists.txt @@ -0,0 +1,34 @@ +if (NOT NNC_FRONTEND_CAFFE2_ENABLED) + return () +endif() + +################### +# Caffe2 proto # +################### + +# Compile caffe2.proto from pytroch sources +# Produces CAFFE2_PROTO_SOURCES and CAFFE2_PROTO_INCLUDE_DIRS variables +Protobuf_Generate(CAFFE2_PROTO "${CMAKE_CURRENT_BINARY_DIR}/generated/caffe2" + "${PytorchSource_DIR}" "caffe2/proto/caffe2.proto") + +add_library(caffe2proto STATIC ${CAFFE2_PROTO_SOURCES}) +set_target_properties(caffe2proto PROPERTIES POSITION_INDEPENDENT_CODE ON) +target_include_directories(caffe2proto PUBLIC ${CAFFE2_PROTO_INCLUDE_DIRS}) +target_link_libraries(caffe2proto libprotobuf) + +################### +# Caffe2 importer # +################### + +file(GLOB caffe2_importer_sources *.cpp) + +add_nnc_library(caffe2_importer SHARED ${caffe2_importer_sources} + ${caffe2_importer_headers}) + +target_link_libraries(caffe2_importer PUBLIC caffe2proto) +target_link_libraries(caffe2_importer PUBLIC nn_import_common) +target_link_libraries(caffe2_importer PRIVATE nnc_support) +target_link_libraries(caffe2_importer PRIVATE nnc_core) + +# install caffe2 frontend library +install_nnc_library(caffe2_importer) diff --git a/contrib/nnc/passes/caffe2_frontend/caffe2_importer.cpp b/contrib/nnc/passes/caffe2_frontend/caffe2_importer.cpp new file mode 100644 index 0000000..69bc7d8 --- /dev/null +++ b/contrib/nnc/passes/caffe2_frontend/caffe2_importer.cpp @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include + +#include "passes/caffe2_frontend/caffe2_importer.h" +#include "passes/common_frontend/shape_helper.h" +#include "passes/common_frontend/proto_helper.h" + +#include "caffe2/proto/caffe2.pb.h" + +#include "caffe2_op_types.h" +// #include "caffe2_op_creator.h" + +#include "core/modelIR/Shape.h" +#include "core/modelIR/operations/VariableOp.h" +#include "pass/PassException.h" + +#include "caffe2_proto_helper.h" + +namespace nnc { + +using namespace ::caffe2; +using VariableOp = nnc::mir::ops::VariableOp; +using nnc::mir::Shape; + +Caffe2Importer::Caffe2Importer(std::string predictNet, std::string initNet, + std::vector> shapes) : + _predictNet(std::move(predictNet)), + _initNet(std::move(initNet)), + _graph(new mir::Graph())/*, + _opCreator(new Caffe2OpCreator(_graph))*/ { + for(auto& shape : shapes) + _inputShapes.emplace_back(shape); +} + +Caffe2Importer::~Caffe2Importer()=default; + +PassData Caffe2Importer::run(PassData) { + import(); + return createIR(); +} + +void Caffe2Importer::cleanup() { + delete _graph; +} + +void Caffe2Importer::import() { + GOOGLE_PROTOBUF_VERIFY_VERSION; + + _net.reset(new NetDef()); + if (!readProtoFromBinaryFile<::caffe2::NetDef>(_predictNet.c_str(), _net.get())) + throw PassException("Could not load model: " + _predictNet+ "\n"); + + std::unique_ptr net2; + net2.reset(new NetDef()); + if (!readProtoFromBinaryFile<::caffe2::NetDef>(_initNet.c_str(), net2.get())) + throw PassException("Could not load model: " + _initNet+ "\n"); + _net->MergeFrom(*net2); + + // collectUnsupportedOps(); + + // preloadAllTensors(); +} + +mir::Graph* Caffe2Importer::createIR() { + throw PassException("Caffe2: NYI"); + /* + for (auto& op : _net->op()) + createMIRNodesFromOp(op); + + setIrNodeNames(); + setGraphOutputs(); + */ + + return _graph; +} + +const std::map Caffe2Importer::_operatorTypes = { + {"AveragePool", SupportedCaffe2OpType::averagePool}, + {"Conv", SupportedCaffe2OpType::conv}, + {"Dropout", SupportedCaffe2OpType::dropout}, + {"FC", SupportedCaffe2OpType::FC}, + {"GivenTensorFill", SupportedCaffe2OpType::givenTensorFill}, + {"MaxPool", SupportedCaffe2OpType::maxPool}, + {"Relu", SupportedCaffe2OpType::relu}, + {"Softmax", SupportedCaffe2OpType::softmax}, + {"Sum", SupportedCaffe2OpType::sum} +}; + +} // namespace nnc diff --git a/contrib/nnc/passes/caffe2_frontend/caffe2_op_types.h b/contrib/nnc/passes/caffe2_frontend/caffe2_op_types.h new file mode 100644 index 0000000..6aa56e7 --- /dev/null +++ b/contrib/nnc/passes/caffe2_frontend/caffe2_op_types.h @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef NNCC_CAFFE2_OP_TYPES_H +#define NNCC_CAFFE2_OP_TYPES_H + +namespace nnc { + +enum class SupportedCaffe2OpType : uint8_t { + averagePool, + conv, + dropout, + FC, + givenTensorFill, + maxPool, + relu, + softmax, + sum +}; + +} // namespace nnc + +#endif // NNCC_CAFFE2_OP_TYPES_H diff --git a/contrib/nnc/passes/caffe_frontend/proto_reader.h b/contrib/nnc/passes/caffe2_frontend/caffe2_proto_helper.cpp similarity index 54% rename from contrib/nnc/passes/caffe_frontend/proto_reader.h rename to contrib/nnc/passes/caffe2_frontend/caffe2_proto_helper.cpp index b27e7b1..b13436c 100644 --- a/contrib/nnc/passes/caffe_frontend/proto_reader.h +++ b/contrib/nnc/passes/caffe2_frontend/caffe2_proto_helper.cpp @@ -14,25 +14,26 @@ * limitations under the License. */ -#ifndef NNCC_PROTO_READER_H -#define NNCC_PROTO_READER_H +#include "caffe2/proto/caffe2.pb.h" -#include "caffe/proto/caffe.pb.h" +#include "pass/PassException.h" -#include "google/protobuf/io/coded_stream.h" -#include "google/protobuf/io/zero_copy_stream_impl.h" -#include "google/protobuf/text_format.h" +#include "caffe2_proto_helper.h" namespace nnc { -using google::protobuf::io::FileInputStream; -using google::protobuf::io::ZeroCopyInputStream; -using google::protobuf::io::CodedInputStream; +const ::caffe2::Argument& findArgumentByName(RepArgument args, std::string name) { + for (auto& arg : args) + if (arg.name() == name) + return arg; + throw PassException("Can't find argument with name: " + name); +} -bool readProtoFromTextFile(const char* filename, ::caffe::NetParameter* proto); - -bool readProtoFromBinaryFile(const char* filename, ::caffe::NetParameter* proto); +const bool hasArgument(RepArgument args, std::string name) { + for (auto& arg : args) + if (arg.name() == name) + return true; + return false; +} } // namespace nnc - -#endif // NNCC_PROTO_READER_H diff --git a/contrib/nnc/passes/caffe2_frontend/caffe2_proto_helper.h b/contrib/nnc/passes/caffe2_frontend/caffe2_proto_helper.h new file mode 100644 index 0000000..750f396 --- /dev/null +++ b/contrib/nnc/passes/caffe2_frontend/caffe2_proto_helper.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef NNCC_CAFFE2_PROTO_HELPER_H +#define NNCC_CAFFE2_PROTO_HELPER_H + +namespace nnc { + +using RepArgument = const ::google::protobuf::RepeatedPtrField<::caffe2::Argument>&; + +const ::caffe2::Argument& findArgumentByName(RepArgument args, std::string name); +const bool hasArgument(RepArgument args, std::string name); + +} // namespace nnc + +#endif // NNCC_CAFFE2_PROTO_HELPER_H diff --git a/contrib/nnc/passes/caffe_frontend/caffe_importer.cpp b/contrib/nnc/passes/caffe_frontend/caffe_importer.cpp index c663fd0..f34d699 100644 --- a/contrib/nnc/passes/caffe_frontend/caffe_importer.cpp +++ b/contrib/nnc/passes/caffe_frontend/caffe_importer.cpp @@ -24,13 +24,13 @@ #include "passes/caffe_frontend/caffe_importer.h" #include "caffe_op_creator.h" #include "caffe_op_types.h" -#include "proto_reader.h" #include "core/modelIR/Shape.h" #include "core/modelIR/TensorUtil.h" #include "pass/PassException.h" #include "passes/common_frontend/shape_helper.h" +#include "passes/common_frontend/proto_helper.h" namespace nnc { @@ -49,7 +49,7 @@ void CaffeImporter::import() { GOOGLE_PROTOBUF_VERIFY_VERSION; _net.reset(new NetParameter()); - if (!readProtoFromBinaryFile(_modelFilename.c_str(), _net.get())) + if (!readProtoFromBinaryFile<::caffe::NetParameter>(_modelFilename.c_str(), _net.get())) throw PassException("Could not load model: " + _modelFilename + "\n"); collectUnsupportedLayers(); diff --git a/contrib/nnc/passes/caffe_frontend/proto_reader.cpp b/contrib/nnc/passes/caffe_frontend/proto_reader.cpp deleted file mode 100644 index 3a366b9..0000000 --- a/contrib/nnc/passes/caffe_frontend/proto_reader.cpp +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include -#include -#include -#include - -#include "caffe/proto/caffe.pb.h" - -#include "proto_reader.h" - -namespace nnc { - -const int protoBytesLimit = INT_MAX; -const int protoBytesWarningLimit = 1024 * 1024 * 512; - -bool readProtoFromTextFile(const char* filename, ::caffe::NetParameter* proto) { - int fd = open(filename, O_RDONLY); - if (fd == -1) { - std::cout << "File not found: " << filename << std::endl; - return false; - } - - FileInputStream input{fd}; - - bool success = google::protobuf::TextFormat::Parse(&input, proto); - - close(fd); - - return success; -} - -bool readProtoFromBinaryFile(const char* filename, ::caffe::NetParameter* proto) { - int fd = open(filename, O_RDONLY); - if (fd == -1) { - std::cout << "File not found: " << filename << std::endl; - return false; - } - - FileInputStream raw_input{fd}; - CodedInputStream coded_input{&raw_input}; - coded_input.SetTotalBytesLimit(protoBytesLimit, protoBytesWarningLimit); - - bool success = proto->ParseFromCodedStream(&coded_input); - - close(fd); - - return success; -} - -} // namespace nnc diff --git a/contrib/nnc/passes/common_frontend/CMakeLists.txt b/contrib/nnc/passes/common_frontend/CMakeLists.txt index 7d6578d..ab2b230 100644 --- a/contrib/nnc/passes/common_frontend/CMakeLists.txt +++ b/contrib/nnc/passes/common_frontend/CMakeLists.txt @@ -8,4 +8,4 @@ set(COMMON_SOURCES add_library(nn_import_common STATIC ${COMMON_SOURCES}) set_target_properties(nn_import_common PROPERTIES POSITION_INDEPENDENT_CODE ON) -target_link_libraries(nn_import_common PRIVATE nnc_core nnc_support) \ No newline at end of file +target_link_libraries(nn_import_common PRIVATE nnc_core nnc_support) diff --git a/contrib/nnc/passes/common_frontend/model_allocation.cpp b/contrib/nnc/passes/common_frontend/model_allocation.cpp index fa6969c..57c51a4 100644 --- a/contrib/nnc/passes/common_frontend/model_allocation.cpp +++ b/contrib/nnc/passes/common_frontend/model_allocation.cpp @@ -21,52 +21,40 @@ #include "passes/common_frontend/model_allocation.h" -namespace nnc -{ +namespace nnc { -ModelAllocation::ModelAllocation(std::string filename) -{ +ModelAllocation::ModelAllocation(std::string filename) { using stat = struct stat; - fd = open(filename.c_str(), O_RDONLY); + _fd = open(filename.c_str(), O_RDONLY); - if (fd == -1) - { + if (_fd == -1) return; - } stat st{}; - int flag = fstat(fd, &st); + int flag = fstat(_fd, &st); if (flag == -1) - { return; - } - numBytes = st.st_size; + _numBytes = st.st_size; - dataPnt = mmap(nullptr, numBytes, PROT_READ, MAP_SHARED, fd, 0); + _dataPnt = mmap(nullptr, _numBytes, PROT_READ, MAP_SHARED, _fd, 0); - if (dataPnt != MAP_FAILED) - { - mmapState = MAPPED; - } + if (_dataPnt != MAP_FAILED) + _mmapState = mapped; } -ModelAllocation::~ModelAllocation() -{ - if (mmapState == MAPPED) - { - munmap(dataPnt, numBytes); - mmapState = UNMAPPED; +ModelAllocation::~ModelAllocation() { + if (_mmapState == mapped) { + munmap(_dataPnt, _numBytes); + _mmapState = unmapped; } - if (fd != -1) - { - close(fd); - } + if (_fd != -1) + close(_fd); } -const void *ModelAllocation::getDataPnt() { return mmapState == MAPPED ? dataPnt : nullptr; } +const void *ModelAllocation::getDataPnt() { return _mmapState == mapped ? _dataPnt : nullptr; } -size_t ModelAllocation::getNumBytes() { return mmapState == MAPPED ? numBytes : 0; } +size_t ModelAllocation::getNumBytes() { return _mmapState == mapped ? _numBytes : 0; } } // namespace nnc diff --git a/contrib/nnc/support/CLOptionChecker.cpp b/contrib/nnc/support/CLOptionChecker.cpp index 5680b9f..3e73760 100644 --- a/contrib/nnc/support/CLOptionChecker.cpp +++ b/contrib/nnc/support/CLOptionChecker.cpp @@ -34,23 +34,6 @@ void checkInFile(const Option &in_file) { fclose(f); } // checkInFile -void checkModelFiles(const Option> &in_files) { - if (in_files.empty()) - throw BadOption("Model file names should not be empty"); - - if ((tflFrontend || caffeFrontend || onnxFrontend) && in_files.size() != 1) - throw BadOption("For caffe, tflite and onnx frameworks single model file must be specified"); - // else if (cli::caffe2Frontend && in_files.size() != 2) - // throw BadOption("For caffe2 framework two model files must be specified (init_net and predict_net)"); - - for (auto& f_name : in_files) { - auto f = fopen(f_name.c_str(), "rb"); - if (!f) - throw BadOption("Cannot open file <" + f_name + ">"); - fclose(f); - } -} // checkModelFiles - void checkOutFile(const Option &out_file) { if ( out_file.empty() ) throw BadOption("Output file name should not be empty"); diff --git a/contrib/nnc/support/CommandLine.cpp b/contrib/nnc/support/CommandLine.cpp index 4f3e3df..14e7cde 100644 --- a/contrib/nnc/support/CommandLine.cpp +++ b/contrib/nnc/support/CommandLine.cpp @@ -565,6 +565,14 @@ void Option>::setValue(const std::string &val) this->push_back(val); } +// vector of ints +template <> +void Option>::setValue(const std::string &val) +{ + if (!val.empty()) + this->push_back(stoi(val)); +} + // bool template <> void Option::setValue(const std::string &val) diff --git a/contrib/nnc/tests/import/caffe.cpp b/contrib/nnc/tests/import/caffe.cpp index c277d0c..ff0d66b 100644 --- a/contrib/nnc/tests/import/caffe.cpp +++ b/contrib/nnc/tests/import/caffe.cpp @@ -27,9 +27,8 @@ int main(int argc, const char **argv) { return 1; cli::CommandLine::getParser()->parseCommandLine(argc, argv); - std::string modelName = cli::inputFiles[0]; - nnc::CaffeImporter importer{modelName}; + nnc::CaffeImporter importer{cli::inputFile}; importer.import(); @@ -37,7 +36,7 @@ int main(int argc, const char **argv) { importer.createIR(); } catch (...) { - std::cout << "Could not create IR for model \"" << modelName << "\"" << std::endl; + std::cout << "Could not create IR for model \"" << cli::inputFile << "\"" << std::endl; return 1; } diff --git a/contrib/nnc/tests/import/tflite.cpp b/contrib/nnc/tests/import/tflite.cpp index 1dc642d..bebc32e 100644 --- a/contrib/nnc/tests/import/tflite.cpp +++ b/contrib/nnc/tests/import/tflite.cpp @@ -30,9 +30,7 @@ int main(int argc, const char **argv) } cli::CommandLine::getParser()->parseCommandLine(argc, argv); - std::string modelName = cli::inputFiles[0]; - - nnc::TfliteImporter importer{modelName}; + nnc::TfliteImporter importer{cli::inputFile}; importer.import(); @@ -42,7 +40,7 @@ int main(int argc, const char **argv) } catch (...) { - std::cout << "Could not create IR for model \"" << modelName << "\"" << std::endl; + std::cout << "Could not create IR for model \"" << cli::inputFile << "\"" << std::endl; return 1; } diff --git a/contrib/nnc/utils/CMakeLists.txt b/contrib/nnc/utils/CMakeLists.txt index cd90353..51399c4 100644 --- a/contrib/nnc/utils/CMakeLists.txt +++ b/contrib/nnc/utils/CMakeLists.txt @@ -7,3 +7,4 @@ add_subdirectory(tflite_model_generator) # dumpers of NN models add_subdirectory(tflite_dot_dumper) add_subdirectory(caffe_dot_dumper) +add_subdirectory(caffe2_dot_dumper) diff --git a/contrib/nnc/utils/caffe2_dot_dumper/CMakeLists.txt b/contrib/nnc/utils/caffe2_dot_dumper/CMakeLists.txt new file mode 100644 index 0000000..3e9a3a4 --- /dev/null +++ b/contrib/nnc/utils/caffe2_dot_dumper/CMakeLists.txt @@ -0,0 +1,7 @@ +if (NOT TARGET caffe2_importer) + return() +endif() + +add_nncc_example_executable(caffe2_model_dumper ${OPTIONS_SRC} model_dump.cpp) +nncc_target_link_libraries(caffe2_model_dumper nnc_support caffe2_importer) +target_include_directories(caffe2_model_dumper PRIVATE ${NNC_CAFFE2_FRONTEND_DIR}) diff --git a/contrib/nnc/utils/caffe2_dot_dumper/model_dump.cpp b/contrib/nnc/utils/caffe2_dot_dumper/model_dump.cpp new file mode 100644 index 0000000..3dfca22 --- /dev/null +++ b/contrib/nnc/utils/caffe2_dot_dumper/model_dump.cpp @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include "support/CommandLine.h" +#include "option/Options.h" +#include "passes/caffe2_frontend/caffe2_importer.h" +#include "core/modelIR/Graph.h" +#include "core/modelIR/IrDotDumper.h" +#include "core/modelIR/ShapeInference.h" +#include "pass/PassException.h" + +using namespace nnc; +using namespace nnc::mir; +using namespace nnc::cli; + +int main(int argc, const char **argv) { + cli::CommandLine::getParser()->parseCommandLine(argc, argv, false); + + // FIXME: caffe2 input shapes are not provided by model and must be set from cli + nnc::Caffe2Importer importer{cli::inputFile, cli::initNet, {cli::inputShapes}}; + + try { + importer.import(); + IrDotDumper dotDumper; + ShapeInference inf; + auto g = static_cast(importer.createIR()); + g->accept(&inf); + g->accept(&dotDumper); + dotDumper.writeDot(std::cout); + } + catch (PassException &e) { + std::cout << "Error: " << e.what() << std::endl; + return -1; + } + + return 0; +} diff --git a/contrib/nnc/utils/caffe_dot_dumper/model_dump.cpp b/contrib/nnc/utils/caffe_dot_dumper/model_dump.cpp index 4493648..7bffc6e 100644 --- a/contrib/nnc/utils/caffe_dot_dumper/model_dump.cpp +++ b/contrib/nnc/utils/caffe_dot_dumper/model_dump.cpp @@ -30,9 +30,7 @@ using namespace nnc::cli; int main(int argc, const char **argv) { cli::CommandLine::getParser()->parseCommandLine(argc, argv, false); - std::string model = cli::inputFiles[0]; - - nnc::CaffeImporter importer{model}; + nnc::CaffeImporter importer{cli::inputFile}; try { importer.import(); diff --git a/contrib/nnc/utils/tflite_dot_dumper/sanity_check.cpp b/contrib/nnc/utils/tflite_dot_dumper/sanity_check.cpp index 9fa193f..31ac07a 100644 --- a/contrib/nnc/utils/tflite_dot_dumper/sanity_check.cpp +++ b/contrib/nnc/utils/tflite_dot_dumper/sanity_check.cpp @@ -30,9 +30,8 @@ using namespace nnc::cli; int main(int argc, const char **argv) { cli::CommandLine::getParser()->parseCommandLine(argc, argv, false); - std::string model = cli::inputFiles[0]; - nnc::TfliteImporter importer{model}; + nnc::TfliteImporter importer{cli::inputFile}; try { importer.import();