XGBoost ile Merhaba Dünya
Extreme Gradient Boosting ya da Türkçesi ile Ekstrem Gradyan Arttırmanın kısaltması olan XGBoost resmi olarak bugün Washington Üniversitesinde öğretim üyesi olan iki akademisyen tarafından ilk olarak 2016 yılında duyuruldu. Yazılım çerçevesi aslında bu akademik yayın öncesinde Tianqi Chen tarafından Dağıtık Makine Öğrenimi Topluluğunun (DMLC) bir parçası olan bir araştırma projesi olarak başlamıştı. Yazılım çerçevesinin kaggle yarışmalarında gösterdiği üstün başarılar popülaritesini arttırdı. XGBoost, aşırı öğrenmeyi engelleme amaçlı regülarizasyon kullanımı, budama ve parelelleştirme gibi düz GBM’in üzerine bazı gelişmeler getirmiştir. Bu yazımızda XGBoost ile bir merhaba dünya uygulaması geliştireceğiz.
Veri seti
Golf veri seti ile çalışacağız. Veri setine buradan erişebilirsiniz. Özetle hava durumu, sıcaklık, nem ve rüzgar durumuna göre golf oynayıp oynamama kararlarını içeren bir veri seti için modelleme yapacağız.
1 2 3 4 5 6 7 |
import pandas as pd df = pd.read_csv("golf2.txt") df.head() |
Veri setinde hava durumu ve rüzgar öznitelikleri kategorikken, sıcaklık ve nem öznitelikleri nümeriktir. Veri setini değiştirsek bile aşağıdaki kod bloğu ile hangi özniteliklerin kategorik olduklarını bulabiliriz.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
categorical_features = [] features_names = [] for col in df.columns: features_names.append(col) if df[col].dtype == 'object': #categorical features if col != target_name: categorical_features.append(col) |
Kodlama
XGBoost, öznitelik ve hedefleri nümerik formatta beklemektedir. Öznitelik eğer haftanın günü gibi sıralı bir bilgi içeriyorsa etiket kodlaması (label encoding) yapabiliriz. Öte yandan özniteliğin kategorik olması halinde, yani şube kodu gibi kodu gibi büyüklük küçüklük içermeyen bir bilgi ise, one-hot kodlama yapmalıyız. Böylece karar ağacı haftanın günü (1: pazartesi, 7: pazar) kontrolünü 6’dan büyük eşit ise gibi haftaiçi ve haftasonlarını gruplamak için kullanabilir. Öte yandan şube kodu kategorik bir alan olduğu için karar ağacı kontrolü her şube için farklı dallara ayrılacaktır.
Etiket kodlaması
Veri setimizde nümerik olmayan iki kolon da aslında kategorik bilgiler içerdiğinden sadece hedef değerine etiket kodlaması uygulayacağız.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
target_name = 'Decision' unique_values = df[target_name].unique() print(target_name," column has ",unique_values," classes") for j in range(0, len(unique_values)): idx = df[df[target_name] == unique_values[j]].index df.loc[idx, target_name] = j print(unique_values[j]," is transformed to ",(j)) |
One-hot kodlama
Hava durumu ve rüzgar öznitelikleri kategorik bilgiler içerdiğinden bu ikisine one-hot encoding uygulayacağız.
1 2 3 4 5 6 7 8 9 10 11 |
for column in categorical_features: unique_values = df[column].unique() one_hot = pd.get_dummies(unique_values, prefix=column) one_hot[column] = unique_values df = df.merge(one_hot, left_on = [column], right_on=[column], how="left") df = df.drop(columns = [column]) |
Kodlama işlemlerinden sonra veri setimiz aşağıdaki şekile dönüşecektir.
Siz farklı bir veri setinde nümerik olmayan kolonlara etiket kodlaması uygulamayı daha uygun görebilirsiniz.
Bir özniteliğinizin 1000 farklı değer alabildiğini düşünün. Veri setinizin boyutu da milyonlar mertebesinde olsun. One-hot kodlamayı pandas aracılığıyla yapıyoruz ve pandas aslında tek core ile çalışan bir kütüphane. Bu ön-işleme işlemi saatler alacaktır. Oysa ki XGBoost’u H2O içerisinde kullanmayı tercih ederseniz ön-işleme ve eğitimi radikal şekilde hızlandırabilirsiniz.
Modelleme
Bu problemi hem sınıflandırma hem de regresyon olarak modelleyeceğiz.
Sınıflandırma
Çok sınıflı bir sınıflandırma probleminde kayıp fonksiyonu cross-entropy olacaktır. İki sınıflı bir sınıflandırma probleminde lojistik ya da sigmoid fonksiyonunu kullanabilecekken çok sınıflı bir sınıflandırma probleminde softmax kullanmak durumdayız.
1 2 3 4 5 6 7 8 9 10 |
if len(df[target_name].unique()) == 2: objective = 'binary:logistic' else: objective = 'multi:softmax' eval_metric = 'logloss' |
Normal şartlarda veri setini eğitim, validasyon ve test için üç sınıfa ayırmalıyız ancak veri setinin örneklem sayısı oldukça az olduğundan tüm veri setini eğitim için kullanacağız.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
params = { 'learning_rate': 0.01 , 'max_depth': 5 , 'min_child_weight': 0.5 , 'n_estimators': 250 , 'object': objective } model = xgboost.XGBClassifier(**params) eval_set = [(df.drop(columns=[target_name]), df[target_name])] model.fit(df.drop(columns=[target_name]), df[target_name] , eval_metric=eval_metric , eval_set=eval_set, early_stopping_rounds=5, verbose=True ) |
Model kurulduktan sonra tahminlemeyi artık yapabiliriz. En dominant tahmini ya da her bir sınıf için tahmin olasılığını hesaplayabiliriz.
1 2 3 4 5 6 |
predictions = model.predict(df.drop(columns=[target_name])) actuals = df[target_name].values |
Model aynı zamanda Evet / Hayır çıktılarının olasılıklarını da söyleyebilir.
1 2 3 4 5 6 |
prediction_proba = model.predict_proba(df.drop(columns=[target_name])) pd.DataFrame(prediction_proba, columns=['P_No', 'P_Yes']) |
Regresyon
Problemi regresyon şeklinde modellersek kayıp fonksiyonunu hataların ortalama karekökü (root mean squared error ) olarak atayabiliriz.
1 2 3 4 5 6 |
objective = 'rmse' eval_metric = 'rmse' |
XGB sınıflandırıcısı yerine XGB regressor’u ile modelleme yapacağız.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
params = { 'learning_rate': 0.01 , 'max_depth': 5 , 'min_child_weight': 0.5 , 'n_estimators': 250 , 'seed': 17 , 'object': objective } model = xgboost.XGBRegressor(**params) model.fit(df.drop(columns=[target_name]), df[target_name]) |
Hayır cevabını 0, evet cevabını da 1 olarak düşünebilirsiniz. Dolayısıyla tahminlerimiz [0, 1] aralığında bir desimal değer olacaktır. Tahmin evet / hayır olarak görmek istiyorsanız en yakın sınıfa atayabilirsiniz.
1 2 3 4 5 6 7 8 9 10 11 12 |
predictions = model.predict(df.drop(columns=[target_name])) for i in range(0, len(predictions)): prediction = predictions[i] actual = actuals[i] error = abs(actual - prediction) mae += error print("Prediction is ", prediction," whereas actual was ", actual," (Error: ",error,")") |
Özniteliklerin önemi
Kurulan model özniteliklerin önemini de içermektedir. Açıklanabilir bir yapay öğrenme modeli olması adına bunun gösterimi önemlidir.
1 2 3 4 5 6 |
from xgboost import plot_importance plot_importance(model) |
Paralellik
GBM algoritması bir karar ağacı kurulduktan sonra bunun yaptığı hatalar ile ikinci bir karar ağacının kurulması prensibine dayanmaktadır. Dolayısıyla ilk ağaç bitmeden ikincisini hesaplamaya başlayamayız. Ancak bir karar ağacı kurulurken dallarını paralelde oluşturabiliriz. Varsayalım ki en domine eden öznitelik hava durumu olsun. Ağacı bir dalı sıcak hava için dallanacakken, diğer dalı kapalı hava için dallanacaktır. Bu iki dalı birbirinden bağımsız şekilde hesaplayabiliriz. Buna seviye temelli ağacın büyümesi diyoruz. İşte XGBoost’u klasik GBM’den hızlı yapan özellik de budur.
GPU ile modelleme
XGBoost her zaman LightGBM ile karşılaştırılır. CPU’da modelleme yapacaksanız LightGBM on kat daha hızlıyken, GPU’da işler tersine dönüyor. Hem kullanım kolaylığı açısından da XGBoost’a sadece GPU kullanmak istediğizine dair bir parametre göndermeniz yeterken LightGBM’i GPU için sizin derlemeniz gerekmekte.
Güçlü modeller
LightGBM daha hızlı olsa bile ürettiği çıktılar olarak XGBoost’un %1 ila %2 arasında gerisinde kalmakta. Çok kaba bir yaklaşım olarak öznitelik mühendisliği yaptığınız dönemlerde modelleri LightGBM ile kurup, nihai hale geldiğinden XGBoost ile modellemek size çokça fayda sağlayacaktır.
Bu yazıda sıfırdan bir XGBoost modelini nasıl kuracağımızı adım adım inceledik. Yazılan koda GitHub üzerinden erişebilirsiniz.
Bu yazı “A Gentle Introduction to XGBoost for Applied Machine Learning” yazısından Türkçe’ye çevrilmiştir.