本文主要內容概覽:
1. CatBoost簡介CatBoost是俄羅斯的搜索巨頭Yandex在2017年開源的機器學習庫,是Boosting族算法的一種。CatBoost和XGBoost、LightGBM並稱為GBDT的三大主流神器,都是在GBDT算法框架下的一種改進實現。XGBoost被廣泛的應用於工業界,LightGBM有效的提升了GBDT的計算效率,而Yandex的CatBoost號稱是比XGBoost和LightGBM在算法準確率等方面表現更為優秀的算法。
CatBoost是一種基於對稱決策樹(oblivious trees)為基學習器實現的參數較少、支持類別型變量和高準確性的GBDT框架,主要解決的痛點是高效合理地處理類別型特徵,這一點從它的名字中可以看出來,CatBoost是由Categorical和Boosting組成。此外,CatBoost還解決了梯度偏差(Gradient Bias)以及預測偏移(Prediction shift)的問題,從而減少過擬合的發生,進而提高算法的準確性和泛化能力。
與XGBoost、LightGBM相比,CatBoost的創新點有:
嵌入了自動將類別型特徵處理為數值型特徵的創新算法。首先對categorical features做一些統計,計算某個類別特徵(category)出現的頻率,之後加上超參數,生成新的數值型特徵(numerical features)。Catboost還使用了組合類別特徵,可以利用到特徵之間的聯繫,這極大的豐富了特徵維度。採用排序提升的方法對抗訓練集中的噪聲點,從而避免梯度估計的偏差,進而解決預測偏移的問題。2. 類別型特徵2.1 類別型特徵的相關工作所謂類別型特徵,即這類特徵不是數值型特徵,而是離散的集合,比如省份名(山東、山西、河北等),城市名(北京、上海、深圳等),學歷(本科、碩士、博士等)。在梯度提升算法中,最常用的是將這些類別型特徵轉為數值型來處理,一般類別型特徵會轉化為一個或多個數值型特徵。
如果某個類別型特徵基數比較低(low-cardinality features),即該特徵的所有值去重後構成的集合元素個數比較少,一般利用One-hot編碼方法將特徵轉為數值型。One-hot編碼可以在數據預處理時完成,也可以在模型訓練的時候完成,從訓練時間的角度,後一種方法的實現更為高效,CatBoost對於基數較低的類別型特徵也是採用後一種實現。
顯然,在高基數類別型特徵(high cardinality features) 當中,比如 user ID,這種編碼方式會產生大量新的特徵,造成維度災難。一種折中的辦法是可以將類別分組成有限個的群體再進行One-hot編碼。一種常被使用的方法是根據目標變量統計(Target Statistics,以下簡稱TS)進行分組,目標變量統計用於估算每個類別的目標變量期望值。甚至有人直接用TS作為一個新的數值型變量來代替原來的類別型變量。重要的是,可以通過對TS數值型特徵的閾值設置,基於對數損失、基尼係數或者均方差,得到一個對於訓練集而言將類別一分為二的所有可能劃分當中最優的那個。在LightGBM當中,類別型特徵用每一步梯度提升時的梯度統計(Gradient Statistics,以下簡稱GS)來表示。雖然為建樹提供了重要的信息,但是這種方法有以下兩個缺點:
增加計算時間,因為需要對每一個類別型特徵,在迭代的每一步,都需要對GS進行計算;增加存儲需求,對於一個類別型變量,需要存儲每一次分離每個節點的類別;為了克服這些缺點,LightGBM以損失部分信息為代價將所有的長尾類別歸為一類,作者聲稱這樣處理高基數類別型特徵時比One-hot編碼還是好不少。不過如果採用TS特徵,那麼對於每個類別只需要計算和存儲一個數字。
因此,採用TS作為一個新的數值型特徵是最有效、信息損失最小的處理類別型特徵的方法。TS也被廣泛應用在點擊預測任務當中,這個場景當中的類別型特徵有用戶、地區、廣告、廣告發布者等。接下來我們著重討論TS,暫時將One-hot編碼和GS放一邊。
2.2 目標變量統計(Target Statistics)CatBoost算法的設計初衷是為了更好的處理GBDT特徵中的categorical features。在處理 GBDT特徵中的categorical features的時候,最簡單的方法是用 categorical feature 對應的標籤的平均值來替換。在決策樹中,標籤平均值將作為節點分裂的標準。這種方法被稱為 Greedy Target-based Statistics , 簡稱 Greedy TS,用公式來表達就是:
這種方法有一個顯而易見的缺陷,就是通常特徵比標籤包含更多的信息,如果強行用標籤的平均值來表示特徵的話,當訓練數據集和測試數據集數據結構和分布不一樣的時候會出條件偏移問題。
一個標準的改進 Greedy TS的方式是添加先驗分布項,這樣可以減少噪聲和低頻率類別型數據對於數據分布的影響:
其中
當然,在論文《CatBoost: unbiased boosting with categorical features》中,還提到了其它幾種改進Greedy TS的方法,分別有:Holdout TS、Leave-one-out TS、Ordered TS。我這裡就不再翻譯論文中的這些方法了,感興趣的同學可以自己翻看一下原論文。
2.3 特徵組合值得注意的是幾個類別型特徵的任意組合都可視為新的特徵。例如,在音樂推薦應用中,我們有兩個類別型特徵:用戶ID和音樂流派。如果有些用戶更喜歡搖滾樂,將用戶ID和音樂流派轉換為數字特徵時,根據上述這些信息就會丟失。結合這兩個特徵就可以解決這個問題,並且可以得到一個新的強大的特徵。然而,組合的數量會隨著數據集中類別型特徵的數量成指數增長,因此不可能在算法中考慮所有組合。為當前樹構造新的分割點時,CatBoost會採用貪婪的策略考慮組合。對於樹的第一次分割,不考慮任何組合。對於下一個分割,CatBoost將當前樹的所有組合、類別型特徵與數據集中的所有類別型特徵相結合,並將新的組合類別型特徵動態地轉換為數值型特徵。CatBoost還通過以下方式生成數值型特徵和類別型特徵的組合:樹中選定的所有分割點都被視為具有兩個值的類別型特徵,並像類別型特徵一樣被進行組合考慮。
2.4 CatBoost處理Categorical features總結首先會計算一些數據的statistics。計算某個category出現的頻率,加上超參數,生成新的numerical features。這一策略要求同一標籤數據不能排列在一起(即先全是對於學習CatBoost克服梯度偏差的內容,我提出了三個問題:
CatBoost和所有標準梯度提升算法一樣,都是通過構建新樹來擬合當前模型的梯度。然而,所有經典的提升算法都存在由有偏的點態梯度估計引起的過擬合問題。在每個步驟中使用的梯度都使用當前模型中的相同的數據點來估計,這導致估計梯度在特徵空間的任何域中的分布與該域中梯度的真實分布相比發生了偏移,從而導致過擬合。為了解決這個問題,CatBoost對經典的梯度提升算法進行了一些改進,簡要介紹如下。
許多利用GBDT技術的算法(例如,XGBoost、LightGBM),構建下一棵樹分為兩個階段:選擇樹結構和在樹結構固定後計算葉子節點的值。為了選擇最佳的樹結構,算法通過枚舉不同的分割,用這些分割構建樹,對得到的葉子節點計算值,然後對得到的樹計算評分,最後選擇最佳的分割。兩個階段葉子節點的值都是被當做梯度或牛頓步長的近似值來計算。在CatBoost中,第一階段採用梯度步長的無偏估計,第二階段使用傳統的GBDT方案執行。既然原來的梯度估計是有偏的,那麼怎麼能改成無偏估計呢?
設
對於學習預測偏移的內容,我提出了兩個問題:
預測偏移(Prediction shift)是由梯度偏差造成的。在GDBT的每一步迭代中, 損失函數使用相同的數據集求得當前模型的梯度, 然後訓練得到基學習器, 但這會導致梯度估計偏差, 進而導致模型產生過擬合的問題。CatBoost通過採用排序提升 (Ordered boosting) 的方式替換傳統算法中梯度估計方法,進而減輕梯度估計的偏差,提高模型的泛化能力。下面我們對預測偏移進行詳細的描述和分析。
首先來看下GBDT的整體迭代過程:
GBDT算法是通過一組分類器的串行迭代,最終得到一個強學習器,以此來進行更高精度的分類。它使用了前向分布算法,弱學習器使用分類回歸樹(CART)。
假設前一輪迭代得到的強學習器是
GBDT使用損失函數的負梯度來擬合每一輪的損失的近似值,式(2)中
通常用式(3)近似擬合
最終得到本輪的強學習器,如式(4)所示:
在這個過程當中,偏移是這樣發生的:
根據
為了克服預測偏移問題,CatBoost提出了一種新的叫做Ordered boosting的算法。
由上圖的Ordered boosting算法可知,為了得到無偏梯度估計, CatBoost對每一個樣本
Ordered boosting算法好是好,但是在大部分的實際任務當中都不具備使用價值,因為需要訓練
前面提到過,在傳統的GBDT框架當中,構建下一棵樹分為兩個階段:選擇樹結構和在樹結構固定後計算葉子節點的值。CatBoost主要在第一階段進行優化。在建樹的階段,CatBoost有兩種提升模式,Ordered和Plain。Plain模式是採用內建的ordered TS對類別型特徵進行轉化後的標準GBDT算法。Ordered則是對Ordered boosting算法的優化。兩種提升模式的具體介紹可以翻看論文《CatBoost: unbiased boosting with categorical features》。
5. 快速評分CatBoost使用對稱樹(oblivious trees)作為基預測器。在這類樹中,相同的分割準則在樹的整個一層上使用。這種樹是平衡的,不太容易過擬合。梯度提升對稱樹被成功地用於各種學習任務中。在對稱樹中,每個葉子節點的索引可以被編碼為長度等於樹深度的二進位向量。這在CatBoost模型評估器中得到了廣泛的應用:我們首先將所有浮點特徵、統計信息和獨熱編碼特徵進行二值化,然後使用二進位特徵來計算模型預測值。
6. 基於GPU實現快速訓練密集的數值特徵。 對於任何GBDT算法而言,最大的難點之一就是搜索最佳分割。尤其是對於密集的數值特徵數據集來說,該步驟是建立決策樹時的主要計算負擔。CatBoost使用oblivious 決策樹作為基模型,並將特徵離散化到固定數量的箱子中以減少內存使用。就GPU內存使用而言,CatBoost至少與LightGBM一樣有效。主要改進之處就是利用了一種不依賴於原子操作的直方圖計算方法。類別型特徵。 CatBoost實現了多種處理類別型特徵的方法,並使用完美哈希來存儲類別型特徵的值,以減少內存使用。由於GPU內存的限制,在CPU RAM中存儲按位壓縮的完美哈希,以及要求的數據流、重疊計算和內存等操作。通過哈希來分組觀察。在每個組中,我們需要計算一些統計量的前綴和。該統計量的計算使用分段掃描GPU圖元實現。多GPU支持。 CatBoost中的GPU實現可支持多個GPU。分布式樹學習可以通過數據或特徵進行並行化。CatBoost採用多個學習數據集排列的計算方案,在訓練期間計算類別型特徵的統計數據。7. CatBoost的優缺點7.1 優點性能卓越: 在性能方面可以匹敵任何先進的機器學習算法;魯棒性/強健性: 它減少了對很多超參數調優的需求,並降低了過度擬合的機會,這也使得模型變得更加具有通用性;易於使用: 提供與scikit集成的Python接口,以及R和命令行界面;7.2 缺點8. CatBoost實例本篇文章所有數據集和代碼均在我的GitHub中,地址:https://github.com/Microstrong0305/WeChat-zhihu-csdnblog-code/tree/master/Ensemble%20Learning/CatBoost
8.1 安裝CatBoost依賴包8.2 CatBoost分類(1)數據集這裡我使用了 2015 年航班延誤的 Kaggle 數據集,其中同時包含類別型變量和數值變量。這個數據集中一共有約 500 萬條記錄,我使用了 1% 的數據:5 萬行記錄。數據集官方地址:https://www.kaggle.com/usdot/flight-delays#flights.csv 。以下是建模使用的特徵:
到達延誤情況: 這個特徵作為預測目標,並轉為二值變量:航班是否延誤超過 10 分鐘實驗說明: 在對 CatBoost 調參時,很難對類別型特徵賦予指標。因此,同時給出了不傳遞類別型特徵時的調參結果,並評估了兩個模型:一個包含類別型特徵,另一個不包含。如果未在cat_features參數中傳遞任何內容,CatBoost會將所有列視為數值變量。注意,如果某一列數據中包含字符串值,CatBoost 算法就會拋出錯誤。另外,帶有默認值的 int 型變量也會默認被當成數值數據處理。在 CatBoost 中,必須對變量進行聲明,才可以讓算法將其作為類別型變量處理。
(2)不加Categorical features選項的代碼import pandas as pd, numpy as npfrom sklearn.model_selection import train_test_split, GridSearchCVfrom sklearn import metricsimport catboost as cb
# 一共有約 500 萬條記錄,我使用了 1% 的數據:5 萬行記錄# data = pd.read_csv("flight-delays/flights.csv")# data = data.sample(frac=0.1, random_state=10) # 500->50# data = data.sample(frac=0.1, random_state=10) # 50->5# data.to_csv("flight-delays/min_flights.csv")
# 讀取 5 萬行記錄data = pd.read_csv("flight-delays/min_flights.csv")print(data.shape) # (58191, 31)
data = data[["MONTH", "DAY", "DAY_OF_WEEK", "AIRLINE", "FLIGHT_NUMBER", "DESTINATION_AIRPORT", "ORIGIN_AIRPORT", "AIR_TIME", "DEPARTURE_TIME", "DISTANCE", "ARRIVAL_DELAY"]]data.dropna(inplace=True)
data["ARRIVAL_DELAY"] = (data["ARRIVAL_DELAY"] > 10) * 1
cols = ["AIRLINE", "FLIGHT_NUMBER", "DESTINATION_AIRPORT", "ORIGIN_AIRPORT"]for item in cols: data[item] = data[item].astype("category").cat.codes + 1
train, test, y_train, y_test = train_test_split(data.drop(["ARRIVAL_DELAY"], axis=1), data["ARRIVAL_DELAY"], random_state=10, test_size=0.25)
cat_features_index = [0, 1, 2, 3, 4, 5, 6]
def auc(m, train, test): return (metrics.roc_auc_score(y_train, m.predict_proba(train)[:, 1]), metrics.roc_auc_score(y_test, m.predict_proba(test)[:, 1]))
# 調參,用網格搜索調出最優參數params = {'depth': [4, 7, 10], 'learning_rate': [0.03, 0.1, 0.15], 'l2_leaf_reg': [1, 4, 9], 'iterations': [300, 500]}cb = cb.CatBoostClassifier()cb_model = GridSearchCV(cb, params, scoring="roc_auc", cv=3)cb_model.fit(train, y_train)# 查看最佳分數print(cb_model.best_score_) # 0.7088001891107445# 查看最佳參數print(cb_model.best_params_) # {'depth': 4, 'iterations': 500, 'l2_leaf_reg': 9, 'learning_rate': 0.15}
# With Categorical features,用最優參數擬合數據clf = cb.CatBoostClassifier(eval_metric="AUC", depth=4, iterations=500, l2_leaf_reg=9, learning_rate=0.15)
clf.fit(train, y_train)
print(auc(clf, train, test)) # (0.7809684655761157, 0.7104617034553192)(3)有Categorical features選項的代碼import pandas as pd, numpy as npfrom sklearn.model_selection import train_test_split, GridSearchCVfrom sklearn import metricsimport catboost as cb
# 讀取 5 萬行記錄data = pd.read_csv("flight-delays/min_flights.csv")print(data.shape) # (58191, 31)
data = data[["MONTH", "DAY", "DAY_OF_WEEK", "AIRLINE", "FLIGHT_NUMBER", "DESTINATION_AIRPORT", "ORIGIN_AIRPORT", "AIR_TIME", "DEPARTURE_TIME", "DISTANCE", "ARRIVAL_DELAY"]]data.dropna(inplace=True)
data["ARRIVAL_DELAY"] = (data["ARRIVAL_DELAY"] > 10) * 1
cols = ["AIRLINE", "FLIGHT_NUMBER", "DESTINATION_AIRPORT", "ORIGIN_AIRPORT"]for item in cols: data[item] = data[item].astype("category").cat.codes + 1
train, test, y_train, y_test = train_test_split(data.drop(["ARRIVAL_DELAY"], axis=1), data["ARRIVAL_DELAY"], random_state=10, test_size=0.25)
cat_features_index = [0, 1, 2, 3, 4, 5, 6]
def auc(m, train, test): return (metrics.roc_auc_score(y_train, m.predict_proba(train)[:, 1]), metrics.roc_auc_score(y_test, m.predict_proba(test)[:, 1]))
# With Categorical featuresclf = cb.CatBoostClassifier(eval_metric="AUC", one_hot_max_size=31, depth=4, iterations=500, l2_leaf_reg=9, learning_rate=0.15)clf.fit(train, y_train, cat_features=cat_features_index)
print(auc(clf, train, test)) # (0.7817912095285117, 0.7152541135019913)8.3 CatBoost回歸from catboost import CatBoostRegressor
# Initialize data
train_data = [[1, 4, 5, 6], [4, 5, 6, 7], [30, 40, 50, 60]]
eval_data = [[2, 4, 6, 8], [1, 4, 50, 60]]
train_labels = [10, 20, 30]# Initialize CatBoostRegressormodel = CatBoostRegressor(iterations=2, learning_rate=1, depth=2)# Fit modelmodel.fit(train_data, train_labels)# Get predictionspreds = model.predict(eval_data)print(preds)9. 關於CatBoost若干問題思考9.1 CatBoost與XGBoost、LightGBM的聯繫與區別?(1)2014年3月XGBoost算法首次被陳天奇提出,但是直到2016年才逐漸著名。2017年1月微軟發布LightGBM第一個穩定版本。2017年4月Yandex開源CatBoost。自從XGBoost被提出之後,很多文章都在對其進行各種改進,CatBoost和LightGBM就是其中的兩種。
(2)CatBoost處理類別型特徵十分靈活,可直接傳入類別型特徵的列標識,模型會自動將其使用One-hot編碼,還可通過設置 one_hot_max_size參數來限制One-hot特徵向量的長度。如果不傳入類別型特徵的列標識,那麼CatBoost會把所有列視為數值特徵。對於One-hot編碼超過設定的one_hot_max_size值的特徵來說,CatBoost將會使用一種高效的encoding方法,與mean encoding類似,但是會降低過擬合。處理過程如下:
將所有的類別型特徵值結果都根據以下公式,轉化為數值結果;其中 countInClass 表示在當前類別型特徵值中有多少樣本的標記值是
;prior 是分子的初始值,根據初始參數確定。totalCount 是在所有樣本中(包含當前樣本)和當前樣本具有相同的類別型特徵值的樣本數量。 LighGBM 和 CatBoost 類似,也可以通過使用特徵名稱的輸入來處理類別型特徵數據,它沒有對數據進行獨熱編碼,因此速度比獨熱編碼快得多。LighGBM 使用了一個特殊的算法來確定屬性特徵的分割值。
train_data = lgb.Dataset(data, label=label, feature_name=['c1', 'c2', 'c3'], categorical_feature=['c3'])注意,在建立適用於 LighGBM 的數據集之前,需要將類別型特徵變量轉化為整型變量,此算法不允許將字符串數據傳給類別型變量參數。
XGBoost 和 CatBoost、 LighGBM 算法不同,XGBoost 本身無法處理類別型特徵,而是像隨機森林一樣,只接受數值數據。因此在將類別型特徵數據傳入 XGBoost 之前,必須通過各種編碼方式:例如,序號編碼、獨熱編碼和二進位編碼等對數據進行處理。
10. ReferenceCatBoost論文解讀:
【1】Prokhorenkova L, Gusev G, Vorobev A, et al. CatBoost: unbiased boosting with categorical features[C]//Advances in Neural Information Processing Systems. 2018: 6638-6648.
【2】Dorogush A V, Ershov V, Gulin A. CatBoost: gradient boosting with categorical features support[J]. arXiv preprint arXiv:1810.11363, 2018.
【3】機器學習算法之Catboost,地址:https://www.biaodianfu.com/catboost.html
【4】oblivious tree在機器學習中有什麼用?- 李大貓的回答 - 知乎 https://www.zhihu.com/question/311641149/answer/593286799
【5】CatBoost算法梳理,地址:http://datacruiser.io/2019/08/19/DataWhale-Workout-No-8-CatBoost-Summary/CatBoost算法講解:
【6】catboost完全指南,地址:https://zhuanlan.zhihu.com/p/102570430
【7】CatBoost原理及實踐 - Dukey的文章 - 知乎 https://zhuanlan.zhihu.com/p/37916954
【8】 22(7).模型融合---CatBoost,地址:https://www.cnblogs.com/nxf-rabbit75/p/10923549.html#auto_id_0
【9】catboost對類別特徵處理的簡單總結,地址:https://blog.csdn.net/weixin_42944192/article/details/102463796
【10】Python3機器學習實踐:集成學習之CatBoost - AnFany的文章 - 知乎 https://zhuanlan.zhihu.com/p/53591724CatBoost實戰:
【11】Catboost 一個超級簡單實用的boost算法,地址:https://www.jianshu.com/p/49ab87122562
【12】苗豐順, 李巖, 高岑, 王美吉, 李冬梅. 基於CatBoost算法的糖尿病預測方法. 計算機系統應用, 2019, 28(9): 215-218.http://www.c-s-a.org.cn/1003-3254/7054.html
【13】Battle of the Boosting Algos: LGB, XGB, Catboost,地址:https://www.kaggle.com/lavanyashukla01/battle-of-the-boosting-algos-lgb-xgb-catboost
【14】Simple CatBoost,地址:https://www.kaggle.com/nicapotato/simple-catboost
【15】CatBoost Usage examples,地址:https://catboost.ai/docs/concepts/python-usages-examples.htmlCatBoost的若干思考:
【16】入門 | 從結構到性能,一文概述XGBoost、Light GBM和CatBoost的同與不同,地址:https://mp.weixin.qq.com/s/TD3RbdDidCrcL45oWpxNmw