XGBoost是一種流行的技術,是傳統回歸/神經網絡的完美替代方案。它代表E X treme G radient Boost ing,基本上構建了一些決策樹來計算梯度
這聽起來很簡單,但可以非常強大。以帕金森檢測為例:我們有幾個指標可以分析,最終我們需要診斷帕金森病(分類!)。
讓我們開始收集一些數據:跳到UCI的ML數據庫并下載帕金森氏調用的數據集parkinsons.data。他們是CSV,所以我們可以用Pandas快速解析它們:
df=pd.read_csv('parkinsons.data')
接下來,我們需要獲取功能和標簽。除了第一列(名稱)外,這些列方便地是所有數字,并且具有標簽的列是“狀態”(已經是0或1)。讓我們忽略他們現在的含義,并盲目分析它們(不要在實踐中這樣做)。這使我們能夠很快地獲得培訓數據:
features=df.loc [:, df.columns!='status']。values [:, 1:]
labels=df.loc [:,'status']。values
接下來,我們需要調整我們的功能,以便它們在-1和1之間,以便它們被標準化。我們可以用這樣sklearn的輝煌來做到這一點MinMaxScaler:
Scaler=MinMaxScaler(( - 1,1))
X=scaler.fit_transform(features)
到目前為止我們已經有5條線了。接下來,讓我們將其分解為訓練和測試數據,以便防止過度擬合。沒有太多的數據點,所以讓我們分成14%的測試數據,這次使用sklearn的train_test_split便利功能:
X_r, X_s, Y_r, Y_s=train_test_split(X, labels, test_size=0.14)
然后我們使用xgboost已經構建好的用于分類并通過xgboost模塊(pip install xgboost)提供的XGBClassifier :
model=XGBClassifier()
model.fit(X_r,Y_r)
讓我們通過評估我們的模型與之前的測試集來評估我們的模型,其accuracy_score功能來自sklearn:
Y_hat=[round(yhat) for yhat in model.predict(X_test)]
print(accuracy_score(Y_test, Y_hat))
你應該看到在90年代的高準確度(在測試集~96.42%)!
就是這樣!10行代碼,你已經為帕金森氏訓練了一個完整的XGBoosting分類器。
XGBoosting功能非常強大,絕對可以成為您下一個項目的有用工具!這更深入 - 對于多輸出,你需要一個MultiOutput模型(SciKit Learn有很好的包裝),為了獲得更準確的信息,你需要微調你的XGBoost模型。
來自俄羅斯在線搜索公司Yandex的CatBoost快速且易于使用,但同一家公司的研究人員最近發布了一種基于神經網絡的新軟件包NODE,聲稱其性能優于CatBoost和所有其他梯度增強方法。 這是真的嗎? 讓我們找出如何同時使用CatBoost和NODE!
盡管我是為那些對機器學習特別是表格數據感興趣的人寫這篇博客的,但是如果您熟悉Python和scikit-learn庫,并且希望跟隨代碼一起學習,對您很有幫助。 否則,希望您會發現理論和概念方面都很有趣!
CatBoost是我建模表格數據的首選包。這是一個梯度增強決策樹的實現,只是做了一些微調,使其與例如xgboost或LightGBM略有不同。它對分類和回歸問題都有效。
關于CatBoost的一些好處:
· 它處理分類特征(雖然不是最優解),所以你不需要擔心如何編碼它們。
· 它通常只需要很少的參數調優。
· 它避免了其他方法可能遭受的某些微妙類型的數據泄漏。
· 它速度很快,如果你想讓它跑得更快,可以在GPU上運行。
這些因素使得CatBoost對我來說,當我需要分析一個新的表格數據集時,首先要做的就是使用它。
如果你只是想使用CatBoost,請跳過這一節!
在更技術的層面上,關于CatBoost的實現有一些有趣的事情。如果您對細節感興趣,我強烈推薦論文Catboost: unbiased boosting with categorical features。我只想強調兩件事。
在論文中,作者指出,標準的梯度增強算法會受到一些微妙的數據泄漏的影響,這些泄漏是由模型的迭代擬合方式引起的。同樣,最有效的對分類特征進行數字編碼的方法(如目標編碼)也容易出現數據泄漏和過擬合。為了避免這種泄漏,CatBoost引入了一個人工時間軸,根據訓練示例到達的時間軸,這樣在計算統計數據時只能使用"以前看到的"示例。
CatBoost實際上并不使用常規決策樹,而是使用遺忘的決策樹。在這些樹中,在樹的每一層上,相同的特性和相同的分割標準被到處使用!這聽起來很奇怪,但有一些不錯的屬性。讓我們看看這是什么意思。
遺忘的決策樹。 每個級別都有相同的拆分。
常規決策樹。 每個級別上都可以存在任何功能或分割點。
在普通的決策樹中,要分割的特性和截止值都取決于到目前為止在樹中所走的路徑。這是有意義的,因為我們可以使用我們已經擁有的信息來決定最有意義的下一個問題。有了健忘決策樹,歷史就不重要了;我們無論如何都要提出同樣的問題。這些樹被稱為"健忘的",因為它們總是"忘記"發生過的事情。
為什么這個有用?健忘決策樹的一個很好的特性是,一個例子可以非常快速地分類或得分——它總是提出相同的N個二叉問題(其中N是樹的深度)。對于許多例子來說,這可以很容易地并行完成。這是CatBoost快速發展的原因之一。另一件要記住的事情是我們這里處理的是一個樹集合。作為一種獨立的算法,健忘決策樹可能沒有那么好,但樹集合的思想是,由于錯誤和偏見被"洗掉",一個弱學習者的聯盟經常工作得很好。通常情況下,弱學習者是一棵標準的決策樹,而在這里,它甚至更弱,也就是健忘決策樹。CatBoost的作者認為,這種特殊的弱學習者在泛化方面工作得很好。
安裝CatBoost是非常簡單的
pip install catboost
我在Mac上有時會遇到這樣的問題。在Linux系統上,比如我現在輸入的Ubuntu系統,或者在谷歌Colaboratory上,它應該"正常工作"。如果安裝時一直有問題,可以考慮使用Docker鏡像。
docker pull yandex/tutorial-catboost-clickhouse docker run -it yandex/tutorial-catboost-clickhouse
讓我們看看如何在表格數據集上使用CatBoost。我們先下載一個稍微預處理過的成人/人口普查收入數據集,下面假設它位于datasets/ Adult .csv中。我選擇這個數據集是因為它混合了分類和數字特征,在數以萬計的示例中有一個很好的可管理的規模,并且沒有太多的特征。它經常用于舉例說明算法,例如在谷歌的What-If工具和許多其他地方。
成人人口普查數據包括"年齡"、"工作類別"、"教育程度"、"受教育程度"、"婚姻狀況"、"職業"、"關系"、"種族"、"性別"、"資本收益"、"資本損失"、"每周小時"、"本土國家"和"<=50K"。任務是預測最后一列' <=50K '的值,該列指示相關人員的年收入是否為50,000美元或更少(數據集來自1994年)。我們認為以下特征是分類的而不是數字的:"工人階級"、"教育"、"婚姻地位"、"職業"、"關系"、"種族"、"性別"、"原住民"。
該代碼與scikit-learn非常相似,除了CatBoost用于將數據集的特征值和目標值捆綁在一起,同時在概念上保持它們分離的Pool數據類型之外。
代碼可以在Colab上找到(colab/drive/1WezJuc3ioEUZYKh_Mm7YVjWcMeYjDNKP)但我將在這里復制以供參考。CatBoost需要知道哪些特性是分類的,然后自動處理它們。在這個代碼片段中,我還使用了5倍(分層)交叉驗證來估計預測精度。
from catboost import CatBoostClassifier, Pool
from hyperopt import fmin, hp, tpe
import pandas as pd
from sklearn.model_selection import StratifiedKFolddf=pd.read_csv('https://docs.google.com/uc?' +
'id=10eFO2rVlsQBUffn0b7UCAp28n0mkLCy7&' +
'export=download')
labels=df.pop('<=50K')categorical_names=['workclass', 'education', 'marital-status',
'occupation', 'relationship', 'race',
'sex', 'native-country']
categoricals=[df.columns.get_loc(i) for i in categorical_names]nfolds=5
skf=StratifiedKFold(n_splits=nfolds, shuffle=True)
acc=[]for train_index, test_index in skf.split(df, labels):
X_train, X_test=df.iloc[train_index].copy(), \
df.iloc[test_index].copy()
y_train, y_test=labels.iloc[train_index], \
labels.iloc[test_index]
train_pool=Pool(X_train, y_train, cat_features=categoricals)
test_pool=Pool(X_test, y_test, cat_features=categoricals)
model=CatBoostClassifier(iterations=100,
depth=8,
learning_rate=1,
loss_function='MultiClass')
model.fit(train_pool)
predictions=model.predict(test_pool)
accuracy=sum(predictions.squeeze()==y_test) / len(predictions)
acc.append(accuracy)mean_acc=sum(acc) / nfolds
print(f'Mean accuracy based on {nfolds} folds: {mean_acc:.3f}')
print(acc)
通過運行此命令(不進行超參數優化的CatBoost),我們往往會獲得85%到86%的平均準確度。 在上次運行中,我獲得了約85.7%的j結果。
如果我們想嘗試優化超參數,可以使用hyperopt(如果您沒有,請使用pip install hyperopt進行安裝)。 為了使用它,您需要定義一個hyperopt試圖最小化的函數。 我們將在此處嘗試優化準確性。 最佳化例如 log loss,等
要優化的主要參數可能是迭代次數,學習率和樹深度。 還有許多其他與過度擬合相關的參數,例如提前停止回合等。 隨意自行探索!
# Optimize between 10 and 1000 iterations and depth between 2 and 12search_space={'iterations': hp.quniform('iterations', 10, 1000, 10),
'depth': hp.quniform('depth', 2, 12, 1),
'lr': hp.uniform('lr', 0.01, 1)
}def opt_fn(search_space): nfolds=5
skf=StratifiedKFold(n_splits=nfolds, shuffle=True)
acc=[] for train_index, test_index in skf.split(df, labels):
X_train, X_test=df.iloc[train_index].copy(), \
df.iloc[test_index].copy()
y_train, y_test=labels.iloc[train_index], \
labels.iloc[test_index]
train_pool=Pool(X_train, y_train, cat_features=categoricals)
test_pool=Pool(X_test, y_test, cat_features=categoricals) model=CatBoostClassifier(iterations=search_space['iterations'],
depth=search_space['depth'],
learning_rate=search_space['lr'],
loss_function='MultiClass',
od_type='Iter') model.fit(train_pool, logging_level='Silent')
predictions=model.predict(test_pool)
accuracy=sum(predictions.squeeze()==y_test) / len(predictions)
acc.append(accuracy) mean_acc=sum(acc) / nfolds
return -1*mean_accbest=fmin(fn=opt_fn,
space=search_space,
algo=tpe.suggest,
max_evals=100)
當我上次運行此代碼時,它花費了5個小時以上,但平均準確度為87.3%。
在這一點上,我們應該問問自己,這些新奇的方法是否真的有必要。在超參數優化之后,一個好的舊邏輯回歸將如何進行開箱即用?
為了簡單起見,這里我將省略重新生成代碼,但它在Colab筆記本中與以前一樣可用。邏輯回歸實現的一個細節是,它不像CatBoost處理分類變量的,所以我決定代碼使用目標編碼,具體分析目標編碼,這是節點和一個相當接近中采取的方法雖然不是相同的模擬CatBoost會發生什么。
長話短說,使用這種編碼方式的邏輯回歸的未調優精度約為80%,在超參數調優后約為81%(在我最近的運行中為80.7%)。這里,一個有趣的替代方法是嘗試自動預處理庫,如vtreat和Automunge,這個作為后續的優化工作吧!
在嘗試NODE之前,到目前為止我們有什么?
Logistic回歸 ,未調整 :80.0%
Logistic回歸,調整后:80.7%
CatBoost ,未調整:85.7%
CatBoost,調整后:87.2%
Yandex研究人員最近的一份論文描述了CatBoost的一個有趣的神經網絡版本,或者至少是一種用于健忘決策樹集成的神經網絡(如果你想提醒自己這里的"健忘"是什么意思,請參閱上面的技術部分)。這種架構稱為NODE,可以用于分類或回歸。
摘要中的一項聲明是:"通過在大量表格數據集上與領先的GBDT包進行廣泛的實驗比較,我們展示了提議的節點架構的優勢,它在大多數任務上優于競爭對手。"這自然激起了我的興趣。這個工具會比CatBoost更好嗎?
你應該去論文上看完整的介紹,但是一些相關的細節是:
entmax激活函數用作常規決策樹中拆分的軟版本。 正如論文所說," entmax能夠產生稀疏的概率分布,其中大多數概率恰好等于0。在這項工作中,我們認為entmax也是我們模型中的一個適當的歸納偏差,它允許在內部樹節點中構造可微的分裂決策。 直觀地講,entmax可以學習基于小子集的數據特征(最多一個,就像在經典的決策樹中一樣)來分割決策,從而避免了其他方面的不良影響。" entmax函數允許神經網絡模仿決策樹類型的系統,同時保持模型的可微性(權重可以基于梯度進行更新)。
作者提出了一種新型的層,即"節點層",可以在神經網絡中使用(使用PyTorch實現)。節點層表示樹集成。
可以堆疊多個節點層,從而產生一個分層模型,其中輸入一次只能通過一個集成樹來提供。輸入表示的連續連接可以用來給出一個模型,這讓人想起用于圖像處理的流行的DenseNet模型,只是專門用于表格數據。
節點模型的參數為:
1. 學習率(本文均為0.001)
1. 節點層數(k)
1. 每層樹的數量(m)
1. 每層樹的深度(d)
為了了解神經網絡體系結構和決策樹集合之間的相似性,這里復制了圖1。
論文中沒有太多的指導;建議采用超參數優化方法。他們提到他們優化了以下空間:
1. num層:{2,4,8}
1. 樹總數:{1024,2048}
1. 樹深度:{6,8}
1. 樹輸出dim: {2,3}
在我的代碼中,我不做網格搜索,而是讓hyperopt在一定范圍內采樣值。我考慮這個問題的方式(可能是錯誤的)是,每一層都表示一個樹集合(比方說CatBoost的單個實例)。對于您添加的每一層,您可能會添加一些表示能力,但是您也會使模型變得更重,難以訓練,并且可能會有過擬合的風險。樹的總數大致類似于CatBoost/xgboost/random forest中的樹的數量,并且有相同的權衡:樹很多時,可以表達更復雜的函數,但是模型需要更長的時間來訓練,并且有過擬合的風險。同樣,樹的深度也有同樣的權衡。至于輸出維度,坦白地說,我不太明白為什么它是一個參數。似乎回歸應該等于1,分類應該等于類的數量。
作者在GitHub上發布了代碼。它們不提供命令行界面,而是建議用戶在提供的Jupyter筆記本中運行它們的模型。在這些筆記本中提供了一個分類示例和一個回歸示例。
README頁面也強烈建議使用GPU來訓練節點模型。(這是支持CatBoost的一個因素。)
我準備了一個合作的筆記本,里面有一些關于如何在NODE上運行分類以及如何用hyperopt優化超參數的示例代碼。
colab/drive/11VH01T5BNDGEBLlZlG28zBB3MIBGWt
這里我將突出顯示代碼的一些部分。
我在修改作者的代碼時遇到的問題主要與數據類型有關。重要的是輸入數據集(Xtrain和Xval)是浮點32格式的數組(numpy或torch);不是float64或者float和int的混合。標簽需要像int64一樣編碼,用于分類,而float32用于回歸。
其他問題與內存有關。這些模型可以快速地消耗GPU內存,特別是在作者的示例筆記本中使用的大批處理尺寸。我簡單地解決了這個問題,在我的筆記本電腦(以及后來的Colab)上使用最大的批量大小。
不過,總的來說,讓代碼正常工作并不難。文檔有點少,但足夠了。
與CatBoost不同,NODE不支持分類變量,因此您必須自己將它們準備成數字格式。我們使用來自category_encoders庫的LeaveOneOutEncoder,與節點作者使用相同的方法對成人人口普查數據集執行此操作。在這里,出于方便,我們使用常規的訓練/測試分割,而不是5倍CV,因為訓練NODE需要很長時間(特別是在超參數優化時)。
from category_encoders import LeaveOneOutEncoder
import numpy as np
import pandas as pd
from sklearn.model_selection import train_test_splitdf=pd.read_csv('https://docs.google.com/uc' +
'?id=10eFO2rVlsQBUffn0b7UCAp28n0mkLCy7&' +
'export=download')
labels=df.pop('<=50K')
X_train, X_val, y_train, y_val=train_test_split(df,
labels,
test_size=0.2)class_to_int={c: i for i, c in enumerate(y_train.unique())}
y_train_int=[class_to_int[v] for v in y_train]
y_val_int=[class_to_int[v] for v in y_val] cat_features=['workclass', 'education', 'marital-status',
'occupation', 'relationship', 'race', 'sex',
'native-country']
cat_encoder=LeaveOneOutEncoder()
cat_encoder.fit(X_train[cat_features], y_train_int)
X_train[cat_features]=cat_encoder.transform(X_train[cat_features])
X_val[cat_features]=cat_encoder.transform(X_val[cat_features])# Node is going to want to have the values as float32 at some points
X_train=X_train.values.astype('float32')
X_val=X_val.values.astype('float32')
y_train=np.array(y_train_int)
y_val=np.array(y_val_int)
代碼的其余部分與作者的回購協議基本相同(hyperopt部分除外)。他們創建了一個名為DenseBlock的Pytorch層,該層實現了節點架構。一個名為Trainer的類保存了關于實驗的信息,并且有一個直接的訓練循環,它跟蹤到目前為止看到的最好的度量標準,并繪制更新后的損失曲線。
通過一些最小的嘗試和錯誤,我能夠找到一個驗證精度約為86%的模型。在使用hyperopt進行超參數優化后(它本應在Colab的GPU上通宵運行,但實際上,經過40次迭代后就超時了),最佳性能達到87.2%。在其他幾輪中,我的成績為87.4%。換句話說,在進行了hyperopt調優之后,NODE的表現確實優于CatBoost,盡管只是略微優于CatBoost。
然而,準確性并不是一切。必須對每個數據集進行代價高昂的優化還是不太方便。
NODE和CatBoost的優點:
1. 似乎可以得到稍微好一點的結果(基于論文和本次測試;我一定會嘗試許多其他數據集!)
CatBoost與NODE的優點:
1. 快得多
1. 少需要超參數優化
1. 沒有GPU運行良好
1. 支持分類變量
實際項目會用哪一個?也許CatBoost仍將是我的首選工具,但如果是kaggle這種線上的比賽NODE是一個不錯的嘗試
同樣重要的是要認識到性能是依賴于數據的,成人人口普查收入數據集并不能代表所有場景。或許更重要的是,分類特征的預處理在NODE中也是相當重要的一個問題。
作者:Mikael Huss
deephub翻譯組