191 lines
7.1 KiB
C++
191 lines
7.1 KiB
C++
/**
|
|
* @file methods/decision_tree/best_binary_numeric_split.hpp
|
|
* @author Ryan Curtin
|
|
*
|
|
* A tree splitter that finds the best binary numeric split.
|
|
*
|
|
* mlpack is free software; you may redistribute it and/or modify it under the
|
|
* terms of the 3-clause BSD license. You should have received a copy of the
|
|
* 3-clause BSD license along with mlpack. If not, see
|
|
* http://www.opensource.org/licenses/BSD-3-Clause for more information.
|
|
*/
|
|
#ifndef MLPACK_METHODS_DECISION_TREE_BEST_BINARY_NUMERIC_SPLIT_HPP
|
|
#define MLPACK_METHODS_DECISION_TREE_BEST_BINARY_NUMERIC_SPLIT_HPP
|
|
|
|
#include <mlpack/prereqs.hpp>
|
|
#include "mse_gain.hpp"
|
|
|
|
#include <mlpack/core/util/sfinae_utility.hpp>
|
|
|
|
namespace mlpack {
|
|
|
|
// This gives us a HasBinaryGains<T, U> type (where U is a function pointer)
|
|
// we can use with SFINAE to catch when a type has a BinaryGains(...) function.
|
|
HAS_MEM_FUNC(BinaryGains, HasBinaryGains);
|
|
|
|
// This struct will have `value` set to `true` if a BinaryGains() function of
|
|
// the right signature is detected. We only check for BinaryGains(), and not
|
|
// BinaryScanInitialize() or BinaryStep(), because those two are template
|
|
// members functions and would make this check far more difficult.
|
|
//
|
|
// The unused UseWeights template parameter is necessary to ensure that the
|
|
// compiler thinks the result `value` depends on a parameter specific to the
|
|
// SplitIfBetter() function in BestBinaryNumericSplit().
|
|
template<typename T, bool /* UseWeights */>
|
|
struct HasOptimizedBinarySplitForms
|
|
{
|
|
const static bool value = HasBinaryGains<T,
|
|
std::tuple<double, double>(T::*)()>::value;
|
|
};
|
|
|
|
/**
|
|
* The BestBinaryNumericSplit is a splitting function for decision trees that
|
|
* will exhaustively search a numeric dimension for the best binary split.
|
|
*
|
|
* @tparam FitnessFunction Fitness function to use to calculate gain.
|
|
*/
|
|
template<typename FitnessFunction>
|
|
class BestBinaryNumericSplit
|
|
{
|
|
public:
|
|
// No extra info needed for split.
|
|
class AuxiliarySplitInfo { };
|
|
|
|
/**
|
|
* Check if we can split a node. If we can split a node in a way that
|
|
* improves on 'bestGain', then we return the improved gain. Otherwise we
|
|
* return the value 'bestGain'. If a split is made, then splitInfo and aux
|
|
* may be modified.
|
|
*
|
|
* This overload is used only for classification tasks.
|
|
*
|
|
* @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 numClasses Number of classes in the dataset.
|
|
* @param weights Weights associated with labels.
|
|
* @param minimumLeafSize Minimum number of points in a leaf node for
|
|
* splitting.
|
|
* @param minimumGainSplit Minimum gain split.
|
|
* @param splitInfo Stores split information on a successful split.
|
|
* @param aux Auxiliary split information, which may be modified on a
|
|
* successful split.
|
|
*/
|
|
template<bool UseWeights, typename VecType, typename WeightVecType>
|
|
static double SplitIfBetter(
|
|
const double bestGain,
|
|
const VecType& data,
|
|
const arma::Row<size_t>& labels,
|
|
const size_t numClasses,
|
|
const WeightVecType& weights,
|
|
const size_t minimumLeafSize,
|
|
const double minimumGainSplit,
|
|
arma::vec& splitInfo,
|
|
AuxiliarySplitInfo& aux);
|
|
|
|
/**
|
|
* Check if we can split a node. If we can split a node in a way that
|
|
* improves on 'bestGain', then we return the improved gain. Otherwise we
|
|
* return the value 'bestGain'. If a split is made, then splitInfo and aux
|
|
* may be modified.
|
|
*
|
|
* This overload is used only for regression tasks.
|
|
*
|
|
* @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 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.
|
|
* @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 is used to
|
|
* evaluate the gain for the split.
|
|
*/
|
|
template<bool UseWeights, typename VecType, typename ResponsesType,
|
|
typename WeightVecType>
|
|
static typename std::enable_if<
|
|
!HasOptimizedBinarySplitForms<FitnessFunction, UseWeights>::value,
|
|
double>::type
|
|
SplitIfBetter(
|
|
const double bestGain,
|
|
const VecType& data,
|
|
const ResponsesType& responses,
|
|
const WeightVecType& weights,
|
|
const size_t minimumLeafSize,
|
|
const double minimumGainSplit,
|
|
double& splitInfo,
|
|
AuxiliarySplitInfo& aux,
|
|
FitnessFunction& fitnessFunction);
|
|
|
|
/**
|
|
* Check if we can split a node. If we can split a node in a way that
|
|
* improves on 'bestGain', then we return the improved gain. Otherwise we
|
|
* return the value 'bestGain'. If a split is made, then splitInfo and aux
|
|
* may be modified.
|
|
*
|
|
* This overload is specialized for any fitness function that implements
|
|
* BinaryScanInitialize(), BinaryStep() and BinaryGains() functions.
|
|
*
|
|
* @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 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.
|
|
* @param splitInfo Stores split information on a successful split.
|
|
* @param aux Auxiliary split information, which may be modified on a
|
|
* successful split.
|
|
*/
|
|
template<bool UseWeights, typename VecType, typename ResponsesType,
|
|
typename WeightVecType>
|
|
static typename std::enable_if<
|
|
HasOptimizedBinarySplitForms<FitnessFunction, UseWeights>::value,
|
|
double>::type
|
|
SplitIfBetter(
|
|
const double bestGain,
|
|
const VecType& data,
|
|
const ResponsesType& responses,
|
|
const WeightVecType& weights,
|
|
const size_t minimumLeafSize,
|
|
const double minimumGainSplit,
|
|
double& splitInfo,
|
|
AuxiliarySplitInfo& /* aux */,
|
|
FitnessFunction& fitnessFunction);
|
|
|
|
/**
|
|
* Returns 2, since the binary split always has two children.
|
|
*/
|
|
static size_t NumChildren(const double& /* splitInfo */,
|
|
const AuxiliarySplitInfo& /* aux */)
|
|
{
|
|
return 2;
|
|
}
|
|
|
|
/**
|
|
* Given a point, calculate which child it should go to (left or right).
|
|
*
|
|
* @param point Point to calculate direction of.
|
|
* @param splitInfo Auxiliary information for the split.
|
|
* @param * (aux) Auxiliary information for the split (Unused).
|
|
*/
|
|
template<typename ElemType>
|
|
static size_t CalculateDirection(
|
|
const ElemType& point,
|
|
const double& splitInfo,
|
|
const AuxiliarySplitInfo& /* aux */);
|
|
};
|
|
|
|
} // namespace mlpack
|
|
|
|
// Include implementation.
|
|
#include "best_binary_numeric_split_impl.hpp"
|
|
|
|
#endif
|