gbrank排序为什么会造成预测值为负的原因

1:说到gbrank,做推荐算法的人都很熟悉,这是一种learning to rank的经典方式,在现有的大公司中依然会充当线上的排序模型,博主在使用这个模型在做排序的时候,遇到一种情况,也就是这篇文章的题目,查阅了一些原理博客和源码后,搞清了这个原因,现做下分享。

2:首先gbrank他不是一个分类模型,是一种排序模型,内部用到的决策树也是回归树,所以得到负分也不足为奇。

3:推荐一篇讲的很好的博客https://www.cnblogs.com/bentuwuying/p/6684585.html

      推荐一个github上的源码https://github.com/szdr/my-gbrank

      虽然这是不同的两个人写的,但是博客中的数学公式都很好的在源码中做了体现

4:具体的内容可以参考那篇博客中的内容,我主要结合原理和源码给大家讲解清楚问什么会有负分的原因

上面公式中的更新变量,实际上是gbrank内部在组建决策树训练所需要的数据的时候,对两个样本的真实label在进行变换,目的是加大这两个样本的label差,所以给其中一个加上常数,另一个减去常数,其中的常数是由自己设定的,一般取0.5即可,这两个公式在代码中的体现在

ys_target_in_qid[ind_1] + self.tau就是公式的体现。

个人觉得博主的gbrank.py这个文件中的fit函数中的 for 循环写的有些冗余。ys_predict = self._predict(X_target, n_tree) 这行代码计算出来预测值,并没有啥意义。和 predict_value相关的代码也都可以删掉。每棵树的样本数都是 N*0.8,但是每棵树都是随机从N个样本中抽取的,因为下面的代码

target_index = np.random.choice(
                X.shape[0],
                int(X.shape[0] * self.sampling_rate),
                replace=False
)

5:博客中下面的公式是结合20棵树对样本的预测结果,进行线性叠加得到最终结果

在代码中的体现为

def _predict(self, X, n_predict_trees):
        # n_predict_trees本の木による予測結果リストを求める
        predict_lst_by_trees = [self.trees[n_tree].predict(X) for n_tree in range(n_predict_trees)]
        # 各木による予測を統合する
        predict_result = predict_lst_by_trees[0]
        for n_tree in range(1, n_predict_trees):
            predict_result = (n_tree * predict_result + self.shrinkage * predict_lst_by_trees[n_tree]) / (n_tree + 1)
        return predict_result

可以看到,predict_lst_by_trees中存放的是20棵树每一棵的预测结果,然后下面用公式进行了线性叠加

n_tree就相当于公式中的k。predict_result就相当于公式中的f(k-1)(x)也就是前面所有棵树的的综合预测结果,predict_lst_by_trees[n_tree]相当于公式中的 g(k)(x),是当前的一棵树的预测结果,self.shrinkage就是公式中的 shrinking系数。

6:有负分的原因,这个首先看 输送到决策树中的样本的label可以看到,有对真实label进行加减一个常数,而且还有一个细微的操作(进行加常数的那个样本的真实label要大,进行减常数的那个样本的真实label要小,所以会将样本间的label差距拉开的更大)

第二步要看回归树是怎么定义预测值的,也就是regression_tree.py 这个函数,我把里面进行样本计算预测值的那几行代码贴出来

for split in range(1, argsort.shape[0]):
    # [0, split), [split, N_target)で分割
    tmp_left_data_index = argsort[:split]
    tmp_right_data_index = argsort[split:]

    left_predict = np.mean(ys_target[tmp_left_data_index])
    left_squared_error = np.sum((ys_target[tmp_left_data_index] - left_predict) ** 2)
    right_predict = np.mean(ys_target[tmp_right_data_index])
    right_squared_error = np.sum((ys_target[tmp_right_data_index] - right_predict) ** 2)

可以看到用到的是“最小二乘法”来计算每个左节点和右节点的误差,用某个节点的所有样本的label均值来当做预测值,又因为每个样本的label有正有负,所以最终得到负的预测值也就可理解了。

猜你喜欢

转载自blog.csdn.net/a1066196847/article/details/83382708