diff --git a/COPYRIGHT.txt b/COPYRIGHT.txt index 7a37f9015c..481990528d 100644 --- a/COPYRIGHT.txt +++ b/COPYRIGHT.txt @@ -90,6 +90,7 @@ Copyright: Copyright 2018, Projyal Dev Copyright 2018, Nikhil Goel Copyright 2018, Shikhar Jaiswal + Copyright 2018, B Kartheek Reddy License: BSD-3-clause All rights reserved. diff --git a/src/mlpack/core.hpp b/src/mlpack/core.hpp index 13871e45b4..66ca75bbae 100644 --- a/src/mlpack/core.hpp +++ b/src/mlpack/core.hpp @@ -233,6 +233,7 @@ * - Projyal Dev * - Nikhil Goel * - Shikhar Jaiswal + * - B Kartheek Reddy */ // First, include all of the prerequisites. diff --git a/src/mlpack/methods/logistic_regression/logistic_regression_main.cpp b/src/mlpack/methods/logistic_regression/logistic_regression_main.cpp index 0720dd028e..9582f4c08d 100644 --- a/src/mlpack/methods/logistic_regression/logistic_regression_main.cpp +++ b/src/mlpack/methods/logistic_regression/logistic_regression_main.cpp @@ -161,6 +161,14 @@ static void mlpackMain() ReportIgnoredParam({{ "test", false }}, "output"); ReportIgnoredParam({{ "test", false }}, "output_probabilities"); + // Max Iterations needs to be positive. + RequireParamValue("max_iterations", [](int x) { return x >= 0; }, + true, "max_iterations must be positive or zero"); + + // Batch Size needs to be greater than zero. + RequireParamValue("batch_size", [](int x) { return x > 0; }, + true, "batch_size must be greater than zero"); + // Tolerance needs to be positive. RequireParamValue("tolerance", [](double x) { return x >= 0.0; }, true, "tolerance must be positive or zero"); @@ -230,6 +238,13 @@ static void mlpackMain() } else if (CLI::HasParam("training")) { + // Checking the size of training data if no labels are passed. + if (regressors.n_rows < 2) + { + Log::Fatal << "Can't get responses from training data " + "since it has less than 2 rows." << endl; + } + // The initial predictors for y, Nx1. responses = arma::conv_to>::from( regressors.row(regressors.n_rows - 1)); @@ -274,6 +289,14 @@ static void mlpackMain() { testSet = std::move(CLI::GetParam("test")); + // Checking the dimensionality of the test data. + if (testSet.n_rows != model->Parameters().n_cols - 1) + { + Log::Fatal << "Test data dimensionality (" << testSet.n_rows << ") must " + << "be the same as the dimensionality of the Training Data (" + << model->Parameters().n_cols-1 << ")!" << endl; + } + // We must perform predictions on the test set. Training (and the // optimizer) are irrelevant here; we'll pass in the model we have. if (CLI::HasParam("output")) diff --git a/src/mlpack/methods/perceptron/perceptron_main.cpp b/src/mlpack/methods/perceptron/perceptron_main.cpp index 0f2e18e0f8..d35e44aeeb 100644 --- a/src/mlpack/methods/perceptron/perceptron_main.cpp +++ b/src/mlpack/methods/perceptron/perceptron_main.cpp @@ -177,9 +177,23 @@ static void mlpackMain() if (CLI::HasParam("labels")) { labelsIn = std::move(CLI::GetParam>("labels")); + + // Checking the size of the responses and training data. + if (labelsIn.n_cols != trainingData.n_cols) + { + Log::Fatal << "The responses must have the same number of columns " + "as the training set." << endl; + } } else { + // Checking the size of training data if no labels are passed. + if (trainingData.n_rows < 2) + { + Log::Fatal << "Can't get responses from training data " + "since it has less than 2 rows." << endl; + } + // Use the last row of the training data as the labels. Log::Info << "Using the last dimension of training set as labels." << endl; diff --git a/src/mlpack/tests/CMakeLists.txt b/src/mlpack/tests/CMakeLists.txt index f034ced4be..c8222e3414 100644 --- a/src/mlpack/tests/CMakeLists.txt +++ b/src/mlpack/tests/CMakeLists.txt @@ -133,6 +133,7 @@ add_executable(mlpack_test main_tests/decision_tree_test.cpp main_tests/decision_stump_test.cpp main_tests/linear_regression_test.cpp + main_tests/logistic_regression_test.cpp main_tests/nbc_test.cpp main_tests/pca_test.cpp main_tests/perceptron_test.cpp diff --git a/src/mlpack/tests/main_tests/logistic_regression_test.cpp b/src/mlpack/tests/main_tests/logistic_regression_test.cpp new file mode 100644 index 0000000000..23687b5356 --- /dev/null +++ b/src/mlpack/tests/main_tests/logistic_regression_test.cpp @@ -0,0 +1,650 @@ +/** + * @file logistic_regression_test.cpp + * @author B Kartheek Reddy + * + * Test mlpackMain() of logistic_regression_main.cpp + * + * mlpack is free software; you may redistribute it and/or modify it under the + * terms of the 3-clause BSD license. You should have received a copy of the + * 3-clause BSD license along with mlpack. If not, see + * http://www.opensource.org/licenses/BSD-3-Clause for more information. + */ +#define BINDING_TYPE BINDING_TYPE_TEST + +static const std::string testName = "LogisticRegression"; + +#include +#include +#include +#include "test_helper.hpp" + +#include + +using namespace mlpack; + + +struct LogisticRegressionTestFixture +{ + public: + LogisticRegressionTestFixture() + { + // Cache in the options for this program. + CLI::RestoreSettings(testName); + } + + ~LogisticRegressionTestFixture() + { + // Clear the settings. + bindings::tests::CleanMemory(); + CLI::ClearSettings(); + } +}; + +BOOST_FIXTURE_TEST_SUITE(LogisticRegressionMainTest, + LogisticRegressionTestFixture); + +/** + * Ensuring that absence of training data is checked. + **/ +BOOST_AUTO_TEST_CASE(LRNoTrainingData) +{ + arma::Row trainY; + // 10 responses. + trainY << 0 << 1 << 0 << 1 << 1 << 1 << 0 << 1 << 0 << 0 << arma::endr; + + SetInputParam("labels", std::move(trainY)); + + // Training data is not provided. Should throw a runtime error. + Log::Fatal.ignoreInput = true; + BOOST_REQUIRE_THROW(mlpackMain(), std::runtime_error); + Log::Fatal.ignoreInput = false; +} + +/** + * Ensuring that absence of responses is checked. + */ +BOOST_AUTO_TEST_CASE(LRNoResponses) +{ + constexpr int N = 10; + constexpr int D = 1; + + arma::mat trainX = arma::randu(D, N); + SetInputParam("training", std::move(trainX)); + + // Labels to the training data is not provided. It should throw a runtime error. + Log::Fatal.ignoreInput = true; + BOOST_REQUIRE_THROW(mlpackMain(), std::runtime_error); + Log::Fatal.ignoreInput = false; +} + +/** + * Checking that that size and dimensionality of prediction is correct. + */ +BOOST_AUTO_TEST_CASE(LRPridictionSizeCheck) +{ + constexpr int N = 10; + constexpr int D = 3; + constexpr int M = 15; + + arma::mat trainX = arma::randu(D, N); + arma::Row trainY; + // 10 responses. + trainY << 0 << 1 << 0 << 1 << 1 << 1 << 0 << 1 << 0 << 0 << arma::endr; + arma::mat testX = arma::randu(D, M); + + SetInputParam("training", std::move(trainX)); + SetInputParam("labels", std::move(trainY)); + SetInputParam("test", std::move(testX)); + + // Training the model. + mlpackMain(); + + // Get the output predictions of the test data. + const arma::Row &testY = CLI::GetParam>("output"); + + // Output predictions size must match the test data set size. + BOOST_REQUIRE_EQUAL(testY.n_rows, 1); + BOOST_REQUIRE_EQUAL(testY.n_cols, M); +} + +/** + * Ensuring that the response size is checked. + **/ +BOOST_AUTO_TEST_CASE(LRWrongResponseSizeTest) +{ + constexpr int D = 3; + constexpr int N = 10; + + arma::mat trainX = arma::randu(D, N); + arma::Row trainY; // Response vector with wrong size. + + // 8 responses - incorrect size. + trainY << 0 << 0 << 1 << 0 << 1 << 1 << 1 << 0 << arma::endr; + + SetInputParam("training", std::move(trainX)); + SetInputParam("labels", std::move(trainY)); + + // Labels with incorrect size. It should throw a runtime error. + Log::Fatal.ignoreInput = true; + BOOST_REQUIRE_THROW(mlpackMain(), std::runtime_error); + Log::Fatal.ignoreInput = false; +} + +/** + * Checking two options of specifying responses (extra row in train matrix and + * extra parameter) and ensuring that predictions are the same. + */ +BOOST_AUTO_TEST_CASE(LRResponsesRepresentationTest) +{ + arma::mat trainX1({{1.0, 2.0, 3.0}, {1.0, 4.0, 9.0}, {0, 1, 1}}); + arma::mat testX({{4.0, 5.0}, {1.0, 6.0}}); + + SetInputParam("training", std::move(trainX1)); + SetInputParam("test", testX); + + // The first solution. + mlpackMain(); + + // Get the output. + const arma::Row testY1 = + std::move(CLI::GetParam>("output")); + + // Reset the settings. + bindings::tests::CleanMemory(); + CLI::ClearSettings(); + CLI::RestoreSettings(testName); + + // Now train by providing labels as extra parameter. + arma::mat trainX2({{1.0, 2.0, 3.0}, {1.0, 4.0, 9.0}}); + arma::Row trainY2({0, 1, 1}); + + SetInputParam("training", std::move(trainX2)); + SetInputParam("labels", std::move(trainY2)); + SetInputParam("test", std::move(testX)); + + // The second solution. + mlpackMain(); + + // get the output + const arma::Row &testY2 = + CLI::GetParam>("output"); + + // Both solutions should be equal. + BOOST_REQUIRE_EQUAL_COLLECTIONS(testY1.begin(), testY1.end(), + testY2.begin(), testY2.end()); +} + +/** + * Check that model can saved / loaded and used. Ensuring that results are the + * same. + */ +BOOST_AUTO_TEST_CASE(LRModelReload) +{ + constexpr int N = 10; + constexpr int D = 3; + constexpr int M = 15; + + arma::mat trainX = arma::randu(D, N); + arma::Row trainY; + + // 10 responses. + trainY << 0 << 1 << 0 << 1 << 1 << 1 << 0 << 1 << 0 << 0 << arma::endr; + + arma::mat testX = arma::randu(D, M); + + SetInputParam("training", std::move(trainX)); + SetInputParam("labels", std::move(trainY)); + SetInputParam("test", testX); + + // First solution + mlpackMain(); + + // Get the output model obtained from training. + LogisticRegression<>* model = + CLI::GetParam*>("output_model"); + // Get the output. + const arma::Row testY1 = + std::move(CLI::GetParam>("output")); + + // Reset the data passed. + CLI::GetSingleton().Parameters()["training"].wasPassed = false; + CLI::GetSingleton().Parameters()["labels"].wasPassed = false; + CLI::GetSingleton().Parameters()["test"].wasPassed = false; + + SetInputParam("input_model", model); + SetInputParam("test", std::move(testX)); + + // Second solution. + mlpackMain(); + + // Get the output. + const arma::Row &testY2 = + CLI::GetParam>("output"); + + // Both solutions must be equal. + BOOST_REQUIRE_EQUAL_COLLECTIONS(testY1.begin(), testY1.end(), + testY2.begin(), testY2.end()); +} + +/** + * Checking for dimensionality of the test data set. + **/ +BOOST_AUTO_TEST_CASE(LRWrongDimOfTestData) +{ + constexpr int N = 10; + constexpr int D = 4; + + arma::mat trainX = arma::randu(D, N); + arma::Row trainY; + + // 10 responses. + trainY << 0 << 1 << 0 << 1 << 1 << 1 << 0 << 1 << 0 << 0 << arma::endr; + + // Test data with wrong dimensionality. + arma::mat testX = arma::randu(D-1, N); + + SetInputParam("training", std::move(trainX)); + SetInputParam("labels", std::move(trainY)); + SetInputParam("test", std::move(testX)); + + // Dimensionality of test data is wrong. It should throw a runtime error. + Log::Fatal.ignoreInput = true; + BOOST_REQUIRE_THROW(mlpackMain(), std::runtime_error); + Log::Fatal.ignoreInput = false; +} + +/** + * Ensuring that test data dimensionality is checked when model is loaded. + */ +BOOST_AUTO_TEST_CASE(LRWrongDimOfTestData2) +{ + constexpr int N = 10; + constexpr int D = 3; + constexpr int M = 15; + + arma::mat trainX = arma::randu(D, N); + arma::Row trainY; + // 10 responses + trainY << 0 << 1 << 0 << 1 << 1 << 1 << 0 << 1 << 0 << 0 << arma::endr; + + SetInputParam("training", std::move(trainX)); + SetInputParam("labels", std::move(trainY)); + + // Training the model. + mlpackMain(); + + // Get the output model obtained from training. + LogisticRegression<>* model = + CLI::GetParam*>("output_model"); + + // Reset the data passed. + CLI::GetSingleton().Parameters()["training"].wasPassed = false; + CLI::GetSingleton().Parameters()["labels"].wasPassed = false; + + // Test data with Wrong dimensionality. + arma::mat testX = arma::randu(D - 1, M); + SetInputParam("input_model", model); + SetInputParam("test", std::move(testX)); + + // Test data dimensionality is wrong. It should throw a runtime error. + Log::Fatal.ignoreInput = true; + BOOST_REQUIRE_THROW(mlpackMain(), std::runtime_error); + Log::Fatal.ignoreInput = false; +} + +/** + * Ensuring that training responses contain only two classes (0 or 1). + **/ +BOOST_AUTO_TEST_CASE(LRTrainWithMoreThanTwoClasses) +{ + constexpr int N = 8; + constexpr int D = 2; + + arma::mat trainX = arma::randu(D, N); + arma::Row trainY; + + // 8 responses containing more than two classes. + trainY << 0 << 1 << 0 << 1 << 2 << 1 << 3 << 1 << arma::endr; + + SetInputParam("training", std::move(trainX)); + SetInputParam("labels", std::move(trainY)); + + // Training data contains more than two classes. It should throw a runtime error. + Log::Fatal.ignoreInput = true; + BOOST_REQUIRE_THROW(mlpackMain(), std::runtime_error); + Log::Fatal.ignoreInput = false; +} + +/** + * Ensuring that max iteration for optimizers is non negative. + **/ +BOOST_AUTO_TEST_CASE(LRNonNegativeMaxIterationTest) +{ + constexpr int N = 10; + constexpr int D = 3; + + arma::mat trainX = arma::randu(D, N); + arma::Row trainY; + + // 10 responses. + trainY << 0 << 1 << 0 << 1 << 1 << 1 << 0 << 1 << 0 << 0 << arma::endr; + + SetInputParam("training", std::move(trainX)); + SetInputParam("labels", std::move(trainY)); + SetInputParam("max_iterations", int(-1)); + + // Maximum iterations is negative. It should a runtime error. + Log::Fatal.ignoreInput = true; + BOOST_REQUIRE_THROW(mlpackMain(), std::runtime_error); + Log::Fatal.ignoreInput = false; +} + +/** + * Ensuring that step size for optimizer is non negative. + **/ +BOOST_AUTO_TEST_CASE(LRNonNegativeStepSizeTest) +{ + constexpr int N = 10; + constexpr int D = 2; + + arma::mat trainX = arma::randu(D, N); + arma::Row trainY; + + // 10 responses. + trainY << 0 << 1 << 0 << 1 << 0 << 1 << 0 << 1 << 0 << 1 << arma::endr; + + SetInputParam("training", std::move(trainX)); + SetInputParam("labels", std::move(trainY)); + SetInputParam("optimizer", std::string("sgd")); + SetInputParam("step_size", double(-0.01)); + + // Step size for optimizer is negative. It should throw a runtime error. + Log::Fatal.ignoreInput = true; + BOOST_REQUIRE_THROW(mlpackMain(), std::runtime_error); + Log::Fatal.ignoreInput = false; +} + +/** + * Ensuring that tolerance is non negative. + **/ +BOOST_AUTO_TEST_CASE(LRNonNegativeToleranceTest) +{ + constexpr int N = 10; + constexpr int D = 3; + + arma::mat trainX = arma::randu(D, N); + arma::Row trainY; + + // 10 responses. + trainY << 1 << 1 << 0 << 1 << 0 << 0 << 0 << 1 << 0 << 1 << arma::endr; + + SetInputParam("training", std::move(trainX)); + SetInputParam("labels", std::move(trainY)); + SetInputParam("tolerance", double(-0.01)); + + // Tolerance is negative. It should throw a runtime error. + Log::Fatal.ignoreInput = true; + BOOST_REQUIRE_THROW(mlpackMain(), std::runtime_error); + Log::Fatal.ignoreInput = false; +} + +/** + * Ensuring changing Maximum number of iterations changes the output model. + **/ +BOOST_AUTO_TEST_CASE(LRMaxIterationsChangeTest) +{ + constexpr int N = 10; + constexpr int D = 3; + + arma::mat trainX = arma::randu(D, N); + arma::Row trainY; + + // 10 responses. + trainY << 1 << 0 << 0 << 1 << 0 << 1 << 0 << 1 << 0 << 1 << arma::endr; + + SetInputParam("training", trainX); + SetInputParam("labels", trainY); + SetInputParam("max_iterations", int(1)); + + // First solution. + mlpackMain(); + + // Get the parameters of the output model obtained after first training. + const arma::rowvec parameters1 = + std::move(CLI::GetParam*>("output_model") + ->Parameters()); + + // Reset the settings. + bindings::tests::CleanMemory(); + CLI::ClearSettings(); + CLI::RestoreSettings(testName); + + SetInputParam("training", std::move(trainX)); + SetInputParam("labels", std::move(trainY)); + SetInputParam("max_iterations", int(100)); + + // Second solution. + mlpackMain(); + + // Get the parameters of the output model obtained after second training. + const arma::rowvec ¶meters2 = + CLI::GetParam*>("output_model")->Parameters(); + + // Check that the parameters (parameters1 and parameters2) are not equal + // which ensures Max Iteration changes the output model. + // arma::all function checks that each element of the vector is equal to zero. + BOOST_REQUIRE_MESSAGE(!arma::all((parameters1-parameters2) == 0), + "Parameter(Max Iteration) has no effect on the output"); +} + +/** + * Ensuring that lambda has some effects on the output. + **/ +BOOST_AUTO_TEST_CASE(LRLambdaChangeTest) +{ + constexpr int N = 10; + constexpr int D = 4; + + arma::mat trainX = arma::randu(D, N); + arma::Row trainY; + + // 10 responses. + trainY << 1 << 0 << 0 << 1 << 0 << 1 << 0 << 1 << 0 << 1 << arma::endr; + + SetInputParam("training", trainX); + SetInputParam("labels", trainY); + SetInputParam("lambda", double(0)); + + // First solution. + mlpackMain(); + + // Get the parameters of the output model obtained after first training. + const arma::rowvec parameters1 = + std::move(CLI::GetParam*>("output_model") + ->Parameters()); + + // Reset the settings. + bindings::tests::CleanMemory(); + CLI::ClearSettings(); + CLI::RestoreSettings(testName); + + SetInputParam("training", std::move(trainX)); + SetInputParam("labels", std::move(trainY)); + SetInputParam("lambda", double(1000)); + + // Second solution. + mlpackMain(); + + // Get the parameters of the output model obtained after second training. + const arma::rowvec ¶meters2 = + CLI::GetParam*>("output_model")->Parameters(); + + // Check that the parameters (parameters1 and parameters2) are not equal + // which ensures lambda changes the output model. + // arma::all function checks that each element of the vector is equal to zero. + BOOST_REQUIRE_MESSAGE(!arma::all((parameters1-parameters2) == 0), + "Parameter(lambda) has no effect on the output"); +} + +/** + * Ensuring that Step size has some effects on the output. + **/ +BOOST_AUTO_TEST_CASE(LRStepSizeChangeTest) +{ + constexpr int N = 10; + constexpr int D = 3; + + arma::mat trainX = arma::randu(D, N); + arma::Row trainY; + + // 10 responses. + trainY << 1 << 0 << 0 << 1 << 0 << 1 << 0 << 1 << 0 << 1 << arma::endr; + + SetInputParam("training", trainX); + SetInputParam("labels", trainY); + SetInputParam("optimizer", std::string("sgd")); + SetInputParam("step_size", double(0.02)); + + // First solution. + mlpackMain(); + + // Get the parameters of the output model obtained after first training. + const arma::rowvec parameters1 = + std::move(CLI::GetParam*>("output_model") + ->Parameters()); + + // Reset the settings. + bindings::tests::CleanMemory(); + CLI::ClearSettings(); + CLI::RestoreSettings(testName); + + SetInputParam("training", std::move(trainX)); + SetInputParam("labels", std::move(trainY)); + SetInputParam("optimizer", std::string("sgd")); + SetInputParam("step_size", double(1.02)); + + // Second solution. + mlpackMain(); + + // Get the parameters of the output model obtained after second training. + const arma::rowvec ¶meters2 = + CLI::GetParam*>("output_model")->Parameters(); + + // Check that the parameters (parameters1 and parameters2) are not equal + // which ensures Step Size changes the output model. + // arma::all function checks that each element of the vector is equal to zero. + BOOST_REQUIRE_MESSAGE(!arma::all((parameters1-parameters2) == 0), + "Parameter(Step Size) has no effect on the output"); +} + +/** + * Ensuring that lbfgs optimizer converges to a different result than sgd. + **/ +BOOST_AUTO_TEST_CASE(LROptimizerChangeTest) +{ + constexpr int N = 10; + constexpr int D = 3; + + arma::mat trainX = arma::randu(D, N); + arma::Row trainY; + + // 10 responses. + trainY << 1 << 0 << 0 << 1 << 0 << 1 << 0 << 1 << 0 << 1 << arma::endr; + + SetInputParam("training", trainX); + SetInputParam("labels", trainY); + SetInputParam("optimizer", std::string("lbfgs")); + SetInputParam("max_iterations", int(1000)); + + // First solution. + mlpackMain(); + + // Get the parameters of the output model obtained after first training. + const arma::rowvec parameters1 = + std::move(CLI::GetParam*>("output_model") + ->Parameters()); + + // Reset the settings. + bindings::tests::CleanMemory(); + CLI::ClearSettings(); + CLI::RestoreSettings(testName); + + SetInputParam("training", std::move(trainX)); + SetInputParam("labels", std::move(trainY)); + SetInputParam("optimizer", std::string("sgd")); + SetInputParam("max_iterations", int(1000)); + + // Second solution. + mlpackMain(); + + // Get the parameters of the output model obtained after second training. + const arma::rowvec ¶meters2 = + CLI::GetParam*>("output_model")->Parameters(); + + // Check that the parameters (parameters1 and parameters2) are not equal which + // ensures that different optimizer converge to different results. + // arma::all function checks that each element of the vector is equal to zero. + BOOST_REQUIRE_MESSAGE(!arma::all((parameters1-parameters2) == 0), + "Parameter(Step Size) has no effect on the output"); +} + +/** + * Ensuring decision_boundary parameter does something. + **/ +BOOST_AUTO_TEST_CASE(LRDecisionBoundaryTest) +{ + constexpr int N = 10; + constexpr int D = 3; + constexpr int M = 15; + + arma::mat trainX = arma::randu(D, N); + arma::Row trainY; + + // 10 responses. + trainY << 1 << 0 << 0 << 1 << 0 << 1 << 0 << 1 << 0 << 1 << arma::endr; + + arma::mat testX = arma::randu(D, M); + + SetInputParam("training", trainX); + SetInputParam("labels", trainY); + SetInputParam("decision_boundary", double(1)); + SetInputParam("test", testX); + + // First solution. + mlpackMain(); + + // Get the output after first training. + const arma::Row &output1 = CLI::GetParam>("output"); + + // Check that the parameters (parameters1 and parameters2) are not equal which + // ensures that decision boundary has some effect on the output. + // arma::all function checks that each element of the vector is equal to zero. + BOOST_REQUIRE_MESSAGE(arma::all(output1 == 0), + "Parameter(Decision Boudary) has" + "no effect on the output"); + + // Reset the settings. + bindings::tests::CleanMemory(); + CLI::ClearSettings(); + CLI::RestoreSettings(testName); + + SetInputParam("training", trainX); + SetInputParam("labels", trainY); + SetInputParam("decision_boundary", double(0)); + SetInputParam("test", testX); + + // Second solution. + mlpackMain(); + + // Get the output after second training. + const arma::Row &output2 = CLI::GetParam>("output"); + + // Check that the parameters (parameters1 and parameters2) are not equal which + // ensures that decision boundary has som effect on the output. + // arma::all function checks that each element of the vector is equal to one. + BOOST_REQUIRE_MESSAGE(arma::all(output2 == 1), + "Parameter(Decision Boudary) has" + "no effect on the output"); +} + +BOOST_AUTO_TEST_SUITE_END(); diff --git a/src/mlpack/tests/main_tests/perceptron_test.cpp b/src/mlpack/tests/main_tests/perceptron_test.cpp index d54dd80aba..c096088bb0 100644 --- a/src/mlpack/tests/main_tests/perceptron_test.cpp +++ b/src/mlpack/tests/main_tests/perceptron_test.cpp @@ -232,4 +232,184 @@ BOOST_AUTO_TEST_CASE(PerceptronMaxItrTest) Log::Fatal.ignoreInput = false; } +/** + * Ensuring that re-training of an existing model + * with different of classes is checked. + **/ +BOOST_AUTO_TEST_CASE(PerceptronReTrainWithWrongClasses) +{ + arma::mat trainX1; + arma::Row labelsX1; + + // Loading a train data set with 3 classes. + if (!data::Load("vc2.csv", trainX1)) + { + BOOST_FAIL("Could not load the train data (vc2.csv)"); + } + + // Loading the corresponding labels to the dataset. + if (!data::Load("vc2_labels.txt", labelsX1)) + { + BOOST_FAIL("Could not load the train data (vc2_labels.csv)"); + } + + SetInputParam("training", std::move(trainX1)); // Training data. + // Labels for the training data. + SetInputParam("labels", std::move(labelsX1)); + + // Training model using first training dataset. + mlpackMain(); + + // Get the output model obtained after training. + PerceptronModel* model = + CLI::GetParam("output_model"); + + // Reset the data passed. + CLI::GetSingleton().Parameters()["training"].wasPassed = false; + CLI::GetSingleton().Parameters()["labels"].wasPassed = false; + + // Creating training data with five classes. + constexpr int D = 3; + constexpr int N = 10; + arma::mat trainX2 = arma::randu(D, N); + arma::Row labelsX2; + + // 10 responses. + labelsX2 << 0 << 1 << 4 << 1 << 2 << 1 << 0 << 3 << 3 << 0 << endr; + + // Last column of trainX2 contains the class labels. + SetInputParam("training", std::move(trainX2)); + SetInputParam("input_model", model); + + // Re-training an existing model of 3 classes + // with training data of 5 classes. It should give runtime error. + Log::Fatal.ignoreInput = true; + BOOST_REQUIRE_THROW(mlpackMain(), std::runtime_error); + Log::Fatal.ignoreInput = false; +} + +/** + * Checking for dimensionality of the test data set. + **/ +BOOST_AUTO_TEST_CASE(PerceptronWrongDimOfTestData) +{ + constexpr int N = 10; + constexpr int D = 4; + constexpr int M = 20; + + arma::mat trainX = arma::randu(D, N); + arma::Row trainY; + + // 10 responses. + trainY << 0 << 1 << 0 << 1 << 1 << 1 << 0 << 1 << 0 << 0 << endr; + + // Test data with wrong dimensionality. + arma::mat testX = arma::randu(D-3, M); + + SetInputParam("training", std::move(trainX)); + SetInputParam("labels", std::move(trainY)); + SetInputParam("test", std::move(testX)); + + // Test data set with wrong dimensionality. It should give runtime error. + Log::Fatal.ignoreInput = true; + BOOST_REQUIRE_THROW(mlpackMain(), std::runtime_error); + Log::Fatal.ignoreInput = false; +} + +/** + * Ensuring that the response size is checked. + **/ +BOOST_AUTO_TEST_CASE(PerceptronWrongResponseSizeTest) +{ + constexpr int D = 2; + constexpr int N = 10; + + arma::mat trainX = arma::randu(D, N); + arma::Row trainY; // Response vector with wrong size. + + // 8 responses. + trainY << 0 << 0 << 1 << 0 << 1 << 1 << 1 << 0 << endr; + + SetInputParam("training", std::move(trainX)); + SetInputParam("labels", std::move(trainY)); + + // Labels for training data have wrong size. It should give runtime error. + Log::Fatal.ignoreInput = true; + BOOST_REQUIRE_THROW(mlpackMain(), std::runtime_error); + Log::Fatal.ignoreInput = false; +} + +/** + * Ensuring that absence of responses is checked. + */ +BOOST_AUTO_TEST_CASE(PerceptronNoResponsesTest) +{ + constexpr int N = 10; + constexpr int D = 1; + + arma::mat trainX = arma::randu(D, N); + SetInputParam("training", std::move(trainX)); + + // No labels for training data. It should give runtime error. + Log::Fatal.ignoreInput = true; + BOOST_REQUIRE_THROW(mlpackMain(), std::runtime_error); + Log::Fatal.ignoreInput = false; +} + +/** + * Ensuring that absence of training data is checked. + */ +BOOST_AUTO_TEST_CASE(PerceptronNoTrainingDataTest) +{ + arma::Row trainY; + trainY << 1 << 1 << 0 << 1 << 0 << 0 <(D, N); + arma::Row trainY; + + // 10 responses. + trainY << 0 << 1 << 0 << 1 << 1 << 1 << 0 << 1 << 0 << 0 << endr; + + SetInputParam("training", std::move(trainX)); + SetInputParam("labels", std::move(trainY)); + + // Training the model. + mlpackMain(); + + // Get the output model obtained after the training. + PerceptronModel* model = + CLI::GetParam("output_model"); + + // Reset the data passed. + CLI::GetSingleton().Parameters()["training"].wasPassed = false; + CLI::GetSingleton().Parameters()["labels"].wasPassed = false; + + // Test data with Wrong dimensionality. + arma::mat testX = arma::randu(D - 1, M); + SetInputParam("input_model", model); + SetInputParam("test", std::move(testX)); + + // Wrong dimensionality of test data. It should give runtime error. + Log::Fatal.ignoreInput = true; + BOOST_REQUIRE_THROW(mlpackMain(), std::runtime_error); + Log::Fatal.ignoreInput = false; +} + BOOST_AUTO_TEST_SUITE_END();