Merge pull request #3014 from RishabhGarg108/refactor-tree

Refactor DecisionTreeRegressor to support XGBoost.
This commit is contained in:
Ryan Curtin
2021-07-28 12:59:34 -04:00
committed by GitHub
11 changed files with 169 additions and 106 deletions
@@ -91,6 +91,8 @@ class AllCategoricalSplit
* @param minimumGainSplit Minimum gain split.
* @param aux Auxiliary split information, which may be modified on a
* successful split.
* @param fitnessFunction The FitnessFunction object instance. It it used to
* evaluate the gain for the split.
*/
template<bool UseWeights, typename VecType, typename ResponsesType,
typename WeightVecType>
@@ -103,7 +105,8 @@ class AllCategoricalSplit
const size_t minimumLeafSize,
const double minimumGainSplit,
double& splitInfo,
AuxiliarySplitInfo& aux);
AuxiliarySplitInfo& aux,
FitnessFunction& fitnessFunction);
/**
* Return the number of children in the split.
@@ -125,7 +125,8 @@ double AllCategoricalSplit<FitnessFunction>::SplitIfBetter(
const size_t minimumLeafSize,
const double minimumGainSplit,
double& splitInfo,
AuxiliarySplitInfo& /* aux */)
AuxiliarySplitInfo& /* aux */,
FitnessFunction& fitnessFunction)
{
// Count the number of elements in each potential child.
const double epsilon = 1e-7; // Tolerance for floating-point errors.
@@ -190,7 +191,7 @@ double AllCategoricalSplit<FitnessFunction>::SplitIfBetter(
const double childPct = UseWeights ?
double(childWeightSums[i]) / sumWeight :
double(counts[i]) / double(data.n_elem);
const double childGain = FitnessFunction::template Evaluate<UseWeights>(
const double childGain = fitnessFunction.template Evaluate<UseWeights>(
childResponses[i], childWeights[i]);
overallGain += childPct * childGain;
@@ -104,6 +104,8 @@ class BestBinaryNumericSplit
* @param splitInfo Stores split information on a successful split.
* @param aux Auxiliary split information, which may be modified on a
* successful split.
* @param fitnessFunction The FitnessFunction object instance. It it used to
* evaluate the gain for the split.
*/
template<bool UseWeights, typename VecType, typename ResponsesType,
typename WeightVecType>
@@ -118,7 +120,8 @@ class BestBinaryNumericSplit
const size_t minimumLeafSize,
const double minimumGainSplit,
double& splitInfo,
AuxiliarySplitInfo& aux);
AuxiliarySplitInfo& aux,
FitnessFunction& fitnessFunction);
/**
* Check if we can split a node. If we can split a node in a way that
@@ -154,7 +157,8 @@ class BestBinaryNumericSplit
const size_t minimumLeafSize,
const double minimumGainSplit,
double& splitInfo,
AuxiliarySplitInfo& /* aux */);
AuxiliarySplitInfo& /* aux */,
FitnessFunction& fitnessFunction);
/**
* Returns 2, since the binary split always has two children.
@@ -200,7 +200,8 @@ BestBinaryNumericSplit<FitnessFunction>::SplitIfBetter(
const size_t minimumLeafSize,
const double minimumGainSplit,
double& splitInfo,
AuxiliarySplitInfo& /* aux */)
AuxiliarySplitInfo& /* aux */,
FitnessFunction& fitnessFunction)
{
typedef typename ResponsesType::elem_type RType;
typedef typename WeightVecType::elem_type WType;
@@ -270,9 +271,9 @@ BestBinaryNumericSplit<FitnessFunction>::SplitIfBetter(
continue;
// Calculate the gain for the left and right child.
const double leftGain = FitnessFunction::template
const double leftGain = fitnessFunction.template
Evaluate<UseWeights>(sortedResponses, sortedWeights, 0, index);
const double rightGain = FitnessFunction::template
const double rightGain = fitnessFunction.template
Evaluate<UseWeights>(sortedResponses, sortedWeights, index,
responses.n_elem);
@@ -338,13 +339,12 @@ BestBinaryNumericSplit<FitnessFunction>::SplitIfBetter(
const size_t minimumLeafSize,
const double minimumGainSplit,
double& splitInfo,
AuxiliarySplitInfo& /* aux */)
AuxiliarySplitInfo& /* aux */,
FitnessFunction& fitnessFunction)
{
typedef typename ResponsesType::elem_type RType;
typedef typename WeightVecType::elem_type WType;
FitnessFunction fitnessFunction;
// First sanity check: if we don't have enough points, we can't split.
if (data.n_elem < (minimumLeafSize * 2))
return DBL_MAX;
@@ -276,6 +276,8 @@ class DecisionTreeRegressor :
* @param minimumGainSplit Minimum gain for the node to split.
* @param maximumDepth Maximum depth for the tree.
* @param dimensionSelector Instantiated dimension selection policy.
* @param fitnessFunction Instantiated fitnessFunction. It is used to
* evaluate the fitness score for splitting each node.
* @return The final entropy of decision tree.
*/
template<typename MatType, typename ResponsesType>
@@ -286,7 +288,8 @@ class DecisionTreeRegressor :
const double minimumGainSplit = 1e-7,
const size_t maximumDepth = 0,
DimensionSelectionType dimensionSelector =
DimensionSelectionType());
DimensionSelectionType(),
FitnessFunction fitnessFunction = FitnessFunction());
/**
* Train the decision tree on the given data, assuming that all dimensions are
@@ -302,6 +305,8 @@ class DecisionTreeRegressor :
* @param minimumGainSplit Minimum gain for the node to split.
* @param maximumDepth Maximum depth for the tree.
* @param dimensionSelector Instantiated dimension selection policy.
* @param fitnessFunction Instantiated fitnessFunction. It is used to
* evaluate the fitness score for splitting each node.
* @return The final entropy of decision tree.
*/
template<typename MatType, typename ResponsesType>
@@ -311,7 +316,8 @@ class DecisionTreeRegressor :
const double minimumGainSplit = 1e-7,
const size_t maximumDepth = 0,
DimensionSelectionType dimensionSelector =
DimensionSelectionType());
DimensionSelectionType(),
FitnessFunction fitnessFunction = FitnessFunction());
/**
* Train the decision tree on the given weighted data. This will overwrite
@@ -331,6 +337,8 @@ class DecisionTreeRegressor :
* @param minimumGainSplit Minimum gain for the node to split.
* @param maximumDepth Maximum depth for the tree.
* @param dimensionSelector Instantiated dimension selection policy.
* @param fitnessFunction Instantiated fitnessFunction. It is used to
* evaluate the fitness score for splitting each node.
* @return The final entropy of decision tree.
*/
template<typename MatType, typename ResponsesType, typename WeightsType>
@@ -343,6 +351,7 @@ class DecisionTreeRegressor :
const size_t maximumDepth = 0,
DimensionSelectionType dimensionSelector =
DimensionSelectionType(),
FitnessFunction fitnessFunction = FitnessFunction(),
const std::enable_if_t<arma::is_arma_type<typename
std::remove_reference<WeightsType>::type>::value>* = 0);
@@ -362,6 +371,8 @@ class DecisionTreeRegressor :
* @param minimumGainSplit Minimum gain for the node to split.
* @param maximumDepth Maximum depth for the tree.
* @param dimensionSelector Instantiated dimension selection policy.
* @param fitnessFunction Instantiated fitnessFunction. It is used to
* evaluate the fitness score for splitting each node.
* @return The final entropy of decision tree.
*/
template<typename MatType, typename ResponsesType, typename WeightsType>
@@ -373,6 +384,7 @@ class DecisionTreeRegressor :
const size_t maximumDepth = 0,
DimensionSelectionType dimensionSelector =
DimensionSelectionType(),
FitnessFunction fitnessFunction = FitnessFunction(),
const std::enable_if_t<arma::is_arma_type<typename
std::remove_reference<WeightsType>::type>::value>* = 0);
@@ -455,13 +467,6 @@ class DecisionTreeRegressor :
typedef typename CategoricalSplit::AuxiliarySplitInfo
CategoricalAuxiliarySplitInfo;
/**
* Calculate the prediction value for the leaf nodes.
*/
template<bool UseWeights, typename ResponsesType, typename WeightsType>
void CalculatePrediction(const ResponsesType& responses,
const WeightsType& weights);
/**
* Corresponding to the public Train() method, this method is designed for
* avoiding unnecessary copies during training. This function is called to
@@ -476,6 +481,8 @@ class DecisionTreeRegressor :
* @param minimumLeafSize Minimum number of points in each leaf node.
* @param minimumGainSplit Minimum gain for the node to split.
* @param maximumDepth Maximum depth for the tree.
* @param fitnessFunction Instantiated fitnessFunction. It is used to
* evaluate the fitness score for splitting each node.
* @return The final entropy of decision tree.
*/
template<bool UseWeights, typename MatType, typename ResponsesType>
@@ -488,7 +495,8 @@ class DecisionTreeRegressor :
const size_t minimumLeafSize,
const double minimumGainSplit,
const size_t maximumDepth,
DimensionSelectionType& dimensionSelector);
DimensionSelectionType& dimensionSelector,
FitnessFunction fitnessFunction = FitnessFunction());
/**
* Corresponding to the public Train() method, this method is designed for
@@ -503,6 +511,8 @@ class DecisionTreeRegressor :
* @param minimumLeafSize Minimum number of points in each leaf node.
* @param minimumGainSplit Minimum gain for the node to split.
* @param maximumDepth Maximum depth for the tree.
* @param fitnessFunction Instantiated fitnessFunction. It is used to
* evaluate the fitness score for splitting each node.
* @return The final entropy of decision tree.
*/
template<bool UseWeights, typename MatType, typename ResponsesType>
@@ -514,7 +524,8 @@ class DecisionTreeRegressor :
const size_t minimumLeafSize,
const double minimumGainSplit,
const size_t maximumDepth,
DimensionSelectionType& dimensionSelector);
DimensionSelectionType& dimensionSelector,
FitnessFunction fitnessFunction = FitnessFunction());
};
@@ -432,7 +432,8 @@ double DecisionTreeRegressor<FitnessFunction,
const size_t minimumLeafSize,
const double minimumGainSplit,
const size_t maximumDepth,
DimensionSelectionType dimensionSelector)
DimensionSelectionType dimensionSelector,
FitnessFunction fitnessFunction)
{
// Sanity check on data.
util::CheckSameSizes(data, responses, "DecisionTreeRegressor::Train()");
@@ -451,7 +452,7 @@ double DecisionTreeRegressor<FitnessFunction,
arma::rowvec weights; // Fake weights, not used.
return Train<false>(tmpData, 0, tmpData.n_cols, datasetInfo, tmpResponses,
weights, minimumLeafSize, minimumGainSplit, maximumDepth,
dimensionSelector);
dimensionSelector, fitnessFunction);
}
//! Train on the given data, assuming all dimensions are numeric.
@@ -471,7 +472,8 @@ double DecisionTreeRegressor<FitnessFunction,
const size_t minimumLeafSize,
const double minimumGainSplit,
const size_t maximumDepth,
DimensionSelectionType dimensionSelector)
DimensionSelectionType dimensionSelector,
FitnessFunction fitnessFunction)
{
// Sanity check on data.
util::CheckSameSizes(data, responses, "DecisionTreeRegressor::Train()");
@@ -490,7 +492,7 @@ double DecisionTreeRegressor<FitnessFunction,
arma::rowvec weights; // Fake weights, not used.
return Train<false>(tmpData, 0, tmpData.n_cols, tmpResponses,
weights, minimumLeafSize, minimumGainSplit, maximumDepth,
dimensionSelector);
dimensionSelector, fitnessFunction);
}
//! Train on the given weighted data.
@@ -513,6 +515,7 @@ double DecisionTreeRegressor<FitnessFunction,
const double minimumGainSplit,
const size_t maximumDepth,
DimensionSelectionType dimensionSelector,
FitnessFunction fitnessFunction,
const std::enable_if_t<
arma::is_arma_type<
typename std::remove_reference<
@@ -536,7 +539,7 @@ double DecisionTreeRegressor<FitnessFunction,
// Pass off work to the Train() method.
return Train<true>(tmpData, 0, tmpData.n_cols, datasetInfo, tmpResponses,
tmpWeights, minimumLeafSize, minimumGainSplit, maximumDepth,
dimensionSelector);
dimensionSelector, fitnessFunction);
}
//! Train on the given weighted all numeric data.
@@ -558,6 +561,7 @@ double DecisionTreeRegressor<FitnessFunction,
const double minimumGainSplit,
const size_t maximumDepth,
DimensionSelectionType dimensionSelector,
FitnessFunction fitnessFunction,
const std::enable_if_t<
arma::is_arma_type<
typename std::remove_reference<
@@ -581,7 +585,7 @@ double DecisionTreeRegressor<FitnessFunction,
// Pass off work to the Train() method.
return Train<true>(tmpData, 0, tmpData.n_cols, tmpResponses,
tmpWeights, minimumLeafSize, minimumGainSplit, maximumDepth,
dimensionSelector);
dimensionSelector, fitnessFunction);
}
//! Train on the given data.
@@ -605,7 +609,8 @@ double DecisionTreeRegressor<FitnessFunction,
const size_t minimumLeafSize,
const double minimumGainSplit,
const size_t maximumDepth,
DimensionSelectionType& dimensionSelector)
DimensionSelectionType& dimensionSelector,
FitnessFunction fitnessFunction)
{
// Clear children if needed.
for (size_t i = 0; i < children.size(); ++i)
@@ -617,8 +622,8 @@ double DecisionTreeRegressor<FitnessFunction,
// in numericAux and categoricalAux (and clear them later if we make no
// split). The split point is stored in splitPointOrPrediction for all
// internal nodes of the tree.
double bestGain = FitnessFunction::template Evaluate<UseWeights>(
responses.subvec(begin, begin + count - 1),
double bestGain = fitnessFunction.template Evaluate<UseWeights>(
responses.cols(begin, begin + count - 1),
UseWeights ? weights.subvec(begin, begin + count - 1) : weights);
size_t bestDim = datasetInfo.Dimensionality(); // This means "no split".
const size_t end = dimensionSelector.End();
@@ -634,23 +639,25 @@ double DecisionTreeRegressor<FitnessFunction,
dimGain = CategoricalSplit::template SplitIfBetter<UseWeights>(bestGain,
data.cols(begin, begin + count - 1).row(i),
datasetInfo.NumMappings(i),
responses.subvec(begin, begin + count - 1),
responses.cols(begin, begin + count - 1),
UseWeights ? weights.subvec(begin, begin + count - 1) : weights,
minimumLeafSize,
minimumGainSplit,
splitPointOrPrediction,
*this);
*this,
fitnessFunction);
}
else if (datasetInfo.Type(i) == data::Datatype::numeric)
{
dimGain = NumericSplit::template SplitIfBetter<UseWeights>(bestGain,
data.cols(begin, begin + count - 1).row(i),
responses.subvec(begin, begin + count - 1),
responses.cols(begin, begin + count - 1),
UseWeights ? weights.subvec(begin, begin + count - 1) : weights,
minimumLeafSize,
minimumGainSplit,
splitPointOrPrediction,
*this);
*this,
fitnessFunction);
}
// If the splitter reported that it did not split, move to the next
@@ -755,10 +762,11 @@ double DecisionTreeRegressor<FitnessFunction,
NumericAuxiliarySplitInfo::operator=(NumericAuxiliarySplitInfo());
CategoricalAuxiliarySplitInfo::operator=(CategoricalAuxiliarySplitInfo());
// Calculate prediction label because we are a leaf.
CalculatePrediction<UseWeights>(
responses.subvec(begin, begin + count - 1),
UseWeights ? weights.subvec(begin, begin + count - 1) : weights);
// Calculate prediction value because we are a leaf.
splitPointOrPrediction =
fitnessFunction.template OutputLeafValue<UseWeights>(
responses.cols(begin, begin + count - 1),
UseWeights ? weights.subvec(begin, begin + count - 1) : weights);
}
return -bestGain;
@@ -784,7 +792,8 @@ double DecisionTreeRegressor<FitnessFunction,
const size_t minimumLeafSize,
const double minimumGainSplit,
const size_t maximumDepth,
DimensionSelectionType& dimensionSelector)
DimensionSelectionType& dimensionSelector,
FitnessFunction fitnessFunction)
{
// Clear children if needed.
for (size_t i = 0; i < children.size(); ++i)
@@ -798,8 +807,8 @@ double DecisionTreeRegressor<FitnessFunction,
// the best numeric split auxiliary information in numericAux (and clear it
// later if we don't make a split). The split point is stored in
// splitPointOrPrediction for all internal nodes of the tree.
double bestGain = FitnessFunction::template Evaluate<UseWeights>(
responses.subvec(begin, begin + count - 1),
double bestGain = fitnessFunction.template Evaluate<UseWeights>(
responses.cols(begin, begin + count - 1),
UseWeights ? weights.subvec(begin, begin + count - 1) : weights);
size_t bestDim = data.n_rows; // This means "no split".
@@ -818,7 +827,8 @@ double DecisionTreeRegressor<FitnessFunction,
minimumLeafSize,
minimumGainSplit,
splitPointOrPrediction,
*this);
*this,
fitnessFunction);
// If the splitter did not report that it improved, then move to the next
// dimension.
@@ -907,10 +917,11 @@ double DecisionTreeRegressor<FitnessFunction,
// We won't be needing these members, so reset them.
NumericAuxiliarySplitInfo::operator=(NumericAuxiliarySplitInfo());
// Calculate prediction label because we are a leaf.
CalculatePrediction<UseWeights>(
responses.subvec(begin, begin + count - 1),
UseWeights ? weights.subvec(begin, begin + count - 1) : weights);
// Calculate prediction value because we are a leaf.
splitPointOrPrediction =
fitnessFunction.template OutputLeafValue<UseWeights>(
responses.cols(begin, begin + count - 1),
UseWeights ? weights.subvec(begin, begin + count - 1) : weights);
}
return -bestGain;
@@ -965,35 +976,6 @@ void DecisionTreeRegressor<FitnessFunction,
predictions[i] = Predict(data.col(i));
}
template<typename FitnessFunction,
template<typename> class NumericSplitType,
template<typename> class CategoricalSplitType,
typename DimensionSelectionType,
bool NoRecursion>
template<bool UseWeights, typename ResponsesType, typename WeightsType>
void DecisionTreeRegressor<FitnessFunction,
NumericSplitType,
CategoricalSplitType,
DimensionSelectionType,
NoRecursion
>::CalculatePrediction(const ResponsesType& responses,
const WeightsType& weights)
{
if (UseWeights)
{
double accWeights, weightedSum;
WeightedSum(responses, weights, 0, responses.n_elem, accWeights,
weightedSum);
splitPointOrPrediction = weightedSum / accWeights;
}
else
{
double sum;
Sum(responses, 0, responses.n_elem, sum);
splitPointOrPrediction = sum / responses.n_elem;
}
}
template<typename FitnessFunction,
template<typename> class NumericSplitType,
template<typename> class CategoricalSplitType,
@@ -98,6 +98,30 @@ class MADGain
return Evaluate<UseWeights>(values, weights, 0, values.n_elem);
}
/**
* Returns the output value for each leaf node for prediction. The output
* value is calculated as the average of all the points in that leaf node.
* This calculation is specific to regression trees only.
*/
template<bool UseWeights, typename ResponsesType, typename WeightsType>
double OutputLeafValue(const ResponsesType& responses,
const WeightsType& weights)
{
if (UseWeights)
{
double accWeights, weightedSum;
WeightedSum(responses, weights, 0, responses.n_elem, accWeights,
weightedSum);
return weightedSum / accWeights;
}
else
{
double sum;
Sum(responses, 0, responses.n_elem, sum);
return sum / responses.n_elem;
}
}
};
} // namespace tree
@@ -95,6 +95,30 @@ class MSEGain
return Evaluate<UseWeights>(values, weights, 0, values.n_elem);
}
/**
* Returns the output value for each leaf node for prediction. The output
* value is calculated as the average of all the points in that leaf node.
* This calculation is specific to regression trees only.
*/
template<bool UseWeights, typename ResponsesType, typename WeightsType>
double OutputLeafValue(const ResponsesType& responses,
const WeightsType& weights)
{
if (UseWeights)
{
double accWeights, weightedSum;
WeightedSum(responses, weights, 0, responses.n_elem, accWeights,
weightedSum);
return weightedSum / accWeights;
}
else
{
double sum;
Sum(responses, 0, responses.n_elem, sum);
return sum / responses.n_elem;
}
}
/**
* Calculates the mean squared error gain for the left and right children
* for the current index.
@@ -107,6 +107,8 @@ class RandomBinaryNumericSplit
* @param splitInfo Stores split information on a successful split.
* @param aux Auxiliary split information, which may be modified on a
* successful split.
* @param fitnessFunction The FitnessFunction object instance. It it used to
* evaluate the gain for the split.
* @param splitIfBetterGain When set to true, it will split only when gain is
* better than the current best gain. Otherwise, it always makes a
* split regardless of gain.
@@ -121,6 +123,7 @@ class RandomBinaryNumericSplit
const double minimumGainSplit,
double& splitInfo,
AuxiliarySplitInfo& aux,
FitnessFunction& fitnessFunction,
const bool splitIfBetterGain = false);
/**
@@ -149,6 +149,7 @@ double RandomBinaryNumericSplit<FitnessFunction>::SplitIfBetter(
const double minimumGainSplit,
double& splitInfo,
AuxiliarySplitInfo& /* aux */,
FitnessFunction& fitnessFunction,
const bool splitIfBetterGain)
{
double bestFoundGain = std::min(bestGain + minimumGainSplit, 0.0);
@@ -230,9 +231,9 @@ double RandomBinaryNumericSplit<FitnessFunction>::SplitIfBetter(
}
// Calculate the gain for the left and right child.
const double leftGain = FitnessFunction::template
const double leftGain = fitnessFunction.template
Evaluate<UseWeights>(leftResponses, leftWeights, 0, leftLeafSize);
const double rightGain = FitnessFunction::template
const double rightGain = fitnessFunction.template
Evaluate<UseWeights>(rightResponses, rightWeights, 0, rightLeafSize);
// Calculate the gain at this split point.
@@ -88,9 +88,9 @@ TEST_CASE("MSEGainEmptyTest", "[DecisionTreeRegressorTest]")
{
arma::rowvec weights = arma::ones<arma::rowvec>(10);
arma::rowvec responses;
REQUIRE(MSEGain::Evaluate<false>(responses, weights) ==
Approx(0.0).margin(1e-5));
REQUIRE(MSEGain::Evaluate<true>(responses, weights) ==
Approx(0.0).margin(1e-5));
}
@@ -107,6 +107,7 @@ TEST_CASE("MSEGainHandCalculation", "[DecisionTreeRegressorTest]")
// Hand calculated gain values.
const double gain = -27.08999;
const double weightedGain = -27.53960;
REQUIRE(MSEGain::Evaluate<false>(responses, weights) ==
Approx(gain).margin(1e-5));
REQUIRE(MSEGain::Evaluate<true>(responses, weights) ==
@@ -142,8 +143,7 @@ TEST_CASE("MADGainNormalTest", "[DecisionTreeRegressorTest")
theoreticalGain /= (double) responses.n_elem;
// Calculated gain.
const double calculatedGain =
MADGain::Evaluate<false>(responses, weights);
const double calculatedGain = MADGain::Evaluate<false>(responses, weights);
REQUIRE(calculatedGain == Approx(theoreticalGain).margin(1e-5));
}
@@ -155,9 +155,9 @@ TEST_CASE("MADGainEmptyTest", "[DecisionTreeRegressorTest]")
{
arma::rowvec weights = arma::ones<arma::rowvec>(10);
arma::rowvec responses;
REQUIRE(MADGain::Evaluate<false>(responses, weights) ==
Approx(0.0).margin(1e-5));
REQUIRE(MADGain::Evaluate<true>(responses, weights) ==
Approx(0.0).margin(1e-5));
}
@@ -174,6 +174,7 @@ TEST_CASE("MADGainHandCalculation", "[DecisionTreeRegressorTest]")
// Hand calculated gain values.
const double gain = -4.1;
const double weightedGain = -3.8592;
REQUIRE(MADGain::Evaluate<false>(responses, weights) ==
Approx(gain).margin(1e-5));
REQUIRE(MADGain::Evaluate<true>(responses, weights) ==
@@ -203,12 +204,13 @@ TEST_CASE("AllCategoricalSplitSimpleSplitTest_", "[DecisionTreeRegressorTest]")
AllCategoricalSplit<MSEGain>::AuxiliarySplitInfo aux;
// Call the method to do the splitting.
const double bestGain = MSEGain::Evaluate<false>(responses, weights);
MSEGain f;
const double bestGain = f.Evaluate<false>(responses, weights);
const double gain = AllCategoricalSplit<MSEGain>::SplitIfBetter<false>(
bestGain, predictor, 2, responses, weights, 3, 1e-7, splitInfo, aux);
bestGain, predictor, 2, responses, weights, 3, 1e-7, splitInfo, aux, f);
const double weightedGain =
AllCategoricalSplit<MSEGain>::SplitIfBetter<true>(bestGain, predictor, 2,
responses, weights, 3, 1e-7, splitInfo, aux);
responses, weights, 3, 1e-7, splitInfo, aux, f);
// Make sure that a split was made.
REQUIRE(gain > bestGain);
@@ -234,9 +236,10 @@ TEST_CASE("AllCategoricalSplitMinSamplesTest_", "[DecisionTreeRegressorTest]")
AllCategoricalSplit<MSEGain>::AuxiliarySplitInfo aux;
// Call the method to do the splitting.
const double bestGain = MSEGain::Evaluate<false>(responses, weights);
MSEGain f;
const double bestGain = f.Evaluate<false>(responses, weights);
const double gain = AllCategoricalSplit<MSEGain>::SplitIfBetter<false>(
bestGain, predictors, 4, responses, weights, 4, 1e-7, splitInfo, aux);
bestGain, predictors, 4, responses, weights, 4, 1e-7, splitInfo, aux, f);
// Make sure it's not split.
REQUIRE(gain == DBL_MAX);
@@ -265,13 +268,14 @@ TEST_CASE("AllCategoricalSplitNoGainTest_", "[DecisionTreeRegressorTest]")
AllCategoricalSplit<MSEGain>::AuxiliarySplitInfo aux;
// Call the method to do the splitting.
const double bestGain = MSEGain::Evaluate<false>(responses, weights);
MSEGain f;
const double bestGain = f.Evaluate<false>(responses, weights);
const double gain = AllCategoricalSplit<MSEGain>::SplitIfBetter<false>(
bestGain, predictors, 10, responses, weights, 10, 1e-7,
splitInfo, aux);
splitInfo, aux, f);
const double weightedGain =
AllCategoricalSplit<MSEGain>::SplitIfBetter<true>(bestGain, predictors,
10, responses, weights, 10, 1e-7, splitInfo, aux);
10, responses, weights, 10, 1e-7, splitInfo, aux, f);
// Make sure that there was no split.
REQUIRE(gain == DBL_MAX);
@@ -296,12 +300,13 @@ TEST_CASE("BestBinaryNumericSplitSimpleSplitTest_",
BestBinaryNumericSplit<MADGain>::AuxiliarySplitInfo aux;
// Call the method to do the splitting.
const double bestGain = MADGain::Evaluate<false>(responses, weights);
MADGain f;
const double bestGain = f.Evaluate<false>(responses, weights);
const double gain = BestBinaryNumericSplit<MADGain>::SplitIfBetter<false>(
bestGain, predictors, responses, weights, 3, 1e-7, splitInfo, aux);
bestGain, predictors, responses, weights, 3, 1e-7, splitInfo, aux, f);
const double weightedGain =
BestBinaryNumericSplit<MADGain>::SplitIfBetter<true>(bestGain, predictors,
responses, weights, 3, 1e-7, splitInfo, aux);
responses, weights, 3, 1e-7, splitInfo, aux, f);
// Make sure that a split was made.
REQUIRE(gain > bestGain);
@@ -332,13 +337,14 @@ TEST_CASE("BestBinaryNumericSplitMinSamplesTest_",
BestBinaryNumericSplit<MSEGain>::AuxiliarySplitInfo aux;
// Call the method to do the splitting.
const double bestGain = MSEGain::Evaluate<false>(responses, weights);
MSEGain f;
const double bestGain = f.Evaluate<false>(responses, weights);
const double gain = BestBinaryNumericSplit<MSEGain>::SplitIfBetter<false>(
bestGain, predictors, responses, weights, 8, 1e-7, splitInfo, aux);
bestGain, predictors, responses, weights, 8, 1e-7, splitInfo, aux, f);
// This should make no difference because it won't split at all.
const double weightedGain =
BestBinaryNumericSplit<MSEGain>::SplitIfBetter<true>(bestGain,
predictors, responses, weights, 8, 1e-7, splitInfo, aux);
predictors, responses, weights, 8, 1e-7, splitInfo, aux, f);
// Make sure that no split was made.
REQUIRE(gain == DBL_MAX);
@@ -366,9 +372,10 @@ TEST_CASE("BestBinaryNumericSplitNoGainTest_", "[DecisionTreeRegressorTest]")
BestBinaryNumericSplit<MSEGain>::AuxiliarySplitInfo aux;
// Call the method to do the splitting.
const double bestGain = MSEGain::Evaluate<false>(responses, weights);
MSEGain f;
const double bestGain = f.Evaluate<false>(responses, weights);
const double gain = BestBinaryNumericSplit<MSEGain>::SplitIfBetter<false>(
bestGain, predictors, responses, weights, 10, 1e-7, splitInfo, aux);
bestGain, predictors, responses, weights, 10, 1e-7, splitInfo, aux, f);
// Make sure there was no split.
REQUIRE(gain == DBL_MAX);
@@ -390,12 +397,13 @@ TEST_CASE("RandomBinaryNumericSplitAlwaysSplit_",
RandomBinaryNumericSplit<MSEGain>::AuxiliarySplitInfo aux;
// Call the method to do the splitting.
const double bestGain = MSEGain::Evaluate<false>(responses, weights);
MSEGain f;
const double bestGain = f.Evaluate<false>(responses, weights);
const double gain = RandomBinaryNumericSplit<MSEGain>::SplitIfBetter<false>(
bestGain, values, responses, weights, 1, 1e-7, splitInfo, aux);
bestGain, values, responses, weights, 1, 1e-7, splitInfo, aux, f);
const double weightedGain =
RandomBinaryNumericSplit<MSEGain>::SplitIfBetter<true>(bestGain, values,
responses, weights, 1, 1e-7, splitInfo, aux);
responses, weights, 1, 1e-7, splitInfo, aux, f);
// Make sure that split was made.
REQUIRE(gain != DBL_MAX);
@@ -417,13 +425,14 @@ TEST_CASE("RandomBinaryNumericSplitMinSamplesTest_",
RandomBinaryNumericSplit<MSEGain>::AuxiliarySplitInfo aux;
// Call the method to do the splitting.
const double bestGain = MSEGain::Evaluate<false>(responses, weights);
MSEGain f;
const double bestGain = f.Evaluate<false>(responses, weights);
const double gain = RandomBinaryNumericSplit<MSEGain>::SplitIfBetter<false>(
bestGain, values, responses, weights, 8, 1e-7, splitInfo, aux);
bestGain, values, responses, weights, 8, 1e-7, splitInfo, aux, f);
// This should make no difference because it won't split at all.
const double weightedGain =
RandomBinaryNumericSplit<MSEGain>::SplitIfBetter<true>(bestGain, values,
responses, weights, 8, 1e-7, splitInfo, aux);
responses, weights, 8, 1e-7, splitInfo, aux, f);
// Make sure that no split was made.
REQUIRE(gain == DBL_MAX);
@@ -451,9 +460,10 @@ TEST_CASE("RandomBinaryNumericSplitNoGainTest_", "[DecisionTreeRegressorTest]")
RandomBinaryNumericSplit<MSEGain>::AuxiliarySplitInfo aux;
// Call the method to do the splitting.
const double bestGain = MSEGain::Evaluate<false>(responses, weights);
MSEGain f;
const double bestGain = f.Evaluate<false>(responses, weights);
const double gain = RandomBinaryNumericSplit<MSEGain>::SplitIfBetter<false>(
bestGain, values, responses, weights, 10, 1e-7, splitInfo, aux, true);
bestGain, values, responses, weights, 10, 1e-7, splitInfo, aux, f, true);
// Make sure there was no split.
REQUIRE(gain == DBL_MAX);