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

Fix pairwise objective with NDCG metric along with custom gain. #10100

Merged
merged 2 commits into from
Mar 11, 2024
Merged
Show file tree
Hide file tree
Changes from all 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
9 changes: 7 additions & 2 deletions src/objective/lambdarank_obj.cc
Original file line number Diff line number Diff line change
Expand Up @@ -474,7 +474,6 @@ class LambdaRankMAP : public LambdaRankObj<LambdaRankMAP, ltr::MAPCache> {
public:
void GetGradientImpl(std::int32_t iter, const HostDeviceVector<float>& predt,
const MetaInfo& info, linalg::Matrix<GradientPair>* out_gpair) {
CHECK(param_.ndcg_exp_gain) << "NDCG gain can not be set for the MAP objective.";
if (ctx_->IsCUDA()) {
return cuda_impl::LambdaRankGetGradientMAP(
ctx_, iter, predt, info, GetCache(), ti_plus_.View(ctx_->Device()),
Expand Down Expand Up @@ -564,7 +563,6 @@ class LambdaRankPairwise : public LambdaRankObj<LambdaRankPairwise, ltr::Ranking
public:
void GetGradientImpl(std::int32_t iter, const HostDeviceVector<float>& predt,
const MetaInfo& info, linalg::Matrix<GradientPair>* out_gpair) {
CHECK(param_.ndcg_exp_gain) << "NDCG gain can not be set for the pairwise objective.";
if (ctx_->IsCUDA()) {
return cuda_impl::LambdaRankGetGradientPairwise(
ctx_, iter, predt, info, GetCache(), ti_plus_.View(ctx_->Device()),
Expand Down Expand Up @@ -610,6 +608,13 @@ class LambdaRankPairwise : public LambdaRankObj<LambdaRankPairwise, ltr::Ranking
[[nodiscard]] const char* DefaultEvalMetric() const override {
return this->RankEvalMetric("ndcg");
}

[[nodiscard]] Json DefaultMetricConfig() const override {
Json config{Object{}};
config["name"] = String{DefaultEvalMetric()};
config["lambdarank_param"] = ToJson(param_);
return config;
}
};

#if !defined(XGBOOST_USE_CUDA)
Expand Down
5 changes: 5 additions & 0 deletions tests/cpp/common/test_parameter.cc
Original file line number Diff line number Diff line change
Expand Up @@ -97,4 +97,9 @@ TEST(XGBoostParameter, Update) {
ASSERT_NEAR(p.f, 2.71828f, kRtEps);
ASSERT_NEAR(p.d, 2.71828, kRtEps); // default
}

// Just in case dmlc's use of global memory has any impact in parameters.
UpdatableParam a, b;
a.UpdateAllowUnknown(xgboost::Args{{"f", "2.71828"}});
ASSERT_NE(a.f, b.f);
}
14 changes: 14 additions & 0 deletions tests/python/test_ranking.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,20 @@ def ndcg_gain(y: np.ndarray) -> np.ndarray:
assert byxgb.evals_result() == bynp.evals_result()
assert byxgb_json == bynp_json

# test pairwise can handle max_rel > 31, while ndcg metric is using custom gain
X, y, q, w = tm.make_ltr(n_samples=1024, n_features=4, n_query_groups=3, max_rel=33)
ranknet = xgboost.XGBRanker(
tree_method="hist",
ndcg_exp_gain=False,
n_estimators=10,
objective="rank:pairwise",
)
ranknet.fit(X, y, qid=q, eval_set=[(X, y)], eval_qid=[q])
history = ranknet.evals_result()
assert (
history["validation_0"]["ndcg@32"][0] < history["validation_0"]["ndcg@32"][-1]
)


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