From 539cef9c16814598a2bca0042d0282015c811d23 Mon Sep 17 00:00:00 2001 From: =?utf8?q?=D0=90=D0=BD=D0=B4=D1=80=D0=B5=D0=B9=20=D0=A2=D0=B8=D1=89?= =?utf8?q?=D0=B5=D0=BD=D0=BA=D0=BE/AI=20Tools=20Lab=20/SRR/Staff=20Enginee?= =?utf8?q?r/=EC=82=BC=EC=84=B1=EC=A0=84=EC=9E=90?= Date: Wed, 30 Jan 2019 18:56:51 +0300 Subject: [PATCH] Multi input data (#2959) Multi-files in command line option --input-model-data. Some nets require several files as input in execution. Signed-off-by: Andrew V. Tischenko a.tischenko@partner.samsung.com --- contrib/nnc/driver/Options.cpp | 8 +- contrib/nnc/include/option/Options.h | 2 +- .../include/passes/interpreter/InterpreterPass.h | 4 +- contrib/nnc/include/support/CommandLine.h | 1 + .../nnc/passes/caffe_frontend/caffe_importer.cpp | 2 +- .../nnc/passes/caffe_frontend/caffe_op_creator.cpp | 2 +- contrib/nnc/passes/interpreter/Interpreter.cpp | 2 +- .../nnc/passes/interpreter/interpreter_pass.cpp | 100 +++++++++++++-------- contrib/nnc/support/CLOptionChecker.cpp | 16 +++- contrib/nnc/utils/input_gen/tensor_gen.cpp | 4 +- 10 files changed, 93 insertions(+), 48 deletions(-) diff --git a/contrib/nnc/driver/Options.cpp b/contrib/nnc/driver/Options.cpp index 1621d74..23805c7 100644 --- a/contrib/nnc/driver/Options.cpp +++ b/contrib/nnc/driver/Options.cpp @@ -162,13 +162,13 @@ Option artifactDir(optname("--output-dir, -d"), /** * Options for *interpreter* */ -Option interInputData(optname("--input-model-data"), +Option> interInputData(optname("--input-model-data"), overview("interpreter option: specify file with neural network input data." - "This file contains array of floats in binary form"), - std::string(), + "These files contain arrays of floats in binary form"), + std::vector{}, optional(true), optvalues(""), - checkInFile); + checkInFiles); } // namespace cli } // namespace nnc diff --git a/contrib/nnc/include/option/Options.h b/contrib/nnc/include/option/Options.h index 1e25eb2..03f983d 100644 --- a/contrib/nnc/include/option/Options.h +++ b/contrib/nnc/include/option/Options.h @@ -57,7 +57,7 @@ extern Option artifactName; // name of artifact /** * Options for interpreter */ -extern Option interInputData; // input data for model +extern Option> interInputData; // input data for model } // namespace cli } // namespace nnc diff --git a/contrib/nnc/include/passes/interpreter/InterpreterPass.h b/contrib/nnc/include/passes/interpreter/InterpreterPass.h index 4a56b77..a3987df 100644 --- a/contrib/nnc/include/passes/interpreter/InterpreterPass.h +++ b/contrib/nnc/include/passes/interpreter/InterpreterPass.h @@ -17,6 +17,7 @@ #ifndef NNCC_INTERPRETERPASS_H #define NNCC_INTERPRETERPASS_H +#include "core/modelIR/operations/InputOp.h" #include "core/modelIR/TensorVariant.h" #include "core/modelIR/Shape.h" @@ -33,7 +34,8 @@ public: PassData run(PassData data) override; private: - nnc::mir::TensorVariant loadInput(const nnc::mir::Shape &); + std::unordered_map loadInput( + const std::vector& ops); nnc::mir::TensorVariant *_out; }; diff --git a/contrib/nnc/include/support/CommandLine.h b/contrib/nnc/include/support/CommandLine.h index 3c1c5af..5ff43f3 100644 --- a/contrib/nnc/include/support/CommandLine.h +++ b/contrib/nnc/include/support/CommandLine.h @@ -557,6 +557,7 @@ Option::Option(const std::vector &optnames, // prototypes of option checker functions // void checkInFile(const Option &); +void checkInFiles(const Option> &); void checkOutFile(const Option &); void checkOutDir(const Option &); void checkDebugFile(const Option &); diff --git a/contrib/nnc/passes/caffe_frontend/caffe_importer.cpp b/contrib/nnc/passes/caffe_frontend/caffe_importer.cpp index 9f95b13..cb0f4f9 100644 --- a/contrib/nnc/passes/caffe_frontend/caffe_importer.cpp +++ b/contrib/nnc/passes/caffe_frontend/caffe_importer.cpp @@ -149,7 +149,7 @@ void CaffeImporter::createMIRNodesFromLayer(const LayerParameter& layer) { assert(false && "All unsupported types should have been found before this pass."); } - assert(layer.top_size() == outputs.size() && "Number of outputs differs."); + assert(layer.top_size() == static_cast(outputs.size()) && "Number of outputs differs."); for (int i = 0; i < layer.top_size(); ++i) _blobNameToIODescriptor[layer.top(i)] = outputs.at(i); } diff --git a/contrib/nnc/passes/caffe_frontend/caffe_op_creator.cpp b/contrib/nnc/passes/caffe_frontend/caffe_op_creator.cpp index 93cb2fa..c9bedbd 100644 --- a/contrib/nnc/passes/caffe_frontend/caffe_op_creator.cpp +++ b/contrib/nnc/passes/caffe_frontend/caffe_op_creator.cpp @@ -616,7 +616,7 @@ CaffeOpCreator::convertEltwise(const caffe::LayerParameter& layer, case EltwiseParameter_EltwiseOp_SUM: optype = ops::ElementwiseOp::OpType::add; if (opts.coeff().size() > 0) { - assert(opts.coeff().size() == inputs.size()); + assert(opts.coeff().size() == static_cast(inputs.size())); for (int i = 0; i < opts.coeff().size(); i++) { if (opts.coeff().Get(i) != 1.0f) { TensorVariant coeff_tensor(DTYPE::FLOAT32, Shape{1}, &opts.coeff().Get(i)); diff --git a/contrib/nnc/passes/interpreter/Interpreter.cpp b/contrib/nnc/passes/interpreter/Interpreter.cpp index 80286d4..16d857a 100644 --- a/contrib/nnc/passes/interpreter/Interpreter.cpp +++ b/contrib/nnc/passes/interpreter/Interpreter.cpp @@ -128,7 +128,7 @@ void NNInterpreter::dump(Operation& op, bool all) { } } -void NNInterpreter::setInput(const std::string& name, const TensorVariant& t) { +void NNInterpreter::setInput(const std::string &name, const TensorVariant& t) { _inputTensors.emplace(name, t); } diff --git a/contrib/nnc/passes/interpreter/interpreter_pass.cpp b/contrib/nnc/passes/interpreter/interpreter_pass.cpp index 12a395d..feefefd 100644 --- a/contrib/nnc/passes/interpreter/interpreter_pass.cpp +++ b/contrib/nnc/passes/interpreter/interpreter_pass.cpp @@ -95,12 +95,12 @@ PassData InterpreterPass::run(PassData data) { NNInterpreter interpreter; // Check ops - const auto& inputs = g->getInputs(); - assert(inputs.size() == 1 && "Interpreter doesn't support networks with multiple input nodes"); + auto inputs = g->getInputs(); - auto input_node = inputs[0]; - auto input_data = loadInput(input_node->getOutputShape(0)); - interpreter.setInput(input_node->getName(), input_data); + auto input_data = loadInput(inputs); + for (auto inp: input_data) { + interpreter.setInput(inp.first, inp.second); + } g->accept(&interpreter); for (auto out_node : g->getOutputs()) { @@ -118,39 +118,69 @@ PassData InterpreterPass::run(PassData data) { return nullptr; } -TensorVariant InterpreterPass::loadInput(const Shape& shape) { - auto f = fopen(cli::interInputData.c_str(), "rb"); - assert(f && "Cannot open file"); - - int is_error = fseek(f, 0L, SEEK_END); - assert(!is_error); - - auto len = ftell(f); - assert(len != -1); - - auto data_size = static_cast(shape.numElements() * sizeof(float)); - - // Check size - if (static_cast(len) != data_size) { - std::stringstream info; - info << "Wrong input file size <" << cli::interInputData << "> = " - << len << ". Should be :" << data_size; +// Return pure file name w/o path and extension +static std::string getFileName(std::string path) { + size_t sep = path.find_last_of("/"); + if (sep != std::string::npos) + path = path.substr(sep + 1, path.size() - sep - 1); + size_t dot = path.find_last_of("."); + // TODO: There could be node names with symbols invalid in filename: if yes we should fix it. + if (dot != std::string::npos) + return path.substr(0, dot); + return path; +} - throw PassException(info.str()); +// Return input operation index with the given name +static int getInputOp(std::string name, const std::vector& ops) { + for (unsigned ndx = 0; ndx < ops.size(); ndx++) { + if (ops[ndx]->getName() == name) + return ndx; } + throw PassException("Input file name (without extension)" + " should be equal to model input node name"); +} - rewind(f); - - std::unique_ptr data(new char[data_size]); - auto rlen = fread(data.get(), data_size, 1, f); - assert(rlen == 1); - (void) rlen; - - is_error = fclose(f); - assert(is_error != EOF && "Can not close file!"); - (void) is_error; - - return TensorVariant(DTYPE::FLOAT32, shape, data.get()); +std::unordered_map +InterpreterPass::loadInput(const std::vector& ops) { + std::unordered_map result; + for (unsigned i = 0; i < ops.size(); i++) { + // We assume that file name is equal to input node name + auto fname = getFileName(cli::interInputData[i]); + auto op_ndx = getInputOp(fname, ops); + auto f = fopen(cli::interInputData[i].c_str(), "rb"); + assert (f); + int is_error = fseek(f, 0L, SEEK_END); + assert(!is_error); + + auto len = ftell(f); + assert(len != -1); + + auto shape = ops[op_ndx]->getOutputShape(0); + auto data_size = static_cast(shape.numElements() * sizeof(float)); + + // Check size + if (static_cast(len) != data_size) { + std::stringstream info; + info << "Wrong input file size <" << cli::interInputData[i] << "> = " + << len << ". Should be :" << data_size; + + throw PassException(info.str()); + } + + rewind(f); + + std::unique_ptr data(new char[data_size]); + auto rlen = fread(data.get(), data_size, 1, f); + assert(rlen == 1); + (void)rlen; + + is_error = fclose(f); + assert(is_error != EOF && "Can not close file!"); + (void)is_error; + + result.emplace(fname, TensorVariant(DTYPE::FLOAT32, shape, data.get())); + } + return result; } InterpreterPass::~InterpreterPass() { diff --git a/contrib/nnc/support/CLOptionChecker.cpp b/contrib/nnc/support/CLOptionChecker.cpp index 3e73760..d43c4ee 100644 --- a/contrib/nnc/support/CLOptionChecker.cpp +++ b/contrib/nnc/support/CLOptionChecker.cpp @@ -14,12 +14,13 @@ * limitations under the License. */ -#include "support/CommandLine.h" #include "option/Options.h" +#include "support/CommandLine.h" +#include +#include #include #include -#include namespace nnc { namespace cli { @@ -34,6 +35,17 @@ void checkInFile(const Option &in_file) { fclose(f); } // checkInFile +void checkInFiles(const Option> &in_files) { + if ( in_files.empty() ) + throw BadOption("Input file name(s) should not be empty"); + + for (auto in_file : in_files) { + std::ifstream ifile(in_file.c_str()); + if (ifile.fail()) + throw BadOption("Cannot open file <" + in_file + ">"); + } +} // checkInFiles + void checkOutFile(const Option &out_file) { if ( out_file.empty() ) throw BadOption("Output file name should not be empty"); diff --git a/contrib/nnc/utils/input_gen/tensor_gen.cpp b/contrib/nnc/utils/input_gen/tensor_gen.cpp index c936f9e..0c7a36c 100644 --- a/contrib/nnc/utils/input_gen/tensor_gen.cpp +++ b/contrib/nnc/utils/input_gen/tensor_gen.cpp @@ -50,7 +50,7 @@ public: float& at(const vector& coords) { int offset = 0; - for (int i = 0; i < coords.size(); ++i) + for (auto i = 0; i < coords.size(); ++i) offset += coords[i] * _strides[i]; return _data[offset]; @@ -59,7 +59,7 @@ public: Tensor transpose(const vector& reshape) { vector tr_shape(_shape.size()); - for (int i = 0; i < _shape.size(); ++i) + for (auto i = 0; i < _shape.size(); ++i) tr_shape[i] = _shape[reshape[i]]; Tensor result(tr_shape); -- 2.7.4