diff --git a/src/mlpack/tests/decision_tree_regressor_test.cpp b/src/mlpack/tests/decision_tree_regressor_test.cpp index 7e395a2d67..3624216f65 100644 --- a/src/mlpack/tests/decision_tree_regressor_test.cpp +++ b/src/mlpack/tests/decision_tree_regressor_test.cpp @@ -579,6 +579,176 @@ TEST_CASE("PerfectTrainingSetWithWeight_", "[DecisionTreeRegressorTest]") } } +/** + * Test that the tree is able to perfectly fit all the obvious splits present + * in the data. + * + * | + * | + * 2 | xxxxxx + * | + * | + * 1 | xxxxxx xxxxxx + * | + * | + * 0 |xxxxxx xxxxxx + * |___________________________________ + */ +TEST_CASE("MultiSplitTest1", "[DecisionTreeRegressorTest]") +{ + arma::mat dataset; + arma::rowvec responses; + arma::rowvec values = {0.0, 1.0, 2.0, 1.0, 0.0}; + + CreateMultiSplitData(dataset, responses, 1000, values); + + arma::rowvec weights(responses.n_elem); + weights.ones(); + + // Minimum leaf size of 1. + DecisionTreeRegressor<> d(dataset, responses, weights, 2, 0.0); + arma::rowvec preds; + d.Predict(dataset, preds); + + // Ensure that the predictions are perfect. + for (size_t i = 0; i < responses.n_elem; ++i) + REQUIRE(preds[i] == responses[i]); + + // Ensure that a split is made only when required and no redundant splits are + // made. + REQUIRE(d.NumLeaves() == 5); +} + +/** + * Test that the tree is able to perfectly fit all the obvious splits present + * in the data. Same test as above, but with less data. + */ +TEST_CASE("MultiSplitTest2", "[DecisionTreeRegressorTest]") +{ + arma::mat dataset; + arma::rowvec responses; + arma::rowvec values = {0.0, 1.0, 2.0, 1.0, 0.0}; + + CreateMultiSplitData(dataset, responses, 100, values); + + arma::rowvec weights(responses.n_elem); + weights.ones(); + + // Minimum leaf size of 1. + DecisionTreeRegressor<> d(dataset, responses, weights, 2, 0.0); + arma::rowvec preds; + d.Predict(dataset, preds); + + // Ensure that the predictions are perfect. + for (size_t i = 0; i < responses.n_elem; ++i) + REQUIRE(preds[i] == responses[i]); + + // Ensure that a split is made only when required and no redundant splits are + // made. + REQUIRE(d.NumLeaves() == 5); +} + +/** + * Test that the tree is able to perfectly fit all the obvious splits present + * in the data. + * + * | + * 20 | xxxxxx + * | + * | + * 15 | xxxxxx + * | + * | + * 10 | xxxxxx + * | + * | + * 5 | xxxxxx + * | + * | + * 0 |xxxxxx + * |________________________________________ + */ +TEST_CASE("MultiSplitTest3", "[DecisionTreeRegressorTest]") +{ + arma::mat dataset; + arma::Row responses; + arma::rowvec values = {0.0, 5.0, 10.0, 15.0, 20.0}; + + CreateMultiSplitData(dataset, responses, 500, values); + + arma::rowvec weights(responses.n_elem); + weights.ones(); + + // Minimum leaf size of 1. + DecisionTreeRegressor<> d(dataset, responses, weights, 2, 0.0); + arma::rowvec preds; + d.Predict(dataset, preds); + + // Ensure that the predictions are perfect. + for (size_t i = 0; i < responses.n_elem; ++i) + REQUIRE(preds[i] == responses[i]); + + // Ensure that a split is made only when required and no redundant splits are + // made. + REQUIRE(d.NumLeaves() == 5); +} + +/** + * Test that the tree builds correctly on unweighted numerical dataset. + */ +TEST_CASE("NumericalBuildTest", "[DecisionTreeRegressorTest]") +{ + arma::mat X; + arma::rowvec Y; + + if (!data::Load("lars_dependent_x.csv", X)) + FAIL("Cannot load dataset lars_dependent_x.csv"); + if (!data::Load("lars_dependent_y.csv", Y)) + FAIL("Cannot load dataset lars_dependent_y.csv"); + + arma::mat XTrain, XTest; + arma::rowvec YTrain, YTest; + data::Split(X, Y, XTrain, XTest, YTrain, YTest, 0.3); + + DecisionTreeRegressor<> tree(XTrain, YTrain, 5); + + arma::rowvec predictions; + tree.Predict(XTest, predictions); + + // Ensuring a decent performance. + const double rmse = RMSE(predictions, YTest); + REQUIRE(rmse < 1.0); +} + +/** + * Test that the tree builds correctly on weighted numerical dataset. + */ +TEST_CASE("NumericalBuildTestWithWeights", "[DecisionTreeRegressorTest]") +{ + arma::mat X; + arma::rowvec Y; + + if (!data::Load("lars_dependent_x.csv", X)) + FAIL("Cannot load dataset lars_dependent_x.csv"); + if (!data::Load("lars_dependent_y.csv", Y)) + FAIL("Cannot load dataset lars_dependent_y.csv"); + + arma::mat XTrain, XTest; + arma::rowvec YTrain, YTest; + data::Split(X, Y, XTrain, XTest, YTrain, YTest, 0.3); + + arma::rowvec weights = arma::ones(XTrain.n_elem); + + DecisionTreeRegressor<> tree(XTrain, YTrain, weights, 5); + + arma::rowvec predictions; + tree.Predict(XTest, predictions); + + // Ensuring a decent performance. + const double rmse = RMSE(predictions, YTest); + REQUIRE(rmse < 1.0); +} + /** * Test that we can build a decision tree on a simple categorical dataset. */ @@ -643,51 +813,6 @@ TEST_CASE("CategoricalBuildTestWithWeight_", "[DecisionTreeRegressorTest]") REQUIRE(rmse < 1.0); } -/** - * Test that we can build a decision tree using weighted data (where the - * low-weighted data is random noise), and that the tree still builds correctly - * enough to get good results. - */ -TEST_CASE("WeightedDecisionTreeTest_", "[DecisionTreeRegressorTest]") -{ - // Loading data. - data::DatasetInfo info; - arma::mat trainData, testData; - arma::rowvec trainResponses, testResponses; - LoadBostonHousingDataset(trainData, testData, trainResponses, testResponses, - info); - - // Add some noise. - arma::mat noise(trainData.n_rows, 500, arma::fill::randu); - arma::rowvec noiseResponses(500); - for (size_t i = 0; i < noiseResponses.n_elem; ++i) - noiseResponses[i] = 15 + math::Random(0, 10); // Random response. - - // Concatenate data matrices. - arma::mat data = arma::join_rows(trainData, noise); - arma::rowvec fullResponses = arma::join_rows(trainResponses, noiseResponses); - - // Now set weights. - arma::rowvec weights(trainData.n_cols + 500); - for (size_t i = 0; i < trainData.n_cols; ++i) - weights[i] = math::Random(0.9, 1.0); - for (size_t i = trainData.n_cols; i < trainData.n_cols + 500; ++i) - weights[i] = math::Random(0.0, 0.01); // Low weights for false points. - - // Now build the decision tree. - DecisionTreeRegressor<> d(data, fullResponses, weights, 5); - - // Now we can check that we get good performance on the test set. - arma::rowvec predictions; - d.Predict(testData, predictions); - - REQUIRE(predictions.n_elem == testData.n_cols); - - // Figure out the accuracy. - double rmse = RMSE(predictions, testResponses); - REQUIRE(rmse < 5.0); -} - /** * Test that we can build a decision tree on a simple categorical dataset using * weights, with low-weight noise added. @@ -743,51 +868,6 @@ TEST_CASE("CategoricalWeightedBuildTest_", "[DecisionTreeRegressorTest]") REQUIRE(rmse < 1.5); } -/** - * Test that we can build a decision tree using weighted data (where the - * low-weighted data is random noise) with MAD gain, and that the tree - * still builds correctly enough to get good results. - */ -TEST_CASE("WeightedDecisionTreeMADGainTest", "[DecisionTreeRegressorTest]") -{ - // Loading data. - data::DatasetInfo info; - arma::mat trainData, testData; - arma::rowvec trainResponses, testResponses; - LoadBostonHousingDataset(trainData, testData, trainResponses, testResponses, - info); - - // Add some noise. - arma::mat noise(trainData.n_rows, 500, arma::fill::randu); - arma::rowvec noiseResponses(500); - for (size_t i = 0; i < noiseResponses.n_elem; ++i) - noiseResponses[i] = 15 + math::Random(0, 10); // Random response. - - // Concatenate data matrices. - arma::mat data = arma::join_rows(trainData, noise); - arma::rowvec fullResponses = arma::join_rows(trainResponses, noiseResponses); - - // Now set weights. - arma::rowvec weights(trainData.n_cols + 500); - for (size_t i = 0; i < trainData.n_cols; ++i) - weights[i] = math::Random(0.9, 1.0); - for (size_t i = trainData.n_cols; i < trainData.n_cols + 500; ++i) - weights[i] = math::Random(0.0, 0.01); // Low weights for false points. - - // Now build the decision tree using MADGain. - DecisionTreeRegressor d(data, fullResponses, weights, 5); - - // Now we can check that we get good performance on the test set. - arma::rowvec predictions; - d.Predict(testData, predictions); - - REQUIRE(predictions.n_elem == testData.n_cols); - - // Figure out the accuracy. - double rmse = RMSE(predictions, testResponses); - REQUIRE(rmse < 5.5); -} - /** * Test that we can build a decision tree using MAD gain on a simple * categorical dataset using weights, with low-weight noise added. @@ -829,7 +909,7 @@ TEST_CASE("CategoricalMADGainWeightedBuildTest", "[DecisionTreeRegressorTest]") arma::rowvec fullResponses = arma::join_rows(trainingResponses, randomResponses); - // Build the tree. + // Build the tree using MAD gain. DecisionTreeRegressor tree(fullData, di, fullResponses, weights, 10); @@ -879,7 +959,7 @@ TEST_CASE("SimpleGeneralizationTest_", "[DecisionTreeRegressorTest]") // Figure out rmse. rmse = RMSE(predictions, testResponses); - REQUIRE(rmse < 4.0); + REQUIRE(rmse < 1.0); } /** @@ -918,166 +998,95 @@ TEST_CASE("SimpleGeneralizationFMatTest_", "[DecisionTreeRegressorTest]") // Figure out the rmse. double wdrmse = RMSE(predictions, testLabels); - REQUIRE(wdrmse < 4.0); + REQUIRE(wdrmse < 1.0); } /** - * Test that the tree is able to perfectly fit all the obvious splits present - * in the data. - * - * | - * | - * 2 | xxxxxx - * | - * | - * 1 | xxxxxx xxxxxx - * | - * | - * 0 |xxxxxx xxxxxx - * |___________________________________ + * Test that we can build a decision tree using weighted data (where the + * low-weighted data is random noise), and that the tree still builds correctly + * enough to get good results. */ -TEST_CASE("MultiSplitTest1", "[DecisionTreeRegressorTest]") +TEST_CASE("WeightedDecisionTreeTest_", "[DecisionTreeRegressorTest]") { - arma::mat dataset; - arma::rowvec responses; - arma::rowvec values = {0.0, 1.0, 2.0, 1.0, 0.0}; + // Loading data. + data::DatasetInfo info; + arma::mat trainData, testData; + arma::rowvec trainResponses, testResponses; + LoadBostonHousingDataset(trainData, testData, trainResponses, testResponses, + info); - CreateMultiSplitData(dataset, responses, 1000, values); + // Add some noise. + arma::mat noise(trainData.n_rows, 200, arma::fill::randu); + arma::rowvec noiseResponses(200); + for (size_t i = 0; i < noiseResponses.n_elem; ++i) + noiseResponses[i] = 15 + math::Random(0, 10); // Random response. - arma::rowvec weights(responses.n_elem); - weights.ones(); + // Concatenate data matrices. + arma::mat data = arma::join_rows(trainData, noise); + arma::rowvec fullResponses = arma::join_rows(trainResponses, noiseResponses); - // Minimum leaf size of 1. - DecisionTreeRegressor<> d(dataset, responses, weights, 2, 0.0); - arma::rowvec preds; - d.Predict(dataset, preds); + // Now set weights. + arma::rowvec weights(trainData.n_cols + 200); + for (size_t i = 0; i < trainData.n_cols; ++i) + weights[i] = math::Random(0.9, 1.0); + for (size_t i = trainData.n_cols; i < trainData.n_cols + 200; ++i) + weights[i] = math::Random(0.0, 0.01); // Low weights for false points. - for (size_t i = 0; i < responses.n_elem; ++i) - REQUIRE(preds[i] == responses[i]); - - REQUIRE(d.NumLeaves() == 5); -} - -/** - * Test that the tree is able to perfectly fit all the obvious splits present - * in the data. Same test as above, but with less data. - */ -TEST_CASE("MultiSplitTest2", "[DecisionTreeRegressorTest]") -{ - arma::mat dataset; - arma::rowvec responses; - arma::rowvec values = {0.0, 1.0, 2.0, 1.0, 0.0}; - - CreateMultiSplitData(dataset, responses, 100, values); - - arma::rowvec weights(responses.n_elem); - weights.ones(); - - // Minimum leaf size of 1. - DecisionTreeRegressor<> d(dataset, responses, weights, 2, 0.0); - arma::rowvec preds; - d.Predict(dataset, preds); - - for (size_t i = 0; i < responses.n_elem; ++i) - REQUIRE(preds[i] == responses[i]); - - REQUIRE(d.NumLeaves() == 5); -} - -/** - * Test that the tree is able to perfectly fit all the obvious splits present - * in the data. - * - * | - * 20 | xxxxxx - * | - * | - * 15 | xxxxxx - * | - * | - * 10 | xxxxxx - * | - * | - * 5 | xxxxxx - * | - * | - * 0 |xxxxxx - * |________________________________________ - */ -TEST_CASE("MultiSplitTest3", "[DecisionTreeRegressorTest]") -{ - arma::mat dataset; - arma::Row responses; - arma::rowvec values = {0.0, 5.0, 10.0, 15.0, 20.0}; - - CreateMultiSplitData(dataset, responses, 500, values); - - arma::rowvec weights(responses.n_elem); - weights.ones(); - - // Minimum leaf size of 1. - DecisionTreeRegressor<> d(dataset, responses, weights, 2, 0.0); - arma::rowvec preds; - d.Predict(dataset, preds); - - for (size_t i = 0; i < responses.n_elem; ++i) - REQUIRE(preds[i] == responses[i]); - - REQUIRE(d.NumLeaves() == 5); -} - -/** - * Test that the tree builds correctly on unweighted numerical dataset. - */ -TEST_CASE("LARSDatasetTest", "[DecisionTreeRegressorTest]") -{ - arma::mat X; - arma::rowvec Y; - - if (!data::Load("lars_dependent_x.csv", X)) - FAIL("Cannot load dataset lars_dependent_x.csv"); - if (!data::Load("lars_dependent_y.csv", Y)) - FAIL("Cannot load dataset lars_dependent_y.csv"); - - arma::mat XTrain, XTest; - arma::rowvec YTrain, YTest; - data::Split(X, Y, XTrain, XTest, YTrain, YTest, 0.3); - - DecisionTreeRegressor<> tree(XTrain, YTrain, 5); + // Now build the decision tree. + DecisionTreeRegressor<> d(data, fullResponses, weights, 5); + // Now we can check that we get good performance on the test set. arma::rowvec predictions; - tree.Predict(XTest, predictions); + d.Predict(testData, predictions); - const double rmse = RMSE(predictions, YTest); + REQUIRE(predictions.n_elem == testData.n_cols); + // Figure out the accuracy. + double rmse = RMSE(predictions, testResponses); REQUIRE(rmse < 1.0); } /** - * Test that the tree builds correctly on weighted numerical dataset. + * Test that we can build a decision tree using weighted data (where the + * low-weighted data is random noise) with MAD gain, and that the tree + * still builds correctly enough to get good results. */ -TEST_CASE("LARSDatasetWeightedTest", "[DecisionTreeRegressorTest]") +TEST_CASE("WeightedDecisionTreeMADGainTest", "[DecisionTreeRegressorTest]") { - arma::mat X; - arma::rowvec Y; + // Loading data. + data::DatasetInfo info; + arma::mat trainData, testData; + arma::rowvec trainResponses, testResponses; + LoadBostonHousingDataset(trainData, testData, trainResponses, testResponses, + info); - if (!data::Load("lars_dependent_x.csv", X)) - FAIL("Cannot load dataset lars_dependent_x.csv"); - if (!data::Load("lars_dependent_y.csv", Y)) - FAIL("Cannot load dataset lars_dependent_y.csv"); + // Add some noise. + arma::mat noise(trainData.n_rows, 200, arma::fill::randu); + arma::rowvec noiseResponses(200); + for (size_t i = 0; i < noiseResponses.n_elem; ++i) + noiseResponses[i] = 15 + math::Random(0, 10); // Random response. - arma::mat XTrain, XTest; - arma::rowvec YTrain, YTest; - data::Split(X, Y, XTrain, XTest, YTrain, YTest, 0.3); + // Concatenate data matrices. + arma::mat data = arma::join_rows(trainData, noise); + arma::rowvec fullResponses = arma::join_rows(trainResponses, noiseResponses); - arma::rowvec weights = arma::ones(XTrain.n_elem); + // Now set weights. + arma::rowvec weights(trainData.n_cols + 200); + for (size_t i = 0; i < trainData.n_cols; ++i) + weights[i] = math::Random(0.9, 1.0); + for (size_t i = trainData.n_cols; i < trainData.n_cols + 200; ++i) + weights[i] = math::Random(0.0, 0.01); // Low weights for false points. - DecisionTreeRegressor<> tree(XTrain, YTrain, weights, 5); + // Now build the decision tree using MADGain. + DecisionTreeRegressor d(data, fullResponses, weights, 5); + // Now we can check that we get good performance on the test set. arma::rowvec predictions; - tree.Predict(XTest, predictions); + d.Predict(testData, predictions); - const double rmse = RMSE(predictions, YTest); + REQUIRE(predictions.n_elem == testData.n_cols); - REQUIRE(rmse < 1.0); + // Figure out the accuracy. + double rmse = RMSE(predictions, testResponses); + REQUIRE(rmse < 1.5); }