Implement new Train() overloads and tests for them.

This commit is contained in:
Ryan Curtin
2023-11-29 11:11:13 -05:00
parent 599a630656
commit c1b7694391
4 changed files with 224 additions and 8 deletions
@@ -63,8 +63,8 @@ inline double RegressionDistribution::Probability(
const arma::vec& observation) const
{
arma::rowvec fitted;
rf.Predict(observation.rows(1, observation.n_rows-1), fitted);
return err.Probability(observation(0)-fitted.t());
rf.Predict(observation.rows(1, observation.n_rows - 1), fitted);
return err.Probability(observation(0) - fitted.t());
}
inline void RegressionDistribution::Predict(const arma::mat& points,
@@ -71,14 +71,90 @@ class LinearRegression
* regularization parameter lambda, call Lambda() or set a different value in
* the constructor.
*
* This version of `Train()` is deprecated and will be removed in mlpack
* 5.0.0. Use the version of `Train()` that specifies `lambda` before
* `intercept` instead.
*
* @param predictors X, the matrix of data points to train the model on.
* @param responses y, the responses to the data points.
* @param intercept Whether or not to fit an intercept term.
* @return The least squares error after training.
*/
mlpack_deprecated /** Will be removed in mlpack 5.0.0. */
double Train(const arma::mat& predictors,
const arma::rowvec& responses,
const bool intercept = true);
const bool intercept);
/**
* Train the LinearRegression model on the given data and weights. Careful!
* This will completely ignore and overwrite the existing model. This
* particular implementation does not have an incremental training algorithm.
* To set the regularization parameter lambda, call Lambda() or set a
* different value in the constructor.
*
* This version of `Train()` is deprecated and will be removed in mlpack
* 5.0.0. Use the version of `Train()` that specifies `lambda` before
* `intercept` instead.
*
* @param predictors X, the matrix of data points to train the model on.
* @param responses y, the responses to the data points.
* @param weights Observation weights (for boosting).
* @param intercept Whether or not to fit an intercept term.
* @return The least squares error after training.
*/
mlpack_deprecated /** Will be removed in mlpack 5.0.0. */
double Train(const arma::mat& predictors,
const arma::rowvec& responses,
const arma::rowvec& weights,
const bool intercept);
/**
* Train the LinearRegression model on the given data and weights. Careful!
* This will completely ignore and overwrite the existing model. This
* particular implementation does not have an incremental training algorithm.
* To set the regularization parameter lambda, call Lambda() or set a
* different value in the constructor.
*
* @param predictors X, the matrix of data points to train the model on.
* @param responses y, the responses to the data points.
* @return The least squares error after training.
*/
double Train(const arma::mat& predictors,
const arma::rowvec& responses);
/**
* Train the LinearRegression model on the given data and weights. Careful!
* This will completely ignore and overwrite the existing model. This
* particular implementation does not have an incremental training algorithm.
* To set the regularization parameter lambda, call Lambda() or set a
* different value in the constructor.
*
* @param predictors X, the matrix of data points to train the model on.
* @param responses y, the responses to the data points.
* @param lambda L2 regularization penalty parameter to use.
* @return The least squares error after training.
*/
double Train(const arma::mat& predictors,
const arma::rowvec& responses,
const double lambda);
/**
* Train the LinearRegression model on the given data and weights. Careful!
* This will completely ignore and overwrite the existing model. This
* particular implementation does not have an incremental training algorithm.
* To set the regularization parameter lambda, call Lambda() or set a
* different value in the constructor.
*
* @param predictors X, the matrix of data points to train the model on.
* @param responses y, the responses to the data points.
* @param lambda L2 regularization penalty parameter to use.
* @param intercept Whether or not to fit an intercept term.
* @return The least squares error after training.
*/
double Train(const arma::mat& predictors,
const arma::rowvec& responses,
const double lambda,
const bool intercept);
/**
* Train the LinearRegression model on the given data and weights. Careful!
@@ -90,13 +166,49 @@ class LinearRegression
* @param predictors X, the matrix of data points to train the model on.
* @param responses y, the responses to the data points.
* @param weights Observation weights (for boosting).
* @return The least squares error after training.
*/
double Train(const arma::mat& predictors,
const arma::rowvec& responses,
const arma::rowvec& weights);
/**
* Train the LinearRegression model on the given data and weights. Careful!
* This will completely ignore and overwrite the existing model. This
* particular implementation does not have an incremental training algorithm.
* To set the regularization parameter lambda, call Lambda() or set a
* different value in the constructor.
*
* @param predictors X, the matrix of data points to train the model on.
* @param responses y, the responses to the data points.
* @param weights Observation weights (for boosting).
* @param lambda L2 regularization penalty parameter to use.
* @return The least squares error after training.
*/
double Train(const arma::mat& predictors,
const arma::rowvec& responses,
const arma::rowvec& weights,
const double lambda);
/**
* Train the LinearRegression model on the given data and weights. Careful!
* This will completely ignore and overwrite the existing model. This
* particular implementation does not have an incremental training algorithm.
* To set the regularization parameter lambda, call Lambda() or set a
* different value in the constructor.
*
* @param predictors X, the matrix of data points to train the model on.
* @param responses y, the responses to the data points.
* @param weights Observation weights (for boosting).
* @param lambda L2 regularization penalty parameter to use.
* @param intercept Whether or not to fit an intercept term.
* @return The least squares error after training.
*/
double Train(const arma::mat& predictors,
const arma::rowvec& responses,
const arma::rowvec& weights,
const bool intercept = true);
const double lambda,
const bool intercept);
/**
* Calculate y_i for each data point in points.
@@ -37,18 +37,67 @@ inline LinearRegression::LinearRegression(
Train(predictors, responses, weights, intercept);
}
mlpack_deprecated /** Will be removed in mlpack 5.0.0. */
inline double LinearRegression::Train(const arma::mat& predictors,
const arma::rowvec& responses,
const bool intercept)
{
return Train(predictors, responses, arma::rowvec(), intercept);
return Train(predictors, responses, arma::rowvec(), this->lambda, intercept);
}
mlpack_deprecated /** Will be removed in mlpack 5.0.0. */
inline double LinearRegression::Train(const arma::mat& predictors,
const arma::rowvec& responses,
const arma::rowvec& weights,
const bool intercept)
{
return Train(predictors, responses, weights, this->lambda, intercept);
}
inline double LinearRegression::Train(const arma::mat& predictors,
const arma::rowvec& responses)
{
return Train(predictors, responses, arma::rowvec(), this->lambda,
this->intercept);
}
inline double LinearRegression::Train(const arma::mat& predictors,
const arma::rowvec& responses,
const double lambda)
{
return Train(predictors, responses, arma::rowvec(), lambda, this->intercept);
}
inline double LinearRegression::Train(const arma::mat& predictors,
const arma::rowvec& responses,
const double lambda,
const bool intercept)
{
return Train(predictors, responses, arma::rowvec(), lambda, intercept);
}
inline double LinearRegression::Train(const arma::mat& predictors,
const arma::rowvec& responses,
const arma::rowvec& weights)
{
return Train(predictors, responses, weights, this->lambda, this->intercept);
}
inline double LinearRegression::Train(const arma::mat& predictors,
const arma::rowvec& responses,
const arma::rowvec& weights,
const double lambda)
{
return Train(predictors, responses, weights, lambda, this->intercept);
}
inline double LinearRegression::Train(const arma::mat& predictors,
const arma::rowvec& responses,
const arma::rowvec& weights,
const double lambda,
const bool intercept)
{
this->lambda = lambda;
this->intercept = intercept;
/*
@@ -106,7 +155,7 @@ inline void LinearRegression::Predict(
// Prevent underflow.
const size_t labels = (parameters.n_rows == 0) ? size_t(0) :
size_t(parameters.n_rows - 1);
util::CheckSameDimensionality(points, labels, "LinearRegression::Predict()",
util::CheckSameDimensionality(points, labels, "LinearRegression::Predict()",
"points");
// Get the predictions, but this ignores the intercept value
// (parameters[0]).
@@ -119,7 +168,7 @@ inline void LinearRegression::Predict(
{
// We want to be sure we have the correct number of dimensions in
// the dataset.
util::CheckSameDimensionality(points, parameters,
util::CheckSameDimensionality(points, parameters,
"LinearRegression::Predict()", "points");
predictions = arma::trans(parameters) * points;
}
@@ -131,7 +180,7 @@ inline double LinearRegression::ComputeError(
{
// Sanity check on data.
util::CheckSameSizes(predictors, responses, "LinearRegression::Train()");
// Get the number of columns and rows of the dataset.
const size_t nCols = predictors.n_cols;
const size_t nRows = predictors.n_rows;
@@ -265,3 +265,58 @@ TEST_CASE("LinearRegressionTrainReturnObjective", "[LinearRegressionTest]")
REQUIRE(std::isfinite(error) == true);
}
/**
* Make sure all versions of Train() work correctly.
*/
TEST_CASE("LinearRegressionAllTrainVersionsTest", "[LinearRegressionTest]")
{
// The data doesn't really matter for this test; mostly we want to make sure
// that all the Train() variants work properly.
arma::mat predictors;
predictors = { { 0, 1, 2, 4, 8, 16 },
{ 16, 8, 4, 2, 1, 0 } };
arma::rowvec responses = "0 2 4 3 8 8";
arma::rowvec weights = "1.0 1.1 1.2 0.8 0.9 1.0";
LinearRegression lr1, lr2, lr3, lr4, lr5, lr6;
lr1.Train(predictors, responses);
lr2.Train(predictors, responses, 0.1);
lr3.Train(predictors, responses, 0.2, false);
lr4.Train(predictors, responses, weights);
lr5.Train(predictors, responses, weights, 0.3);
lr6.Train(predictors, responses, weights, 0.4, false);
// We don't care about the specifics of the trained model, but we want to just
// make sure everything appears to be correct from the sizes and
// hyperparameters.
REQUIRE(lr1.Lambda() == Approx(0.0).margin(1e-10));
REQUIRE(lr1.Intercept() == true);
REQUIRE(lr1.Parameters().n_elem == 3);
REQUIRE(lr2.Lambda() == Approx(0.1).margin(1e-10));
REQUIRE(lr2.Intercept() == true);
REQUIRE(lr2.Parameters().n_elem == 3);
REQUIRE(lr3.Lambda() == Approx(0.2).margin(1e-10));
REQUIRE(lr3.Intercept() == false);
REQUIRE(lr3.Parameters().n_elem == 2);
REQUIRE(lr4.Lambda() == Approx(0.0).margin(1e-10));
REQUIRE(lr4.Intercept() == true);
REQUIRE(lr4.Parameters().n_elem == 3);
REQUIRE(lr5.Lambda() == Approx(0.3).margin(1e-10));
REQUIRE(lr5.Intercept() == true);
REQUIRE(lr5.Parameters().n_elem == 3);
REQUIRE(lr6.Lambda() == Approx(0.4).margin(1e-10));
REQUIRE(lr6.Intercept() == false);
REQUIRE(lr6.Parameters().n_elem == 2);
// We can also check that the weighted model is different from the unweighted
// model.
REQUIRE(!arma::approx_equal(lr1.Parameters(), lr4.Parameters(), "absdiff",
1e-5));
}