Boosting Kutu Açılışı

Daha önceki yazılarımda Gradient Boosting, XGBoost, Light GBM ve CatBoost algoritmalarının çalışma mantıkları ve hiperparametrelerini ele almış ve ufak uygulamalar yapmıştım . Bu yazıda ise öncelikle kısa bir şekilde model için veri hazırlığı yapılacak, sonrasında incelemesi yapılmış modellere hiperparametre optimizasyonu yapılarak modellerin performansları incelenecektir. Algoritma inceleme yazılarıma link‘ten ulaşabilirsiniz.

Yapılacak uygulama için Kaggle web sitesinden indirilebileceğiniz “Telco Customer Churn” veri seti kullanılacaktır. Veri setine link’ten ulaşabilirsiniz.

Uygulama Jupyter Notebook üzerinden Python kodları kullanılarak yapılacaktır.

VERİ SETİNE GENEL BAKIŞ

“Customer Churn” müşterilerin belirli bir süre içinde şirketin verdiği hizmeti kullanmayı bırakmasını ifade eder. Müşteri kazanımı maliyetlerinin yüksek olması nedeniyle şirketler ellerindeki müşterileri tutmak için özel bir çaba gösterir ve stratejiler belirlerler. Bu stratejilerin başarılı olması için ise “churn” etme eğilimindeki müşterilerin önceden tahmin edilip gerekli aksiyonların alınması gereklidir.

Kullanacağımız veri seti ile telekominikasyon sektöründe “churn” etme tahmininin yapılması amaçlanmaktadır. Bu tahminleme için veri setinde her biri tek bir müşteriyi temsil eden 7.043 satır her biri farklı bir özniteliği(feature) temsil eden 19 sütun bulunmaktadır.

“Churn” sütunu bir önceki ay “churn” eden müşteri bilgisini vermektedir. Veri setinde bağımsız değişken olarak kullanabileceğimiz; müşterinin kayıtlı olduğu hizmetler, internet kullanımı, cihaz koruması, teknik destek, müşteri olma süresi, ödeme yöntemi, müşteri olduğu süre boyunca yaptığı toplam ödeme, cinsiyet, yaş aralığı gibi bilgiler bulunmaktadır.

import pandas as pd
import numpy as np

df = pd.read_csv('WA_Fn-UseC_-Telco-Customer-Churn.csv')

df.head()

df.info()

Veri setinde veri tipi yanlış olan değişkenler vardır. “SeniorCitizen” kolonu kategorik, “TotalCharges” kolonu sayısal veriye dönüştürülmelidir.

df["TotalCharges"] = pd.to_numeric(df.TotalCharges, errors='coerce') 
df["SeniorCitizen"] = df["SeniorCitizen"].astype('object')
df.info()

Veri setini tekrar incelediğimizde “TotalCharges” kolonunda 11 adet boş veri olduğu görülmektedir.

df[["tenure","MonthlyCharges","TotalCharges"]].sort_values(by=['tenure'], ascending=True).head(15)

Veriler “TotalCharges” kolonuna göre küçükten büyüğe doğru sıralandığında “tenure” değişkeninin “0” olduğu durumlarda bu kolonun boş geldiği görülmektedir. Yani verinin boş gelmesinin sebebi, müşterilerin 1 yıllarını doldurmakları için yıllık ödeme bilgilerinin gelmemesidir. Bu müşterilerde sadece aylık ödeme(MonthlyCharges) bilgisinin dolu geldiğini görülüyor. Bir kabul alarak, “tenure” değişkeni “0” olanların yıllık ödemelerilerine aylık ödemeleri atanmıştır. Bu gibi varsayımlar veri setine ve sektöre göre değişkenlik göstermekle birlikte yoruma açıktır.

df["TotalCharges"]=df["TotalCharges"].fillna(df["MonthlyCharges"])

Bağımlı değişken(Churn) incelendiğinde, veri setinin dengesiz olduğu görülmektedir. Dengesiz veri seti, tahminleme yapılacak olan bağımlı değişkenin içindeki kategorilerden birinin diğerine göre çok fazla sayıda olmasıdır. Bu durum modelin yanlı tahmin yapmasına neden olabilir. Örneğin elimizdeki veri setinde “No” bağımlı değişkeni “Yes”e göre oldukça fazladır. Bu durumda model “No”ları çok iyi öğrenip “Yes”leri az öğrenebilir ve model sonucunda bütün datayı “No” olarak tahmin etse bile %73,5 tahmin başarısı sağlar. Bu sorunun nasıl aşılacağı ilerleyen bir sonraki bölümde anlatılacaktır.

