From d697b28daa096646ea968bc295a08b0d8059183b Mon Sep 17 00:00:00 2001 From: =?utf8?q?=D0=A1=D0=B5=D1=80=D0=B3=D0=B5=D0=B9=20=D0=91=D0=B0=D1=80?= =?utf8?q?=D0=B0=D0=BD=D0=BD=D0=B8=D0=BA=D0=BE=D0=B2/AI=20Tools=20Lab=20/S?= =?utf8?q?RR/Engineer/=EC=82=BC=EC=84=B1=EC=A0=84=EC=9E=90?= Date: Thu, 31 Jan 2019 15:05:59 +0300 Subject: [PATCH] [nnc] Change interpreter compiler option (#2973) Replace interpreter compiler option "--input-model-data" with "--input-data-dir". The latter specifies directory in which the data files for the model inputs can be found. Refactor interpreter to account for this change. Signed-off-by: Sergei Barannikov --- contrib/nnc/driver/Options.cpp | 15 +-- contrib/nnc/include/option/Options.h | 2 +- .../include/passes/interpreter/InterpreterPass.h | 5 - contrib/nnc/include/support/CommandLine.h | 9 +- .../nnc/passes/interpreter/interpreter_pass.cpp | 110 +++++++-------------- contrib/nnc/support/CLOptionChecker.cpp | 49 ++++----- 6 files changed, 66 insertions(+), 124 deletions(-) diff --git a/contrib/nnc/driver/Options.cpp b/contrib/nnc/driver/Options.cpp index 23805c7..c073d31 100644 --- a/contrib/nnc/driver/Options.cpp +++ b/contrib/nnc/driver/Options.cpp @@ -162,13 +162,14 @@ Option artifactDir(optname("--output-dir, -d"), /** * Options for *interpreter* */ -Option> interInputData(optname("--input-model-data"), - overview("interpreter option: specify file with neural network input data." - "These files contain arrays of floats in binary form"), - std::vector{}, - optional(true), - optvalues(""), - checkInFiles); +Option interInputDataDir(optname("--input-data-dir"), + overview("specify directory with binary files " + "containing the input data for the model " + "(one file for each input with the same name)"), + ".", // default is current directory + optional(true), + optvalues(""), + checkInDir); } // namespace cli } // namespace nnc diff --git a/contrib/nnc/include/option/Options.h b/contrib/nnc/include/option/Options.h index 03f983d..803fdcc 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 interInputDataDir; // directory with input data files } // namespace cli } // namespace nnc diff --git a/contrib/nnc/include/passes/interpreter/InterpreterPass.h b/contrib/nnc/include/passes/interpreter/InterpreterPass.h index a3987df..2febd08 100644 --- a/contrib/nnc/include/passes/interpreter/InterpreterPass.h +++ b/contrib/nnc/include/passes/interpreter/InterpreterPass.h @@ -32,11 +32,6 @@ public: ~InterpreterPass() override; PassData run(PassData data) override; - -private: - std::unordered_map loadInput( - const std::vector& ops); - nnc::mir::TensorVariant *_out; }; } // namespace nnc diff --git a/contrib/nnc/include/support/CommandLine.h b/contrib/nnc/include/support/CommandLine.h index 5ff43f3..ada5b5b 100644 --- a/contrib/nnc/include/support/CommandLine.h +++ b/contrib/nnc/include/support/CommandLine.h @@ -556,11 +556,10 @@ 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 &); +void checkInFile(const Option& in_file); +void checkOutFile(const Option& out_file); +void checkInDir(const Option& dir); +void checkOutDir(const Option& dir); } // namespace cli } // namespace nnc diff --git a/contrib/nnc/passes/interpreter/interpreter_pass.cpp b/contrib/nnc/passes/interpreter/interpreter_pass.cpp index feefefd..a8edcd5 100644 --- a/contrib/nnc/passes/interpreter/interpreter_pass.cpp +++ b/contrib/nnc/passes/interpreter/interpreter_pass.cpp @@ -88,19 +88,48 @@ static void writeTensorToHDF5File(const TensorVariant& tensor, #endif // NNC_HDF5_SUPPORTED +static TensorVariant readTensorFromFile(const std::string& filename, DTYPE dtype, + const Shape& shape) { + assert(dtype == DTYPE::FLOAT32); + std::size_t input_data_size = shape.numElements() * sizeof(float); + + std::ifstream stream(filename, std::ios::in | std::ios::binary); + if (stream.fail()) + throw PassException("Couldn't open file \"" + filename + "\"."); + + stream.seekg(0, std::ios::end); + std::streampos end = stream.tellg(); + stream.seekg(0, std::ios::beg); + std::streampos begin = stream.tellg(); + long file_size = end - begin; + + if (static_cast(file_size) != input_data_size) + throw PassException("File \"" + filename + "\" has incorrect size: " + + std::to_string(file_size) + + "(expected: " + std::to_string(input_data_size) + ")."); + + std::unique_ptr data(new char[input_data_size]); + stream.read(data.get(), input_data_size); + if (stream.fail()) + throw PassException("Couldn't read file \"" + filename + "\"."); + + return TensorVariant(dtype, shape, data.get()); +} + PassData InterpreterPass::run(PassData data) { auto g = static_cast(data); assert(g); NNInterpreter interpreter; - // Check ops - auto inputs = g->getInputs(); - - auto input_data = loadInput(inputs); - for (auto inp: input_data) { - interpreter.setInput(inp.first, inp.second); + for (const auto* input_op : g->getInputs()) { + std::string tensor_name = input_op->getName(); + std::replace(tensor_name.begin(), tensor_name.end(), '/', '_'); + std::string filename = cli::interInputDataDir + "/" + tensor_name + ".dat"; + auto tensor = readTensorFromFile(filename, DTYPE::FLOAT32, input_op->getOutputShape(0)); + interpreter.setInput(input_op->getName(), tensor); } + g->accept(&interpreter); for (auto out_node : g->getOutputs()) { @@ -118,73 +147,6 @@ PassData InterpreterPass::run(PassData data) { return nullptr; } -// 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; -} - -// 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"); -} - -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() { - delete _out; -} +InterpreterPass::~InterpreterPass() = default; } // namespace nnc diff --git a/contrib/nnc/support/CLOptionChecker.cpp b/contrib/nnc/support/CLOptionChecker.cpp index d43c4ee..390398b 100644 --- a/contrib/nnc/support/CLOptionChecker.cpp +++ b/contrib/nnc/support/CLOptionChecker.cpp @@ -18,6 +18,7 @@ #include "support/CommandLine.h" #include +#include #include #include #include @@ -35,17 +36,6 @@ 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"); @@ -54,33 +44,28 @@ void checkOutFile(const Option &out_file) { } // checkOutFile -void checkOutDir(const Option &out_dir) { - auto dir = opendir(out_dir.c_str()); +void checkInDir(const Option& dir) { + auto stream = opendir(dir.c_str()); - if (dir) { - closedir(dir); - return; - } + if (stream == NULL) + throw BadOption(std::string("Could not open directory: ") + std::strerror(errno) + "."); - auto err = errno; + closedir(stream); +} // checkInDir - switch (err) { - case ENOENT: +void checkOutDir(const Option &out_dir) { + auto stream = opendir(out_dir.c_str()); + + if (stream == NULL) { + // Do not consider the missing directory an error. + if (errno == ENOENT) return; - case ENOTDIR: - throw BadOption("Output path is not directory"); - case EACCES: - throw BadOption("Has no permission to open output directory"); - default: - throw BadOption("Can not open output directory"); - } -} // checkOutDir -void checkDebugFile(const Option &in_file) { - if (access(in_file.c_str(), W_OK) != 0) { - throw BadOption("Has no permission to open debug output file"); + throw BadOption(std::string("Could not open directory: ") + std::strerror(errno) + "."); } -} // checkDebugFile + + closedir(stream); +} // checkOutDir } // namespace cli } // namespace ncc -- 2.7.4