Skip to content

Commit f0cfbff

Browse files
authored
[CUDA] Add rmse metric for new CUDA version (#5611)
* add rmse metric for new cuda version * add Init for CUDAMetricInterface * fix lint errors
1 parent 38a1f58 commit f0cfbff

17 files changed

+230
-25
lines changed

CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -417,6 +417,8 @@ endif()
417417
if(USE_CUDA_EXP)
418418
src/boosting/cuda/*.cpp
419419
src/boosting/cuda/*.cu
420+
src/metric/cuda/*.cpp
421+
src/metric/cuda/*.cu
420422
src/objective/cuda/*.cpp
421423
src/objective/cuda/*.cu
422424
src/treelearner/cuda/*.cpp
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
/*!
2+
* Copyright (c) 2021 Microsoft Corporation. All rights reserved.
3+
* Licensed under the MIT License. See LICENSE file in the project root for
4+
* license information.
5+
*/
6+
7+
#ifndef LIGHTGBM_CUDA_CUDA_METRIC_HPP_
8+
#define LIGHTGBM_CUDA_CUDA_METRIC_HPP_
9+
10+
#ifdef USE_CUDA_EXP
11+
12+
#include <LightGBM/metric.h>
13+
14+
namespace LightGBM {
15+
16+
template <typename HOST_METRIC>
17+
class CUDAMetricInterface: public HOST_METRIC {
18+
public:
19+
explicit CUDAMetricInterface(const Config& config): HOST_METRIC(config) {
20+
cuda_labels_ = nullptr;
21+
cuda_weights_ = nullptr;
22+
}
23+
24+
void Init(const Metadata& metadata, data_size_t num_data) override {
25+
HOST_METRIC::Init(metadata, num_data);
26+
cuda_labels_ = metadata.cuda_metadata()->cuda_label();
27+
cuda_weights_ = metadata.cuda_metadata()->cuda_weights();
28+
}
29+
30+
bool IsCUDAMetric() const { return true; }
31+
32+
protected:
33+
const label_t* cuda_labels_;
34+
const label_t* cuda_weights_;
35+
};
36+
37+
} // namespace LightGBM
38+
39+
#endif // USE_CUDA_EXP
40+
41+
#endif // LIGHTGBM_CUDA_CUDA_METRIC_HPP_

include/LightGBM/cuda/cuda_objective_function.hpp

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -31,14 +31,8 @@ class CUDAObjectiveInterface: public HOST_OBJECTIVE {
3131
cuda_weights_ = metadata.cuda_metadata()->cuda_weights();
3232
}
3333

34-
virtual void ConvertOutputCUDA(const data_size_t num_data, const double* input, double* output) const {
35-
LaunchConvertOutputCUDAKernel(num_data, input, output);
36-
}
37-
38-
std::function<void(data_size_t, const double*, double*)> GetCUDAConvertOutputFunc() const override {
39-
return [this] (data_size_t num_data, const double* input, double* output) {
40-
ConvertOutputCUDA(num_data, input, output);
41-
};
34+
virtual const double* ConvertOutputCUDA(const data_size_t num_data, const double* input, double* output) const {
35+
return LaunchConvertOutputCUDAKernel(num_data, input, output);
4236
}
4337

4438
double BoostFromScore(int class_id) const override {
@@ -67,7 +61,7 @@ class CUDAObjectiveInterface: public HOST_OBJECTIVE {
6761
return HOST_OBJECTIVE::BoostFromScore(class_id);
6862
}
6963

70-
virtual void LaunchConvertOutputCUDAKernel(const data_size_t /*num_data*/, const double* /*input*/, double* /*output*/) const {}
64+
virtual const double* LaunchConvertOutputCUDAKernel(const data_size_t /*num_data*/, const double* input, double* /*output*/) const { return input; }
7165

7266
virtual void LaunchRenewTreeOutputCUDAKernel(
7367
const double* /*score*/, const data_size_t* /*data_indices_in_leaf*/, const data_size_t* /*num_data_in_leaf*/,

include/LightGBM/objective_function.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -99,10 +99,10 @@ class ObjectiveFunction {
9999

100100
#ifdef USE_CUDA_EXP
101101
/*!
102-
* \brief Get output convert function for CUDA version
102+
* \brief Convert output for CUDA version
103103
*/
104-
virtual std::function<void(data_size_t, const double*, double*)> GetCUDAConvertOutputFunc() const {
105-
return [] (data_size_t, const double*, double*) {};
104+
const double* ConvertOutputCUDA(data_size_t /*num_data*/, const double* input, double* /*output*/) const {
105+
return input;
106106
}
107107
#endif // USE_CUDA_EXP
108108
};

src/cuda/cuda_algorithms.cu

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,7 @@ void ShuffleReduceSumGlobal(const VAL_T* values, size_t n, REDUCE_T* block_buffe
127127
}
128128

129129
template void ShuffleReduceSumGlobal<label_t, double>(const label_t* values, size_t n, double* block_buffer);
130+
template void ShuffleReduceSumGlobal<double, double>(const double* values, size_t n, double* block_buffer);
130131

131132
template <typename VAL_T, typename REDUCE_T>
132133
__global__ void ShuffleReduceMinGlobalKernel(const VAL_T* values, const data_size_t num_value, REDUCE_T* block_buffer) {
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
/*!
2+
* Copyright (c) 2022 Microsoft Corporation. All rights reserved.
3+
* Licensed under the MIT License. See LICENSE file in the project root for
4+
* license information.
5+
*/
6+
7+
#ifdef USE_CUDA_EXP
8+
9+
#include <vector>
10+
11+
#include "cuda_regression_metric.hpp"
12+
13+
namespace LightGBM {
14+
15+
template <typename HOST_METRIC, typename CUDA_METRIC>
16+
void CUDARegressionMetricInterface<HOST_METRIC, CUDA_METRIC>::Init(const Metadata& metadata, data_size_t num_data) {
17+
CUDAMetricInterface<HOST_METRIC>::Init(metadata, num_data);
18+
const int max_num_reduce_blocks = (this->num_data_ + NUM_DATA_PER_EVAL_THREAD - 1) / NUM_DATA_PER_EVAL_THREAD;
19+
if (this->cuda_weights_ == nullptr) {
20+
reduce_block_buffer_.Resize(max_num_reduce_blocks);
21+
} else {
22+
reduce_block_buffer_.Resize(max_num_reduce_blocks * 2);
23+
}
24+
const int max_num_reduce_blocks_inner = (max_num_reduce_blocks + NUM_DATA_PER_EVAL_THREAD - 1) / NUM_DATA_PER_EVAL_THREAD;
25+
if (this->cuda_weights_ == nullptr) {
26+
reduce_block_buffer_inner_.Resize(max_num_reduce_blocks_inner);
27+
} else {
28+
reduce_block_buffer_inner_.Resize(max_num_reduce_blocks_inner * 2);
29+
}
30+
}
31+
32+
template <typename HOST_METRIC, typename CUDA_METRIC>
33+
std::vector<double> CUDARegressionMetricInterface<HOST_METRIC, CUDA_METRIC>::Eval(const double* score, const ObjectiveFunction* objective) const {
34+
const double* score_convert = objective->ConvertOutputCUDA(this->num_data_, score, score_convert_buffer_.RawData());
35+
const double eval_score = LaunchEvalKernel(score_convert);
36+
return std::vector<double>{eval_score};
37+
}
38+
39+
CUDARMSEMetric::CUDARMSEMetric(const Config& config): CUDARegressionMetricInterface<RMSEMetric, CUDARMSEMetric>(config) {}
40+
41+
} // namespace LightGBM
42+
43+
#endif // USE_CUDA_EXP
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
/*!
2+
* Copyright (c) 2022 Microsoft Corporation. All rights reserved.
3+
* Licensed under the MIT License. See LICENSE file in the project root for
4+
* license information.
5+
*/
6+
7+
#ifdef USE_CUDA_EXP
8+
9+
#include <LightGBM/cuda/cuda_algorithms.hpp>
10+
11+
#include "cuda_regression_metric.hpp"
12+
13+
namespace LightGBM {
14+
15+
template <typename CUDA_METRIC, bool USE_WEIGHTS>
16+
__global__ void EvalKernel(const data_size_t num_data, const label_t* labels, const label_t* weights,
17+
const double* scores, double* reduce_block_buffer) {
18+
__shared__ double shared_mem_buffer[32];
19+
const data_size_t index = static_cast<data_size_t>(threadIdx.x + blockIdx.x * blockDim.x);
20+
double point_metric = 0.0;
21+
if (index < num_data) {
22+
point_metric = CUDA_METRIC::MetricOnPointCUDA(labels[index], scores[index]);
23+
}
24+
const double block_sum_point_metric = ShuffleReduceSum<double>(point_metric, shared_mem_buffer, NUM_DATA_PER_EVAL_THREAD);
25+
reduce_block_buffer[blockIdx.x] = block_sum_point_metric;
26+
if (USE_WEIGHTS) {
27+
double weight = 0.0;
28+
if (index < num_data) {
29+
weight = static_cast<double>(weights[index]);
30+
const double block_sum_weight = ShuffleReduceSum<double>(weight, shared_mem_buffer, NUM_DATA_PER_EVAL_THREAD);
31+
reduce_block_buffer[blockIdx.x + blockDim.x] = block_sum_weight;
32+
}
33+
}
34+
}
35+
36+
template <typename HOST_METRIC, typename CUDA_METRIC>
37+
double CUDARegressionMetricInterface<HOST_METRIC, CUDA_METRIC>::LaunchEvalKernel(const double* score) const {
38+
const int num_blocks = (this->num_data_ + NUM_DATA_PER_EVAL_THREAD - 1) / NUM_DATA_PER_EVAL_THREAD;
39+
if (this->cuda_weights_ != nullptr) {
40+
EvalKernel<CUDA_METRIC, true><<<num_blocks, NUM_DATA_PER_EVAL_THREAD>>>(
41+
this->num_data_, this->cuda_labels_, this->cuda_weights_, score, reduce_block_buffer_.RawData());
42+
} else {
43+
EvalKernel<CUDA_METRIC, false><<<num_blocks, NUM_DATA_PER_EVAL_THREAD>>>(
44+
this->num_data_, this->cuda_labels_, this->cuda_weights_, score, reduce_block_buffer_.RawData());
45+
}
46+
ShuffleReduceSumGlobal<double, double>(reduce_block_buffer_.RawData(), num_blocks, reduce_block_buffer_inner_.RawData());
47+
double sum_loss = 0.0;
48+
CopyFromCUDADeviceToHost<double>(&sum_loss, reduce_block_buffer_inner_.RawData(), 1, __FILE__, __LINE__);
49+
double sum_weight = static_cast<double>(this->num_data_);
50+
if (this->cuda_weights_ != nullptr) {
51+
ShuffleReduceSumGlobal<double, double>(reduce_block_buffer_.RawData() + num_blocks, num_blocks, reduce_block_buffer_inner_.RawData());
52+
CopyFromCUDADeviceToHost<double>(&sum_weight, reduce_block_buffer_inner_.RawData(), 1, __FILE__, __LINE__);
53+
}
54+
return this->AverageLoss(sum_loss, sum_weight);
55+
}
56+
57+
template double CUDARegressionMetricInterface<RMSEMetric, CUDARMSEMetric>::LaunchEvalKernel(const double* score) const;
58+
59+
} // namespace LightGBM
60+
61+
#endif // USE_CUDA_EXP
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
/*!
2+
* Copyright (c) 2022 Microsoft Corporation. All rights reserved.
3+
* Licensed under the MIT License. See LICENSE file in the project root for
4+
* license information.
5+
*/
6+
7+
#ifndef LIGHTGBM_METRIC_CUDA_CUDA_REGRESSION_METRIC_HPP_
8+
#define LIGHTGBM_METRIC_CUDA_CUDA_REGRESSION_METRIC_HPP_
9+
10+
#ifdef USE_CUDA_EXP
11+
12+
#include <LightGBM/cuda/cuda_metric.hpp>
13+
#include <LightGBM/cuda/cuda_utils.h>
14+
15+
#include <vector>
16+
17+
#include "../regression_metric.hpp"
18+
19+
#define NUM_DATA_PER_EVAL_THREAD (1024)
20+
21+
namespace LightGBM {
22+
23+
template <typename HOST_METRIC, typename CUDA_METRIC>
24+
class CUDARegressionMetricInterface: public CUDAMetricInterface<HOST_METRIC> {
25+
public:
26+
explicit CUDARegressionMetricInterface(const Config& config): CUDAMetricInterface<HOST_METRIC>(config) {}
27+
28+
virtual ~CUDARegressionMetricInterface() {}
29+
30+
void Init(const Metadata& metadata, data_size_t num_data) override;
31+
32+
std::vector<double> Eval(const double* score, const ObjectiveFunction* objective) const override;
33+
34+
protected:
35+
double LaunchEvalKernel(const double* score_convert) const;
36+
37+
CUDAVector<double> score_convert_buffer_;
38+
CUDAVector<double> reduce_block_buffer_;
39+
CUDAVector<double> reduce_block_buffer_inner_;
40+
};
41+
42+
class CUDARMSEMetric: public CUDARegressionMetricInterface<RMSEMetric, CUDARMSEMetric> {
43+
public:
44+
explicit CUDARMSEMetric(const Config& config);
45+
46+
virtual ~CUDARMSEMetric() {}
47+
48+
__device__ static double MetricOnPointCUDA(label_t label, double score) {
49+
return (score - static_cast<double>(label));
50+
}
51+
};
52+
53+
} // namespace LightGBM
54+
55+
#endif // USE_CUDA_EXP
56+
57+
#endif // LIGHTGBM_METRIC_CUDA_CUDA_REGRESSION_METRIC_HPP_

src/metric/metric.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111
#include "regression_metric.hpp"
1212
#include "xentropy_metric.hpp"
1313

14+
#include "cuda/cuda_regression_metric.hpp"
15+
1416
namespace LightGBM {
1517

1618
Metric* Metric::CreateMetric(const std::string& type, const Config& config) {
@@ -20,8 +22,7 @@ Metric* Metric::CreateMetric(const std::string& type, const Config& config) {
2022
Log::Warning("Metric l2 is not implemented in cuda_exp version. Fall back to evaluation on CPU.");
2123
return new L2Metric(config);
2224
} else if (type == std::string("rmse")) {
23-
Log::Warning("Metric rmse is not implemented in cuda_exp version. Fall back to evaluation on CPU.");
24-
return new RMSEMetric(config);
25+
return new CUDARMSEMetric(config);
2526
} else if (type == std::string("l1")) {
2627
Log::Warning("Metric l1 is not implemented in cuda_exp version. Fall back to evaluation on CPU.");
2728
return new L1Metric(config);

src/metric/regression_metric.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ class RegressionMetric: public Metric {
101101
inline static void CheckLabel(label_t) {
102102
}
103103

104-
private:
104+
protected:
105105
/*! \brief Number of data */
106106
data_size_t num_data_;
107107
/*! \brief Pointer of label */

0 commit comments

Comments
 (0)