import matplotlib.pyplot as plt
labels = df.Churn.value_counts().index
colors = ["cornflowerblue","darkorange"]
explode = [0,0]
sizes = df.Churn.value_counts().values
#visualization
plt.figure(figsize=(5,5))
plt.pie(sizes,colors=colors,explode=explode,labels=labels,autopct="%1.1f%%")
plt.title("Churn")
plt.show()

 

MODEL HAZIRLIĞI

Veri setinin modele girebilmesi için öncelikle kategorik verilerin kukla(dummy) değişkene dönüştürülmesi ve sayısal verilerin standardize edilmesi gerekmektedir.

#Katergorik Verilerin Hazırlanması
df_cat = df.select_dtypes(include = ['object'])
#Churn ve customerID kolonları bağımsız değişken olmadıkları için kaldırılmıştır
df_cat=df_cat.drop(columns=['Churn','customerID'])
df_cat_cols=df_cat.columns

df_cont = df.select_dtypes(include = ['int64','int32','float64'])

#Kukla(dummy) Değişkenlerin Yaratılması
for i in df_cat: 
    df_cat = pd.concat([df_cat,pd.get_dummies(df_cat[str(i)],\
                                            drop_first=True,prefix=str(i))],axis=1)
df_cat = df_cat.drop(columns=df_cat_cols)

#Sayısal Verilerin Standardize Edilmesi
from sklearn.preprocessing import MinMaxScaler
features = df_cont.columns.values
scaler = MinMaxScaler(feature_range = (0,1))
scaler.fit(df_cont)
df_cont = pd.DataFrame(scaler.transform(df_cont))
df_cont.columns = features

df["Churn"] = df["Churn"].replace({"Yes":1,"No":0})
y= df['Churn']

X = pd.concat([df_cont,df_cat],axis=1)

X.head()

 

Kategorik değişkenlerin kukla değişken haline gelmesi ile veri setinde artık 30 adet bağımsız değişken bulunmaktadır. Bu kadar fazla değişken ile model kurmak modelin karmaşıklığını ve dolayısıyla da modelin çalışma süresini arttıracaktır. Model kurmaktaki tek amaç, çoğu zaman, sadece olabilecek yüksek tahmin gücü elde etmek değildir. Model kurarken en sade şekilde en yüksek tahmin gücünü elde etmek en iyi senaryo olacaktır. Hangi verinin modelde kullanılacağına karar vermek için çeşitli algoritmalardan yararlanılabilir. Algoritma hangi değişkenlerin modelde daha etkili olacağı çıktısını verecektir. Bu çalışma kapsamında Boruta algoritması kullanılmıştır.

from boruta import BorutaPy
from sklearn.ensemble import RandomForestClassifier
 
rf_boruta = RandomForestClassifier(n_estimators=200, n_jobs=-1, class_weight='balanced', max_depth=6)

boruta_selector = BorutaPy(rf_boruta, n_estimators='auto', verbose=2)

Algortima sonucunda; ‘tenure’, ‘MonthlyCharges’, ‘TotalCharges’, ‘InternetService_Fiber optic’, ‘InternetService_No’, ‘OnlineSecurity_No internet service’, ‘OnlineSecurity_Yes’,  ‘OnlineBackup_No internet service’, ‘DeviceProtection_No internet service’, ‘TechSupport_No internet service’, ‘TechSupport_Yes’, ‘StreamingTV_No internet service’,  ‘StreamingMovies_No internet service’, ‘Contract_One year’, ‘Contract_Two year’, ‘PaymentMethod_Electronic check’ değişkenlerinin modele girmesine karar verilmiştir.

“Veri Setine Genel Bakış” başlığı altında veri setinin dengesiz olduğu ve bunun yanlı tahminlere neden olabileceği üzerinde durulmuştu. Bu sorunu aşmak için; çok sayıda olan kategoriyi az sayıda olan kategori sayısına gelene kadar azaltabilir ya da az sayıda olan kategoriyi çok sayıda olan kategori sayısına kadar yapay veri ile çoğaltabiliriz(oversampling). Yapay veri hazırlığı için çeşitli algoritmalar kullanılabilir. Bu çalışmada SMOTE algoritması kullanılmıştır.

