Skip to content

Commit 1450aeb

Browse files
authored
Fix pairwise objective with NDCG metric along with custom gain. (#10100)
* Fix pairwise objective with NDCG metric. - Allow setting `ndcg_exp_gain` for `rank:pairwise`. This is useful when using pairwise for objective but ndcg for metric.
1 parent 06c9702 commit 1450aeb

File tree

3 files changed

+26
-2
lines changed

3 files changed

+26
-2
lines changed

src/objective/lambdarank_obj.cc

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -474,7 +474,6 @@ class LambdaRankMAP : public LambdaRankObj<LambdaRankMAP, ltr::MAPCache> {
474474
public:
475475
void GetGradientImpl(std::int32_t iter, const HostDeviceVector<float>& predt,
476476
const MetaInfo& info, linalg::Matrix<GradientPair>* out_gpair) {
477-
CHECK(param_.ndcg_exp_gain) << "NDCG gain can not be set for the MAP objective.";
478477
if (ctx_->IsCUDA()) {
479478
return cuda_impl::LambdaRankGetGradientMAP(
480479
ctx_, iter, predt, info, GetCache(), ti_plus_.View(ctx_->Device()),
@@ -564,7 +563,6 @@ class LambdaRankPairwise : public LambdaRankObj<LambdaRankPairwise, ltr::Ranking
564563
public:
565564
void GetGradientImpl(std::int32_t iter, const HostDeviceVector<float>& predt,
566565
const MetaInfo& info, linalg::Matrix<GradientPair>* out_gpair) {
567-
CHECK(param_.ndcg_exp_gain) << "NDCG gain can not be set for the pairwise objective.";
568566
if (ctx_->IsCUDA()) {
569567
return cuda_impl::LambdaRankGetGradientPairwise(
570568
ctx_, iter, predt, info, GetCache(), ti_plus_.View(ctx_->Device()),
@@ -610,6 +608,13 @@ class LambdaRankPairwise : public LambdaRankObj<LambdaRankPairwise, ltr::Ranking
610608
[[nodiscard]] const char* DefaultEvalMetric() const override {
611609
return this->RankEvalMetric("ndcg");
612610
}
611+
612+
[[nodiscard]] Json DefaultMetricConfig() const override {
613+
Json config{Object{}};
614+
config["name"] = String{DefaultEvalMetric()};
615+
config["lambdarank_param"] = ToJson(param_);
616+
return config;
617+
}
613618
};
614619

615620
#if !defined(XGBOOST_USE_CUDA)

tests/cpp/common/test_parameter.cc

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,4 +97,9 @@ TEST(XGBoostParameter, Update) {
9797
ASSERT_NEAR(p.f, 2.71828f, kRtEps);
9898
ASSERT_NEAR(p.d, 2.71828, kRtEps); // default
9999
}
100+
101+
// Just in case dmlc's use of global memory has any impact in parameters.
102+
UpdatableParam a, b;
103+
a.UpdateAllowUnknown(xgboost::Args{{"f", "2.71828"}});
104+
ASSERT_NE(a.f, b.f);
100105
}

tests/python/test_ranking.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,20 @@ def ndcg_gain(y: np.ndarray) -> np.ndarray:
5454
assert byxgb.evals_result() == bynp.evals_result()
5555
assert byxgb_json == bynp_json
5656

57+
# test pairwise can handle max_rel > 31, while ndcg metric is using custom gain
58+
X, y, q, w = tm.make_ltr(n_samples=1024, n_features=4, n_query_groups=3, max_rel=33)
59+
ranknet = xgboost.XGBRanker(
60+
tree_method="hist",
61+
ndcg_exp_gain=False,
62+
n_estimators=10,
63+
objective="rank:pairwise",
64+
)
65+
ranknet.fit(X, y, qid=q, eval_set=[(X, y)], eval_qid=[q])
66+
history = ranknet.evals_result()
67+
assert (
68+
history["validation_0"]["ndcg@32"][0] < history["validation_0"]["ndcg@32"][-1]
69+
)
70+
5771

5872
def test_ranking_with_unweighted_data():
5973
Xrow = np.array([1, 2, 6, 8, 11, 14, 16, 17])

0 commit comments

Comments
 (0)