Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rewrite random forest gtests #4038

Merged
merged 10 commits into from
Jul 20, 2021
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 0 additions & 18 deletions cpp/include/cuml/ensemble/randomforest.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -119,9 +119,6 @@ struct RandomForestMetaData {
}
};

template <class T, class L>
void null_trees_ptr(RandomForestMetaData<T, L>*& forest);

template <class T, class L>
void delete_rf_metadata(RandomForestMetaData<T, L>* forest);

Expand Down Expand Up @@ -183,21 +180,6 @@ void predict(const raft::handle_t& user_handle,
int* predictions,
int verbosity = CUML_LEVEL_INFO);

void predictGetAll(const raft::handle_t& user_handle,
const RandomForestClassifierF* forest,
const float* input,
int n_rows,
int n_cols,
int* predictions,
int verbosity = CUML_LEVEL_INFO);
void predictGetAll(const raft::handle_t& user_handle,
const RandomForestClassifierD* forest,
const double* input,
int n_rows,
int n_cols,
int* predictions,
int verbosity = CUML_LEVEL_INFO);

RF_metrics score(const raft::handle_t& user_handle,
const RandomForestClassifierF* forest,
const int* ref_labels,
Expand Down
10 changes: 10 additions & 0 deletions cpp/include/cuml/tree/flatnode.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,16 @@ struct SparseTreeNode {
uint32_t instance_count = UINT32_MAX; // UINT32_MAX indicates n/a
};

template <typename DataT, typename LabelT, typename IdxT>
bool operator==(const SparseTreeNode<DataT, LabelT, IdxT>& lhs,
const SparseTreeNode<DataT, LabelT, IdxT>& rhs)
{
return (lhs.prediction == rhs.prediction) && (lhs.colid == rhs.colid) &&
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we expect the floating points values such as prediction etc, match exactly?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've disabled any checks like this for now, but in the future I don't think that's an unreasonable goal for classification.

(lhs.quesval == rhs.quesval) && (lhs.best_metric_val == rhs.best_metric_val) &&
(lhs.left_child_id == rhs.left_child_id) && (lhs.unique_id == rhs.unique_id) &&
(lhs.instance_count == rhs.instance_count);
}

template <typename T, typename L>
struct Node_ID_info {
const SparseTreeNode<T, L>* node;
Expand Down
2 changes: 2 additions & 0 deletions cpp/src/decisiontree/batched-levelalgo/builder_base.cuh
Original file line number Diff line number Diff line change
Expand Up @@ -333,6 +333,8 @@ struct Builder {

// get the current set of nodes to be worked upon
raft::update_device(curr_nodes, h_nodes.data() + node_start, batchSize, s);
CUDA_CHECK(
cudaMemsetAsync(next_nodes, 0, sizeof(Node<DataT, LabelT, IdxT>) * 2 * batchSize, s));

int total_samples_in_curr_batch = 0;
int n_large_nodes_in_curr_batch =
Expand Down
2 changes: 2 additions & 0 deletions cpp/src/decisiontree/batched-levelalgo/node.cuh
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@ struct Node {
info.quesval = DataT(0);
info.best_metric_val = DataT(0);
info.left_child_id = Leaf;
info.unique_id = UINT32_MAX;
info.instance_count = UINT32_MAX;
}

/**
Expand Down
6 changes: 1 addition & 5 deletions cpp/src/decisiontree/decisiontree.cuh
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
#include <locale>
#include <map>
#include <numeric>
#include <raft/handle.hpp>
#include <raft/mr/device/allocator.hpp>
#include <raft/mr/host/allocator.hpp>
#include <random>
Expand Down Expand Up @@ -289,11 +290,6 @@ class DecisionTree {
(std::numeric_limits<L>::is_integer) ? CRITERION::ENTROPY : CRITERION::MSE;

validity_check(tree_params);
if (tree_params.n_bins > n_sampled_rows) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this check removed? Is it moved to some place else

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's not correct because the quantiles have already been computed at this stage, checked with @venkywonka on this. I don't necessarily see any reason to enforce nbins < nrows.

CUML_LOG_WARN("Calling with number of bins > number of rows!");
CUML_LOG_WARN("Resetting n_bins to %d.", n_sampled_rows);
tree_params.n_bins = n_sampled_rows;
}

if (tree_params.split_criterion ==
CRITERION::CRITERION_END) { // Set default to GINI (classification) or MSE (regression)
Expand Down
9 changes: 7 additions & 2 deletions cpp/src/decisiontree/quantile/quantile.cuh
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
*/

#pragma once
#include <thrust/execution_policy.h>
#include <thrust/fill.h>
#include <cub/cub.cuh>
#include <raft/cuda_utils.cuh>
#include <raft/mr/device/allocator.hpp>
Expand All @@ -39,6 +41,7 @@ __global__ void computeQuantilesSorted(T* quantiles,
int tid = threadIdx.x + blockIdx.x * blockDim.x;
double bin_width = static_cast<double>(length) / n_bins;
int index = int(round((tid + 1) * bin_width)) - 1;
index = min(max(0, index), length - 1);
// Old way of computing quantiles. Kept here for comparison.
RAMitchell marked this conversation as resolved.
Show resolved Hide resolved
// To be deleted eventually
// int index = (tid + 1) * floor(bin_width) - 1;
Expand All @@ -56,6 +59,8 @@ void computeQuantiles(T* quantiles,
const std::shared_ptr<raft::mr::device::allocator> device_allocator,
cudaStream_t stream)
{
thrust::fill(
thrust::cuda::par(*device_allocator).on(stream), quantiles, quantiles + n_bins * n_cols, 0.0);
// Determine temporary device storage requirements
std::unique_ptr<device_buffer<char>> d_temp_storage = nullptr;
size_t temp_storage_bytes = 0;
Expand Down Expand Up @@ -83,7 +88,7 @@ void computeQuantiles(T* quantiles,

CUDA_CHECK(cub::DeviceRadixSort::SortKeys((void*)d_temp_storage->data(),
temp_storage_bytes,
&data[col_offset],
data + col_offset,
single_column_sorted->data(),
n_rows,
0,
Expand All @@ -93,7 +98,7 @@ void computeQuantiles(T* quantiles,
int blocks = raft::ceildiv(n_bins, 128);

computeQuantilesSorted<<<blocks, 128, 0, stream>>>(
&quantiles[quantile_offset], n_bins, single_column_sorted->data(), n_rows);
quantiles + quantile_offset, n_bins, single_column_sorted->data(), n_rows);

CUDA_CHECK(cudaGetLastError());
}
Expand Down
59 changes: 0 additions & 59 deletions cpp/src/randomforest/randomforest.cu
Original file line number Diff line number Diff line change
Expand Up @@ -193,16 +193,6 @@ void print(const RF_params rf_params)
DT::print(rf_params.tree_params);
}

/**
* @brief Set the trees pointer of RandomForestMetaData to nullptr.
* @param[in, out] forest: CPU pointer to RandomForestMetaData.
*/
template <class T, class L>
void null_trees_ptr(RandomForestMetaData<T, L>*& forest)
{
forest->trees = nullptr;
}

/**
* @brief Deletes RandomForestMetaData object
* @param[in] forest: CPU pointer to RandomForestMetaData.
Expand Down Expand Up @@ -562,50 +552,6 @@ void predict(const raft::handle_t& user_handle,
std::make_shared<RandomForest<double, int>>(forest->rf_params, RF_type::CLASSIFICATION);
rf_classifier->predict(user_handle, input, n_rows, n_cols, predictions, forest, verbosity);
}
/** @} */

/**
* @addtogroup RandomForestClassificationPredict
* @brief Predict target feature for input data; n-ary classification for
single feature supported.
* @param[in] user_handle: raft::handle_t.
* @param[in] forest: CPU pointer to RandomForestMetaData object.
* The user should have previously called fit to build the random forest.
* @param[in] input: test data (n_rows samples, n_cols features) in row major format. GPU pointer.
* @param[in] n_rows: number of data samples.
* @param[in] n_cols: number of features (excluding target feature).
* @param[in, out] predictions: n_rows predicted labels. GPU pointer, user allocated.
* @param[in] verbosity: verbosity level for logging messages during execution
* @{
*/
void predictGetAll(const raft::handle_t& user_handle,
const RandomForestClassifierF* forest,
const float* input,
int n_rows,
int n_cols,
int* predictions,
int verbosity)
{
ASSERT(forest->trees, "Cannot predict! No trees in the forest.");
std::shared_ptr<RandomForest<float, int>> rf_classifier =
std::make_shared<RandomForest<float, int>>(forest->rf_params, RF_type::CLASSIFICATION);
rf_classifier->predictGetAll(user_handle, input, n_rows, n_cols, predictions, forest, verbosity);
}

void predictGetAll(const raft::handle_t& user_handle,
const RandomForestClassifierD* forest,
const double* input,
int n_rows,
int n_cols,
int* predictions,
int verbosity)
{
ASSERT(forest->trees, "Cannot predict! No trees in the forest.");
std::shared_ptr<RandomForest<double, int>> rf_classifier =
std::make_shared<RandomForest<double, int>>(forest->rf_params, RF_type::CLASSIFICATION);
rf_classifier->predictGetAll(user_handle, input, n_rows, n_cols, predictions, forest, verbosity);
}
/** @} */

/**
* @defgroup RandomForestClassificationScore Random Forest Classification - Score function
Expand Down Expand Up @@ -843,11 +789,6 @@ template std::string get_rf_json<double, int>(const RandomForestClassifierD* for
template std::string get_rf_json<float, float>(const RandomForestRegressorF* forest);
template std::string get_rf_json<double, double>(const RandomForestRegressorD* forest);

template void null_trees_ptr<float, int>(RandomForestClassifierF*& forest);
template void null_trees_ptr<double, int>(RandomForestClassifierD*& forest);
template void null_trees_ptr<float, float>(RandomForestRegressorF*& forest);
template void null_trees_ptr<double, double>(RandomForestRegressorD*& forest);

template void delete_rf_metadata<float, int>(RandomForestClassifierF* forest);
template void delete_rf_metadata<double, int>(RandomForestClassifierD* forest);
template void delete_rf_metadata<float, float>(RandomForestRegressorF* forest);
Expand Down
61 changes: 1 addition & 60 deletions cpp/src/randomforest/randomforest.cuh
Original file line number Diff line number Diff line change
Expand Up @@ -329,64 +329,6 @@ class RandomForest {
CUDA_CHECK(cudaStreamSynchronize(stream));
}

/**
* @brief Predict target features for input data for each estimator seperately without ensembling
* @param[in] user_handle: raft::handle_t.
* @param[in] input: test data (n_rows samples, n_cols features) in row major format. GPU pointer.
* @param[in] n_rows: number of data samples.
* @param[in] n_cols: number of features (excluding target feature).
* @param[in, out] predictions: n_rows predicted labels. GPU pointer, user allocated.
* @param[in] verbosity: verbosity level for logging messages during execution
*/
void predictGetAll(const raft::handle_t& user_handle,
const T* input,
int n_rows,
int n_cols,
L* predictions,
const RandomForestMetaData<T, L>* forest,
int verbosity)
{
// ASSERT(rf_type == RF_type::CLASSIFICATION, "This method does not supported for regression
// task ");
ML::Logger::get().setLevel(verbosity);
int num_trees = this->rf_params.n_trees;
std::vector<L> h_predictions(n_rows * num_trees);

std::vector<T> h_input(n_rows * n_cols);
cudaStream_t stream = user_handle.get_stream();
raft::update_host(h_input.data(), input, n_rows * n_cols, stream);
CUDA_CHECK(cudaStreamSynchronize(stream));

int row_size = n_cols;
int pred_id = 0;

for (int row_id = 0; row_id < n_rows; row_id++) {
if (ML::Logger::get().shouldLogFor(CUML_LEVEL_DEBUG)) {
std::stringstream ss;
ss << "Predict for sample: ";
for (int i = 0; i < n_cols; i++)
ss << h_input[row_id * row_size + i] << ", ";
CUML_LOG_DEBUG(ss.str().c_str());
}

for (int i = 0; i < num_trees; i++) {
L prediction;
trees[i].predict(user_handle,
&forest->trees[i],
&h_input[row_id * row_size],
1,
n_cols,
&prediction,
verbosity);
h_predictions[pred_id] = prediction;
pred_id++;
}
}

raft::update_device(predictions, h_predictions.data(), n_rows * num_trees, stream);
CUDA_CHECK(cudaStreamSynchronize(stream));
}

/**
* @brief Predict target feature for input data and score against ref_labels.
* @param[in] user_handle: raft::handle_t.
Expand Down Expand Up @@ -428,8 +370,7 @@ class RandomForest {
mean_abs_error,
mean_squared_error,
median_abs_error);
RF_metrics stats =
set_rf_metrics_regression(mean_abs_error, mean_squared_error, median_abs_error);
stats = set_rf_metrics_regression(mean_abs_error, mean_squared_error, median_abs_error);
if (ML::Logger::get().shouldLogFor(CUML_LEVEL_DEBUG)) print(stats);
}

Expand Down
6 changes: 0 additions & 6 deletions cpp/test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,6 @@ if(BUILD_CUML_TESTS)
add_executable(${CUML_CPP_TEST_TARGET}
sg/cd_test.cu
sg/dbscan_test.cu
sg/decisiontree_batchedlevel_algo.cu
sg/decisiontree_batchedlevel_unittest.cu
sg/fil_test.cu
sg/fnv_hash_test.cpp
Expand All @@ -60,11 +59,6 @@ if(BUILD_CUML_TESTS)
sg/ols.cu
sg/pca_test.cu
sg/quasi_newton.cu
sg/rf_accuracy_test.cu
sg/rf_batched_classification_test.cu
sg/rf_batched_regression_test.cu
sg/rf_depth_test.cu
sg/rf_quantiles_test.cu
sg/rf_test.cu
sg/rf_treelite_test.cu
sg/ridge.cu
Expand Down
Loading