from imblearn.over_sampling import SMOTE
from sklearn.model_selection import train_test_split

X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=0)

print("Öğrenim Datası Bağımsız Değişkenleri: ", X_train.shape)
print("Öğrenim Datası Bağımlı Değişkenleri: ", y_train.shape)
print("Test Datası Bağımsız Değişkenleri: ", X_test.shape)
print("Test Datası Bağımlı Değişkenleri: ", y_test.shape)

Veri seti %70 öğrenim(train), %30 test datası olacak şekilde ayrılmıştır. Yukarıda görüleceği gibi öğrenim datası 4.930 satırdan, test datası 2.113 satırdan oluşmaktadır.

print("Yapay Veri Ekleme Öncesi Churn='Yes' olanlar: {}".format(sum(y_train["Churn"]==1)))
print("Yapay Veri Ekleme Öncesi Churn='No' olanlar: {} \n".format(sum(y_train["Churn"]==0)))

sm = SMOTE(random_state=2)
smote_train_X, smote_train_Y = sm.fit_sample(X_train, y_train)

print('Yapay Veri Ekleme Sonrası Öğrenim Datası Bağımsız Değişkenleri: {}'.format(smote_train_X.shape))
print('Yapay Veri Ekleme Sonrası Öğrenim Datası Bağımlı Değişkenleri: {} \n'.format(smote_train_Y.shape))

print("Yapay Veri Ekleme Sonrası Churn='Yes' olanlar: {}".format(sum(smote_train_Y["Churn"]==1)))
print("Yapay Veri Ekleme Sonrası Churn='No' olanlar: {}".format(sum(smote_train_Y["Churn"]==0)))

MODELLEME

4 farklı algortima için, her algoritmaya özel olacak şekilde farklı hiperparametreler kullanarak Grid Search ile modeller kurulmuş ve her algoritma için en iyi tahmini veren model hiperparametreleri bulunmuştur.

  • Gradient Boosting için; “learning rate”, “n_estimators”, “min_samples_split”, “min_samples_leaf” ve “max_depth”,
  • XGBoost için; “gamma”, “learning_rate”, “max_depth”, “n_estimators” ve “subsample”,
  • Light GBM için; “max_depth”, “learning_rate”, “n_estimators”, “subsample” ve “min_data_in_leaf”,
  • Catboost için; “max_depth”, “learning_rate” ve “iterations”

hiperparametrelerinde farklı değerler denenmiştir. Ayrıca Catboost algoritmasının grafik seçeneği ile iterasyon sayısı arttıkça tahmin hatası’nın nasıl azaldığı görselleştirilmiştir.

Gradient Boosting

from sklearn.ensemble import GradientBoostingClassifier
from sklearn.model_selection import GridSearchCV

gbm_parameters = {"learning_rate" : [0.05,0.1],
                  "n_estimators": [50,500,1000],
                  "min_samples_split": [10,20,50],
                  "min_samples_leaf":[1,10,50],
                  "max_depth": [3,10,20]}

gbm = GradientBoostingClassifier()
gbm_cv = GridSearchCV(gbm, gbm_parameters, cv = 5, n_jobs = -1, verbose = 2)
gbm_cv.fit(smote_train_X, smote_train_Y)
gbm_cv.best_params_

 

 

 

 

gbm = GradientBoostingClassifier(learning_rate= 0.1,
                                 max_depth=10,
                                 min_samples_leaf= 1,
                                 min_samples_split= 10,
                                 n_estimators=500)

gbm_tuned =  gbm.fit(smote_train_X, smote_train_Y)

XGBoost

from xgboost import XGBClassifier
xgb_parameters = {'gamma' : [0,1,5],
                'learning_rate': [0.1,0.3],
                'max_depth': [3,6,12],
                'n_estimators': [100,500,800],
                'subsample': [0.5, 0.8, 1]}

xgb = XGBClassifier()
xgb_cv = GridSearchCV(xgb, xgb_parameters, cv = 5, n_jobs = -1, verbose = 2)
xgb_cv.fit(smote_train_X, smote_train_Y)
xgb_cv.best_params_

 

 

 

 

