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有正有負,所以最終得到負的預測值也就可理解了。