Merge branch 'master' of https://github.com/mlpack/mlpack into embedding

This commit is contained in:
Mrityunjay Tripathi
2020-08-02 08:46:26 +05:30
11 changed files with 1113 additions and 0 deletions
+5
View File
@@ -11,6 +11,11 @@
* Additional functionality for the ARFF loader (#2486); use case sensitive
categories (#2516).
* Add `bayesian_linear_regression` binding for the command-line, Python,
Julia, and Go. Also called "Bayesian Ridge", this is equivalent to a
version of linear regression where the regularization parameter is
automatically tuned (#2030).
### mlpack 3.3.2
###### 2020-06-18
* Added Noisy DQN to q_networks (#2446).
+1
View File
@@ -6,6 +6,7 @@ set(DIRS
ann
approx_kfn
bias_svd
bayesian_linear_regression
block_krylov_svd
cf
dbscan
@@ -0,0 +1,21 @@
# Define the files we need to compile
# Anything not in this list will not be compiled into the output library
set(SOURCES
bayesian_linear_regression.hpp
bayesian_linear_regression_impl.hpp
bayesian_linear_regression.cpp
)
# add directory name to sources
set(DIR_SRCS)
foreach(file ${SOURCES})
set(DIR_SRCS ${DIR_SRCS} ${CMAKE_CURRENT_SOURCE_DIR}/${file})
endforeach()
# append sources (with directory name) to list of all mlpack sources (used at the parent scope)
set(MLPACK_SRCS ${MLPACK_SRCS} ${DIR_SRCS} PARENT_SCOPE)
add_cli_executable(bayesian_linear_regression)
add_python_binding(bayesian_linear_regression)
add_julia_binding(bayesian_linear_regression)
add_go_binding(bayesian_linear_regression)
add_markdown_docs(bayesian_linear_regression "cli;python;julia;go" "regression")
@@ -0,0 +1,188 @@
/**
* @file methods/bayesian_linear_regression/bayesian_linear_regression.cpp
* @author Clement Mercier
*
* Implementation of Bayesian linear regression.
*
* 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.
*/
#include "bayesian_linear_regression.hpp"
#include <mlpack/core/util/log.hpp>
#include <mlpack/core/util/timers.hpp>
using namespace mlpack;
using namespace mlpack::regression;
BayesianLinearRegression::BayesianLinearRegression(const bool centerData,
const bool scaleData,
const size_t nIterMax,
const double tol) :
centerData(centerData),
scaleData(scaleData),
nIterMax(nIterMax),
tol(tol),
responsesOffset(0.0),
alpha(0.0),
beta(0.0),
gamma(0.0)
{/* Nothing to do */}
double BayesianLinearRegression::Train(const arma::mat& data,
const arma::rowvec& responses)
{
Timer::Start("bayesian_linear_regression");
arma::mat phi;
arma::rowvec t;
arma::colvec eigVal;
arma::mat eigVec;
// Preprocess the data. Center and scale.
responsesOffset = CenterScaleData(data, responses, phi, t);
if (!arma::eig_sym(eigVal, eigVec, arma::symmatu(phi * phi.t())))
{
Log::Fatal << "BayesianLinearRegression::Train(): Eigendecomposition "
<< "of covariance failed!" << std::endl;
}
// Compute this quantities once and for all.
const arma::mat eigVecInv = inv(eigVec);
const arma::colvec eigVecInvPhitT = eigVecInv * phi * t.t();
// Initialize the hyperparameters and begin with an infinitely broad prior.
alpha = 1e-6;
beta = 1 / (var(t, 1) * 0.1);
unsigned short i = 0;
double deltaAlpha = 1.0, deltaBeta = 1.0, crit = 1.0;
while ((crit > tol) && (i < nIterMax))
{
deltaAlpha = -alpha;
deltaBeta = -beta;
// Update the solution.
omega = eigVec * diagmat(1 / (eigVal + (alpha / beta))) * eigVecInvPhitT;
// Update alpha.
gamma = sum(eigVal / (alpha / beta + eigVal));
alpha = gamma / dot(omega, omega);
// Update beta.
const arma::rowvec temp = t - omega.t() * phi;
beta = (data.n_cols - gamma) / dot(temp, temp);
// Compute the stopping criterion.
deltaAlpha += alpha;
deltaBeta += beta;
crit = std::abs(deltaAlpha / alpha + deltaBeta / beta);
i++;
}
// Compute the covariance matrix for the uncertainties later.
matCovariance = eigVec * diagmat(1 / (beta * eigVal + alpha)) * eigVecInv;
Timer::Stop("bayesian_linear_regression");
return RMSE(data, responses);
}
void BayesianLinearRegression::Predict(const arma::mat& points,
arma::rowvec& predictions) const
{
// Center and scale the points before applying the model.
arma::mat matX;
CenterScaleDataPred(points, matX);
predictions = omega.t() * matX + responsesOffset;
}
void BayesianLinearRegression::Predict(const arma::mat& points,
arma::rowvec& predictions,
arma::rowvec& std) const
{
// Center and scale the points before applying the model.
arma::mat matX;
CenterScaleDataPred(points, matX);
predictions = omega.t() * matX + responsesOffset;
// Compute the standard deviation for each point.
std = sqrt(Variance() + sum(matX % (matCovariance * matX), 0));
}
double BayesianLinearRegression::RMSE(const arma::mat& data,
const arma::rowvec& responses) const
{
arma::rowvec predictions;
Predict(data, predictions);
return sqrt(mean(square(responses - predictions)));
}
double BayesianLinearRegression::CenterScaleData(const arma::mat& data,
const arma::rowvec& responses,
arma::mat& dataProc,
arma::rowvec& responsesProc)
{
if (!centerData && !scaleData)
{
dataProc = arma::mat(const_cast<double*>(data.memptr()), data.n_rows,
data.n_cols, false, true);
responsesProc = arma::rowvec(const_cast<double*>(responses.memptr()),
responses.n_elem, false,
true);
}
else if (centerData && !scaleData)
{
dataOffset = mean(data, 1);
responsesOffset = mean(responses);
dataProc = data.each_col() - dataOffset;
responsesProc = responses - responsesOffset;
}
else if (!centerData && scaleData)
{
dataScale = stddev(data, 0, 1);
dataProc = data.each_col() / dataScale;
responsesProc = arma::rowvec(const_cast<double*>(responses.memptr()),
responses.n_elem, false,
true);
}
else
{
dataOffset = mean(data, 1);
dataScale = stddev(data, 0, 1);
responsesOffset = mean(responses);
dataProc = (data.each_col() - dataOffset).each_col() / dataScale;
responsesProc = responses - responsesOffset;
}
return responsesOffset;
}
void BayesianLinearRegression::CenterScaleDataPred(
const arma::mat& data,
arma::mat& dataProc) const
{
if (!centerData && !scaleData)
{
dataProc = arma::mat(const_cast<double*>(data.memptr()), data.n_rows,
data.n_cols, false, true);
}
else if (centerData && !scaleData)
{
dataProc = data.each_col() - dataOffset;
}
else if (!centerData && scaleData)
{
dataProc = data.each_col() / dataScale;
}
else
{
dataProc = (data.each_col() - dataOffset).each_col() / dataScale;
}
}
@@ -0,0 +1,285 @@
/**
* @file methods/bayesian_linear_regression/bayesian_linear_regression.hpp
* @author Clement Mercier
*
* Definition of the BayesianRidge class, which performs the
* bayesian linear regression. According to the armadillo standards,
* all the functions consider data in column-major format.
**/
#ifndef MLPACK_METHODS_BAYESIAN_LINEAR_REGRESSION_HPP
#define MLPACK_METHODS_BAYESIAN_LINEAR_REGRESSION_HPP
#include <mlpack/prereqs.hpp>
namespace mlpack {
namespace regression {
/**
* A Bayesian approach to the maximum likelihood estimation of the parameters
* \f$ \omega \f$ of the linear regression model. The Complexity is governed by
* the addition of a gaussian isotropic prior of precision \f$ \alpha \f$ over
* \f$ \omega \f$:
*
* \f[
* p(\omega|\alpha) = \mathcal{N}(\omega|0, \alpha^{-1}I)
* \f]
*
* The optimization procedure calculates the posterior distribution of
* \f$ \omega \f$ knowing the data by maximizing an approximation of the log
* marginal likelihood derived from a type II maximum likelihood approximation.
* The determination of \f$ alpha \f$ and of the noise precision \f$ beta \f$
* is part of the optimization process, leading to an automatic determination of
* w. The model being entirely based on probabilty distributions, uncertainties
* are available and easly computed for both the parameters and the predictions.
*
* The advantage over linear regression and ridge regression is that the
* regularization is determined from all the training data alone without any
* require to an hold out method.
*
* The code below is an implementation of the maximization of the evidence
* function described in the section 3.5.2 of the C.Bishop book, Pattern
* Recognition and Machine Learning.
*
* @code
* @article{MacKay91bayesianinterpolation,
* author = {David J.C. MacKay},
* title = {Bayesian Interpolation},
* journal = {NEURAL COMPUTATION},
* year = {1991},
* volume = {4},
* pages = {415--447}
* }
* @endcode
*
* @code
* @book{Bishop:2006:PRM:1162264,
* author = {Bishop, Christopher M.},
* title = {Pattern Recognition and Machine Learning (Information Science
* and Statistics)},
* chapter = {3}
* year = {2006},
* isbn = {0387310738},
* publisher = {Springer-Verlag},
* address = {Berlin, Heidelberg},
* }
* @endcode
*
* Example of use:
*
* @code
* arma::mat xTrain; // Train data matrix. Column-major.
* arma::rowvec yTrain; // Train target values.
* // Train the model. Regularization strength is optimally tunned with the
* // training data alone by applying the Train method.
* BayesianLinearRegression estimator(); // Instanciate the estimator with default option.
* estimator.Train(xTrain, yTrain);
* // Prediction on test points.
* arma::mat xTest; // Test data matrix. Column-major.
* arma::rowvec predictions;
* estimator.Predict(xTest, prediction);
* arma::rowvec yTest; // Test target values.
* estimator.RMSE(xTest, yTest); // Evaluate using the RMSE score.
* // Compute the standard deviations of the predictions.
* arma::rowvec stds;
* estimator.Predict(xTest, responses, stds)
* @endcode
*/
class BayesianLinearRegression
{
public:
/**
* Set the parameters of Bayesian Ridge regression object. The
* regularization parameter is automatically set to its optimal value by
* maximization of the marginal likelihood.
*
* @param centerData Whether or not center the data according to the
* examples.
* @param scaleData Whether or not scale the data according to the
* standard deviation of each feature.
* @param nIterMax Maximum number of iterations for convergency.
* @param tol Level from which the solution is considered sufficientlly
* stable.
*/
BayesianLinearRegression(const bool centerData = true,
const bool scaleData = false,
const size_t nIterMax = 50,
const double tol = 1e-4);
/**
* Run BayesianLinearRegression. The input matrix (like all mlpack matrices) should be
* column-major -- each column is an observation and each row is a dimension.
*
* @param data Column-major input data, dim(P, N).
* @param responses A vector of targets, dim(N).
* @return Root mean squared error.
*/
double Train(const arma::mat& data,
const arma::rowvec& responses);
/**
* Predict \f$y_{i}\f$ for each data point in the given data matrix using the
* currently-trained Bayesian Ridge model.
*
* @param points The data points to apply the model.
* @param predictions y, Contains the predicted values on completion.
* @return Root mean squared error computed on the train set.
*/
void Predict(const arma::mat& points,
arma::rowvec& predictions) const;
/**
* Predict \f$y_{i}\f$ and the standard deviation of the predictive posterior
* distribution for each data point in the given data matrix, using the
* currently-trained Bayesian Ridge estimator.
*
* @param points The data point to apply the model.
* @param predictions Vector which will contain calculated values on completion.
* @param std Standard deviations of the predictions.
*/
void Predict(const arma::mat& points,
arma::rowvec& predictions,
arma::rowvec& std) const;
/**
* Compute the Root Mean Square Error between the predictions returned by the
* model and the true responses.
*
* @param data Data points to predict
* @param responses A vector of targets.
* @return Root mean squared error.
**/
double RMSE(const arma::mat& data,
const arma::rowvec& responses) const;
/**
* Get the solution vector.
*
* @return omega Solution vector.
*/
const arma::colvec& Omega() const { return omega; }
/**
* Get the precision (or inverse variance) of the gaussian prior. Train()
* must be called before.
*
* @return \f$ \alpha \f$
*/
double Alpha() const { return alpha; }
/**
* Get the precision (or inverse variance) beta of the model. Train() must be
* called before.
*
* @return \f$ \beta \f$
*/
double Beta() const { return beta; }
/**
* Get the estimated variance. Train() must be called before.
*
* @return 1.0 / \f$ \beta \f$
*/
double Variance() const { return 1.0 / Beta(); }
/**
* Get the mean vector computed on the features over the training points.
*
* @return responsesOffset
*/
const arma::colvec& DataOffset() const { return dataOffset; }
/**
* Get the vector of standard deviations computed on the features over the
* training points.
*
* @return dataOffset
*/
const arma::colvec& DataScale() const { return dataScale; }
/**
* Get the mean value of the train responses.
*
* @return responsesOffset
*/
double ResponsesOffset() const { return responsesOffset; }
/**
* Serialize the BayesianLinearRegression model.
**/
template<typename Archive>
void serialize(Archive& ar, const unsigned int /* version */);
private:
//! Center the data if true.
bool centerData;
//! Scale the data by standard deviations if true.
bool scaleData;
//! Maximum number of iterations for convergency.
size_t nIterMax;
//! Level from which the solution is considered sufficientlly stable.
double tol;
//! Mean vector computed over the points.
arma::colvec dataOffset;
//! Std vector computed over the points.
arma::colvec dataScale;
//! Mean of the response vector computed over the points.
double responsesOffset;
//! Precision of the prior pdf (gaussian).
double alpha;
//! Noise inverse variance.
double beta;
//! Effective number of parameters.
double gamma;
//! Solution vector
arma::colvec omega;
//! Covariance matrix of the solution vector omega.
arma::mat matCovariance;
/**
* Center and scale the data accordind to centerData and scaleData.
* Allows future modifications of new points.
*
* @param data Design matrix in column-major format, dim(P, N).
* @param responses A vector of targets.
* @param dataProc Data processed, dim(P, N).
* @param responsesProc Responses processed, dim(N).
* @return reponsesOffset Mean of responses.
*/
double CenterScaleData(const arma::mat& data,
const arma::rowvec& responses,
arma::mat& dataProc,
arma::rowvec& responsesProc);
/**
* Center and scale the points before prediction.
*
* @param data Design matrix in column-major format, dim(P, N).
* @param dataProc Data processed, dim(P, N).
*/
void CenterScaleDataPred(const arma::mat& data,
arma::mat& dataProc) const;
};
} // namespace regression
} // namespace mlpack
// Include implementation of serialize.
#include "bayesian_linear_regression_impl.hpp"
#endif
@@ -0,0 +1,44 @@
/**
* @file methods/bayesian_linear_regression/bayesian_linear_regression_impl.hpp
* @author Clement Mercier
*
* Implementation of templated BayesianLinearRegression functions.
*
* 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_BAYESIAN_LINEAR_REGRESSION_IMPL_HPP
#define MLPACK_METHODS_BAYESIAN_LINEAR_REGRESSION_IMPL_HPP
#include "bayesian_linear_regression.hpp"
namespace mlpack {
namespace regression {
/**
* Serialize the Bayesian linear regression model.
*/
template<typename Archive>
void BayesianLinearRegression::serialize(Archive& ar,
const unsigned int /* version */)
{
ar & BOOST_SERIALIZATION_NVP(centerData);
ar & BOOST_SERIALIZATION_NVP(scaleData);
ar & BOOST_SERIALIZATION_NVP(nIterMax);
ar & BOOST_SERIALIZATION_NVP(tol);
ar & BOOST_SERIALIZATION_NVP(dataOffset);
ar & BOOST_SERIALIZATION_NVP(dataScale);
ar & BOOST_SERIALIZATION_NVP(responsesOffset);
ar & BOOST_SERIALIZATION_NVP(alpha);
ar & BOOST_SERIALIZATION_NVP(beta);
ar & BOOST_SERIALIZATION_NVP(gamma);
ar & BOOST_SERIALIZATION_NVP(omega);
ar & BOOST_SERIALIZATION_NVP(matCovariance);
}
} // namespace regression
} // namespace mlpack
#endif
@@ -0,0 +1,199 @@
/**
* @file methods/bayesian_linear_regression/bayesian_linear_regression_main.cpp
* @author Clement Mercier
*
* Executable for BayesianLinearRegression.
*
* 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.
*/
#include <mlpack/prereqs.hpp>
#include <mlpack/core/util/io.hpp>
#include <mlpack/core/util/mlpack_main.hpp>
#include "bayesian_linear_regression.hpp"
using namespace arma;
using namespace std;
using namespace mlpack;
using namespace mlpack::regression;
using namespace mlpack::util;
PROGRAM_INFO("BayesianLinearRegression",
// Short description.
"An implementation of the bayesian linear regression.",
// Long description.
"An implementation of the bayesian linear regression."
"\n"
"This model is a probabilistic view and implementation of the linear "
"regression. The final solution is obtained by computing a posterior "
"distribution from gaussian likelihood and a zero mean gaussian isotropic "
" prior distribution on the solution. "
"\n"
"Optimization is AUTOMATIC and does not require cross validation. "
"The optimization is performed by maximization of the evidence function. "
"Parameters are tuned during the maximization of the marginal likelihood. "
"This procedure includes the Ockham's razor that penalizes over complex "
"solutions. "
"\n\n"
"This program is able to train a Bayesian linear regression model or load "
"a model from file, output regression predictions for a test set, and save "
"the trained model to a file."
"\n\n"
"To train a BayesianLinearRegression model, the " +
PRINT_PARAM_STRING("input") + " and " + PRINT_PARAM_STRING("responses") +
"parameters must be given. The " + PRINT_PARAM_STRING("center") +
"and " + PRINT_PARAM_STRING("scale") + " parameters control the "
"centering and the normalizing options. A trained model can be saved with "
"the " + PRINT_PARAM_STRING("output_model") + ". If no training is desired "
"at all, a model can be passed via the " +
PRINT_PARAM_STRING("input_model") + " parameter."
"\n\n"
"The program can also provide predictions for test data using either the "
"trained model or the given input model. Test points can be specified "
"with the " + PRINT_PARAM_STRING("test") + " parameter. Predicted "
"responses to the test points can be saved with the " +
PRINT_PARAM_STRING("predictions") + " output parameter. The "
"corresponding standard deviation can be save by precising the " +
PRINT_PARAM_STRING("stds") + " parameter."
"\n\n"
"For example, the following command trains a model on the data " +
PRINT_DATASET("data") + " and responses " + PRINT_DATASET("responses") +
"with center set to true and scale set to false (so, Bayesian "
"linear regression is being solved, and then the model is saved to " +
PRINT_MODEL("bayesian_linear_regression_model") + ":"
"\n\n" +
PRINT_CALL("bayesian_linear_regression", "input", "data", "responses",
"responses", "center", 1, "scale", 0, "output_model",
"bayesian_linear_regression_model") +
"\n\n"
"The following command uses the " +
PRINT_MODEL("bayesian_linear_regression_model") + " to provide predicted " +
" responses for the data " + PRINT_DATASET("test") + " and save those " +
" responses to " + PRINT_DATASET("test_predictions") + ": "
"\n\n" +
PRINT_CALL("bayesian_linear_regression", "input_model",
"bayesian_linear_regression_model", "test", "test",
"predictions", "test_predictions") +
"\n\n"
"Because the estimator computes a predictive distribution instead of "
"simple point estimate, the " + PRINT_PARAM_STRING("stds") + " parameter "
"allows to save the prediction uncertainties: "
"\n\n" +
PRINT_CALL("bayesian_linear_regression", "input_model",
"bayesian_linear_regression_model", "test", "test",
"predictions", "test_predictions", "stds", "stds"),
SEE_ALSO("Bayesian Interpolation",
"https://authors.library.caltech.edu/13792/1/MACnc92a.pdf"),
SEE_ALSO("Bayesian Linear Regression, Section 3.3",
"MLA Bishop, Christopher M. Pattern Recognition and Machine "
"Learning. New York :Springer, 2006, section 3.3."),
SEE_ALSO("mlpack::regression::BayesianLinearRegression C++ class "
"documentation",
"@doxygen/classmlpack_1_1regression_1_1BayesianLinearRegression.html"));
PARAM_MATRIX_IN("input", "Matrix of covariates (X).", "i");
PARAM_ROW_IN("responses", "Matrix of responses/observations (y).", "r");
PARAM_MODEL_IN(BayesianLinearRegression, "input_model", "Trained "
"BayesianLinearRegression model to use.", "m");
PARAM_MODEL_OUT(BayesianLinearRegression, "output_model", "Output "
"BayesianLinearRegression model.", "M");
PARAM_MATRIX_IN("test", "Matrix containing points to regress on (test "
"points).", "t");
PARAM_MATRIX_OUT("predictions", "If --test_file is specified, this "
"file is where the predicted responses will be saved.", "o");
PARAM_MATRIX_OUT("stds", "If specified, this is where the standard deviations "
"of the predictive distribution will be saved.", "u");
PARAM_FLAG("center", "Center the data and fit the intercept if enabled.", "c");
PARAM_FLAG("scale", "Scale each feature by their standard deviations if "
"enabled.", "s");
static void mlpackMain()
{
bool center = IO::GetParam<bool>("center");
bool scale = IO::GetParam<bool>("scale");
// Check parameters -- make sure everything given makes sense.
RequireOnlyOnePassed({"input", "input_model"}, true);
if (IO::HasParam("input"))
{
RequireOnlyOnePassed({"responses"}, true, "if input data is specified, "
"responses must also be specified");
}
ReportIgnoredParam({{"input", false }}, "responses");
RequireAtLeastOnePassed({"predictions", "output_model", "stds"}, false,
"no results will be saved");
// Ignore out_predictions unless test is specified.
ReportIgnoredParam({{"test", false}}, "predictions");
BayesianLinearRegression* bayesLinReg;
if (IO::HasParam("input"))
{
Log::Info << "input detected " << std::endl;
// Initialize the object.
bayesLinReg = new BayesianLinearRegression(center, scale);
// Load covariates. We can avoid LARS transposing our data by choosing to
// not transpose this data (that's why we used PARAM_TMATRIX_IN).
mat matX = std::move(IO::GetParam<arma::mat>("input"));
// Load responses. The responses should be a one-dimensional vector, and it
// seems more likely that these will be stored with one response per line
// (one per row). So we should not transpose upon loading.
arma::rowvec responses = std::move(
IO::GetParam<arma::rowvec>("responses"));
if (responses.n_elem != matX.n_cols)
{
delete bayesLinReg;
Log::Fatal << "Number of responses must be equal to number of rows of X!"
<< endl;
}
arma::rowvec predictionsTrain;
// The Train method is ready to take data in column-major format.
bayesLinReg->Train(matX, responses);
}
else // We must have --input_model_file.
{
bayesLinReg = IO::GetParam<BayesianLinearRegression*>("input_model");
}
if (IO::HasParam("test"))
{
Log::Info << "Regressing on test points." << endl;
// Load test points.
mat testPoints = std::move(IO::GetParam<arma::mat>("test"));
arma::rowvec predictions;
if (IO::HasParam("stds"))
{
arma::rowvec std;
bayesLinReg->Predict(testPoints, predictions, std);
// Save the standard deviation of the test points (one per line).
IO::GetParam<arma::mat>("stds") = std::move(std);
}
else
{
bayesLinReg->Predict(testPoints, predictions);
}
// Save test predictions (one per line).
IO::GetParam<arma::mat>("predictions") = std::move(predictions);
}
IO::GetParam<BayesianLinearRegression*>("output_model") = bayesLinReg;
}
+2
View File
@@ -9,6 +9,7 @@ add_executable(mlpack_test
async_learning_test.cpp
augmented_rnns_tasks_test.cpp
binarize_test.cpp
bayesian_linear_regression_test.cpp
callback_test.cpp
cf_test.cpp
cli_binding_test.cpp
@@ -100,6 +101,7 @@ add_executable(mlpack_test
union_find_test.cpp
vantage_point_tree_test.cpp
wgan_test.cpp
main_tests/bayesian_linear_regression_test.cpp
main_tests/cf_test.cpp
main_tests/dbscan_test.cpp
main_tests/decision_stump_test.cpp
@@ -0,0 +1,194 @@
/**
* @file tests/bayesian_linear_regression_test.cpp
* @author Clement Mercier
*
* Test for BayesianLinearRegression.
*
* 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.
*/
#include <mlpack/core/data/load.hpp>
#include <mlpack/methods/bayesian_linear_regression/bayesian_linear_regression.hpp>
#include <mlpack/methods/linear_regression/linear_regression.hpp>
#include <boost/test/unit_test.hpp>
using namespace mlpack::regression;
using namespace mlpack::data;
BOOST_AUTO_TEST_SUITE(BayesianLinearRegressionTest);
void GenerateProblem(arma::mat& matX,
arma::rowvec& y,
size_t nPoints,
size_t nDims,
float sigma = 0.0)
{
matX = arma::randn(nDims, nPoints);
arma::colvec omega = arma::randn(nDims);
// Compute y and add noise.
y = omega.t() * matX + arma::randn(nPoints).t() * sigma;
}
// Ensure that predictions are close enough to the target
// for a free noise dataset.
BOOST_AUTO_TEST_CASE(BayesianLinearRegressionRegressionTest)
{
arma::mat matX;
arma::rowvec y, predictions;
GenerateProblem(matX, y, 200, 10);
// Instanciate and train the estimator.
BayesianLinearRegression estimator(true);
estimator.Train(matX, y);
estimator.Predict(matX, predictions);
// Check the predictions are close enough to the targets in a free noise case.
for (size_t i = 0; i < y.size(); i++)
BOOST_REQUIRE_CLOSE(predictions[i], y[i], 1e-6);
// Check that the estimated variance is zero.
BOOST_REQUIRE_SMALL(estimator.Variance(), 1e-6);
}
// Verify centerData and scaleData equal false do not affect the solution.
BOOST_AUTO_TEST_CASE(TestCenter0ScaleData0)
{
arma::mat matX;
arma::rowvec y;
size_t nDims = 30, nPoints = 100;
GenerateProblem(matX, y, nPoints, nDims, 0.5);
BayesianLinearRegression estimator(false, false);
estimator.Train(matX, y);
// Check dataOffset is empty.
BOOST_REQUIRE(estimator.DataOffset().n_elem == 0);
// To be neutral responseOffset must be 0.
BOOST_REQUIRE(estimator.ResponsesOffset() == 0);
// Check dataScale is empty.
BOOST_REQUIRE(estimator.DataScale().n_elem == 0);
}
// Verify that centering and normalization are correct.
BOOST_AUTO_TEST_CASE(TestCenterDataTrueScaleDataTrue)
{
arma::mat matX;
arma::rowvec y;
size_t nDims = 5, nPoints = 100;
GenerateProblem(matX, y, nPoints, nDims, 0.5);
BayesianLinearRegression estimator(true, true);
estimator.Train(matX, y);
arma::colvec xMean = arma::mean(matX, 1);
arma::colvec xStd = arma::stddev(matX, 0, 1);
double yMean = arma::mean(y);
BOOST_REQUIRE_SMALL((double) abs(sum(estimator.DataOffset() - xMean)), 1e-6);
BOOST_REQUIRE_SMALL((double) abs(sum(estimator.DataScale() - xStd)), 1e-6);
BOOST_REQUIRE_CLOSE(estimator.ResponsesOffset(), yMean, 1e-6);
}
// Make sure a model with center ans scale option set is different than a model
// without it set.
BOOST_AUTO_TEST_CASE(OptionsMakeModelDifferent)
{
arma::mat matX;
arma::rowvec y;
size_t nDims = 10, nPoints = 100;
GenerateProblem(matX, y, nPoints, nDims, 0.5);
BayesianLinearRegression blr(false, false), blrC(true, false),
blrCS(true, true);
blr.Train(matX, y);
blrC.Train(matX, y);
blrCS.Train(matX, y);
for (size_t i = 0; i < nDims; ++i)
BOOST_REQUIRE((blr.Omega()(i) != blrC.Omega()(i)) &&
(blr.Omega()(i) != blrCS.Omega()(i)) &&
(blrC.Omega()(i) != blrCS.Omega()(i)));
}
// Check that Train() does not fail with two colinear vectors.
BOOST_AUTO_TEST_CASE(SingularMatix)
{
arma::mat matX;
arma::rowvec y;
GenerateProblem(matX, y, 200, 10);
// Now the first and the second rows are indentical.
matX.row(1) = matX.row(0);
BayesianLinearRegression estimator;
estimator.Train(matX, y);
}
// Check that std are well computed/coherent. At least higher than the
// estimated predictive variance.
BOOST_AUTO_TEST_CASE(PredictiveUncertainties)
{
arma::mat matX;
arma::rowvec y;
GenerateProblem(matX, y, 100, 10, 1);
BayesianLinearRegression estimator(true, true);
estimator.Train(matX, y);
arma::rowvec responses, std;
estimator.Predict(matX, responses, std);
const double estStd = sqrt(estimator.Variance());
for (size_t i = 0; i < matX.n_cols; i++)
BOOST_REQUIRE_GT(std[i], estStd);
// Check that the estimated variance is close to 1.
BOOST_REQUIRE_CLOSE(estStd, 1, 30);
}
// Check the solution is equal to the classical ridge.
BOOST_AUTO_TEST_CASE(EqualtoRidge)
{
arma::mat matX;
arma::rowvec y, blrPred, ridgePred;
size_t trial = 0;
for ( ; trial < 3; ++trial)
{
GenerateProblem(matX, y, 100, 10, 1);
BayesianLinearRegression blr(false, false);
blr.Train(matX, y);
LinearRegression ridge(matX, y, blr.Alpha() / blr.Beta(), false);
blr.Predict(matX, blrPred);
ridge.Predict(matX, ridgePred);
// If the predictions seem far off, just try again.
if (arma::norm(blrPred - ridgePred) > 1e-5)
continue;
// Check the predictions are close enough between ridge and our blr.
for (size_t i = 0; i < y.size(); ++i)
BOOST_REQUIRE_CLOSE(blrPred[i], ridgePred[i], 1);
// Exit once a test case has completed.
break;
}
BOOST_REQUIRE_LT(trial, 3);
}
BOOST_AUTO_TEST_SUITE_END();
@@ -0,0 +1,143 @@
/**
* @file tests/main_tests/bayesian_linear_regression_test.cpp
* @author Clement Mercier
*
* Test mlpackMain() of bayesian_linear_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.
*/
#include <string>
#define BINDING_TYPE BINDING_TYPE_TEST
static const std::string testName = "BayesianLinearRegression";
#include <mlpack/core.hpp>
#include <mlpack/core/util/mlpack_main.hpp>
#include "test_helper.hpp"
#include <mlpack/methods/bayesian_linear_regression/bayesian_linear_regression_main.cpp>
#include <boost/test/unit_test.hpp>
#include "../test_tools.hpp"
using namespace mlpack;
struct BRTestFixture
{
public:
BRTestFixture()
{
// Cache in the options for this program.
IO::RestoreSettings(testName);
}
~BRTestFixture()
{
// Clear the settings.
bindings::tests::CleanMemory();
IO::ClearSettings();
}
};
BOOST_FIXTURE_TEST_SUITE(BayesianLinearRegressionMainTest, BRTestFixture);
/**
* Check the center and scale options.
*/
BOOST_AUTO_TEST_CASE(BRCenter0Scale0)
{
int n = 50, m = 4;
arma::mat matX = arma::randu<arma::mat>(m, n);
arma::rowvec omega = arma::randu<arma::rowvec>(m);
arma::rowvec y = omega * matX;
SetInputParam("input", std::move(matX));
SetInputParam("responses", std::move(y));
SetInputParam("center", false);
mlpackMain();
BayesianLinearRegression* estimator =
IO::GetParam<BayesianLinearRegression*>("output_model");
BOOST_REQUIRE(estimator->DataOffset().n_elem == 0);
BOOST_REQUIRE(estimator->DataScale().n_elem == 0);
}
/**
* Check predictions of saved model and in code model are equal.
*/
BOOST_AUTO_TEST_CASE(BayesianLinearRegressionSavedEqualCode)
{
int n = 10, m = 4;
arma::mat matX = arma::randu<arma::mat>(m, n);
arma::mat matXtest = arma::randu<arma::mat>(m, 2 * n);
const arma::rowvec omega = arma::randu<arma::rowvec>(m);
arma::rowvec y = omega * matX;
BayesianLinearRegression model;
model.Train(matX, y);
arma::rowvec responses;
model.Predict(matXtest, responses);
SetInputParam("input", std::move(matX));
SetInputParam("responses", std::move(y));
mlpackMain();
IO::GetSingleton().Parameters()["input"].wasPassed = false;
IO::GetSingleton().Parameters()["responses"].wasPassed = false;
SetInputParam("input_model",
IO::GetParam<BayesianLinearRegression*>("output_model"));
SetInputParam("test", std::move(matXtest));
mlpackMain();
arma::mat ytest = std::move(responses);
// Check that initial output and output using saved model are same.
CheckMatrices(ytest, IO::GetParam<arma::mat>("predictions"));
}
/**
* Check a crash happens if neither input or input_model are specified.
* Check a crash happens if both input and input_model are specified.
*/
BOOST_AUTO_TEST_CASE(CheckParamsPassed)
{
int n = 10, m = 4;
arma::mat matX = arma::randu<arma::mat>(m, n);
arma::mat matXtest = arma::randu<arma::mat>(m, 2 * n);
const arma::rowvec omega = arma::randu<arma::rowvec>(m);
arma::rowvec y = omega * matX;
BayesianLinearRegression model;
model.Train(matX, y);
arma::rowvec responses;
model.Predict(matXtest, responses);
// Check that std::runtime_error is thrown if neither input or input_model
// is specified.
SetInputParam("responses", std::move(y));
BOOST_REQUIRE_THROW(mlpackMain(), std::runtime_error);
// Continue only with input passed.
SetInputParam("input", std::move(matX));
mlpackMain();
// Now pass the previous trained model and one input matrix at the same time.
// An error should occur.
SetInputParam("input", std::move(matX));
SetInputParam("input_model",
IO::GetParam<BayesianLinearRegression*>("output_model"));
SetInputParam("test", std::move(matXtest));
BOOST_REQUIRE_THROW(mlpackMain(), std::runtime_error);
}
BOOST_AUTO_TEST_SUITE_END();
+31
View File
@@ -39,6 +39,7 @@
#include <mlpack/methods/lsh/lsh_search.hpp>
#include <mlpack/methods/decision_stump/decision_stump.hpp>
#include <mlpack/methods/lars/lars.hpp>
#include <mlpack/methods/bayesian_linear_regression/bayesian_linear_regression.hpp>
#include <mlpack/methods/ann/rbm/rbm.hpp>
#include <mlpack/methods/ann/init_rules/gaussian_init.hpp>
@@ -1602,4 +1603,34 @@ BOOST_AUTO_TEST_CASE(ssRBMTest)
CheckMatrices(Rbm.Weight(), RbmBinary.Weight());
}
// Make sure serialization works for BayesianLinearRegression.
BOOST_AUTO_TEST_CASE(BayesianLinearRegressionTest)
{
using namespace mlpack::regression;
// Create a dataset.
arma::mat matX = arma::randn(75, 250);
arma::vec omega = arma::randn(75, 1);
arma::rowvec y = omega.t() * matX;
BayesianLinearRegression blr(false, false);
blr.Train(matX, y);
arma::vec omegaOpt = blr.Omega();
// Now, serialize.
BayesianLinearRegression xmlBlr(false, false), binaryBlr(false, false),
textBlr(false, false);
SerializeObjectAll(blr, xmlBlr, binaryBlr, textBlr);
// Now, check that predictions are the same.
arma::rowvec pred, xmlPred, textPred, binaryPred;
blr.Predict(matX, pred);
xmlBlr.Predict(matX, xmlPred);
textBlr.Predict(matX, textPred);
binaryBlr.Predict(matX, binaryPred);
CheckMatrices(pred, xmlPred, textPred, binaryPred);
}
BOOST_AUTO_TEST_SUITE_END();