Changed labels to responses throughtout regression tree codebase

This commit is contained in:
Rishabh Garg
2021-07-12 10:32:56 +05:30
parent 26477c778c
commit 4dc37b23a8
9 changed files with 372 additions and 359 deletions
@@ -74,8 +74,8 @@ class BestBinaryNumericSplit
* @param bestGain Best gain seen so far (we'll only split if we find gain
* better than this).
* @param data The dimension of data points to check for a split in.
* @param labels Labels for each point.
* @param weights Weights associated with labels.
* @param responses Responses for each point.
* @param weights Weights associated with responses.
* @param minimumLeafSize Minimum number of points in a leaf node for
* splitting.
* @param minimumGainSplit Minimum gain split.
@@ -87,7 +87,7 @@ class BestBinaryNumericSplit
static double SplitIfBetter(
const double bestGain,
const VecType& data,
const arma::Row<double>& labels,
const arma::rowvec& responses,
const WeightVecType& weights,
const size_t minimumLeafSize,
const double minimumGainSplit,
@@ -189,7 +189,7 @@ template<bool UseWeights, typename VecType, typename WeightVecType>
double BestBinaryNumericSplit<FitnessFunction>::SplitIfBetter(
const double bestGain,
const VecType& data,
const arma::Row<double>& labels,
const arma::rowvec& responses,
const WeightVecType& weights,
const size_t minimumLeafSize,
const double minimumGainSplit,
@@ -204,10 +204,10 @@ double BestBinaryNumericSplit<FitnessFunction>::SplitIfBetter(
// Next, sort the data.
arma::uvec sortedIndices = arma::sort_index(data);
arma::Row<double> sortedLabels(labels.n_elem);
arma::rowvec sortedResponses(responses.n_elem);
arma::rowvec sortedWeights;
for (size_t i = 0; i < sortedLabels.n_elem; ++i)
sortedLabels[i] = labels[sortedIndices[i]];
for (size_t i = 0; i < sortedResponses.n_elem; ++i)
sortedResponses[i] = responses[sortedIndices[i]];
// Sanity check: if the first element is the same as the last, we can't split
// in this dimension.
@@ -217,9 +217,9 @@ double BestBinaryNumericSplit<FitnessFunction>::SplitIfBetter(
// Only initialize if we are using weights.
if (UseWeights)
{
sortedWeights.set_size(sortedLabels.n_elem);
// The weights must keep the same order as the labels.
for (size_t i = 0; i < sortedLabels.n_elem; ++i)
sortedWeights.set_size(sortedResponses.n_elem);
// The weights must keep the same order as the responses.
for (size_t i = 0; i < sortedResponses.n_elem; ++i)
sortedWeights[i] = weights[sortedIndices[i]];
}
@@ -260,16 +260,18 @@ double BestBinaryNumericSplit<FitnessFunction>::SplitIfBetter(
if (data[sortedIndices[index]] == data[sortedIndices[index - 1]])
continue;
/* TODO: The following function calculates the gain for each split each time from scratch
This can be greatly improved using advanced techniques like prefix sum and
prefix sum of squares etc. This will have drastic effects on runtime and is
definitely something we would want in future.
/* TODO: The following function calculates the gain for each split each
time from scratch. This can be greatly improved using advanced
techniques like prefix sum and prefix sum of squares etc. This
will have drastic effects on runtime and is definitely something
we would want in future.
*/
// Calculate the gain for the left and right child.
const double leftGain = FitnessFunction::template Evaluate<UseWeights>(sortedLabels,
sortedWeights, 0, index);
const double rightGain = FitnessFunction::template Evaluate<UseWeights>(sortedLabels,
sortedWeights, index, labels.n_elem);
const double leftGain = FitnessFunction::template
Evaluate<UseWeights>(sortedResponses, sortedWeights, 0, index);
const double rightGain = FitnessFunction::template
Evaluate<UseWeights>(sortedResponses, sortedWeights, index,
responses.n_elem);
double gain;
if (UseWeights)
@@ -280,7 +282,7 @@ double BestBinaryNumericSplit<FitnessFunction>::SplitIfBetter(
{
// Calculate the gain at this split point.
gain = double(index) * leftGain +
double(sortedLabels.n_elem - index) * rightGain;
double(sortedResponses.n_elem - index) * rightGain;
}
// Corner case: is this the best possible split?
@@ -56,25 +56,25 @@ class DecisionTreeRegressor :
DecisionTreeRegressor();
/**
* Construct the decision tree on the given data and labels, where the data
* can be both numeric and categorical. Setting minimumLeafSize and
* Construct the decision tree on the given data and responses, where the
* data can be both numeric and categorical. Setting minimumLeafSize and
* minimumGainSplit too small may cause the tree to overfit, but setting them
* too large may cause it to underfit.
*
* Use std::move if data or labels are no longer needed to avoid copies.
* Use std::move if data or responses are no longer needed to avoid copies.
*
* @param data Dataset to train on.
* @param datasetInfo Type information for each dimension of the dataset.
* @param labels Labels for each training point.
* @param responses Responses for each training point.
* @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 dimensionSelector Instantiated dimension selection policy.
*/
template<typename MatType, typename LabelsType>
template<typename MatType, typename ResponsesType>
DecisionTreeRegressor(MatType data,
const data::DatasetInfo& datasetInfo,
LabelsType labels,
ResponsesType responses,
const size_t minimumLeafSize = 10,
const double minimumGainSplit = 1e-7,
const size_t maximumDepth = 0,
@@ -82,23 +82,23 @@ class DecisionTreeRegressor :
DimensionSelectionType());
/**
* Construct the decision tree on the given data and labels, assuming that
* Construct the decision tree on the given data and responses, assuming that
* the data is all of the numeric type. Setting minimumLeafSize and
* minimumGainSplit too small may cause the tree to overfit, but setting them
* too large may cause it to underfit.
*
* Use std::move if data or labels are no longer needed to avoid copies.
* Use std::move if data or responses are no longer needed to avoid copies.
*
* @param data Dataset to train on.
* @param labels Labels for each training point.
* @param responses Responses for each training point.
* @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 dimensionSelector Instantiated dimension selection policy.
*/
template<typename MatType, typename LabelsType>
template<typename MatType, typename ResponsesType>
DecisionTreeRegressor(MatType data,
LabelsType labels,
ResponsesType responses,
const size_t minimumLeafSize = 10,
const double minimumGainSplit = 1e-7,
const size_t maximumDepth = 0,
@@ -106,28 +106,28 @@ class DecisionTreeRegressor :
DimensionSelectionType());
/**
* Construct the decision tree on the given data and labels with weights,
* Construct the decision tree on the given data and responses with weights,
* where the data can be both numeric and categorical. Setting minimumLeafSize
* and minimumGainSplit too small may cause the tree to overfit, but setting
* them too large may cause it to underfit.
*
* Use std::move if data, labels or weights are no longer needed to avoid
* Use std::move if data, responses or weights are no longer needed to avoid
* copies.
*
* @param data Dataset to train on.
* @param datasetInfo Type information for each dimension of the dataset.
* @param labels Labels for each training point.
* @param responses Responses for each training point.
* @param weights The weight list of given label.
* @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 dimensionSelector Instantiated dimension selection policy.
*/
template<typename MatType, typename LabelsType, typename WeightsType>
template<typename MatType, typename ResponsesType, typename WeightsType>
DecisionTreeRegressor(
MatType data,
const data::DatasetInfo& datasetInfo,
LabelsType labels,
ResponsesType responses,
WeightsType weights,
const size_t minimumLeafSize = 10,
const double minimumGainSplit = 1e-7,
@@ -137,26 +137,26 @@ class DecisionTreeRegressor :
typename std::remove_reference<WeightsType>::type>::value>* = 0);
/**
* Construct the decision tree on the given data and labels with weights,
* Construct the decision tree on the given data and responses with weights,
* assuming that the data is all of the numeric type. Setting minimumLeafSize
* and minimumGainSplit too small may cause the tree to overfit, but setting
* them too large may cause it to underfit.
*
* Use std::move if data, labels or weights are no longer needed to avoid
* Use std::move if data, responses or weights are no longer needed to avoid
* copies.
*
* @param data Dataset to train on.
* @param labels Labels for each training point.
* @param responses Responses for each training point.
* @param weights The Weight list of given labels.
* @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 dimensionSelector Instantiated dimension selection policy.
*/
template<typename MatType, typename LabelsType, typename WeightsType>
template<typename MatType, typename ResponsesType, typename WeightsType>
DecisionTreeRegressor(
MatType data,
LabelsType labels,
ResponsesType responses,
WeightsType weights,
const size_t minimumLeafSize = 10,
const double minimumGainSplit = 1e-7,
@@ -167,27 +167,28 @@ class DecisionTreeRegressor :
/**
* Take ownership of another decision tree and train on the given data and
* labels with weights, where the data can be both numeric and categorical.
* Setting minimumLeafSize and minimumGainSplit too small may cause the
* tree to overfit, but setting them too large may cause it to underfit.
* responses with weights, where the data can be both numeric and
* categorical. Setting minimumLeafSize and minimumGainSplit too small may
* cause the tree to overfit, but setting them too large may cause it to
* underfit.
*
* Use std::move if data, labels or weights are no longer needed to avoid
* Use std::move if data, responses or weights are no longer needed to avoid
* copies.
*
* @param other Tree to take ownership of.
* @param data Dataset to train on.
* @param datasetInfo Type information for each dimension of the dataset.
* @param labels Labels for each training point.
* @param responses Responses for each training point.
* @param weights The weight list of given label.
* @param minimumLeafSize Minimum number of points in each leaf node.
* @param minimumGainSplit Minimum gain for the node to split.
*/
template<typename MatType, typename LabelsType, typename WeightsType>
template<typename MatType, typename ResponsesType, typename WeightsType>
DecisionTreeRegressor(
const DecisionTreeRegressor& other,
MatType data,
const data::DatasetInfo& datasetInfo,
LabelsType labels,
ResponsesType responses,
WeightsType weights,
const size_t minimumLeafSize = 10,
const double minimumGainSplit = 1e-7,
@@ -195,27 +196,27 @@ class DecisionTreeRegressor :
typename std::remove_reference<WeightsType>::type>::value>* = 0);
/**
* Take ownership of another decision tree and train on the given data and labels
* with weights, assuming that the data is all of the numeric type. Setting
* minimumLeafSize and minimumGainSplit too small may cause the tree to
* overfit, but setting them too large may cause it to underfit.
* Take ownership of another decision tree and train on the given data and
* responses with weights, assuming that the data is all of the numeric type.
* Setting minimumLeafSize and minimumGainSplit too small may cause the tree
* to overfit, but setting them too large may cause it to underfit.
*
* Use std::move if data, labels or weights are no longer needed to avoid
* Use std::move if data, responses or weights are no longer needed to avoid
* copies.
* @param other Tree to take ownership of.
* @param data Dataset to train on.
* @param labels Labels for each training point.
* @param responses Responses for each training point.
* @param weights The Weight list of given labels.
* @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 dimensionSelector Instantiated dimension selection policy.
*/
template<typename MatType, typename LabelsType, typename WeightsType>
template<typename MatType, typename ResponsesType, typename WeightsType>
DecisionTreeRegressor(
const DecisionTreeRegressor& other,
MatType data,
LabelsType labels,
ResponsesType responses,
WeightsType weights,
const size_t minimumLeafSize = 10,
const double minimumGainSplit = 1e-7,
@@ -261,26 +262,26 @@ class DecisionTreeRegressor :
/**
* Train the decision tree on the given data. This will overwrite the
* existing model. The data may have numeric and categorical types, specified
* existing model. The data may have numeric and categorical types, specified
* by the datasetInfo parameter. Setting minimumLeafSize and
* minimumGainSplit too small may cause the tree to overfit, but setting them
* too large may cause it to underfit.
*
* Use std::move if data or labels are no longer needed to avoid copies.
* Use std::move if data or responses are no longer needed to avoid copies.
*
* @param data Dataset to train on.
* @param datasetInfo Type information for each dimension.
* @param labels Labels for each training point.
* @param responses Responses for each training point.
* @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 dimensionSelector Instantiated dimension selection policy.
* @return The final entropy of decision tree.
*/
template<typename MatType, typename LabelsType>
template<typename MatType, typename ResponsesType>
double Train(MatType data,
const data::DatasetInfo& datasetInfo,
LabelsType labels,
ResponsesType responses,
const size_t minimumLeafSize = 10,
const double minimumGainSplit = 1e-7,
const size_t maximumDepth = 0,
@@ -293,19 +294,19 @@ class DecisionTreeRegressor :
* minimumGainSplit too small may cause the tree to overfit, but setting them
* too large may cause it to underfit.
*
* Use std::move if data or labels are no longer needed to avoid copies.
* Use std::move if data or responses are no longer needed to avoid copies.
*
* @param data Dataset to train on.
* @param labels Labels for each training point.
* @param responses Responses for each training point.
* @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 dimensionSelector Instantiated dimension selection policy.
* @return The final entropy of decision tree.
*/
template<typename MatType, typename LabelsType>
template<typename MatType, typename ResponsesType>
double Train(MatType data,
LabelsType labels,
ResponsesType responses,
const size_t minimumLeafSize = 10,
const double minimumGainSplit = 1e-7,
const size_t maximumDepth = 0,
@@ -319,12 +320,12 @@ class DecisionTreeRegressor :
* minimumGainSplit too small may cause the tree to overfit, but setting them
* too large may cause it to underfit.
*
* Use std::move if data, labels or weights are no longer needed to avoid
* Use std::move if data, responses or weights are no longer needed to avoid
* copies.
*
* @param data Dataset to train on.
* @param datasetInfo Type information for each dimension.
* @param labels Labels for each training point.
* @param responses Responses for each training point.
* @param weights Weights of all the labels
* @param minimumLeafSize Minimum number of points in each leaf node.
* @param minimumGainSplit Minimum gain for the node to split.
@@ -332,10 +333,10 @@ class DecisionTreeRegressor :
* @param dimensionSelector Instantiated dimension selection policy.
* @return The final entropy of decision tree.
*/
template<typename MatType, typename LabelsType, typename WeightsType>
template<typename MatType, typename ResponsesType, typename WeightsType>
double Train(MatType data,
const data::DatasetInfo& datasetInfo,
LabelsType labels,
ResponsesType responses,
WeightsType weights,
const size_t minimumLeafSize = 10,
const double minimumGainSplit = 1e-7,
@@ -351,11 +352,11 @@ class DecisionTreeRegressor :
* minimumLeafSize and minimumGainSplit too small may cause the tree to
* overfit, but setting them too large may cause it to underfit.
*
* Use std::move if data, labels or weights are no longer needed to avoid
* Use std::move if data, responses or weights are no longer needed to avoid
* copies.
*
* @param data Dataset to train on.
* @param labels Labels for each training point.
* @param responses Responses for each training point.
* @param weights Weights of all the labels
* @param minimumLeafSize Minimum number of points in each leaf node.
* @param minimumGainSplit Minimum gain for the node to split.
@@ -363,9 +364,9 @@ class DecisionTreeRegressor :
* @param dimensionSelector Instantiated dimension selection policy.
* @return The final entropy of decision tree.
*/
template<typename MatType, typename LabelsType, typename WeightsType>
template<typename MatType, typename ResponsesType, typename WeightsType>
double Train(MatType data,
LabelsType labels,
ResponsesType responses,
WeightsType weights,
const size_t minimumLeafSize = 10,
const double minimumGainSplit = 1e-7,
@@ -386,7 +387,7 @@ class DecisionTreeRegressor :
/**
* Make prediction for the given points, using the entire tree. The predicted
* labels for each point are stored in the given vector.
* responses for each point are stored in the given vector.
*
* @param data Set of points to predict.
* @param predictions This will be filled with predictions for each point.
@@ -436,7 +437,7 @@ class DecisionTreeRegressor :
size_t dimensionType;
/**
* This variable may hold different things. If the node has no children, then
* it is guaranteed to hold the prediction label for that node. If the node
* it is guaranteed to hold the prediction value for that node. If the node
* has children, then it may be used arbitrarily by the split type's
* CalculateDirection() and SplitIfBetter() function. In this case, it stores
* the point at which the split was made.
@@ -452,10 +453,10 @@ class DecisionTreeRegressor :
CategoricalAuxiliarySplitInfo;
/**
* Calculate the prediction label for the leaf nodes.
* Calculate the prediction value for the leaf nodes.
*/
template<bool UseWeights, typename LabelsType, typename WeightsType>
void CalculatePrediction(const LabelsType& labels,
template<bool UseWeights, typename ResponsesType, typename WeightsType>
void CalculatePrediction(const ResponsesType& responses,
const WeightsType& weights);
/**
@@ -468,19 +469,19 @@ class DecisionTreeRegressor :
* this node.
* @param count Number of points in this node.
* @param datasetInfo Type information for each dimension.
* @param labels Labels for each training point.
* @param responses Responses for each training point.
* @param numClasses Number of classes in the dataset.
* @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.
* @return The final entropy of decision tree.
*/
template<bool UseWeights, typename MatType, typename LabelsType>
template<bool UseWeights, typename MatType, typename ResponsesType>
double Train(MatType& data,
const size_t begin,
const size_t count,
const data::DatasetInfo& datasetInfo,
LabelsType& labels,
ResponsesType& responses,
const size_t numClasses,
arma::rowvec& weights,
const size_t minimumLeafSize,
@@ -497,18 +498,18 @@ class DecisionTreeRegressor :
* @param begin Index of the starting point in the dataset that belongs to
* this node.
* @param count Number of points in this node.
* @param labels Labels for each training point.
* @param responses Responses for each training point.
* @param numClasses Number of classes in the dataset.
* @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.
* @return The final entropy of decision tree.
*/
template<bool UseWeights, typename MatType, typename LabelsType>
template<bool UseWeights, typename MatType, typename ResponsesType>
double Train(MatType& data,
const size_t begin,
const size_t count,
LabelsType& labels,
ResponsesType& responses,
const size_t numClasses,
arma::rowvec& weights,
const size_t minimumLeafSize,
@@ -42,7 +42,7 @@ template<typename FitnessFunction,
template<typename> class CategoricalSplitType,
typename DimensionSelectionType,
bool NoRecursion>
template<typename MatType, typename LabelsType>
template<typename MatType, typename ResponsesType>
DecisionTreeRegressor<FitnessFunction,
NumericSplitType,
CategoricalSplitType,
@@ -50,25 +50,25 @@ DecisionTreeRegressor<FitnessFunction,
NoRecursion>::DecisionTreeRegressor(
MatType data,
const data::DatasetInfo& datasetInfo,
LabelsType labels,
ResponsesType responses,
const size_t minimumLeafSize,
const double minimumGainSplit,
const size_t maximumDepth,
DimensionSelectionType dimensionSelector)
{
using TrueMatType = typename std::decay<MatType>::type;
using TrueLabelsType = typename std::decay<LabelsType>::type;
using TrueResponsesType = typename std::decay<ResponsesType>::type;
// Copy or move data.
TrueMatType tmpData(std::move(data));
TrueLabelsType tmpLabels(std::move(labels));
TrueResponsesType tmpResponses(std::move(responses));
// Set the correct dimensionality for the dimension selector.
dimensionSelector.Dimensions() = tmpData.n_rows;
// Pass off work to the Train() method.
arma::rowvec weights; // Fake weights, not used.
Train<false>(tmpData, 0, tmpData.n_cols, datasetInfo, tmpLabels, 0,
Train<false>(tmpData, 0, tmpData.n_cols, datasetInfo, tmpResponses, 0,
weights, minimumLeafSize, minimumGainSplit, maximumDepth,
dimensionSelector);
}
@@ -79,32 +79,32 @@ template<typename FitnessFunction,
template<typename> class CategoricalSplitType,
typename DimensionSelectionType,
bool NoRecursion>
template<typename MatType, typename LabelsType>
template<typename MatType, typename ResponsesType>
DecisionTreeRegressor<FitnessFunction,
NumericSplitType,
CategoricalSplitType,
DimensionSelectionType,
NoRecursion>::DecisionTreeRegressor(
MatType data,
LabelsType labels,
ResponsesType responses,
const size_t minimumLeafSize,
const double minimumGainSplit,
const size_t maximumDepth,
DimensionSelectionType dimensionSelector)
{
using TrueMatType = typename std::decay<MatType>::type;
using TrueLabelsType = typename std::decay<LabelsType>::type;
using TrueResponsesType = typename std::decay<ResponsesType>::type;
// Copy or move data.
TrueMatType tmpData(std::move(data));
TrueLabelsType tmpLabels(std::move(labels));
TrueResponsesType tmpResponses(std::move(responses));
// Set the correct dimensionality for the dimension selector.
dimensionSelector.Dimensions() = tmpData.n_rows;
// Pass off work to the Train() method.
arma::rowvec weights; // Fake weights, not used.
Train<false>(tmpData, 0, tmpData.n_cols, tmpLabels, 0, weights,
Train<false>(tmpData, 0, tmpData.n_cols, tmpResponses, 0, weights,
minimumLeafSize, minimumGainSplit, maximumDepth, dimensionSelector);
}
@@ -114,7 +114,7 @@ template<typename FitnessFunction,
template<typename> class CategoricalSplitType,
typename DimensionSelectionType,
bool NoRecursion>
template<typename MatType, typename LabelsType, typename WeightsType>
template<typename MatType, typename ResponsesType, typename WeightsType>
DecisionTreeRegressor<FitnessFunction,
NumericSplitType,
CategoricalSplitType,
@@ -122,7 +122,7 @@ DecisionTreeRegressor<FitnessFunction,
NoRecursion>::DecisionTreeRegressor(
MatType data,
const data::DatasetInfo& datasetInfo,
LabelsType labels,
ResponsesType responses,
WeightsType weights,
const size_t minimumLeafSize,
const double minimumGainSplit,
@@ -132,19 +132,19 @@ DecisionTreeRegressor<FitnessFunction,
typename std::remove_reference<WeightsType>::type>::value>*)
{
using TrueMatType = typename std::decay<MatType>::type;
using TrueLabelsType = typename std::decay<LabelsType>::type;
using TrueResponsesType = typename std::decay<ResponsesType>::type;
using TrueWeightsType = typename std::decay<WeightsType>::type;
// Copy or move data.
TrueMatType tmpData(std::move(data));
TrueLabelsType tmpLabels(std::move(labels));
TrueResponsesType tmpResponses(std::move(responses));
TrueWeightsType tmpWeights(std::move(weights));
// Set the correct dimensionality for the dimension selector.
dimensionSelector.Dimensions() = tmpData.n_rows;
// Pass off work to the weighted Train() method.
Train<true>(tmpData, 0, tmpData.n_cols, datasetInfo, tmpLabels, 0,
Train<true>(tmpData, 0, tmpData.n_cols, datasetInfo, tmpResponses, 0,
tmpWeights, minimumLeafSize, minimumGainSplit, maximumDepth,
dimensionSelector);
}
@@ -155,14 +155,14 @@ template<typename FitnessFunction,
template<typename> class CategoricalSplitType,
typename DimensionSelectionType,
bool NoRecursion>
template<typename MatType, typename LabelsType, typename WeightsType>
template<typename MatType, typename ResponsesType, typename WeightsType>
DecisionTreeRegressor<FitnessFunction,
NumericSplitType,
CategoricalSplitType,
DimensionSelectionType,
NoRecursion>::DecisionTreeRegressor(
MatType data,
LabelsType labels,
ResponsesType responses,
WeightsType weights,
const size_t minimumLeafSize,
const double minimumGainSplit,
@@ -174,19 +174,19 @@ DecisionTreeRegressor<FitnessFunction,
WeightsType>::type>::value>*)
{
using TrueMatType = typename std::decay<MatType>::type;
using TrueLabelsType = typename std::decay<LabelsType>::type;
using TrueResponsesType = typename std::decay<ResponsesType>::type;
using TrueWeightsType = typename std::decay<WeightsType>::type;
// Copy or move data.
TrueMatType tmpData(std::move(data));
TrueLabelsType tmpLabels(std::move(labels));
TrueResponsesType tmpResponses(std::move(responses));
TrueWeightsType tmpWeights(std::move(weights));
// Set the correct dimensionality for the dimension selector.
dimensionSelector.Dimensions() = tmpData.n_rows;
// Pass off work to the weighted Train() method.
Train<true>(tmpData, 0, tmpData.n_cols, tmpLabels, 0, tmpWeights,
Train<true>(tmpData, 0, tmpData.n_cols, tmpResponses, 0, tmpWeights,
minimumLeafSize, minimumGainSplit, maximumDepth, dimensionSelector);
}
@@ -196,7 +196,7 @@ template<typename FitnessFunction,
template<typename> class CategoricalSplitType,
typename DimensionSelectionType,
bool NoRecursion>
template<typename MatType, typename LabelsType, typename WeightsType>
template<typename MatType, typename ResponsesType, typename WeightsType>
DecisionTreeRegressor<FitnessFunction,
NumericSplitType,
CategoricalSplitType,
@@ -205,7 +205,7 @@ DecisionTreeRegressor<FitnessFunction,
const DecisionTreeRegressor& other,
MatType data,
const data::DatasetInfo& datasetInfo,
LabelsType labels,
ResponsesType responses,
WeightsType weights,
const size_t minimumLeafSize,
const double minimumGainSplit,
@@ -215,16 +215,16 @@ DecisionTreeRegressor<FitnessFunction,
CategoricalAuxiliarySplitInfo(other)
{
using TrueMatType = typename std::decay<MatType>::type;
using TrueLabelsType = typename std::decay<LabelsType>::type;
using TrueResponsesType = typename std::decay<ResponsesType>::type;
using TrueWeightsType = typename std::decay<WeightsType>::type;
// Copy or move data.
TrueMatType tmpData(std::move(data));
TrueLabelsType tmpLabels(std::move(labels));
TrueResponsesType tmpResponses(std::move(responses));
TrueWeightsType tmpWeights(std::move(weights));
// Pass off work to the weighted Train() method.
Train<true>(tmpData, 0, tmpData.n_cols, datasetInfo, tmpLabels, 0,
Train<true>(tmpData, 0, tmpData.n_cols, datasetInfo, tmpResponses, 0,
tmpWeights, minimumLeafSize, minimumGainSplit);
}
@@ -234,7 +234,7 @@ template<typename FitnessFunction,
template<typename> class CategoricalSplitType,
typename DimensionSelectionType,
bool NoRecursion>
template<typename MatType, typename LabelsType, typename WeightsType>
template<typename MatType, typename ResponsesType, typename WeightsType>
DecisionTreeRegressor<FitnessFunction,
NumericSplitType,
CategoricalSplitType,
@@ -242,7 +242,7 @@ DecisionTreeRegressor<FitnessFunction,
NoRecursion>::DecisionTreeRegressor(
const DecisionTreeRegressor& other,
MatType data,
LabelsType labels,
ResponsesType responses,
WeightsType weights,
const size_t minimumLeafSize,
const double minimumGainSplit,
@@ -255,19 +255,19 @@ DecisionTreeRegressor<FitnessFunction,
CategoricalAuxiliarySplitInfo(other) // other info does need to copy
{
using TrueMatType = typename std::decay<MatType>::type;
using TrueLabelsType = typename std::decay<LabelsType>::type;
using TrueResponsesType = typename std::decay<ResponsesType>::type;
using TrueWeightsType = typename std::decay<WeightsType>::type;
// Copy or move data.
TrueMatType tmpData(std::move(data));
TrueLabelsType tmpLabels(std::move(labels));
TrueResponsesType tmpResponses(std::move(responses));
TrueWeightsType tmpWeights(std::move(weights));
// Set the correct dimensionality for the dimension selector.
dimensionSelector.Dimensions() = tmpData.n_rows;
// Pass off work to the weighted Train() method.
Train<true>(tmpData, 0, tmpData.n_cols, tmpLabels, 0, tmpWeights,
Train<true>(tmpData, 0, tmpData.n_cols, tmpResponses, 0, tmpWeights,
minimumLeafSize, minimumGainSplit, maximumDepth, dimensionSelector);
}
@@ -421,7 +421,7 @@ template<typename FitnessFunction,
template<typename> class CategoricalSplitType,
typename DimensionSelectionType,
bool NoRecursion>
template<typename MatType, typename LabelsType>
template<typename MatType, typename ResponsesType>
double DecisionTreeRegressor<FitnessFunction,
NumericSplitType,
CategoricalSplitType,
@@ -429,28 +429,28 @@ double DecisionTreeRegressor<FitnessFunction,
NoRecursion>::Train(
MatType data,
const data::DatasetInfo& datasetInfo,
LabelsType labels,
ResponsesType responses,
const size_t minimumLeafSize,
const double minimumGainSplit,
const size_t maximumDepth,
DimensionSelectionType dimensionSelector)
{
// Sanity check on data.
util::CheckSameSizes(data, labels, "DecisionTreeRegressor::Train()");
util::CheckSameSizes(data, responses, "DecisionTreeRegressor::Train()");
using TrueMatType = typename std::decay<MatType>::type;
using TrueLabelsType = typename std::decay<LabelsType>::type;
using TrueResponsesType = typename std::decay<ResponsesType>::type;
// Copy or move data.
TrueMatType tmpData(std::move(data));
TrueLabelsType tmpLabels(std::move(labels));
TrueResponsesType tmpResponses(std::move(responses));
// Set the correct dimensionality for the dimension selector.
dimensionSelector.Dimensions() = tmpData.n_rows;
// Pass off work to the Train() method.
arma::rowvec weights; // Fake weights, not used.
return Train<false>(tmpData, 0, tmpData.n_cols, datasetInfo, tmpLabels,
return Train<false>(tmpData, 0, tmpData.n_cols, datasetInfo, tmpResponses,
0, weights, minimumLeafSize, minimumGainSplit, maximumDepth,
dimensionSelector);
}
@@ -461,35 +461,35 @@ template<typename FitnessFunction,
template<typename> class CategoricalSplitType,
typename DimensionSelectionType,
bool NoRecursion>
template<typename MatType, typename LabelsType>
template<typename MatType, typename ResponsesType>
double DecisionTreeRegressor<FitnessFunction,
NumericSplitType,
CategoricalSplitType,
DimensionSelectionType,
NoRecursion>::Train(
MatType data,
LabelsType labels,
ResponsesType responses,
const size_t minimumLeafSize,
const double minimumGainSplit,
const size_t maximumDepth,
DimensionSelectionType dimensionSelector)
{
// Sanity check on data.
util::CheckSameSizes(data, labels, "DecisionTreeRegressor::Train()");
util::CheckSameSizes(data, responses, "DecisionTreeRegressor::Train()");
using TrueMatType = typename std::decay<MatType>::type;
using TrueLabelsType = typename std::decay<LabelsType>::type;
using TrueResponsesType = typename std::decay<ResponsesType>::type;
// Copy or move data.
TrueMatType tmpData(std::move(data));
TrueLabelsType tmpLabels(std::move(labels));
TrueResponsesType tmpResponses(std::move(responses));
// Set the correct dimensionality for the dimension selector.
dimensionSelector.Dimensions() = tmpData.n_rows;
// Pass off work to the Train() method.
arma::rowvec weights; // Fake weights, not used.
return Train<false>(tmpData, 0, tmpData.n_cols, tmpLabels, 0,
return Train<false>(tmpData, 0, tmpData.n_cols, responses, 0,
weights, minimumLeafSize, minimumGainSplit, maximumDepth,
dimensionSelector);
}
@@ -500,7 +500,7 @@ template<typename FitnessFunction,
template<typename> class CategoricalSplitType,
typename DimensionSelectionType,
bool NoRecursion>
template<typename MatType, typename LabelsType, typename WeightsType>
template<typename MatType, typename ResponsesType, typename WeightsType>
double DecisionTreeRegressor<FitnessFunction,
NumericSplitType,
CategoricalSplitType,
@@ -508,7 +508,7 @@ double DecisionTreeRegressor<FitnessFunction,
NoRecursion>::Train(
MatType data,
const data::DatasetInfo& datasetInfo,
LabelsType labels,
ResponsesType responses,
WeightsType weights,
const size_t minimumLeafSize,
const double minimumGainSplit,
@@ -520,22 +520,22 @@ double DecisionTreeRegressor<FitnessFunction,
WeightsType>::type>::value>*)
{
// Sanity check on data.
util::CheckSameSizes(data, labels, "DecisionTreeRegressor::Train()");
util::CheckSameSizes(data, responses, "DecisionTreeRegressor::Train()");
using TrueMatType = typename std::decay<MatType>::type;
using TrueLabelsType = typename std::decay<LabelsType>::type;
using TrueResponsesType = typename std::decay<ResponsesType>::type;
using TrueWeightsType = typename std::decay<WeightsType>::type;
// Copy or move data.
TrueMatType tmpData(std::move(data));
TrueLabelsType tmpLabels(std::move(labels));
TrueResponsesType tmpResponses(std::move(responses));
TrueWeightsType tmpWeights(std::move(weights));
// Set the correct dimensionality for the dimension selector.
dimensionSelector.Dimensions() = tmpData.n_rows;
// Pass off work to the Train() method.
return Train<true>(tmpData, 0, tmpData.n_cols, datasetInfo, tmpLabels,
return Train<true>(tmpData, 0, tmpData.n_cols, datasetInfo, tmpResponses,
0, tmpWeights, minimumLeafSize, minimumGainSplit, maximumDepth,
dimensionSelector);
}
@@ -546,14 +546,14 @@ template<typename FitnessFunction,
template<typename> class CategoricalSplitType,
typename DimensionSelectionType,
bool NoRecursion>
template<typename MatType, typename LabelsType, typename WeightsType>
template<typename MatType, typename ResponsesType, typename WeightsType>
double DecisionTreeRegressor<FitnessFunction,
NumericSplitType,
CategoricalSplitType,
DimensionSelectionType,
NoRecursion>::Train(
MatType data,
LabelsType labels,
ResponsesType responses,
WeightsType weights,
const size_t minimumLeafSize,
const double minimumGainSplit,
@@ -565,22 +565,22 @@ double DecisionTreeRegressor<FitnessFunction,
WeightsType>::type>::value>*)
{
// Sanity check on data.
util::CheckSameSizes(data, labels, "DecisionTreeRegressor::Train()");
util::CheckSameSizes(data, responses, "DecisionTreeRegressor::Train()");
using TrueMatType = typename std::decay<MatType>::type;
using TrueLabelsType = typename std::decay<LabelsType>::type;
using TrueResponsesType = typename std::decay<ResponsesType>::type;
using TrueWeightsType = typename std::decay<WeightsType>::type;
// Copy or move data.
TrueMatType tmpData(std::move(data));
TrueLabelsType tmpLabels(std::move(labels));
TrueResponsesType tmpResponses(std::move(responses));
TrueWeightsType tmpWeights(std::move(weights));
// Set the correct dimensionality for the dimension selector.
dimensionSelector.Dimensions() = tmpData.n_rows;
// Pass off work to the Train() method.
return Train<true>(tmpData, 0, tmpData.n_cols, tmpLabels, 0,
return Train<true>(tmpData, 0, tmpData.n_cols, tmpResponses, 0,
tmpWeights, minimumLeafSize, minimumGainSplit, maximumDepth,
dimensionSelector);
}
@@ -591,7 +591,7 @@ template<typename FitnessFunction,
template<typename> class CategoricalSplitType,
typename DimensionSelectionType,
bool NoRecursion>
template<bool UseWeights, typename MatType, typename LabelsType>
template<bool UseWeights, typename MatType, typename ResponsesType>
double DecisionTreeRegressor<FitnessFunction,
NumericSplitType,
CategoricalSplitType,
@@ -601,7 +601,7 @@ double DecisionTreeRegressor<FitnessFunction,
const size_t begin,
const size_t count,
const data::DatasetInfo& datasetInfo,
LabelsType& labels,
ResponsesType& responses,
const size_t numClasses,
arma::rowvec& weights,
const size_t minimumLeafSize,
@@ -618,7 +618,7 @@ double DecisionTreeRegressor<FitnessFunction,
// We'll cache the best numeric and categorical split auxiliary information in
// numericAux and categoricalAux (and clear them later if we make no split),
double bestGain = FitnessFunction::template Evaluate<UseWeights>(
labels.subvec(begin, begin + count - 1),
responses.subvec(begin, begin + count - 1),
numClasses,
UseWeights ? weights.subvec(begin, begin + count - 1) : weights);
size_t bestDim = datasetInfo.Dimensionality(); // This means "no split".
@@ -635,7 +635,7 @@ double DecisionTreeRegressor<FitnessFunction,
dimGain = CategoricalSplit::template SplitIfBetter<UseWeights>(bestGain,
data.cols(begin, begin + count - 1).row(i),
datasetInfo.NumMappings(i),
labels.subvec(begin, begin + count - 1),
responses.subvec(begin, begin + count - 1),
numClasses,
UseWeights ? weights.subvec(begin, begin + count - 1) : weights,
minimumLeafSize,
@@ -647,7 +647,7 @@ double DecisionTreeRegressor<FitnessFunction,
{
dimGain = NumericSplit::template SplitIfBetter<UseWeights>(bestGain,
data.cols(begin, begin + count - 1).row(i),
labels.subvec(begin, begin + count - 1),
responses.subvec(begin, begin + count - 1),
UseWeights ? weights.subvec(begin, begin + count - 1) : weights,
minimumLeafSize,
minimumGainSplit,
@@ -722,7 +722,7 @@ double DecisionTreeRegressor<FitnessFunction,
{
childAssignments.swap_cols(currentCol - begin, j - begin);
data.swap_cols(currentCol, j);
labels.swap_cols(currentCol, j);
responses.swap_cols(currentCol, j);
if (UseWeights)
weights.swap_cols(currentCol, j);
++currentCol;
@@ -734,7 +734,7 @@ double DecisionTreeRegressor<FitnessFunction,
if (NoRecursion)
{
child->Train<UseWeights>(data, currentChildBegin,
currentCol - currentChildBegin, datasetInfo, labels, numClasses,
currentCol - currentChildBegin, datasetInfo, responses, numClasses,
weights, currentCol - currentChildBegin, minimumGainSplit,
maximumDepth - 1, dimensionSelector);
}
@@ -742,7 +742,7 @@ double DecisionTreeRegressor<FitnessFunction,
{
// During recursion entropy of child node may change.
double childGain = child->Train<UseWeights>(data, currentChildBegin,
currentCol - currentChildBegin, datasetInfo, labels, numClasses,
currentCol - currentChildBegin, datasetInfo, responses, numClasses,
weights, minimumLeafSize, minimumGainSplit, maximumDepth - 1,
dimensionSelector);
bestGain += double(childCounts[i]) / double(count) * (-childGain);
@@ -758,7 +758,7 @@ double DecisionTreeRegressor<FitnessFunction,
// Calculate prediction label because we are a leaf.
CalculatePrediction<UseWeights>(
labels.subvec(begin, begin + count - 1),
responses.subvec(begin, begin + count - 1),
UseWeights ? weights.subvec(begin, begin + count - 1) : weights);
std::cout << "Number of points in leaf: " << count <<
" Prediction: " << splitPointOrPrediction << std::endl;
@@ -773,7 +773,7 @@ template<typename FitnessFunction,
template<typename> class CategoricalSplitType,
typename DimensionSelectionType,
bool NoRecursion>
template<bool UseWeights, typename MatType, typename LabelsType>
template<bool UseWeights, typename MatType, typename ResponsesType>
double DecisionTreeRegressor<FitnessFunction,
NumericSplitType,
CategoricalSplitType,
@@ -782,7 +782,7 @@ double DecisionTreeRegressor<FitnessFunction,
MatType& data,
const size_t begin,
const size_t count,
LabelsType& labels,
ResponsesType& responses,
const size_t numClasses,
arma::rowvec& weights,
const size_t minimumLeafSize,
@@ -804,7 +804,7 @@ double DecisionTreeRegressor<FitnessFunction,
// information. Later we'll overwrite classProbabilities to the empirical
// class probabilities if we do not split.
double bestGain = FitnessFunction::template Evaluate<UseWeights>(
labels.subvec(begin, begin + count - 1),
responses.subvec(begin, begin + count - 1),
numClasses,
UseWeights ? weights.subvec(begin, begin + count - 1) : weights);
size_t bestDim = data.n_rows; // This means "no split".
@@ -817,7 +817,7 @@ double DecisionTreeRegressor<FitnessFunction,
const double dimGain = NumericSplitType<FitnessFunction>::template
SplitIfBetter<UseWeights>(bestGain,
data.cols(begin, begin + count - 1).row(i),
labels.cols(begin, begin + count - 1),
responses.cols(begin, begin + count - 1),
UseWeights ?
weights.cols(begin, begin + count - 1) :
weights,
@@ -882,7 +882,7 @@ double DecisionTreeRegressor<FitnessFunction,
{
childAssignments.swap_cols(currentCol - begin, j - begin);
data.swap_cols(currentCol, j);
labels.swap_cols(currentCol, j);
responses.swap_cols(currentCol, j);
if (UseWeights)
weights.swap_cols(currentCol, j);
++currentCol;
@@ -894,7 +894,7 @@ double DecisionTreeRegressor<FitnessFunction,
if (NoRecursion)
{
child->Train<UseWeights>(data, currentChildBegin,
currentCol - currentChildBegin, labels, numClasses, weights,
currentCol - currentChildBegin, responses, numClasses, weights,
currentCol - currentChildBegin, minimumGainSplit, maximumDepth - 1,
dimensionSelector);
}
@@ -902,7 +902,7 @@ double DecisionTreeRegressor<FitnessFunction,
{
// During recursion entropy of child node may change.
double childGain = child->Train<UseWeights>(data, currentChildBegin,
currentCol - currentChildBegin, labels, numClasses, weights,
currentCol - currentChildBegin, responses, numClasses, weights,
minimumLeafSize, minimumGainSplit, maximumDepth - 1,
dimensionSelector);
bestGain += double(childCounts[i]) / double(count) * (-childGain);
@@ -917,7 +917,7 @@ double DecisionTreeRegressor<FitnessFunction,
// Calculate prediction label because we are a leaf.
CalculatePrediction<UseWeights>(
labels.subvec(begin, begin + count - 1),
responses.subvec(begin, begin + count - 1),
UseWeights ? weights.subvec(begin, begin + count - 1) : weights);
std::cout << "Number of points in leaf: " << count <<
" Prediction: " << splitPointOrPrediction << std::endl;
@@ -980,25 +980,27 @@ template<typename FitnessFunction,
template<typename> class CategoricalSplitType,
typename DimensionSelectionType,
bool NoRecursion>
template<bool UseWeights, typename LabelsType, typename WeightsType>
template<bool UseWeights, typename ResponsesType, typename WeightsType>
void DecisionTreeRegressor<FitnessFunction,
NumericSplitType,
CategoricalSplitType,
DimensionSelectionType,
NoRecursion
>::CalculatePrediction(const LabelsType& labels, const WeightsType& weights)
>::CalculatePrediction(const ResponsesType& responses,
const WeightsType& weights)
{
if (UseWeights)
{
double accWeights, weightedSum;
WeightedSum(labels, weights, 0, labels.n_elem, accWeights, weightedSum);
WeightedSum(responses, weights, 0, responses.n_elem, accWeights,
weightedSum);
splitPointOrPrediction = weightedSum / accWeights;
}
else
{
double sum;
Sum(labels, 0, labels.n_elem, sum);
splitPointOrPrediction = sum / labels.n_elem;
Sum(responses, 0, responses.n_elem, sum);
splitPointOrPrediction = sum / responses.n_elem;
}
}
+13 -13
View File
@@ -34,16 +34,16 @@ class MADGain
* Evaluate the mean absolute deviation gain from begin to end index. Note
* that gain can be slightly greater than 0 due to floating-point
* representation issues. Thus if you are checking for perfect fit, be sure
* to use 'gain >= 0.0'. Not 'gain == 0.0'. The labels should always be of
* to use 'gain >= 0.0'. Not 'gain == 0.0'. The values should always be of
* type arma::Row<double> or arma::rowvec.
*
* @param labels Set of labels to evaluate MAD gain on.
* @param weights Weight of labels.
* @param values Set of values to evaluate MAD gain on.
* @param weights Weights associated to each value.
* @param begin Start index.
* @param end End index.
*/
template<bool UseWeights, typename WeightVecType>
static double Evaluate(const arma::rowvec& labels,
static double Evaluate(const arma::rowvec& values,
const WeightVecType& weights,
const size_t begin,
const size_t end)
@@ -55,7 +55,7 @@ class MADGain
double accWeights = 0.0;
double weightedMean = 0.0;
WeightedSum(labels, weights, begin, end, accWeights, weightedMean);
WeightedSum(values, weights, begin, end, accWeights, weightedMean);
// Catch edge case: if there are no weights, the impurity is zero.
if (accWeights == 0.0)
@@ -65,18 +65,18 @@ class MADGain
for (size_t i = begin; i < end; ++i)
{
mad += weights[i] * (std::abs(labels[i] - weightedMean));
mad += weights[i] * (std::abs(values[i] - weightedMean));
}
mad /= accWeights;
}
else
{
double mean = 0.0;
Sum(labels, begin, end, mean);
Sum(values, begin, end, mean);
mean /= (double) (end - begin);
for (size_t i = begin; i < end; ++i)
mad += std::abs(labels[i] - mean);
mad += std::abs(values[i] - mean);
mad /= (double) (end - begin);
}
@@ -87,19 +87,19 @@ class MADGain
/**
* Evaluate the MAD gain on the complete vector.
*
* @param labels Set of labels to evaluate MAD gain on.
* @param weights Weights associated to each label.
* @param values Set of values to evaluate MAD gain on.
* @param weights Weights associated to each value.
*/
template<bool UseWeights, typename WeightVecType>
static double Evaluate(const arma::rowvec& labels,
static double Evaluate(const arma::rowvec& values,
const size_t /* numClasses */,
const WeightVecType& weights)
{
// Corner case: if there are no elements, the impurity is zero.
if (labels.n_elem == 0)
if (values.n_elem == 0)
return 0.0;
return Evaluate<UseWeights>(labels, weights, 0, labels.n_elem);
return Evaluate<UseWeights>(values, weights, 0, values.n_elem);
}
};
+15 -15
View File
@@ -29,19 +29,19 @@ class MSEGain
{
public:
/**
* Evaluate the mean squared error gain of labls from begin to end index.
* Evaluate the mean squared error gain of values from begin to end index.
* Note that gain can be slightly greater than 0 due to floating-point
* representation issues. Thus if you are checking for perfect fit, be
* sure to use 'gain >= 0.0' and not 'gain == 0.0'. The labels vector should
* always be of type arma::Row<double> or arma::rowvec.
* sure to use 'gain >= 0.0' and not 'gain == 0.0'. The values vector
* should always be of type arma::Row<double> or arma::rowvec.
*
* @param labels Set of labels to evaluate MAD gain on.
* @param weights Weight of labels.
* @param values Set of values to evaluate MAD gain on.
* @param weights Weights associated to each value.
* @param begin Start index.
* @param end End index.
*/
template<bool UseWeights, typename WeightVecType>
static double Evaluate(const arma::rowvec& labels,
static double Evaluate(const arma::rowvec& values,
const WeightVecType& weights,
const size_t begin,
const size_t end)
@@ -52,7 +52,7 @@ class MSEGain
{
double accWeights = 0.0;
double weightedMean = 0.0;
WeightedSum(labels, weights, begin, end, accWeights, weightedMean);
WeightedSum(values, weights, begin, end, accWeights, weightedMean);
// Catch edge case: if there are no weights, the impurity is zero.
if (accWeights == 0.0)
@@ -61,18 +61,18 @@ class MSEGain
weightedMean /= accWeights;
for (size_t i = begin; i < end; ++i)
mse += weights[i] * std::pow(labels[i] - weightedMean, 2);
mse += weights[i] * std::pow(values[i] - weightedMean, 2);
mse /= accWeights;
}
else
{
double mean = 0.0;
Sum(labels, begin, end, mean);
Sum(values, begin, end, mean);
mean /= (double) (end - begin);
for (size_t i = begin; i < end; ++i)
mse += std::pow(labels[i] - mean, 2);
mse += std::pow(values[i] - mean, 2);
mse /= (double) (end - begin);
}
@@ -83,19 +83,19 @@ class MSEGain
/**
* Evaluate the MSE gain on the complete vector.
*
* @param labels Set of labels to evaluate MAD gain on.
* @param weights Weights associated to each label.
* @param values Set of values to evaluate MSE gain on.
* @param weights Weights associated to each value.
*/
template<bool UseWeights, typename WeightVecType>
static double Evaluate(const arma::rowvec& labels,
static double Evaluate(const arma::rowvec& values,
const size_t /* numClasses */,
const WeightVecType& weights)
{
// Corner case: if there are no elements, the impurity is zero.
if (labels.n_elem == 0)
if (values.n_elem == 0)
return 0.0;
return Evaluate<UseWeights>(labels, weights, 0, labels.n_elem);
return Evaluate<UseWeights>(values, weights, 0, values.n_elem);
}
};
+171 -163
View File
@@ -28,56 +28,56 @@ using namespace mlpack::distribution;
/**
* Creates dataset with 5 groups with all the points in same group have exactly
* same label.
* same responses.
*/
void CreateMultiSplitData(arma::mat& d, arma::rowvec& l, const size_t count,
void CreateMultiSplitData(arma::mat& d, arma::rowvec& r, const size_t count,
arma::rowvec& values)
{
d = arma::mat(10, count, arma::fill::randu);
l = arma::rowvec(count);
r = arma::rowvec(count);
// Group 1.
for (size_t i = 0; i < count / 5; i++)
{
d(3, i) = i;
l(i) = values[0];
r(i) = values[0];
}
// Group 2.
for (size_t i = count / 5; i < (count / 5) * 2; i++)
{
d(3, i) = i;
l(i) = values[1];
r(i) = values[1];
}
// Group 3.
for (size_t i = (count / 5) * 2; i < (count / 5) * 3; i++)
{
d(3, i) = i;
l(i) = values[2];
r(i) = values[2];
}
// Group 4.
for (size_t i = (count / 5) * 3; i < (count / 5) * 4; i++)
{
d(3, i) = i;
l(i) = values[3];
r(i) = values[3];
}
// Group 5.
for (size_t i = (count / 5) * 4; i < count; i++)
{
d(3, i) = i;
l(i) = values[4];
r(i) = values[4];
}
}
/**
* Make sure the MSE gain is zero when the labels are perfect.
* Make sure the MSE gain is zero when the responses are perfect.
*/
TEST_CASE("MSEGainPerfectTest", "[DecisionTreeRegressorTest]")
{
arma::rowvec weights(10, arma::fill::ones);
arma::rowvec labels;
labels.ones(10);
arma::rowvec responses;
responses.ones(10);
REQUIRE(MSEGain::Evaluate<false>(labels, 0, weights) ==
REQUIRE(MSEGain::Evaluate<false>(responses, 0, weights) ==
Approx(0.0).margin(1e-5));
}
@@ -87,11 +87,11 @@ TEST_CASE("MSEGainPerfectTest", "[DecisionTreeRegressorTest]")
TEST_CASE("MSEGainEmptyTest", "[DecisionTreeRegressorTest]")
{
arma::rowvec weights = arma::ones<arma::rowvec>(10);
arma::rowvec labels;
REQUIRE(MSEGain::Evaluate<false>(labels, 0, weights) ==
arma::rowvec responses;
REQUIRE(MSEGain::Evaluate<false>(responses, 0, weights) ==
Approx(0.0).margin(1e-5));
REQUIRE(MSEGain::Evaluate<true>(labels, 0, weights) ==
REQUIRE(MSEGain::Evaluate<true>(responses, 0, weights) ==
Approx(0.0).margin(1e-5));
}
@@ -101,48 +101,49 @@ TEST_CASE("MSEGainEmptyTest", "[DecisionTreeRegressorTest]")
*/
TEST_CASE("MSEGainHandCalculation", "[DecisionTreeRegressorTest]")
{
arma::rowvec labels = {4., 2., 3., 4., 13., 6., 20., 8., 9., 10.};
arma::rowvec responses = {4., 2., 3., 4., 13., 6., 20., 8., 9., 10.};
arma::rowvec weights = {0.3, 0.3, 0.3, 0.3, 0.3, 0.7, 0.7, 0.7, 0.7, 0.7};
// Hand calculated gain values.
const double gain = -27.08999;
const double weightedGain = -27.53960;
REQUIRE(MSEGain::Evaluate<false>(labels, 0, weights) ==
REQUIRE(MSEGain::Evaluate<false>(responses, 0, weights) ==
Approx(gain).margin(1e-5));
REQUIRE(MSEGain::Evaluate<true>(labels, 0, weights) ==
REQUIRE(MSEGain::Evaluate<true>(responses, 0, weights) ==
Approx(weightedGain).margin(1e-5));
}
/**
* Make sure the MAD gain is zero when the labels are perfect.
* Make sure the MAD gain is zero when the responses are perfect.
*/
TEST_CASE("MADGainPerfectTest", "[DecisionTreeRegressorTest]")
{
arma::rowvec weights(10, arma::fill::ones);
arma::rowvec labels;
labels.ones(10);
arma::rowvec responses;
responses.ones(10);
REQUIRE(MADGain::Evaluate<false>(labels, 0, weights) ==
REQUIRE(MADGain::Evaluate<false>(responses, 0, weights) ==
Approx(0.0).margin(1e-5));
}
/**
* Make sure that when mean of labels is zero, MAD_gain = mean of
* Make sure that when mean of responses is zero, MAD_gain = mean of
* absolute values of the distribution.
*/
TEST_CASE("MADGainNormalTest", "[DecisionTreeRegressorTest")
{
arma::rowvec weights(10, arma::fill::ones);
arma::rowvec labels = { 1, 2, 3, 4, 5, -1, -2, -3, -4, -5 }; // Mean = 0.
arma::rowvec responses = { 1, 2, 3, 4, 5, -1, -2, -3, -4, -5 }; // Mean = 0.
// Theoretical gain.
double theoreticalGain = 0.0;
for (size_t i = 0; i < labels.n_elem; ++i)
theoreticalGain -= std::abs(labels[i]);
theoreticalGain /= (double) labels.n_elem;
for (size_t i = 0; i < responses.n_elem; ++i)
theoreticalGain -= std::abs(responses[i]);
theoreticalGain /= (double) responses.n_elem;
// Calculated gain.
const double calculatedGain = MADGain::Evaluate<false>(labels, 0, weights);
const double calculatedGain =
MADGain::Evaluate<false>(responses, 0, weights);
REQUIRE(calculatedGain == Approx(theoreticalGain).margin(1e-5));
}
@@ -153,11 +154,11 @@ TEST_CASE("MADGainNormalTest", "[DecisionTreeRegressorTest")
TEST_CASE("MADGainEmptyTest", "[DecisionTreeRegressorTest]")
{
arma::rowvec weights = arma::ones<arma::rowvec>(10);
arma::rowvec labels;
REQUIRE(MADGain::Evaluate<false>(labels, 0, weights) ==
arma::rowvec responses;
REQUIRE(MADGain::Evaluate<false>(responses, 0, weights) ==
Approx(0.0).margin(1e-5));
REQUIRE(MADGain::Evaluate<true>(labels, 0, weights) ==
REQUIRE(MADGain::Evaluate<true>(responses, 0, weights) ==
Approx(0.0).margin(1e-5));
}
@@ -167,15 +168,15 @@ TEST_CASE("MADGainEmptyTest", "[DecisionTreeRegressorTest]")
*/
TEST_CASE("MADGainHandCalculation", "[DecisionTreeRegressorTest]")
{
arma::rowvec labels = {4., 2., 3., 4., 13., 6., 20., 8., 9., 10.};
arma::rowvec responses = {4., 2., 3., 4., 13., 6., 20., 8., 9., 10.};
arma::rowvec weights = {0.3, 0.3, 0.3, 0.3, 0.3, 0.7, 0.7, 0.7, 0.7, 0.7};
// Hand calculated gain values.
const double gain = -4.1;
const double weightedGain = -3.8592;
REQUIRE(MADGain::Evaluate<false>(labels, 0, weights) ==
REQUIRE(MADGain::Evaluate<false>(responses, 0, weights) ==
Approx(gain).margin(1e-5));
REQUIRE(MADGain::Evaluate<true>(labels, 0, weights) ==
REQUIRE(MADGain::Evaluate<true>(responses, 0, weights) ==
Approx(weightedGain).margin(1e-5));
}
@@ -186,28 +187,28 @@ TEST_CASE("MADGainHandCalculation", "[DecisionTreeRegressorTest]")
TEST_CASE("AllCategoricalSplitSimpleSplitTest_", "[DecisionTreeRegressorTest]")
{
arma::vec predictor(100);
arma::rowvec labels(100);
arma::rowvec weights(labels.n_elem);
arma::rowvec responses(100);
arma::rowvec weights(responses.n_elem);
weights.ones();
for (size_t i = 0; i < 100; i+=2)
{
predictor[i] = 0;
labels[i] = 5.0;
responses[i] = 5.0;
predictor[i + 1] = 1;
labels[i + 1] = 100;
responses[i + 1] = 100;
}
double splitInfo;
AllCategoricalSplit<MSEGain>::AuxiliarySplitInfo aux;
// Call the method to do the splitting.
const double bestGain = MSEGain::Evaluate<false>(labels, 0, weights);
const double bestGain = MSEGain::Evaluate<false>(responses, 0, weights);
const double gain = AllCategoricalSplit<MSEGain>::SplitIfBetter<false>(
bestGain, predictor, 2, labels, 0, weights, 3, 1e-7, splitInfo, aux);
bestGain, predictor, 2, responses, 0, weights, 3, 1e-7, splitInfo, aux);
const double weightedGain =
AllCategoricalSplit<MSEGain>::SplitIfBetter<true>(bestGain, predictor, 2,
labels, 0, weights, 3, 1e-7, splitInfo, aux);
responses, 0, weights, 3, 1e-7, splitInfo, aux);
// Make sure that a split was made.
REQUIRE(gain > bestGain);
@@ -225,17 +226,17 @@ TEST_CASE("AllCategoricalSplitSimpleSplitTest_", "[DecisionTreeRegressorTest]")
TEST_CASE("AllCategoricalSplitMinSamplesTest_", "[DecisionTreeRegressorTest]")
{
arma::rowvec predictors = {0, 0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 3};
arma::rowvec labels = {0, 0, 0, 2, 2, 2, 1, 1, 1, 2, 2, 2};
arma::rowvec weights(labels.n_elem);
arma::rowvec responses = {0, 0, 0, 2, 2, 2, 1, 1, 1, 2, 2, 2};
arma::rowvec weights(responses.n_elem);
weights.ones();
double splitInfo;
AllCategoricalSplit<MSEGain>::AuxiliarySplitInfo aux;
// Call the method to do the splitting.
const double bestGain = MSEGain::Evaluate<false>(labels, 0, weights);
const double bestGain = MSEGain::Evaluate<false>(responses, 0, weights);
const double gain = AllCategoricalSplit<MSEGain>::SplitIfBetter<false>(
bestGain, predictors, 4, labels, 0, weights, 4, 1e-7, splitInfo, aux);
bestGain, predictors, 4, responses, 0, weights, 4, 1e-7, splitInfo, aux);
// Make sure it's not split.
REQUIRE(gain == DBL_MAX);
@@ -247,30 +248,30 @@ TEST_CASE("AllCategoricalSplitMinSamplesTest_", "[DecisionTreeRegressorTest]")
TEST_CASE("AllCategoricalSplitNoGainTest_", "[DecisionTreeRegressorTest]")
{
arma::rowvec predictors(300);
arma::rowvec labels(300);
arma::rowvec responses(300);
arma::rowvec weights = arma::ones<arma::rowvec>(300);
for (size_t i = 0; i < 300; i += 3)
{
predictors[i] = int(i / 3) % 10;
labels[i] = -0.5;
responses[i] = -0.5;
predictors[i + 1] = int(i / 3) % 10;
labels[i + 1] = 0;
responses[i + 1] = 0;
predictors[i + 2] = int(i / 3) % 10;
labels[i + 2] = 0.5;
responses[i + 2] = 0.5;
}
double splitInfo;
AllCategoricalSplit<MSEGain>::AuxiliarySplitInfo aux;
// Call the method to do the splitting.
const double bestGain = MSEGain::Evaluate<false>(labels, 0, weights);
const double bestGain = MSEGain::Evaluate<false>(responses, 0, weights);
const double gain = AllCategoricalSplit<MSEGain>::SplitIfBetter<false>(
bestGain, predictors, 10, labels, 0, weights, 10, 1e-7,
bestGain, predictors, 10, responses, 0, weights, 10, 1e-7,
splitInfo, aux);
const double weightedGain =
AllCategoricalSplit<MSEGain>::SplitIfBetter<true>(bestGain, predictors,
10, labels, 0, predictors, 10, 1e-7, splitInfo, aux);
10, responses, 0, predictors, 10, 1e-7, splitInfo, aux);
// Make sure that there was no split.
REQUIRE(gain == DBL_MAX);
@@ -281,23 +282,26 @@ TEST_CASE("AllCategoricalSplitNoGainTest_", "[DecisionTreeRegressorTest]")
* Check that the BestBinaryNumericSplit will split on an obviously splittable
* dimension.
*/
TEST_CASE("BestBinaryNumericSplitSimpleSplitTest_", "[DecisionTreeRegressorTest]")
TEST_CASE("BestBinaryNumericSplitSimpleSplitTest_",
"[DecisionTreeRegressorTest]")
{
arma::rowvec predictors = { 0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0 };
arma::rowvec labels = { 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0 };
arma::rowvec weights(labels.n_elem);
arma::rowvec predictors =
{ 0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0 };
arma::rowvec responses =
{ 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0 };
arma::rowvec weights(responses.n_elem);
weights.ones();
double splitInfo;
BestBinaryNumericSplit<MADGain>::AuxiliarySplitInfo aux;
// Call the method to do the splitting.
const double bestGain = MADGain::Evaluate<false>(labels, 0, weights);
const double bestGain = MADGain::Evaluate<false>(responses, 0, weights);
const double gain = BestBinaryNumericSplit<MADGain>::SplitIfBetter<false>(
bestGain, predictors, labels, weights, 3, 1e-7, splitInfo, aux);
bestGain, predictors, responses, weights, 3, 1e-7, splitInfo, aux);
const double weightedGain =
BestBinaryNumericSplit<MADGain>::SplitIfBetter<true>(bestGain, predictors,
labels, weights, 3, 1e-7, splitInfo, aux);
responses, weights, 3, 1e-7, splitInfo, aux);
// Make sure that a split was made.
REQUIRE(gain > bestGain);
@@ -315,23 +319,26 @@ TEST_CASE("BestBinaryNumericSplitSimpleSplitTest_", "[DecisionTreeRegressorTest]
* Check that the BestBinaryNumericSplit won't split if not enough points are
* given.
*/
TEST_CASE("BestBinaryNumericSplitMinSamplesTest_", "[DecisionTreeRegressorTest]")
TEST_CASE("BestBinaryNumericSplitMinSamplesTest_",
"[DecisionTreeRegressorTest]")
{
arma::rowvec predictors = { 0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0 };
arma::rowvec labels = { 0.5, 0.5, 0.5, 0.5, 0.5, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0 };
arma::rowvec weights(labels.n_elem);
arma::rowvec predictors =
{ 0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0 };
arma::rowvec responses =
{ 0.5, 0.5, 0.5, 0.5, 0.5, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0 };
arma::rowvec weights(responses.n_elem);
double splitInfo;
BestBinaryNumericSplit<MSEGain>::AuxiliarySplitInfo aux;
// Call the method to do the splitting.
const double bestGain = MSEGain::Evaluate<false>(labels, 0, weights);
const double bestGain = MSEGain::Evaluate<false>(responses, 0, weights);
const double gain = BestBinaryNumericSplit<MSEGain>::SplitIfBetter<false>(
bestGain, predictors, labels, weights, 8, 1e-7, splitInfo, aux);
bestGain, predictors, responses, weights, 8, 1e-7, splitInfo, aux);
// This should make no difference because it won't split at all.
const double weightedGain =
BestBinaryNumericSplit<MSEGain>::SplitIfBetter<true>(bestGain, predictors,
labels, weights, 8, 1e-7, splitInfo, aux);
BestBinaryNumericSplit<MSEGain>::SplitIfBetter<true>(bestGain,
predictors, responses, weights, 8, 1e-7, splitInfo, aux);
// Make sure that no split was made.
REQUIRE(gain == DBL_MAX);
@@ -339,30 +346,29 @@ TEST_CASE("BestBinaryNumericSplitMinSamplesTest_", "[DecisionTreeRegressorTest]"
}
/**
* Check that the BestBinaryNumericSplit doesn't split a dimension that gives no
* gain.
* Check that the BestBinaryNumericSplit doesn't split a dimension that gives
* no gain.
*/
TEST_CASE("BestBinaryNumericSplitNoGainTest_", "[DecisionTreeRegressorTest]")
{
arma::rowvec predictors(100);
arma::rowvec labels(100);
arma::rowvec responses(100);
arma::rowvec weights;
for (size_t i = 0; i < 100; i += 2)
{
predictors[i] = i;
labels[i] = 0.0;
responses[i] = 0.0;
predictors[i + 1] = i;
labels[i + 1] = 1.0;
responses[i + 1] = 1.0;
}
double splitInfo;
BestBinaryNumericSplit<MSEGain>::AuxiliarySplitInfo aux;
// Call the method to do the splitting.
const double bestGain = MSEGain::Evaluate<false>(labels, 0, weights);
const double bestGain = MSEGain::Evaluate<false>(responses, 0, weights);
const double gain = BestBinaryNumericSplit<MSEGain>::SplitIfBetter<false>(
bestGain, predictors, labels, weights, 10, 1e-7, splitInfo,
aux);
bestGain, predictors, responses, weights, 10, 1e-7, splitInfo, aux);
// Make sure there was no split.
REQUIRE(gain == DBL_MAX);
@@ -376,20 +382,20 @@ TEST_CASE("RandomBinaryNumericSplitAlwaysSplit_",
"[DecisionTreeRegressorTest]")
{
arma::vec values("0.0 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1.0");
arma::rowvec labels("0 0 0 0 0 1 1 1 1 1 1");
arma::rowvec responses("0 0 0 0 0 1 1 1 1 1 1");
arma::rowvec weights;
weights.ones(labels.n_elem);
weights.ones(responses.n_elem);
double splitInfo;
RandomBinaryNumericSplit<MSEGain>::AuxiliarySplitInfo aux;
// Call the method to do the splitting.
const double bestGain = MSEGain::Evaluate<false>(labels, 2, weights);
const double bestGain = MSEGain::Evaluate<false>(responses, 2, weights);
const double gain = RandomBinaryNumericSplit<MSEGain>::SplitIfBetter<false>(
bestGain, values, labels, weights, 1, 1e-7, splitInfo, aux);
bestGain, values, responses, weights, 1, 1e-7, splitInfo, aux);
const double weightedGain =
RandomBinaryNumericSplit<MSEGain>::SplitIfBetter<true>(bestGain, values,
labels, weights, 1, 1e-7, splitInfo, aux);
responses, weights, 1, 1e-7, splitInfo, aux);
// Make sure that split was made.
REQUIRE(gain != DBL_MAX);
@@ -404,20 +410,20 @@ TEST_CASE("RandomBinaryNumericSplitMinSamplesTest_",
"[DecisionTreeRegressorTest]")
{
arma::vec values("0.0 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1.0");
arma::rowvec labels("0 0 0 0 0 1 1 1 1 1 1");
arma::rowvec weights(labels.n_elem);
arma::rowvec responses("0 0 0 0 0 1 1 1 1 1 1");
arma::rowvec weights(responses.n_elem);
double splitInfo;
RandomBinaryNumericSplit<MSEGain>::AuxiliarySplitInfo aux;
// Call the method to do the splitting.
const double bestGain = MSEGain::Evaluate<false>(labels, 2, weights);
const double bestGain = MSEGain::Evaluate<false>(responses, 2, weights);
const double gain = RandomBinaryNumericSplit<MSEGain>::SplitIfBetter<false>(
bestGain, values, labels, weights, 8, 1e-7, splitInfo, aux);
bestGain, values, responses, weights, 8, 1e-7, splitInfo, aux);
// This should make no difference because it won't split at all.
const double weightedGain =
RandomBinaryNumericSplit<MSEGain>::SplitIfBetter<true>(bestGain, values,
labels, weights, 8, 1e-7, splitInfo, aux);
responses, weights, 8, 1e-7, splitInfo, aux);
// Make sure that no split was made.
REQUIRE(gain == DBL_MAX);
@@ -431,23 +437,23 @@ TEST_CASE("RandomBinaryNumericSplitMinSamplesTest_",
TEST_CASE("RandomBinaryNumericSplitNoGainTest_", "[DecisionTreeRegressorTest]")
{
arma::vec values(100);
arma::Row<double> labels(100);
arma::rowvec responses(100);
arma::rowvec weights;
for (size_t i = 0; i < 100; i += 2)
{
values[i] = i;
labels[i] = 0.0;
responses[i] = 0.0;
values[i + 1] = i;
labels[i + 1] = 1.0;
responses[i + 1] = 1.0;
}
double splitInfo;
RandomBinaryNumericSplit<MSEGain>::AuxiliarySplitInfo aux;
// Call the method to do the splitting.
const double bestGain = MSEGain::Evaluate<false>(labels, 2, weights);
const double bestGain = MSEGain::Evaluate<false>(responses, 2, weights);
const double gain = RandomBinaryNumericSplit<MSEGain>::SplitIfBetter<false>(
bestGain, values, labels, weights, 10, 1e-7, splitInfo, aux, true);
bestGain, values, responses, weights, 10, 1e-7, splitInfo, aux, true);
// Make sure there was no split.
REQUIRE(gain == DBL_MAX);
@@ -460,48 +466,48 @@ TEST_CASE("RandomBinaryNumericSplitNoGainTest_", "[DecisionTreeRegressorTest]")
TEST_CASE("BasicConstructionTest_", "[DecisionTreeRegressorTest]")
{
arma::mat dataset(10, 100, arma::fill::randu);
arma::Row<double> labels(100);
arma::rowvec responses(100);
for (size_t i = 0; i < 50; ++i)
{
dataset(3, i) = i;
labels[i] = 0.0;
responses[i] = 0.0;
}
for (size_t i = 50; i < 100; ++i)
{
dataset(3, i) = i;
labels[i] = 1.0;
responses[i] = 1.0;
}
// Use default parameters.
DecisionTreeRegressor<> d(dataset, labels);
DecisionTreeRegressor<> d(dataset, responses);
// Now require that we have some children.
REQUIRE(d.NumChildren() > 0);
}
/**
* Construct a tree with weighted labels.
* Construct a tree with weighted responses.
*/
TEST_CASE("BasicConstructionTestWithWeight_", "[DecisionTreeRegressorTest]")
{
arma::mat dataset(10, 100, arma::fill::randu);
arma::Row<double> labels(100);
arma::rowvec responses(100);
for (size_t i = 0; i < 50; ++i)
{
dataset(3, i) = i;
labels[i] = 0.0;
responses[i] = 0.0;
}
for (size_t i = 50; i < 100; ++i)
{
dataset(3, i) = i;
labels[i] = 1.0;
responses[i] = 1.0;
}
arma::rowvec weights(labels.n_elem);
arma::rowvec weights(responses.n_elem);
weights.ones();
// Use default parameters.
DecisionTreeRegressor<> wd(dataset, labels, weights);
DecisionTreeRegressor<> d(dataset, labels);
DecisionTreeRegressor<> wd(dataset, responses, weights);
DecisionTreeRegressor<> d(dataset, responses);
// Now require that we have some children.
REQUIRE(wd.NumChildren() > 0);
@@ -515,53 +521,53 @@ TEST_CASE("BasicConstructionTestWithWeight_", "[DecisionTreeRegressorTest]")
TEST_CASE("PerfectTrainingSet_", "[DecisionTreeRegressorTest]")
{
arma::mat dataset(10, 100, arma::fill::randu);
arma::Row<double> labels(100);
arma::rowvec responses(100);
for (size_t i = 0; i < 50; ++i)
{
dataset(3, i) = i;
labels[i] = 0.0;
responses[i] = 0.0;
}
for (size_t i = 50; i < 100; ++i)
{
dataset(3, i) = i;
labels[i] = 1.0;
responses[i] = 1.0;
}
DecisionTreeRegressor<> d(dataset, labels, 1, 0.0); // Minimum leaf size of 1.
// Minimum leaf size of 1.
DecisionTreeRegressor<> d(dataset, responses, 1, 0.0);
// Make sure that we can get perfect accuracy on the training set.
// Make sure that we can get perfect fit on the training set.
for (size_t i = 0; i < 100; ++i)
{
double prediction;
prediction = d.Predict(dataset.col(i));
REQUIRE(prediction == Approx(labels[i]).epsilon(1e-7));
REQUIRE(prediction == Approx(responses[i]).epsilon(1e-7));
}
}
/**
* Construct the decision tree with weighted labels
* Construct the decision tree with weighted responses.
*/
TEST_CASE("PerfectTrainingSetWithWeight_", "[DecisionTreeRegressorTest]")
{
// Completely random dataset with no structure.
arma::mat dataset(10, 100, arma::fill::randu);
arma::Row<double> labels(100);
arma::rowvec responses(100);
for (size_t i = 0; i < 50; ++i)
{
dataset(3, i) = i;
labels[i] = 0.0;
responses[i] = 0.0;
}
for (size_t i = 50; i < 100; ++i)
{
dataset(3, i) = i;
labels[i] = 1.0;
responses[i] = 1.0;
}
arma::rowvec weights(labels.n_elem);
weights.ones();
arma::rowvec weights = arma::ones<arma::rowvec>(responses.n_elem);
// Minimum leaf size of 1.
DecisionTreeRegressor<> d(dataset, labels, weights, 1, 0.0);
DecisionTreeRegressor<> d(dataset, responses, weights, 1, 0.0);
// This part of code is dupliacte with no weighted one.
for (size_t i = 0; i < 100; ++i)
@@ -569,7 +575,7 @@ TEST_CASE("PerfectTrainingSetWithWeight_", "[DecisionTreeRegressorTest]")
size_t prediction;
prediction = d.Predict(dataset.col(i));
REQUIRE(prediction == Approx(labels[i]).epsilon(1e-7));
REQUIRE(prediction == Approx(responses[i]).epsilon(1e-7));
}
}
@@ -579,18 +585,18 @@ TEST_CASE("PerfectTrainingSetWithWeight_", "[DecisionTreeRegressorTest]")
TEST_CASE("CategoricalBuildTest_", "[DecisionTreeRegressorTest]")
{
arma::mat d;
arma::rowvec l;
arma::rowvec r;
data::DatasetInfo di;
MockCategoricalData(d, l, di);
MockCategoricalData(d, r, di);
// Split into a training set and a test set.
arma::mat trainingData = d.cols(0, 1999);
arma::mat testData = d.cols(2000, 3999);
arma::rowvec trainingLabels = l.subvec(0, 1999);
arma::rowvec testLabels = l.subvec(2000, 3999);
arma::rowvec trainingResponses = r.subvec(0, 1999);
arma::rowvec testResponses = r.subvec(2000, 3999);
// Build the tree.
DecisionTreeRegressor<> tree(trainingData, di, trainingLabels, 10);
DecisionTreeRegressor<> tree(trainingData, di, trainingResponses, 10);
// Now evaluate the quality of predictions.
arma::rowvec predictions;
@@ -599,7 +605,7 @@ TEST_CASE("CategoricalBuildTest_", "[DecisionTreeRegressorTest]")
REQUIRE(predictions.n_elem == testData.n_cols);
// Make sure we get reasonable rmse.
const double rmse = RMSE(predictions, testLabels);
const double rmse = RMSE(predictions, testResponses);
REQUIRE(rmse < 1.0);
}
@@ -610,21 +616,21 @@ TEST_CASE("CategoricalBuildTest_", "[DecisionTreeRegressorTest]")
TEST_CASE("CategoricalBuildTestWithWeight_", "[DecisionTreeRegressorTest]")
{
arma::mat d;
arma::rowvec l;
arma::rowvec r;
data::DatasetInfo di;
MockCategoricalData(d, l, di);
MockCategoricalData(d, r, di);
// Split into a training set and a test set.
arma::mat trainingData = d.cols(0, 1999);
arma::mat testData = d.cols(2000, 3999);
arma::rowvec trainingLabels = l.subvec(0, 1999);
arma::rowvec testLabels = l.subvec(2000, 3999);
arma::rowvec trainingResponses = r.subvec(0, 1999);
arma::rowvec testResponses = r.subvec(2000, 3999);
arma::rowvec weights = arma::ones<arma::rowvec>(
trainingLabels.n_elem);
arma::rowvec weights = arma::ones<arma::rowvec>(trainingResponses.n_elem);
// Build the tree.
DecisionTreeRegressor<> tree(trainingData, di, trainingLabels, weights, 10);
DecisionTreeRegressor<> tree(trainingData, di, trainingResponses, weights,
10);
// Now evaluate the quality of predictions.
arma::rowvec predictions;
@@ -633,7 +639,7 @@ TEST_CASE("CategoricalBuildTestWithWeight_", "[DecisionTreeRegressorTest]")
REQUIRE(predictions.n_elem == testData.n_cols);
// Make sure we get reasonable rmse.
const double rmse = RMSE(predictions, testLabels);
const double rmse = RMSE(predictions, testResponses);
REQUIRE(rmse < 1.0);
}
@@ -689,19 +695,19 @@ TEST_CASE("CategoricalBuildTestWithWeight_", "[DecisionTreeRegressorTest]")
TEST_CASE("CategoricalWeightedBuildTest_", "[DecisionTreeRegressorTest]")
{
arma::mat d;
arma::rowvec l;
arma::rowvec r;
data::DatasetInfo di;
MockCategoricalData(d, l, di);
MockCategoricalData(d, r, di);
// Split into a training set and a test set.
arma::mat trainingData = d.cols(0, 1999);
arma::mat testData = d.cols(2000, 3999);
arma::rowvec trainingLabels = l.subvec(0, 1999);
arma::rowvec testLabels = l.subvec(2000, 3999);
arma::rowvec trainingResponses = r.subvec(0, 1999);
arma::rowvec testResponses = r.subvec(2000, 3999);
// Now create random points.
arma::mat randomNoise(5, 2000);
arma::rowvec randomLabels(2000);
arma::rowvec randomResponses(2000);
for (size_t i = 0; i < 2000; ++i)
{
randomNoise(0, i) = math::Random();
@@ -709,7 +715,7 @@ TEST_CASE("CategoricalWeightedBuildTest_", "[DecisionTreeRegressorTest]")
randomNoise(2, i) = math::Random();
randomNoise(3, i) = math::RandInt(0, 2);
randomNoise(4, i) = math::RandInt(0, 5);
randomLabels[i] = math::Random(-10, 18);
randomResponses[i] = math::Random(-10, 18);
}
// Generate weights.
@@ -720,10 +726,11 @@ TEST_CASE("CategoricalWeightedBuildTest_", "[DecisionTreeRegressorTest]")
weights[i] = math::Random(0.0, 0.001);
arma::mat fullData = arma::join_rows(trainingData, randomNoise);
arma::rowvec fullLabels = arma::join_rows(trainingLabels, randomLabels);
arma::rowvec fullResponses = arma::join_rows(trainingResponses,
randomResponses);
// Build the tree.
DecisionTreeRegressor<> tree(fullData, di, fullLabels, weights, 10);
DecisionTreeRegressor<> tree(fullData, di, fullResponses, weights, 10);
// Now evaluate the quality of predictions.
arma::rowvec predictions;
@@ -732,7 +739,7 @@ TEST_CASE("CategoricalWeightedBuildTest_", "[DecisionTreeRegressorTest]")
REQUIRE(predictions.n_elem == testData.n_cols);
// Make sure we get reasonable rmse.
const double rmse = RMSE(predictions, testLabels);
const double rmse = RMSE(predictions, testResponses);
REQUIRE(rmse < 1.5);
}
@@ -856,29 +863,30 @@ TEST_CASE("SimpleGeneralizationTest_", "[DecisionTreeRegressorTest]")
// Loading data.
data::DatasetInfo info;
arma::mat trainData, testData;
arma::Row<double> trainLabels, testLabels;
arma::rowvec trainResponses, testResponses;
arma::rowvec weights = arma::ones<arma::rowvec>(355);
LoadBostonHousingDataset(trainData, testData, trainLabels, testLabels, info);
LoadBostonHousingDataset(trainData, testData, trainResponses, testResponses,
info);
// Build decision tree.
DecisionTreeRegressor<MSEGain> d(trainData, info, trainLabels);
DecisionTreeRegressor<MSEGain> d(trainData, info, trainResponses);
// Get the predicted test labels.
// Get the predicted test responses.
arma::Row<double> predictions;
d.Predict(testData, predictions);
REQUIRE(predictions.n_elem == testData.n_cols);
// Figure out rmse.
double rmse = RMSE(predictions, testLabels);
double rmse = RMSE(predictions, testResponses);
// REQUIRE(rmse < 9.21);
// std::cout << predictions << std::endl << testLabels;
// std::cout << predictions << std::endl << testResponses;
arma::Row<double> trainPred;
d.Predict(trainData, trainPred);
// std::cout << trainPred;
std::cout << "Train RMSE: " << RMSE(trainLabels, trainPred) << std::endl;
std::cout << "Train RMSE: " << RMSE(trainResponses, trainPred) << std::endl;
}
/**
@@ -959,21 +967,21 @@ TEST_CASE("SimpleGeneralizationTest_", "[DecisionTreeRegressorTest]")
TEST_CASE("MultiSplitTest1", "[DecisionTreeRegressorTest]")
{
arma::mat dataset;
arma::rowvec labels;
arma::rowvec responses;
arma::rowvec values = {0.0, 1.0, 2.0, 1.0, 0.0};
CreateMultiSplitData(dataset, labels, 1000, values);
CreateMultiSplitData(dataset, responses, 1000, values);
arma::rowvec weights(labels.n_elem);
arma::rowvec weights(responses.n_elem);
weights.ones();
// Minimum leaf size of 1.
DecisionTreeRegressor<> d(dataset, labels, weights, 2, 0.0);
DecisionTreeRegressor<> d(dataset, responses, weights, 2, 0.0);
arma::rowvec preds;
d.Predict(dataset, preds);
for (size_t i = 0; i < labels.n_elem; ++i)
REQUIRE(preds[i] == labels[i]);
for (size_t i = 0; i < responses.n_elem; ++i)
REQUIRE(preds[i] == responses[i]);
REQUIRE(d.NumLeaves() == 5);
}
@@ -985,21 +993,21 @@ TEST_CASE("MultiSplitTest1", "[DecisionTreeRegressorTest]")
TEST_CASE("MultiSplitTest2", "[DecisionTreeRegressorTest]")
{
arma::mat dataset;
arma::rowvec labels;
arma::rowvec responses;
arma::rowvec values = {0.0, 1.0, 2.0, 1.0, 0.0};
CreateMultiSplitData(dataset, labels, 100, values);
CreateMultiSplitData(dataset, responses, 100, values);
arma::rowvec weights(labels.n_elem);
arma::rowvec weights(responses.n_elem);
weights.ones();
// Minimum leaf size of 1.
DecisionTreeRegressor<> d(dataset, labels, weights, 2, 0.0);
DecisionTreeRegressor<> d(dataset, responses, weights, 2, 0.0);
arma::rowvec preds;
d.Predict(dataset, preds);
for (size_t i = 0; i < labels.n_elem; ++i)
REQUIRE(preds[i] == labels[i]);
for (size_t i = 0; i < responses.n_elem; ++i)
REQUIRE(preds[i] == responses[i]);
REQUIRE(d.NumLeaves() == 5);
}
@@ -1027,21 +1035,21 @@ TEST_CASE("MultiSplitTest2", "[DecisionTreeRegressorTest]")
TEST_CASE("MultiSplitTest3", "[DecisionTreeRegressorTest]")
{
arma::mat dataset;
arma::Row<double> labels;
arma::Row<double> responses;
arma::rowvec values = {0.0, 5.0, 10.0, 15.0, 20.0};
CreateMultiSplitData(dataset, labels, 500, values);
CreateMultiSplitData(dataset, responses, 500, values);
arma::rowvec weights(labels.n_elem);
arma::rowvec weights(responses.n_elem);
weights.ones();
// Minimum leaf size of 1.
DecisionTreeRegressor<> d(dataset, labels, weights, 2, 0.0);
DecisionTreeRegressor<> d(dataset, responses, weights, 2, 0.0);
arma::rowvec preds;
d.Predict(dataset, preds);
for (size_t i = 0; i < labels.n_elem; ++i)
REQUIRE(preds[i] == labels[i]);
for (size_t i = 0; i < responses.n_elem; ++i)
REQUIRE(preds[i] == responses[i]);
REQUIRE(d.NumLeaves() == 5);
}
+9 -9
View File
@@ -83,20 +83,20 @@ inline void LogisticRegressionTestData(arma::mat& data,
inline void LoadBostonHousingDataset(arma::mat& trainData,
arma::mat& testData,
arma::Row<double>& trainLabels,
arma::Row<double>& testLabels,
arma::rowvec& trainResponses,
arma::rowvec& testResponses,
data::DatasetInfo& info)
{
arma::mat dataset;
arma::Row<double> labels;
arma::rowvec responses;
if (!data::Load("boston_housing_price.csv", dataset, info))
FAIL("Cannot load test dataset boston_housing_price.csv!");
if (!data::Load("boston_housing_price_labels.csv", labels))
FAIL("Cannot load test dataset boston_housing_price_labels.csv!");
if (!data::Load("boston_housing_price_responses.csv", responses))
FAIL("Cannot load test dataset boston_housing_price_responses.csv!");
data::Split(dataset, labels, trainData, testData,
trainLabels, testLabels, 0.3);
data::Split(dataset, responses, trainData, testData,
trainResponses, testResponses, 0.3);
// info.Type(3) = data::Datatype::categorical;
// info.Type(8) = data::Datatype::categorical;
@@ -115,9 +115,9 @@ inline void LoadBostonHousingDataset(arma::mat& trainData,
}
inline double RMSE(const arma::Row<double>& predictions,
const arma::Row<double>& trueLabels)
const arma::Row<double>& trueResponses)
{
double mse = arma::accu(arma::square(predictions - trueLabels)) /
double mse = arma::accu(arma::square(predictions - trueResponses)) /
predictions.n_elem;
return sqrt(mse);
}