我目前正在XGBoost / lightGBM之间运行测试,因为他们能够对项目进行排名。我正在复制这里提出的基准:https://github.com/guolinke/boosting_tree_benchmarks。
我已经能够成功地重现他们工作中提到的基准。我想确保我正确实现自己的ndcg指标版本,并正确理解排名问题。
我的问题是:
使用ndcg创建测试集的验证时 - 有一个test.group文件,表示前X行是0组,等等。为了获得组的推荐,我得到了预测值和已知的相关性得分和排序按每组降序预测值列出?
为了从上面创建的列表中获得最终的ndcg分数 - 我是否获得ndcg分数并取得所有分数的均值?这与评估阶段XGBoost / lightGBM的评估方法相同吗?
以下是我在模型完成培训后评估测试集的方法。
对于运行lightGBM
时的最终树,我在验证集上获取这些值:
[500] valid_0's ndcg@1: 0.513221 valid_0's ndcg@3: 0.499337 valid_0's ndcg@5: 0.505188 valid_0's ndcg@10: 0.523407
我的最后一步是获取测试集的预测输出并计算预测的ndcg值。
这是我的用于计算ndcg的python代码:
import numpy as np
def dcg_at_k(r, k):
r = np.asfarray(r)[:k]
if r.size:
return np.sum(np.subtract(np.power(2, r), 1) / np.log2(np.arange(2, r.size + 2)))
return 0.
def ndcg_at_k(r, k):
idcg = dcg_at_k(sorted(r, reverse=True), k)
if not idcg:
return 0.
return dcg_at_k(r, k) / idcg
在我得到特定组( GROUP-0 )的测试集的预测后,我有这些预测:
query_id predict
0 0 (2.0, -0.221681199441)
1 0 (1.0, 0.109895548348)
2 0 (1.0, 0.0262799346312)
3 0 (0.0, -0.595343431322)
4 0 (0.0, -0.52689043426)
5 0 (0.0, -0.542221350664)
6 0 (1.0, -0.448015576024)
7 0 (1.0, -0.357090949646)
8 0 (0.0, -0.279677741045)
9 0 (0.0, 0.2182200869)
注意
Group-0 实际上有大约112行。
然后我按降序对元组列表进行排序,提供相关性分数列表:
def get_recommendations(x):
sorted_list = sorted(list(x), key=lambda i: i[1], reverse=True)
return [k for k, _ in sorted_list]
relavance = evaluation.groupby('query_id').predict.apply(get_recommendations)
query_id
0 [4.0, 2.0, 2.0, 3.0, 2.0, 2.0, 2.0, 2.0, 2.0, ...
1 [4.0, 2.0, 2.0, 2.0, 1.0, 1.0, 3.0, 2.0, 1.0, ...
2 [2.0, 3.0, 2.0, 2.0, 1.0, 0.0, 2.0, 2.0, 1.0, ...
3 [2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, ...
4 [1.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, ...
最后,对于每个查询ID,我计算了相关性列表中的ndcg分数,然后取出为每个查询ID计算的所有ndcg分数的均值:
relavance.apply(lambda x: ndcg_at_k(x, 10)).mean()
我获得的值是~0.497193
。
答案 0 :(得分:1)
交叉发布我对这个交叉发布问题的交叉验证答案: https://stats.stackexchange.com/questions/303385/how-does-xgboost-lightgbm-evaluate-ndcg-metric-for-ranking/487487#487487
我自己也遇到过这个问题,最后深入代码找出了答案。
不同之处在于对丢失的 IDCG 的处理。您的代码返回 0,而 LightGBM is treating that case as a 1。
以下代码为我生成了匹配结果:
import numpy as np
def dcg_at_k(r, k):
r = np.asfarray(r)[:k]
if r.size:
return np.sum(np.subtract(np.power(2, r), 1) / np.log2(np.arange(2, r.size + 2)))
return 0.
def ndcg_at_k(r, k):
idcg = dcg_at_k(sorted(r, reverse=True), k)
if not idcg:
return 1. # CHANGE THIS
return dcg_at_k(r, k) / idcg
答案 1 :(得分:0)
我认为问题是由同一查询中具有相同标签的数据引起的。 在这种情况下,XGBoost和LightGBM都将为该查询生成ndcg 1。