xgb = XGBClassifier(gamma = 0, 
                    learning_rate = 0.3,
                    max_depth = 12,
                    n_estimators = 500,
                    subsample = 0.5)

xgb_tuned =  xgb.fit(smote_train_X, smote_train_Y)

Light GBM

from lightgbm import LGBMClassifier

lgbm_parameters = {'max_depth': [5,10,15],
                    'learning_rate': [0.05,0.1], 
                    'n_estimators': [100,500,1000],  
                    'subsample': [0.5,0.8,1],    
                    'min_data_in_leaf':[10,50,100]}

lgbm = LGBMClassifier()
lgbm_cv = GridSearchCV(lgbm, lgbm_parameters, cv = 5, n_jobs = -1, verbose = 2)
lgbm_cv.fit(smote_train_X, smote_train_Y)
lgbm_cv.best_params_

 

 

 

 

lgbm = LGBMClassifier(learning_rate = 0.05, 
                       max_depth = 15,
                       min_data_in_leaf = 10,
                       n_estimators = 1000,
                       subsample = 1)

lgbm_tuned = lgbm.fit(smote_train_X, smote_train_Y)

Catboost

from catboost import CatBoostClassifier, Pool

catb_params = {
    'max_depth' : [5,10,15],
    'learning_rate': [0.05, 0.1],
    'iterations': [100,200,500]}

catb = CatBoostClassifier()
catboost_cv = GridSearchCV(catb, catb_params, cv=5, n_jobs = -1, verbose = 2)
catboost_cv.fit(smote_train_X, smote_train_Y)
catboost_cv.best_params_

 

 

catboost = CatBoostClassifier(iterations = 500, 
                          learning_rate = 0.1, 
                          max_depth = 10,
                          save_snapshot=True, 
                          snapshot_file="catboost_save",
                          early_stopping_rounds=50)

catboost_tuned = catboost.fit(smote_train_X, smote_train_Y, plot=True)

SONUÇ

Modellerin öğrenimleri test verileri ile değerlendirilmiştir.  Sırasıyla doğru tahmin oranı, çapraz doğrulama oranı, F skoru, AUC ve Kappa skoru değerleri aşağıdaki tabloda yer almaktadır.

 

Sonuçlar incelendiğinde 4 modelin de birbirlerine çok çok yakın olduğu ancak doğru tahmin oranı, çapraz doğrulama ve tahmin oranı ile çapraz doğrulama arasındaki tutarlılık sayesinde bu veri setinde Catboost’un en iyi, XGBoost’un en kötü performansı gösterdiği söylenebilir. Ancak burada konuşulan farklar %1-2 seviyesinden daha yüksek değildir.

Günün sonunda, yeni çıkan algoritmaların giderek daha iyi sonuç verecekleri kaçınılmaz bir gerçektir. Ancak tutarlı ve yüksek tahmin gücü olan bir modelleme çalışması için veri hazırlamanın, yeni değişkenler yaratmanın kısaca değişken mühendisliğinin(feature engineering) en önemli parametre olduğunu her zaman hatırlamak gerekmektedir.

Gelecek yazılarda görüşmek dileğiyle.

Yazar Hakkında
Toplam 10 yazı
Emre Rıdvan Muratlar
Emre Rıdvan Muratlar
2016 yılından bu yana finans sektöründe veri bilimi üzerine çalışmaktadır. Yıldız Teknik Üniversitesi İstatistik bölümü doktora öğrencisidir.
Yorumlar (2 yorum)
Şakir ÖZDEN
Şakir ÖZDEN Cevapla
- 21:26

Yapay veri ekleme bölümünde Keyerror: Churn hatası alıyorum. Sizin kodlarınızı kopyala yapıştır yapmama rağmen.

Rasmiya
Rasmiya Cevapla
- 10:48

Bu məqalə üçün minnətdaram. Mənim elmi işim “Kompüter riyaziyyatında informasiya təhlükəsizliyi”di. İnformasiya təhlükəsizliyində Python-un tətbiqini edə bilərəmmi?

Bir cevap yazın

E-posta hesabınız yayımlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir

×

Bir Şeyler Ara