diff options
Diffstat (limited to 'compiler/record-minmax/src')
-rw-r--r-- | compiler/record-minmax/src/HDF5Importer.cpp | 28 | ||||
-rw-r--r-- | compiler/record-minmax/src/MinMaxObserver.cpp | 32 | ||||
-rw-r--r-- | compiler/record-minmax/src/RecordMinMax.cpp | 129 |
3 files changed, 164 insertions, 25 deletions
diff --git a/compiler/record-minmax/src/HDF5Importer.cpp b/compiler/record-minmax/src/HDF5Importer.cpp index a0e65eeb7..cfb270ce0 100644 --- a/compiler/record-minmax/src/HDF5Importer.cpp +++ b/compiler/record-minmax/src/HDF5Importer.cpp @@ -59,7 +59,30 @@ DataType toInternalDtype(const H5::DataType &h5_type) { return DataType::S64; } - // Only support three datatypes for now + if (h5_type.getClass() == H5T_class_t::H5T_ENUM) + { + // We follow the numpy format + // In numpy 1.19.0, np.bool_ is saved as H5T_ENUM + // - (name, value) -> (FALSE, 0) and (TRUE, 1) + // - value dtype is H5T_STD_I8LE + // TODO Find a general way to recognize BOOL type + char name[10]; + int8_t value[2] = {0, 1}; + if (H5Tenum_nameof(h5_type.getId(), value, name, 10) < 0) + return DataType::Unknown; + + if (std::string(name) != "FALSE") + return DataType::Unknown; + + if (H5Tenum_nameof(h5_type.getId(), value + 1, name, 10) < 0) + return DataType::Unknown; + + if (std::string(name) != "TRUE") + return DataType::Unknown; + + return DataType::BOOL; + } + // TODO Support more datatypes return DataType::Unknown; } @@ -125,6 +148,9 @@ void HDF5Importer::readTensor(int32_t record_idx, int32_t input_idx, DataType *d case DataType::S64: readTensorData(tensor, static_cast<int64_t *>(buffer)); break; + case DataType::BOOL: + readTensorData(tensor, static_cast<uint8_t *>(buffer)); + break; default: throw std::runtime_error{"Unsupported data type for input data (.h5)"}; } diff --git a/compiler/record-minmax/src/MinMaxObserver.cpp b/compiler/record-minmax/src/MinMaxObserver.cpp index c22cb4132..40c9b730d 100644 --- a/compiler/record-minmax/src/MinMaxObserver.cpp +++ b/compiler/record-minmax/src/MinMaxObserver.cpp @@ -18,6 +18,8 @@ #include <luci/IR/CircleOpcode.h> +#include <math.h> + using DataType = luci_interpreter::DataType; namespace record_minmax @@ -51,6 +53,12 @@ void MinMaxObserver::postTensorWrite(const luci::CircleNode *node, return; } + if (node->dtype() == DataType::BOOL) + { + // Bool type tensor is not quantized + return; + } + // Only support recording of float32 values if (tensor->element_type() != DataType::FLOAT32) throw std::runtime_error("Tensor's data type is not float"); @@ -59,9 +67,27 @@ void MinMaxObserver::postTensorWrite(const luci::CircleNode *node, const auto num_elements = tensor->shape().num_elements(); std::vector<float> buf(data, data + num_elements); - auto minmax = std::minmax_element(buf.begin(), buf.end()); - float min = *minmax.first; - float max = *minmax.second; + + float max = std::numeric_limits<float>::lowest(); + float min = std::numeric_limits<float>::max(); + + bool all_nan = true; + for (auto number : buf) + { + if (isnan(number)) + continue; + + all_nan = false; + + if (number > max) + max = number; + + if (number < min) + min = number; + } + + if (all_nan) + throw std::runtime_error("All values are NaN(Not a Number)"); _minmax_data.recordMinMax(node, min, max); } diff --git a/compiler/record-minmax/src/RecordMinMax.cpp b/compiler/record-minmax/src/RecordMinMax.cpp index cd5f29352..333ff5e3b 100644 --- a/compiler/record-minmax/src/RecordMinMax.cpp +++ b/compiler/record-minmax/src/RecordMinMax.cpp @@ -30,6 +30,7 @@ #include <numeric> #include <stdexcept> #include <iostream> +#include <random> using Shape = luci_interpreter::Shape; using DataType = luci_interpreter::DataType; @@ -37,6 +38,18 @@ using DataType = luci_interpreter::DataType; namespace { +std::vector<uint8_t> genRandomBoolData(std::mt19937 &gen, uint32_t num_elements) +{ + std::uniform_int_distribution<> dist(0, 1); + std::vector<uint8_t> input_data(num_elements); + + // Write random data + for (auto &iter : input_data) + iter = static_cast<uint8_t>(dist(gen)); + + return input_data; +} + /** * @brief getTensorSize will return size in bytes */ @@ -68,6 +81,38 @@ void verifyTypeShape(const luci::CircleInput *input_node, const DataType &dtype, } } +void update_quantparam(record_minmax::MinMaxObserver *observer, const std::string &mode, + float min_percentile, float max_percentile) +{ + auto minmax_map = observer->minMaxData()->getMap(); + for (auto iter = minmax_map->begin(); iter != minmax_map->end(); ++iter) + { + auto node = iter->first; + auto minmax = iter->second; + + float min{0.0f}, max{0.0f}; + if (mode == "percentile") + { + min = record_minmax::getNthPercentile(minmax.min_vector, min_percentile); + max = record_minmax::getNthPercentile(minmax.max_vector, max_percentile); + } + else if (mode == "moving_average") + { + min = record_minmax::getMovingAverage(minmax.min_vector, 0.9, 16, true); + max = record_minmax::getMovingAverage(minmax.max_vector, 0.9, 16, false); + } + assert(mode == "percentile" || mode == "moving_average"); + auto quantparam = std::make_unique<luci::CircleQuantParam>(); + quantparam->min.push_back(min); + quantparam->max.push_back(max); + + assert(node->quantparam() == nullptr); + + auto mutable_node = const_cast<luci::CircleNode *>(node); + mutable_node->quantparam(std::move(quantparam)); + } +} + } // namespace namespace record_minmax @@ -169,33 +214,75 @@ void RecordMinMax::profileData(const std::string &mode, const std::string &input throw std::runtime_error("HDF5 error occurred."); } - auto minmax_map = _observer->minMaxData()->getMap(); - for (auto iter = minmax_map->begin(); iter != minmax_map->end(); ++iter) + update_quantparam(_observer.get(), mode, min_percentile, max_percentile); +} + +void RecordMinMax::profileDataWithRandomInputs(const std::string &mode, float min_percentile, + float max_percentile) +{ + // We use three randomly-generated records + const uint32_t num_records = 3; + + const auto input_nodes = loco::input_nodes(_module->graph()); + const auto num_inputs = input_nodes.size(); + + std::random_device rd; + std::mt19937 gen(rd()); + std::uniform_real_distribution<> dist(-5, 5); + + for (int32_t record_idx = 0; record_idx < num_records; record_idx++) { - auto node = iter->first; - auto minmax = iter->second; + std::cout << "Recording " << record_idx << "'th data" << std::endl; - float min{0.0f}, max{0.0f}; - if (mode == "percentile") + for (int32_t input_idx = 0; input_idx < num_inputs; input_idx++) { - min = getNthPercentile(minmax.min_vector, min_percentile); - max = getNthPercentile(minmax.max_vector, max_percentile); - } - else if (mode == "moving_average") - { - min = getMovingAverage(minmax.min_vector, 0.9, 16, true); - max = getMovingAverage(minmax.max_vector, 0.9, 16, false); - } - assert(mode == "percentile" || mode == "moving_average"); - auto quantparam = std::make_unique<luci::CircleQuantParam>(); - quantparam->min.push_back(min); - quantparam->max.push_back(max); + const auto *input_node = loco::must_cast<const luci::CircleInput *>(input_nodes[input_idx]); + assert(input_node->index() == input_idx); + uint32_t num_elements = 1; + for (uint32_t i = 0; i < input_node->rank(); i++) + { + if (!input_node->dim(i).known()) + throw std::runtime_error("Input dimension must be known"); - assert(node->quantparam() == nullptr); + num_elements *= input_node->dim(i).value(); + } - auto mutable_node = const_cast<luci::CircleNode *>(node); - mutable_node->quantparam(std::move(quantparam)); + if (num_elements == 0) + throw std::runtime_error("Only support non-zero sized inputs"); + + // TODO Support more input data types + assert(input_node->dtype() == loco::DataType::FLOAT32 || + input_node->dtype() == loco::DataType::BOOL); + + if (input_node->dtype() == DataType::FLOAT32) + // clang-format off + { + std::vector<float> input_data(num_elements); + + // Write random data + for (auto &iter : input_data) + iter = static_cast<float>(dist(gen)); + + // TODO: Input data is copied twice (file -> buffer (input_data) -> interpreter inputs) + // We can redcue the copy by directly writing data from file to interpreter inputs + _interpreter->writeInputTensor(input_node, input_data.data(), + input_data.size() * sizeof(float)); + } + // clang-format on + else if (input_node->dtype() == DataType::BOOL) + { + auto input_data = genRandomBoolData(gen, num_elements); + _interpreter->writeInputTensor(input_node, input_data.data(), + input_data.size() * sizeof(uint8_t)); + } + } + + _interpreter->interpret(); } + + std::cout << "Recording finished. Number of recorded data: " << num_records << std::endl; + + update_quantparam(_observer.get(), mode, min_percentile, max_percentile); } void RecordMinMax::saveModel(const std::string &output_model_path) |