Add Classify() functions and tests.
This commit is contained in:
@@ -152,6 +152,8 @@ class LogisticRegression
|
||||
* the decision boundary, the response is taken to be 1; otherwise, it is 0.
|
||||
* By default the decision boundary is 0.5.
|
||||
*
|
||||
* This method is deprecated---you should use Classify() instead.
|
||||
*
|
||||
* @param predictors Input predictors.
|
||||
* @param responses Vector to put output predictions of responses into.
|
||||
* @param decisionBoundary Decision boundary (default 0.5).
|
||||
@@ -160,6 +162,45 @@ class LogisticRegression
|
||||
arma::Row<size_t>& responses,
|
||||
const double decisionBoundary = 0.5) const;
|
||||
|
||||
/**
|
||||
* Classify the given point. The predicted label is returned. Optionally,
|
||||
* specify the decision boundary; logistic regression returns a value between
|
||||
* 0 and 1. If the value is greater than the decision boundary, the response
|
||||
* is taken to be 1; otherwise, it is 0. By default the decision boundary is
|
||||
* 0.5.
|
||||
*
|
||||
* @param point Point to classify.
|
||||
* @param decisionBoundary Decision boundary (default 0.5).
|
||||
* @return Predicted label of point.
|
||||
*/
|
||||
template<typename VecType>
|
||||
size_t Classify(const VecType& point,
|
||||
const double decisionBoundary = 0.5) const;
|
||||
|
||||
/**
|
||||
* Classify the given points, returning the predicted labels for each point.
|
||||
* Optionally, specify the decision boundary; logistic regression returns a
|
||||
* value between 0 and 1. If the value is greater than the decision boundary,
|
||||
* the response is taken to be 1; otherwise, it is 0. By default the decision
|
||||
* boundary is 0.5.
|
||||
*
|
||||
* @param dataset Set of points to classify.
|
||||
* @param labels Predicted labels for each point.
|
||||
* @param decisionBoundary Decision boundary (default 0.5).
|
||||
*/
|
||||
void Classify(const MatType& dataset,
|
||||
arma::Row<size_t>& labels,
|
||||
const double decisionBoundary = 0.5) const;
|
||||
|
||||
/**
|
||||
* Classify the given points, returning class probabilities for each point.
|
||||
*
|
||||
* @param dataset Set of points to classify.
|
||||
* @param probabilities Class probabilities for each point (output).
|
||||
*/
|
||||
void Classify(const MatType& dataset,
|
||||
arma::mat& probabilities) const;
|
||||
|
||||
/**
|
||||
* Compute the accuracy of the model on the given predictors and responses,
|
||||
* optionally using the given decision boundary. The responses should be
|
||||
|
||||
@@ -105,6 +105,37 @@ void LogisticRegression<MatType>::Predict(const MatType& predictors,
|
||||
(1.0 - decisionBoundary));
|
||||
}
|
||||
|
||||
template<typename MatType>
|
||||
template<typename VecType>
|
||||
size_t LogisticRegression<MatType>::Classify(const VecType& point,
|
||||
const double decisionBoundary)
|
||||
const
|
||||
{
|
||||
return size_t(1.0 / (1.0 + std::exp(-parameters(0) - arma::dot(point,
|
||||
parameters.subvec(1, parameters.n_elem - 1)))) +
|
||||
(1.0 - decisionBoundary));
|
||||
}
|
||||
|
||||
template<typename MatType>
|
||||
void LogisticRegression<MatType>::Classify(const MatType& dataset,
|
||||
arma::Row<size_t>& labels,
|
||||
const double decisionBoundary) const
|
||||
{
|
||||
Predict(dataset, labels, decisionBoundary);
|
||||
}
|
||||
|
||||
template<typename MatType>
|
||||
void LogisticRegression<MatType>::Classify(const MatType& dataset,
|
||||
arma::mat& probabilities) const
|
||||
{
|
||||
// Set correct size of output matrix.
|
||||
probabilities.set_size(2, dataset.n_cols);
|
||||
|
||||
probabilities.row(1) = 1.0 / (1.0 + arma::exp(-parameters(0) - dataset.t() *
|
||||
parameters.subvec(1, parameters.n_elem - 1))).t();
|
||||
probabilities.row(0) = 1.0 - probabilities.row(1);
|
||||
}
|
||||
|
||||
template<typename MatType>
|
||||
double LogisticRegression<MatType>::ComputeError(
|
||||
const MatType& predictors,
|
||||
|
||||
@@ -807,4 +807,154 @@ BOOST_AUTO_TEST_CASE(LogisticRegressionSparseSGDTest)
|
||||
BOOST_REQUIRE_CLOSE(lr.Parameters()[i], lrSparse.Parameters()[i], 1e-5);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test multi-point classification (Classify()).
|
||||
*/
|
||||
BOOST_AUTO_TEST_CASE(ClassifyTest)
|
||||
{
|
||||
// Generate a two-Gaussian dataset.
|
||||
GaussianDistribution g1(arma::vec("1.0 1.0 1.0"), arma::eye<arma::mat>(3, 3));
|
||||
GaussianDistribution g2(arma::vec("9.0 9.0 9.0"), arma::eye<arma::mat>(3, 3));
|
||||
|
||||
arma::mat data(3, 1000);
|
||||
arma::Row<size_t> responses(1000);
|
||||
for (size_t i = 0; i < 500; ++i)
|
||||
{
|
||||
data.col(i) = g1.Random();
|
||||
responses[i] = 0;
|
||||
}
|
||||
for (size_t i = 500; i < 1000; ++i)
|
||||
{
|
||||
data.col(i) = g2.Random();
|
||||
responses[i] = 1;
|
||||
}
|
||||
|
||||
// Now train a logistic regression object on it.
|
||||
LogisticRegression<> lr(data.n_rows, 0.5);
|
||||
lr.Train<>(data, responses);
|
||||
|
||||
// Create a test set.
|
||||
for (size_t i = 0; i < 500; ++i)
|
||||
{
|
||||
data.col(i) = g1.Random();
|
||||
responses[i] = 0;
|
||||
}
|
||||
for (size_t i = 500; i < 1000; ++i)
|
||||
{
|
||||
data.col(i) = g2.Random();
|
||||
responses[i] = 1;
|
||||
}
|
||||
|
||||
arma::Row<size_t> predictions;
|
||||
lr.Classify(data, predictions);
|
||||
|
||||
BOOST_REQUIRE_GE((double) arma::accu(predictions == responses), 900);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that single-point classification gives the same results as multi-point
|
||||
* classification.
|
||||
*/
|
||||
BOOST_AUTO_TEST_CASE(SinglePointClassifyTest)
|
||||
{
|
||||
// Generate a two-Gaussian dataset.
|
||||
GaussianDistribution g1(arma::vec("1.0 1.0 1.0"), arma::eye<arma::mat>(3, 3));
|
||||
GaussianDistribution g2(arma::vec("9.0 9.0 9.0"), arma::eye<arma::mat>(3, 3));
|
||||
|
||||
arma::mat data(3, 1000);
|
||||
arma::Row<size_t> responses(1000);
|
||||
for (size_t i = 0; i < 500; ++i)
|
||||
{
|
||||
data.col(i) = g1.Random();
|
||||
responses[i] = 0;
|
||||
}
|
||||
for (size_t i = 500; i < 1000; ++i)
|
||||
{
|
||||
data.col(i) = g2.Random();
|
||||
responses[i] = 1;
|
||||
}
|
||||
|
||||
// Now train a logistic regression object on it.
|
||||
LogisticRegression<> lr(data.n_rows, 0.5);
|
||||
lr.Train<>(data, responses);
|
||||
|
||||
// Create a test set.
|
||||
for (size_t i = 0; i < 500; ++i)
|
||||
{
|
||||
data.col(i) = g1.Random();
|
||||
responses[i] = 0;
|
||||
}
|
||||
for (size_t i = 500; i < 1000; ++i)
|
||||
{
|
||||
data.col(i) = g2.Random();
|
||||
responses[i] = 1;
|
||||
}
|
||||
|
||||
arma::Row<size_t> predictions;
|
||||
lr.Classify(data, predictions);
|
||||
|
||||
for (size_t i = 0; i < data.n_cols; ++i)
|
||||
{
|
||||
size_t pred = lr.Classify(data.col(i));
|
||||
|
||||
BOOST_REQUIRE_EQUAL(pred, predictions[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that giving point probabilities works.
|
||||
*/
|
||||
BOOST_AUTO_TEST_CASE(ClassifyProbabilitiesTest)
|
||||
{
|
||||
// Generate a two-Gaussian dataset.
|
||||
GaussianDistribution g1(arma::vec("1.0 1.0 1.0"), arma::eye<arma::mat>(3, 3));
|
||||
GaussianDistribution g2(arma::vec("9.0 9.0 9.0"), arma::eye<arma::mat>(3, 3));
|
||||
|
||||
arma::mat data(3, 1000);
|
||||
arma::Row<size_t> responses(1000);
|
||||
for (size_t i = 0; i < 500; ++i)
|
||||
{
|
||||
data.col(i) = g1.Random();
|
||||
responses[i] = 0;
|
||||
}
|
||||
for (size_t i = 500; i < 1000; ++i)
|
||||
{
|
||||
data.col(i) = g2.Random();
|
||||
responses[i] = 1;
|
||||
}
|
||||
|
||||
// Now train a logistic regression object on it.
|
||||
LogisticRegression<> lr(data.n_rows, 0.5);
|
||||
lr.Train<>(data, responses);
|
||||
|
||||
// Create a test set.
|
||||
for (size_t i = 0; i < 500; ++i)
|
||||
{
|
||||
data.col(i) = g1.Random();
|
||||
responses[i] = 0;
|
||||
}
|
||||
for (size_t i = 500; i < 1000; ++i)
|
||||
{
|
||||
data.col(i) = g2.Random();
|
||||
responses[i] = 1;
|
||||
}
|
||||
|
||||
arma::mat probabilities;
|
||||
lr.Classify(data, probabilities);
|
||||
|
||||
BOOST_REQUIRE_EQUAL(probabilities.n_cols, data.n_cols);
|
||||
BOOST_REQUIRE_EQUAL(probabilities.n_rows, 2);
|
||||
|
||||
for (size_t i = 0; i < data.n_cols; ++i)
|
||||
{
|
||||
BOOST_REQUIRE_CLOSE(probabilities(0, i) + probabilities(1, i), 1.0, 1e-5);
|
||||
|
||||
// 10% tolerance.
|
||||
if (responses[i] == 0)
|
||||
BOOST_REQUIRE_CLOSE(probabilities(0, i), 1.0, 10.0);
|
||||
else
|
||||
BOOST_REQUIRE_CLOSE(probabilities(1, i), 1.0, 10.0);
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END();
|
||||
|
||||
Reference in New Issue
Block a user