From 98d055e2f242ad70b75c3869870ccf44e9e756f0 Mon Sep 17 00:00:00 2001 From: jatin Date: Tue, 28 Nov 2023 13:52:04 -0800 Subject: [PATCH] Bring back MicroTCN test --- tests/functional/CMakeLists.txt | 1 + tests/functional/torch_conv1d_groups_test.cpp | 33 +--- tests/functional/torch_microtcn_test.cpp | 143 ++++++++++++++ tests/functional/torch_microtcn_test.hpp | 186 ------------------ 4 files changed, 154 insertions(+), 209 deletions(-) create mode 100644 tests/functional/torch_microtcn_test.cpp delete mode 100644 tests/functional/torch_microtcn_test.hpp diff --git a/tests/functional/CMakeLists.txt b/tests/functional/CMakeLists.txt index 342b5c0..d74965f 100644 --- a/tests/functional/CMakeLists.txt +++ b/tests/functional/CMakeLists.txt @@ -10,4 +10,5 @@ rtneural_add_test( torch_conv1d_groups_test.cpp torch_lstm_test.cpp torch_gru_test.cpp + torch_microtcn_test.cpp DEPENDENCIES PRIVATE RTNeural) diff --git a/tests/functional/torch_conv1d_groups_test.cpp b/tests/functional/torch_conv1d_groups_test.cpp index f444ad9..f2c0000 100644 --- a/tests/functional/torch_conv1d_groups_test.cpp +++ b/tests/functional/torch_conv1d_groups_test.cpp @@ -1,7 +1,7 @@ #include -#include "load_csv.hpp" #include "RTNeural/RTNeural.h" +#include "load_csv.hpp" namespace { @@ -15,21 +15,14 @@ void expectNear(T const& expected, T const& actual) int computeCrop(int input_size, int kernel_size, int dilation_rate) { - int output_size = (input_size - dilation_rate * (kernel_size - 1) - 1) + 1; + int output_size = (input_size - dilation_rate * (kernel_size - 1) - 1) + 1; return input_size - output_size; } template void testTorchConv1DGroupModel() { - const auto model_file = - std::string { RTNEURAL_ROOT_DIR } + - "models/conv1d_torch_group_" + - std::to_string(input_size) + "_" + - std::to_string(output_size) + "_" + - std::to_string(kernel_size) + "_" + - std::to_string(dilation_rate) + "_" + - std::to_string(groups_of) + ".json"; + const auto model_file = std::string { RTNEURAL_ROOT_DIR } + "models/conv1d_torch_group_" + std::to_string(input_size) + "_" + std::to_string(output_size) + "_" + std::to_string(kernel_size) + "_" + std::to_string(dilation_rate) + "_" + std::to_string(groups_of) + ".json"; std::ifstream jsonStream(model_file, std::ifstream::binary); nlohmann::json modelJson; @@ -44,24 +37,18 @@ void testTorchConv1DGroupModel() std::vector> outputs {}; outputs.resize(inputs.size(), {}); - for (size_t i = 0; i < inputs.size(); ++i) + for(size_t i = 0; i < inputs.size(); ++i) { - alignas (16) T input_copy[input_size + 4] {}; - std::copy (inputs[i].begin(), inputs[i].end(), std::begin (input_copy)); + alignas(16) T input_copy[input_size + 4] {}; + std::copy(inputs[i].begin(), inputs[i].end(), std::begin(input_copy)); model.forward(input_copy); std::copy(model.getOutputs(), model.getOutputs() + output_size, outputs[i].begin()); } - std::ifstream modelOutputsFile { - std::string { RTNEURAL_ROOT_DIR } + - "test_data/conv1d_torch_group_y_python_" + - std::to_string(input_size) + "_" + - std::to_string(output_size) + "_" + - std::to_string(kernel_size) + "_" + - std::to_string(dilation_rate) + "_" + - std::to_string(groups_of) + ".csv" - }; - const auto expected_y = RTNeural::torch_helpers::detail::transpose(load_csv::loadFile2d (modelOutputsFile)); + std::ifstream modelOutputsFile { + std::string { RTNEURAL_ROOT_DIR } + "test_data/conv1d_torch_group_y_python_" + std::to_string(input_size) + "_" + std::to_string(output_size) + "_" + std::to_string(kernel_size) + "_" + std::to_string(dilation_rate) + "_" + std::to_string(groups_of) + ".csv" + }; + const auto expected_y = RTNeural::torch_helpers::detail::transpose(load_csv::loadFile2d(modelOutputsFile)); int crop = computeCrop(static_cast(inputs.size()), kernel_size, dilation_rate); diff --git a/tests/functional/torch_microtcn_test.cpp b/tests/functional/torch_microtcn_test.cpp new file mode 100644 index 0000000..c602725 --- /dev/null +++ b/tests/functional/torch_microtcn_test.cpp @@ -0,0 +1,143 @@ +#include + +#include "RTNeural/RTNeural.h" +#include "load_csv.hpp" + +#if ! (RTNEURAL_USE_EIGEN || RTNEURAL_USE_XSIMD) + +namespace +{ +template +void expectNear(T const& expected, T const& actual) +{ + EXPECT_THAT( + static_cast(expected), + testing::DoubleNear(static_cast(actual), 1e-6)); +} + +int computeCrop(int input_size, int kernel_size, int dilation_rate) +{ + int output_size = (input_size - dilation_rate * (kernel_size - 1) - 1) + 1; + return input_size - output_size; +} + +template +class TCNBlock +{ +public: + T outs alignas(RTNEURAL_DEFAULT_ALIGNMENT)[out_ch]; + RTNeural::Conv1DT conv1; + RTNeural::BatchNorm1DT bn; + RTNeural::PReLUActivationT relu; + RTNeural::Conv1DT res; + + TCNBlock() + { + const auto conv1_weights = std::string { RTNEURAL_ROOT_DIR } + "models/microtcn/conv1.json"; + const auto bn_weights = std::string { RTNEURAL_ROOT_DIR } + "models/microtcn/bn.json"; + const auto relu_weights = std::string { RTNEURAL_ROOT_DIR } + "models/microtcn/relu.json"; + const auto res_weights = std::string { RTNEURAL_ROOT_DIR } + "models/microtcn/res.json"; + + std::ifstream conv1_stream(conv1_weights, std::ifstream::binary); + nlohmann::json conv1_json; + conv1_stream >> conv1_json; + RTNeural::torch_helpers::loadConv1D(conv1_json, "", conv1, false); + + std::ifstream bn_stream(bn_weights, std::ifstream::binary); + nlohmann::json bn_json; + bn_stream >> bn_json; + + T epsilon = bn_json.at("eps"); + std::vector gamma = bn_json.at("weight"); + std::vector beta = bn_json.at("bias"); + std::vector runningMean = bn_json.at("running_mean"); + std::vector runningVariance = bn_json.at("running_var"); + + bn.setEpsilon(epsilon); + bn.setGamma(gamma); + bn.setBeta(beta); + bn.setRunningMean(runningMean); + bn.setRunningVariance(runningVariance); + + std::ifstream relu_stream(relu_weights, std::ifstream::binary); + nlohmann::json relu_json; + relu_stream >> relu_json; + std::vector alphaVals = relu_json.at("weight"); + relu.setAlphaVals(alphaVals); + + std::ifstream res_stream(res_weights, std::ifstream::binary); + nlohmann::json res_json; + res_stream >> res_json; + RTNeural::torch_helpers::loadConv1D(res_json, "", res, false); + } + + inline void forward(const T (&ins)[in_ch]) noexcept + { + conv1.forward(ins); + bn.forward(conv1.outs); + relu.forward(bn.outs); + res.forward(ins); + + for(int i = 0; i < out_ch; ++i) + { + outs[i] = relu.outs[i] + res.outs[i]; + } + } + + void reset() + { + conv1.reset(); + bn.reset(); + relu.reset(); + res.reset(); + } +}; + +template +void testMicroTCN() +{ + if(std::is_same::value) + std::cout << "TESTING TORCH/CONV1D GROUP MODEL WITH DATA TYPE: FLOAT" << std::endl; + else + std::cout << "TESTING TORCH/CONV1D GROUP MODEL WITH DATA TYPE: DOUBLE" << std::endl; + + RTNeural::ModelT> model; + model.reset(); + + std::ifstream modelInputsFile { std::string { RTNEURAL_ROOT_DIR } + "test_data/microtcn_x.csv" }; + const auto inputs = load_csv::loadFile2d(modelInputsFile); + std::vector> outputs {}; + outputs.resize(inputs.size(), {}); + + for(size_t i = 0; i < inputs.size(); ++i) + { + model.forward(inputs[i].data()); + std::copy(model.getOutputs(), model.getOutputs() + 32, outputs[i].begin()); + } + + std::ifstream modelOutputsFile { std::string { RTNEURAL_ROOT_DIR } + "test_data/microtcn_y.csv" }; + const auto expected_y = RTNeural::torch_helpers::detail::transpose(load_csv::loadFile2d(modelOutputsFile)); + + int crop = computeCrop(static_cast(inputs.size()), 4, 10); + + for(size_t n = 0; n < expected_y.size(); ++n) + { + for(size_t j = 0; j < outputs[crop + n].size(); ++j) + { + expectNear(outputs[n + crop][j], expected_y[n][j]); + } + } +} +} + +TEST(TestTorchMicroTCN, modelOutputMatchesPythonImplementationForFloats) +{ + testMicroTCN(); +} + +TEST(TestTorchMicroTCN, modelOutputMatchesPythonImplementationForDoubles) +{ + testMicroTCN(); +} + +#endif diff --git a/tests/functional/torch_microtcn_test.hpp b/tests/functional/torch_microtcn_test.hpp deleted file mode 100644 index f20f767..0000000 --- a/tests/functional/torch_microtcn_test.hpp +++ /dev/null @@ -1,186 +0,0 @@ -#include "RTNeural/RTNeural.h" - -#if !(RTNEURAL_USE_XSIMD || RTNEURAL_USE_EIGEN) -namespace torch_microtcn_test -{ - template - std::vector> loadFile2D(std::ifstream& stream) - { - std::vector> vec; - - std::string line; - if(stream.is_open()) { - while(std::getline(stream, line)) { - std::vector lineVec; - std::string num; - for (auto ch : line) { - if (ch == ',') { - lineVec.push_back(static_cast(std::stod(num))); - num.clear(); - continue; - } - - num.push_back(ch); - } - - lineVec.push_back(static_cast(std::stod(num))); - vec.push_back(lineVec); - } - - stream.close(); - } - - return RTNeural::torch_helpers::detail::transpose(vec); - } - - int computeCrop(int input_size, int kernel_size, int dilation_rate) - { - int output_size = (input_size - dilation_rate * (kernel_size - 1) - 1) + 1; - return input_size - output_size; - } - - template - class TCNBlock - { - public: - T outs alignas(RTNEURAL_DEFAULT_ALIGNMENT)[out_ch]; - RTNeural::Conv1DT conv1; - RTNeural::BatchNorm1DT bn; - RTNeural::PReLUActivationT relu; - RTNeural::Conv1DT res; - - TCNBlock() { - const auto conv1_weights = std::string { RTNEURAL_ROOT_DIR } + "models/microtcn/conv1.json"; - const auto bn_weights = std::string { RTNEURAL_ROOT_DIR } + "models/microtcn/bn.json"; - const auto relu_weights = std::string { RTNEURAL_ROOT_DIR } + "models/microtcn/relu.json"; - const auto res_weights = std::string { RTNEURAL_ROOT_DIR } + "models/microtcn/res.json"; - - std::ifstream conv1_stream (conv1_weights, std::ifstream::binary); - nlohmann::json conv1_json; - conv1_stream >> conv1_json; - RTNeural::torch_helpers::loadConv1D(conv1_json, "", conv1, false); - - // (TODO) purefunctor: add this to torch_helpers? - std::ifstream bn_stream (bn_weights, std::ifstream::binary); - nlohmann::json bn_json; - bn_stream >> bn_json; - - T epsilon = bn_json.at("eps"); - std::vector gamma = bn_json.at("weight"); - std::vector beta = bn_json.at("bias"); - std::vector runningMean = bn_json.at("running_mean"); - std::vector runningVariance = bn_json.at("running_var"); - - bn.setEpsilon(epsilon); - bn.setGamma(gamma); - bn.setBeta(beta); - bn.setRunningMean(runningMean); - bn.setRunningVariance(runningVariance); - - std::ifstream relu_stream (relu_weights, std::ifstream::binary); - nlohmann::json relu_json; - relu_stream >> relu_json; - std::vector alphaVals = relu_json.at("weight"); - relu.setAlphaVals(alphaVals); - - std::ifstream res_stream (res_weights, std::ifstream::binary); - nlohmann::json res_json; - res_stream >> res_json; - RTNeural::torch_helpers::loadConv1D(res_json, "", res, false); - } - - inline void forward(const T (&ins)[in_ch]) noexcept - { - conv1.forward(ins); - bn.forward(conv1.outs); - relu.forward(bn.outs); - res.forward(ins); - - for (int i = 0; i < out_ch; ++i) - { - outs[i] = relu.outs[i] + res.outs[i]; - } - } - - void reset() { - conv1.reset(); - bn.reset(); - relu.reset(); - res.reset(); - } - }; - - template - int testMicroTCN() - { - if (std::is_same::value) - std::cout << "TESTING TORCH/CONV1D GROUP MODEL WITH DATA TYPE: FLOAT" << std::endl; - else - std::cout << "TESTING TORCH/CONV1D GROUP MODEL WITH DATA TYPE: DOUBLE" << std::endl; - - RTNeural::ModelT> model; - model.reset(); - - std::ifstream modelInputsFile { std::string { RTNEURAL_ROOT_DIR } + "test_data/microtcn_x.csv" }; - const auto inputs = load_csv::loadFile2d(modelInputsFile); - std::vector> outputs {}; - outputs.resize(inputs.size(), {}); - - for (size_t i = 0; i < inputs.size(); ++i) - { - model.forward(inputs[i].data()); - std::copy(model.getOutputs(), model.getOutputs() + 32, outputs[i].begin()); - } - - std::ifstream modelOutputsFile { std::string { RTNEURAL_ROOT_DIR } + "test_data/microtcn_y.csv" }; - const auto expected_y = loadFile2D (modelOutputsFile); - - int crop = computeCrop(static_cast(inputs.size()), 4, 10); - - size_t nErrs = 0; - T max_error = (T)0; - for(size_t n = 0; n < expected_y.size(); ++n) - { - for(size_t j = 0; j < outputs[crop + n].size(); ++j) - { - auto err = std::abs(outputs[crop + n][j] - expected_y[n][j]); - if(err > (T)1.0e-6) - { - max_error = std::max(err, max_error); - nErrs++; - - // For debugging purposes - std::cout << "ERR: " << err << ", idx: " << n << std::endl; - std::cout << "Output: " << outputs[crop + n][j] << std::endl; - std::cout << "Expected: " << expected_y[n][j] << std::endl; - break; - } - } - } - - if(nErrs > 0) - { - std::cout << "FAIL: " << nErrs << " errors!" << std::endl; - std::cout << "Maximum error: " << max_error << std::endl; - return 1; - } - - std::cout << "SUCCESS" << std::endl; - return 0; - } -} - -int microtcn_test() -{ - int result = 0; - result |= torch_microtcn_test::testMicroTCN(); - result |= torch_microtcn_test::testMicroTCN(); - return result; -} -#else -int microtcn_test() -{ - // @TODO - return 0; -} -#endif