diff --git a/COPYRIGHT.txt b/COPYRIGHT.txt index 0cdf5c2d85..ce41bcf39e 100644 --- a/COPYRIGHT.txt +++ b/COPYRIGHT.txt @@ -74,6 +74,8 @@ Copyright: Copyright 2017, Praveen Ch Copyright 2017, Kirill Mishchenko Copyright 2017, Abhinav Moudgil + Copyright 2017, Thyrix Yang + Copyright 2017, Sagar B Hathwar License: BSD-3-clause All rights reserved. diff --git a/README.md b/README.md index a22f3a7e38..4fa6181c73 100644 --- a/README.md +++ b/README.md @@ -21,8 +21,7 @@ a machine learning analog to LAPACK. It aims to implement a wide array of machine learning methods and functions as a "swiss army knife" for machine learning researchers. -0. Contents ------------ +### 0. Contents 1. [Introduction](#1-introduction) 2. [Citation details](#2-citation-details) @@ -32,8 +31,7 @@ learning researchers. 6. [Further documentation](#6-further-documentation) 7. [Bug reporting](#7-bug-reporting) -1. Introduction ---------------- +### 1. Introduction The mlpack website can be found at http://www.mlpack.org and contains numerous tutorials and extensive documentation. This README serves as a guide for what @@ -45,8 +43,7 @@ documentation. The website should be consulted for further information: - [Development Site (Github)](http://www.github.com/mlpack/mlpack/) - [API documentation](http://www.mlpack.org/docs/mlpack-git/doxygen.php) -2. Citation details -------------------- +### 2. Citation details If you use mlpack in your research or software, please cite mlpack using the citation below (given in BiBTeX format): @@ -64,8 +61,7 @@ citation below (given in BiBTeX format): Citations are beneficial for the growth and improvement of mlpack. -3. Dependencies ---------------- +### 3. Dependencies mlpack has the following dependencies: @@ -80,8 +76,7 @@ each of those packages for more information. If you are compiling Armadillo by hand, ensure that LAPACK and BLAS are enabled. -4. Building mlpack from source ------------------------------- +### 4. Building mlpack from source This section discusses how to build mlpack from source. However, mlpack is in the repositories of many Linux distributions and so it may be easier to use the @@ -178,8 +173,7 @@ manually). One way to do this, on Linux, is to ensure that the (or whatever directory `libmlpack.so` is installed in.) -5. Running mlpack programs --------------------------- +### 5. Running mlpack programs After building mlpack, the executables will reside in `build/bin/`. You can call them from there, or you can install the library and (depending on system @@ -211,8 +205,7 @@ unique to `mlpack_knn` but is available in all mlpack programs. Verbose output also gives timing output at the end of the program, which can be very useful. -6. Further documentation ------------------------- +### 6. Further documentation The documentation given here is only a fraction of the available documentation for mlpack. If doxygen is installed, you can type `make doc` to build the @@ -224,8 +217,8 @@ older versions of mlpack: - [Development Site (Github)](https://www.github.com/mlpack/mlpack/) - [API documentation](http://www.mlpack.org/docs/mlpack-git/doxygen.php) -7. Bug reporting ----------------- +### 7. Bug reporting + (see also [mlpack help](http://www.mlpack.org/help.html)) If you find a bug in mlpack or have any problems, numerous routes are available diff --git a/src/mlpack/core.hpp b/src/mlpack/core.hpp index 6ea8dfb8b6..112e781048 100644 --- a/src/mlpack/core.hpp +++ b/src/mlpack/core.hpp @@ -215,6 +215,8 @@ * - Praveen Ch * - Kirill Mishchenko * - Abhinav Moudgil + * - Thyrix Yang + * - Sagar B Hathwar */ // First, include all of the prerequisites. diff --git a/src/mlpack/core/data/load.cpp b/src/mlpack/core/data/load.cpp index 1a57b8173b..16cae0bc0f 100644 --- a/src/mlpack/core/data/load.cpp +++ b/src/mlpack/core/data/load.cpp @@ -20,10 +20,20 @@ template bool Load(const std::string&, const bool, const bool); -template bool Load(const std::string&, - arma::Mat&, - const bool, - const bool); +template bool Load(const std::string&, + arma::Mat&, + const bool, + const bool); + +template bool Load(const std::string&, + arma::Mat&, + const bool, + const bool); + +template bool Load(const std::string&, + arma::Mat&, + const bool, + const bool); template bool Load(const std::string&, arma::Mat&, @@ -35,22 +45,32 @@ template bool Load(const std::string&, const bool, const bool); -template bool Load(const std::string&, - arma::Mat&, - const bool, - const bool); - template bool Load(const std::string&, arma::Mat&, DatasetMapper&, const bool, const bool); -template bool Load(const std::string&, - arma::Mat&, - DatasetMapper&, - const bool, - const bool); +template bool Load( + const std::string&, + arma::Mat&, + DatasetMapper&, + const bool, + const bool); + +template bool Load( + const std::string&, + arma::Mat&, + DatasetMapper&, + const bool, + const bool); + +template bool Load( + const std::string&, + arma::Mat&, + DatasetMapper&, + const bool, + const bool); template bool Load(const std::string&, arma::Mat&, @@ -64,14 +84,5 @@ template bool Load(const std::string&, const bool, const bool); -#ifndef _WIN32 -template bool Load( - const std::string&, - arma::Mat&, - DatasetMapper&, - const bool, - const bool); -#endif - } // namespace data } // namespace mlpack diff --git a/src/mlpack/core/data/load.hpp b/src/mlpack/core/data/load.hpp index db58166e49..767ac0d3cd 100644 --- a/src/mlpack/core/data/load.hpp +++ b/src/mlpack/core/data/load.hpp @@ -69,27 +69,32 @@ extern template bool Load(const std::string&, const bool, const bool); -extern template bool Load(const std::string&, - arma::Mat&, - const bool, - const bool); +// size_t and uword should be one of these three typedefs. +extern template bool Load(const std::string&, + arma::Mat&, + const bool, + const bool); + +extern template bool Load(const std::string&, + arma::Mat&, + const bool, + const bool); + +extern template bool Load(const std::string&, + arma::Mat&, + const bool, + const bool); + extern template bool Load(const std::string&, arma::Mat&, const bool, const bool); + extern template bool Load(const std::string&, arma::Mat&, const bool, const bool); -#ifndef _WIN32 -extern template bool Load( - const std::string&, - arma::Mat&, - const bool, - const bool); -#endif - /** * Load a column vector from a file, guessing the filetype from the extension. * @@ -200,9 +205,16 @@ extern template bool Load( const bool, const bool); -extern template bool Load( +extern template bool Load( const std::string&, - arma::Mat&, + arma::Mat&, + DatasetMapper&, + const bool, + const bool); + +extern template bool Load( + const std::string&, + arma::Mat&, DatasetMapper&, const bool, const bool); @@ -221,13 +233,6 @@ extern template bool Load( const bool, const bool); -extern template bool Load( - const std::string&, - arma::Mat&, - DatasetMapper&, - const bool, - const bool); - /** * Load a model from a file, guessing the filetype from the extension, or, * optionally, loading the specified format. If automatic extension detection diff --git a/src/mlpack/methods/ann/init_rules/CMakeLists.txt b/src/mlpack/methods/ann/init_rules/CMakeLists.txt index 981ceaa23d..78dffb67d1 100644 --- a/src/mlpack/methods/ann/init_rules/CMakeLists.txt +++ b/src/mlpack/methods/ann/init_rules/CMakeLists.txt @@ -6,6 +6,7 @@ set(SOURCES kathirvalavakumar_subavathi_init.hpp nguyen_widrow_init.hpp zero_init.hpp + gaussian_init.hpp ) # Add directory name to sources. diff --git a/src/mlpack/methods/ann/init_rules/gaussian_init.hpp b/src/mlpack/methods/ann/init_rules/gaussian_init.hpp new file mode 100644 index 0000000000..aa4120e8f2 --- /dev/null +++ b/src/mlpack/methods/ann/init_rules/gaussian_init.hpp @@ -0,0 +1,88 @@ +/** + * @file gaussian_init.hpp + * @author Kris Singh + * + * Intialization rule for the neural networks. This simple initialization is + * performed by assigning a gaussian matrix with a given mean and variance + * to the weight matrix. + * + * 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. + */ +#ifndef MLPACK_METHODS_ANN_INIT_RULES_GAUSSIAN_INIT_HPP +#define MLPACK_METHODS_ANN_INIT_RULES_GAUSSIAN_INIT_HPP + +#include +#include + +using namespace mlpack::math; + +namespace mlpack { +namespace ann /** Artificial Neural Network. */ { + +/** + * This class is used to initialize weigth matrix with a gaussian. + */ +class GaussianInitialization +{ + public: + /** + * Initialize the gaussian with the given mean and variance. + * + * @param mean Mean of the gaussian + * @param variance Variance of the gaussian + */ + GaussianInitialization(const double mean = 0, const double variance = 1) : + mean(mean), variance(variance) + { + // Nothing to do here. + } + + /** + * Initialize the elements weight matrix using a Gaussian Distribution. + * + * @param W Weight matrix to initialize. + * @param rows Number of rows. + * @param cols Number of columns. + */ + void Initialize(arma::mat& W, + const size_t rows, + const size_t cols) + { + W = arma::mat(rows, cols); + W.imbue( [&]() { return arma::as_scalar(RandNormal(mean, variance)); } ); + } + + /** + * Initialize randomly the elements of the specified weight 3rd order tensor. + * + * @param W Weight matrix to initialize. + * @param rows Number of rows. + * @param cols Number of columns. + * @param slice Numbers of slices. + */ + void Initialize(arma::cube & W, + const size_t rows, + const size_t cols, + const size_t slices) + { + W = arma::cube(rows, cols, slices); + + for (size_t i = 0; i < slices; i++) + Initialize(W.slice(i), rows, cols); + } + + private: + //! Mean of the gaussian. + const double mean; + + //! Variance of the gaussian. + const double variance; +}; // class GaussianInitialization + +} // namespace ann +} // namespace mlpack + +#endif diff --git a/src/mlpack/methods/decision_tree/decision_tree_impl.hpp b/src/mlpack/methods/decision_tree/decision_tree_impl.hpp index 49b9cea084..0104f006d2 100644 --- a/src/mlpack/methods/decision_tree/decision_tree_impl.hpp +++ b/src/mlpack/methods/decision_tree/decision_tree_impl.hpp @@ -227,6 +227,16 @@ void DecisionTree& labels) const; + /** + * Classify the given point. The predicted class label is returned. + * The function calculates the probabilites for every class, given the point. + * It then chooses the class which has the highest probability among all. + * + * @param point Point to be classified. + * @return Predicted class label of the point. + */ + template + size_t Classify(const VecType& point) const; + + /** + * Classify the given points, returning class probabilities and predicted + * class label for each point. + * The function calculates the probabilities for every class, given a data + * point. It then chooses the class which has the highest probability among + * all. + * + * @param dataset Matrix of data points to be classified. + * @param labels Predicted labels for each point. + * @param probabilities Class probabilities for each point. + */ + void Classify(const arma::mat& dataset, arma::Row& labels, + arma::mat& probabilites) const; + + /** + * Classify the given points, returning class probabilities for each point. + * + * @param dataset Matrix of data points to be classified. + * @param probabilities Class probabilities for each point. + */ + void Classify(const arma::mat& dataset, + arma::mat& probabilities) const; + /** * Computes accuracy of the learned model given the feature data and the * labels associated with each data point. Predictions are made using the @@ -161,7 +195,7 @@ class SoftmaxRegression * @param numClasses Number of classes for classification. * @return Objective value of the final point. */ - double Train(const arma::mat &data, const arma::Row& labels, + double Train(const arma::mat& data, const arma::Row& labels, const size_t numClasses); //! Sets the number of classes. diff --git a/src/mlpack/methods/softmax_regression/softmax_regression_impl.hpp b/src/mlpack/methods/softmax_regression/softmax_regression_impl.hpp index b818e4e6a6..57c4f78b4e 100644 --- a/src/mlpack/methods/softmax_regression/softmax_regression_impl.hpp +++ b/src/mlpack/methods/softmax_regression/softmax_regression_impl.hpp @@ -75,35 +75,8 @@ void SoftmaxRegression::Classify(const arma::mat& dataset, arma::Row& labels) const { - if (dataset.n_rows != FeatureSize()) - { - std::ostringstream oss; - oss << "SoftmaxRegression::Classify(): dataset has " << dataset.n_rows - << " dimensions, but model has " << FeatureSize() << "dimensions"; - throw std::invalid_argument(oss.str()); - } - - // Calculate the probabilities for each test input. - arma::mat hypothesis, probabilities; - if (fitIntercept) - { - // In order to add the intercept term, we should compute following matrix: - // [1; data] = arma::join_cols(ones(1, data.n_cols), data) - // hypothesis = arma::exp(parameters * [1; data]). - // - // Since the cost of join maybe high due to the copy of original data, - // split the hypothesis computation to two components. - hypothesis = arma::exp( - arma::repmat(parameters.col(0), 1, dataset.n_cols) + - parameters.cols(1, parameters.n_cols - 1) * dataset); - } - else - { - hypothesis = arma::exp(parameters * dataset); - } - - probabilities = hypothesis / arma::repmat(arma::sum(hypothesis, 0), - numClasses, 1); + arma::mat probabilities; + Classify(dataset, probabilities); // Prepare necessary data. labels.zeros(dataset.n_cols); @@ -128,6 +101,82 @@ void SoftmaxRegression::Classify(const arma::mat& dataset, } } +template class OptimizerType> +template +size_t SoftmaxRegression::Classify(const VecType& point) const +{ + arma::Row label(1); + Classify(point, label); + return size_t(label(0)); +} + +template class OptimizerType> +void SoftmaxRegression::Classify(const arma::mat& dataset, + arma::Row& labels, + arma::mat& probabilities) + const +{ + Classify(dataset, probabilities); + + // Prepare necessary data. + labels.zeros(dataset.n_cols); + double maxProbability = 0; + + // For each test input. + for (size_t i = 0; i < dataset.n_cols; i++) + { + // For each class. + for (size_t j = 0; j < numClasses; j++) + { + // If a higher class probability is encountered, change prediction. + if (probabilities(j, i) > maxProbability) + { + maxProbability = probabilities(j, i); + labels(i) = j; + } + } + + // Set maximum probability to zero for the next input. + maxProbability = 0; + } +} + +template class OptimizerType> +void SoftmaxRegression::Classify(const arma::mat& dataset, + arma::mat& probabilities) + const +{ + if (dataset.n_rows != FeatureSize()) + { + std::ostringstream oss; + oss << "SoftmaxRegression::Classify(): dataset has " << dataset.n_rows + << " dimensions, but model has " << FeatureSize() << "dimensions"; + throw std::invalid_argument(oss.str()); + } + + // Calculate the probabilities for each test input. + arma::mat hypothesis; + if (fitIntercept) + { + // In order to add the intercept term, we should compute following matrix: + // [1; data] = arma::join_cols(ones(1, data.n_cols), data) + // hypothesis = arma::exp(parameters * [1; data]). + // + // Since the cost of join maybe high due to the copy of original data, + // split the hypothesis computation to two components. + hypothesis = arma::exp( + arma::repmat(parameters.col(0), 1, dataset.n_cols) + + parameters.cols(1, parameters.n_cols - 1) * dataset); + } + else + { + hypothesis = arma::exp(parameters * dataset); + } + + probabilities = hypothesis / arma::repmat(arma::sum(hypothesis, 0), + numClasses, 1); +} + template class OptimizerType> double SoftmaxRegression::ComputeAccuracy( const arma::mat& testData, diff --git a/src/mlpack/tests/cli_test.cpp b/src/mlpack/tests/cli_test.cpp index 21f9b6fa27..8a911d8875 100644 --- a/src/mlpack/tests/cli_test.cpp +++ b/src/mlpack/tests/cli_test.cpp @@ -136,17 +136,15 @@ BOOST_AUTO_TEST_CASE(TestBooleanOption) // Now, if we specify this flag, it should be true. int argc = 2; - char* argv[2]; - argv[0] = strcpy(new char[strlen("programname") + 1], "programname"); - argv[1] = strcpy(new char[strlen("--flag_test") + 1], "--flag_test"); + const char* argv[2]; + argv[0] = "programname"; + argv[1] = "--flag_test"; - CLI::ParseCommandLine(argc, argv); + CLI::ParseCommandLine(argc, const_cast(argv)); BOOST_REQUIRE_EQUAL(CLI::GetParam("flag_test"), true); BOOST_REQUIRE_EQUAL(CLI::HasParam("flag_test"), true); - delete[] argv[0]; - delete[] argv[1]; } /** diff --git a/src/mlpack/tests/init_rules_test.cpp b/src/mlpack/tests/init_rules_test.cpp index 3ea0f8a6c9..7880724908 100644 --- a/src/mlpack/tests/init_rules_test.cpp +++ b/src/mlpack/tests/init_rules_test.cpp @@ -10,6 +10,7 @@ * http://www.opensource.org/licenses/BSD-3-Clause for more information. */ #include +#include #include #include @@ -17,6 +18,8 @@ #include #include #include +#include + #include #include "test_tools.hpp" @@ -123,4 +126,58 @@ BOOST_AUTO_TEST_CASE(OivsInitTest) BOOST_REQUIRE_EQUAL(1, 1); } +// Test the GaussianInitialization class. +BOOST_AUTO_TEST_CASE(GaussianInitTest) +{ + const size_t row = 7; + const size_t col = 7; + const size_t slice = 2; + + double mean = 1; + double mean3d = 1; + double var = 1; + double var3d = 1; + + arma::mat weights; + arma::cube weights3d; + + GaussianInitialization t(0, 0.2); + + // It isn't guaranteed that the method will converge in the specified number + // of iterations using random weights. If this works 1 of 5 times, I'm fine + // with that. + size_t counter = 0; + for(size_t trial = 0; trial < 5; trial++) + { + for(size_t i = 0; i < 10; i++) + { + t.Initialize(weights, row, col); + t.Initialize(weights3d, row, col, slice); + + // Calaculate mean and variance over the dense matrix. + mean += arma::accu(weights) / weights.n_elem; + var += arma::accu(pow((weights.t() - mean), 2)) / weights.n_elem - 1; + + // Calaculate mean and variance over the 3rd order tensor. + mean3d += arma::accu(weights3d.slice(0)) / weights3d.slice(0).n_elem; + var3d += arma::accu(pow((weights3d.slice(0) - mean), 2)) / + weights3d.slice(0).n_elem - 1; + } + + mean /= 10; + var /= 10; + mean3d /= 10; + var3d /= 10; + + if ((mean > 0 && mean < 0.4) && (var > 0 && var < 0.6) && + (mean3d > 0 && mean3d < 0.4) && (var3d > 0 && var3d < 0.6)) + { + counter++; + break; + } + } + + BOOST_REQUIRE(counter >= 1); +} + BOOST_AUTO_TEST_SUITE_END(); diff --git a/src/mlpack/tests/softmax_regression_test.cpp b/src/mlpack/tests/softmax_regression_test.cpp index d749582aba..d5741843b3 100644 --- a/src/mlpack/tests/softmax_regression_test.cpp +++ b/src/mlpack/tests/softmax_regression_test.cpp @@ -413,4 +413,262 @@ BOOST_AUTO_TEST_CASE(SoftmaxRegressionOptimizerTrainTest) } } +BOOST_AUTO_TEST_CASE(SoftmaxRegressionClassifySinglePointTest) +{ + const size_t points = 5000; + const size_t inputSize = 5; + const size_t numClasses = 5; + const double lambda = 0.5; + + // Generate five-Gaussian dataset. + arma::mat identity = arma::eye(5, 5); + GaussianDistribution g1(arma::vec("1.0 9.0 1.0 2.0 2.0"), identity); + GaussianDistribution g2(arma::vec("4.0 3.0 4.0 2.0 2.0"), identity); + GaussianDistribution g3(arma::vec("3.0 2.0 7.0 0.0 5.0"), identity); + GaussianDistribution g4(arma::vec("4.0 1.0 1.0 2.0 7.0"), identity); + GaussianDistribution g5(arma::vec("1.0 0.0 1.0 8.0 3.0"), identity); + + arma::mat data(inputSize, points); + arma::Row labels(points); + + for (size_t i = 0; i < points / 5; i++) + { + data.col(i) = g1.Random(); + labels(i) = 0; + } + for (size_t i = points / 5; i < (2 * points) / 5; i++) + { + data.col(i) = g2.Random(); + labels(i) = 1; + } + for (size_t i = (2 * points) / 5; i < (3 * points) / 5; i++) + { + data.col(i) = g3.Random(); + labels(i) = 2; + } + for (size_t i = (3 * points) / 5; i < (4 * points) / 5; i++) + { + data.col(i) = g4.Random(); + labels(i) = 3; + } + for (size_t i = (4 * points) / 5; i < points; i++) + { + data.col(i) = g5.Random(); + labels(i) = 4; + } + + // Train softmax regression object. + SoftmaxRegression<> sr(data, labels, numClasses, lambda); + + // Create test dataset. + for (size_t i = 0; i < points / 5; i++) + { + data.col(i) = g1.Random(); + labels(i) = 0; + } + for (size_t i = points / 5; i < (2 * points) / 5; i++) + { + data.col(i) = g2.Random(); + labels(i) = 1; + } + for (size_t i = (2 * points) / 5; i < (3 * points) / 5; i++) + { + data.col(i) = g3.Random(); + labels(i) = 2; + } + for (size_t i = (3 * points) / 5; i < (4 * points) / 5; i++) + { + data.col(i) = g4.Random(); + labels(i) = 3; + } + for (size_t i = (4 * points) / 5; i < points; i++) + { + data.col(i) = g5.Random(); + labels(i) = 4; + } + + sr.Classify(data, labels); + + for(size_t i = 0; i < data.n_cols; ++i) + { + BOOST_REQUIRE_EQUAL(sr.Classify(data.col(i)), labels(i)); + } +} + +BOOST_AUTO_TEST_CASE(SoftmaxRegressionComputeProbabilitiesTest) +{ + const size_t points = 5000; + const size_t inputSize = 5; + const size_t numClasses = 5; + const double lambda = 0.5; + + // Generate five-Gaussian dataset. + arma::mat identity = arma::eye(5, 5); + GaussianDistribution g1(arma::vec("1.0 9.0 1.0 2.0 2.0"), identity); + GaussianDistribution g2(arma::vec("4.0 3.0 4.0 2.0 2.0"), identity); + GaussianDistribution g3(arma::vec("3.0 2.0 7.0 0.0 5.0"), identity); + GaussianDistribution g4(arma::vec("4.0 1.0 1.0 2.0 7.0"), identity); + GaussianDistribution g5(arma::vec("1.0 0.0 1.0 8.0 3.0"), identity); + + arma::mat data(inputSize, points); + arma::Row labels(points); + + for (size_t i = 0; i < points / 5; i++) + { + data.col(i) = g1.Random(); + labels(i) = 0; + } + for (size_t i = points / 5; i < (2 * points) / 5; i++) + { + data.col(i) = g2.Random(); + labels(i) = 1; + } + for (size_t i = (2 * points) / 5; i < (3 * points) / 5; i++) + { + data.col(i) = g3.Random(); + labels(i) = 2; + } + for (size_t i = (3 * points) / 5; i < (4 * points) / 5; i++) + { + data.col(i) = g4.Random(); + labels(i) = 3; + } + for (size_t i = (4 * points) / 5; i < points; i++) + { + data.col(i) = g5.Random(); + labels(i) = 4; + } + + // Train softmax regression object. + SoftmaxRegression<> sr(data, labels, numClasses, lambda); + + // Create test dataset. + for (size_t i = 0; i < points / 5; i++) + { + data.col(i) = g1.Random(); + labels(i) = 0; + } + for (size_t i = points / 5; i < (2 * points) / 5; i++) + { + data.col(i) = g2.Random(); + labels(i) = 1; + } + for (size_t i = (2 * points) / 5; i < (3 * points) / 5; i++) + { + data.col(i) = g3.Random(); + labels(i) = 2; + } + for (size_t i = (3 * points) / 5; i < (4 * points) / 5; i++) + { + data.col(i) = g4.Random(); + labels(i) = 3; + } + for (size_t i = (4 * points) / 5; i < points; i++) + { + data.col(i) = g5.Random(); + labels(i) = 4; + } + + arma::mat probabilities; + sr.Classify(data, probabilities); + + BOOST_REQUIRE_EQUAL(probabilities.n_cols, data.n_cols); + BOOST_REQUIRE_EQUAL(probabilities.n_rows, sr.NumClasses()); + + for(size_t i = 0; i < data.n_cols; ++i) + { + BOOST_REQUIRE_CLOSE(arma::sum(probabilities.col(i)), 1.0, 1e-5); + } +} + +BOOST_AUTO_TEST_CASE(SoftmaxRegressionComputeProbabilitiesAndLabelsTest) +{ + const size_t points = 5000; + const size_t inputSize = 5; + const size_t numClasses = 5; + const double lambda = 0.5; + + // Generate five-Gaussian dataset. + arma::mat identity = arma::eye(5, 5); + GaussianDistribution g1(arma::vec("1.0 9.0 1.0 2.0 2.0"), identity); + GaussianDistribution g2(arma::vec("4.0 3.0 4.0 2.0 2.0"), identity); + GaussianDistribution g3(arma::vec("3.0 2.0 7.0 0.0 5.0"), identity); + GaussianDistribution g4(arma::vec("4.0 1.0 1.0 2.0 7.0"), identity); + GaussianDistribution g5(arma::vec("1.0 0.0 1.0 8.0 3.0"), identity); + + arma::mat data(inputSize, points); + arma::Row labels(points); + + for (size_t i = 0; i < points / 5; i++) + { + data.col(i) = g1.Random(); + labels(i) = 0; + } + for (size_t i = points / 5; i < (2 * points) / 5; i++) + { + data.col(i) = g2.Random(); + labels(i) = 1; + } + for (size_t i = (2 * points) / 5; i < (3 * points) / 5; i++) + { + data.col(i) = g3.Random(); + labels(i) = 2; + } + for (size_t i = (3 * points) / 5; i < (4 * points) / 5; i++) + { + data.col(i) = g4.Random(); + labels(i) = 3; + } + for (size_t i = (4 * points) / 5; i < points; i++) + { + data.col(i) = g5.Random(); + labels(i) = 4; + } + + // Train softmax regression object. + SoftmaxRegression<> sr(data, labels, numClasses, lambda); + + // Create test dataset. + for (size_t i = 0; i < points / 5; i++) + { + data.col(i) = g1.Random(); + labels(i) = 0; + } + for (size_t i = points / 5; i < (2 * points) / 5; i++) + { + data.col(i) = g2.Random(); + labels(i) = 1; + } + for (size_t i = (2 * points) / 5; i < (3 * points) / 5; i++) + { + data.col(i) = g3.Random(); + labels(i) = 2; + } + for (size_t i = (3 * points) / 5; i < (4 * points) / 5; i++) + { + data.col(i) = g4.Random(); + labels(i) = 3; + } + for (size_t i = (4 * points) / 5; i < points; i++) + { + data.col(i) = g5.Random(); + labels(i) = 4; + } + + arma::mat probabilities; + arma::Row testLabels; + + sr.Classify(data, labels); + sr.Classify(data, testLabels, probabilities); + + BOOST_REQUIRE_EQUAL(probabilities.n_cols, data.n_cols); + BOOST_REQUIRE_EQUAL(probabilities.n_rows, sr.NumClasses()); + + for(size_t i = 0; i < data.n_cols; ++i) + { + BOOST_REQUIRE_CLOSE(arma::sum(probabilities.col(i)), 1.0, 1e-5); + BOOST_REQUIRE_EQUAL(testLabels(i), labels(i)); + } +} + BOOST_AUTO_TEST_SUITE_END();