Bu yazının asıl amacı Pipeline ve ColumnTransformer yapılarına örnek vermek olduğu için projeyi geliştirme aşamasındaki adımları kendinize göre uyarlayabileceğinizi unutmadan okumanızı tavsiye ederim.
Pipeline Nedir ?
Pipeline; bir önceki adımdaki çıktının, bir sonraki adımdaki girdiye dönüştüğü birden fazla adımı birbirine bağlayan zincirleme bir yöntemdir. Modelin başından sonuna kadar her bir işlem adımı, tek bir işlem ile tanımlandığı için kodların takibi kolaydır.
Pipeline yapısını kurabilmek için kullanacağınız fonksiyonlarda fit ve transform metodlarının bulunması gereklidir. Yapacağımız projede örnek olarak kendimiz de bir sınıf yapısı oluşturacağız.
ColumnTransformer Nedir ?
ColumnTransfomer; Özellikle karışık türde verilerin olduğu (object,float vb. ) verisetlerinde, farklı değişken veya değişken alt kümelerinin ayrı ayrı dönüştürülmesine ve daha sonra bunları birleştirmeye izin veren bir fonksiyondur.
Pipeline ve ColumnTransformer Farkı Nedir ?
Pipeline aynı değişkene uygulanan sıralı işlemleri belirtirken, ColumnTransformer her bir değişken setini (projede numerik ve kategorik değişkenler) sonradan birleştirmek için dönüştürmeye olanak sağlar.
Terimlerin ne olduklarından ve birbirlerinden farklarından bahsettiğimize göre örnek projemize geçebiliriz.
Not: Yukarıda da bahsettim amacımızın bu yapıları tanımak olduğunu, şu an için model metriklerini iyileştirmek olmadığını unutmayın.
Bu çalışma için dataseti buradan indirebilirsiniz. Dataseti,python dosyanızın olduğu dizine "loan_dataset_train" adıyla yükleyin.
Proje için kullanılacak kütüphaneler ve hangi amaçlar için kullanılacağı bilgisi aşağıdadır.
import numpy as np # Veri manipülasyonu için import pandas as pd # Veri manipülasyonu için from sklearn.model_selection import train_test_split # eğitim ve test setini ayırmak için from sklearn.pipeline import Pipeline #tek seferde birçok işlem yapmak için from sklearn.compose import ColumnTransformer # değişken kümelerine işlem yapabilmek için from sklearn.base import BaseEstimator # kendi sınıfımızı kullanabilmek için from sklearn.impute import SimpleImputer # null değerleri doldurmak için from sklearn.preprocessing import OneHotEncoder # kategorik değişkenlerin değerlerini yeni değişkenler yapmak için from sklearn.ensemble import RandomForestClassifier #modelimiz from sklearn.model_selection import cross_val_score #çapraz doğrulama yapmak için from sklearn.model_selection import GridSearchCV #modelin parametreler içerisinden en iyi sonucu verenini bulması için from sklearn import set_config # pipeline ı gösterebilmek için import joblib # modeli kaydedip sonradan kullanabilmek için import warnings # ekrana gelen uyarıları kapatmak için warnings.filterwarnings('ignore')
Kütüphaneleri import ettikten sonra tablomuzu okumaya başlayabiliriz.
DF=pd.read_csv("loan_dataset_train.csv") DF.head().T
Tablomuzu import ettikten ve transpozunu alıp değişkenlerine ve değerlerine kısa bir göz attıktan sonra tabloyu train ve test olarak bölmemiz gerekiyor. Bunu için aşağıdaki kodu kullanıyoruz. "Loan_ID" değişkeni anlamlı bir değişken olmadığı için "Loan_Status" değişkeni ise hedef değişkenimiz olduğu için X oluşturulurken bu değişkenler veri setinden çıkarılmıştır. y ise Loan_Status değişkeni Y ise 1'e, N ise 0'a dönüştürülerek alınmıştır.
X=DF.drop(["Loan_ID","Loan_Status"],axis=1) y=np.where(DF["Loan_Status"]=='Y',1,0) X_train,X_test,y_train,y_test=train_test_split(X,y,test_size=0.3,random_state=24,
stratify=y)
Koddaki ilk 2 satırın ne olduklarından yukarıda bahsettim. 3.satır ise oluşturduğumuz X ve y değişkenlerine göre datayı train ve test olarak ayırmaya olanak sağlıyor. Bu ayırma işlemini datanın %30 u (test_size) test, %70 'i train olacak ve ana tablodaki 0 ve 1 oranını sabit tutacak (stratify=y) şekilde yapıyor. Çalışma tekrarlandığında farklı sonuçlar vermesin diye de random_state parametresi kullanılmıştır.
ColumnTransformer ile değişken alt kümelerine ayrı ayrı işlem yapabileceğimizden bahsetmiştim. Bu işlemi gerçekleştirebilmek için aşağıdaki kodu kullanarak değişkenleri numerik ve kategorik değişkenler olarak ayırıyoruz.
numerik_degiskenler=X_train.select_dtypes(exclude="object").columns.tolist() kategorik_degiskenler=X_train.select_dtypes(include="object").columns.tolist() print("Numerik değişkenler: ",numerik_degiskenler) print("Kategorik değişkenler: ",kategorik_degiskenler)
Değişkenlerimizi veri tiplerine göre ayırdıktan sonra bu değişken gruplarına yapılacak veri ön işleme adımlarını belirtmemiz gerekiyor. Kategorik değişken grubu için; null değerler NA ile dolduruldu ve ardından OneHotEncoder uygulandı.
Not:Pipeline oluştururken aşağıdaki örnekte yer alan "imputer","encoder" gibi isimleri unique olmak şartı ile istediğiniz gibi verebilirsiniz.
kategorik_islem=Pipeline([("imputer",SimpleImputer(strategy="constant", fill_value="NA")), ("encoder",OneHotEncoder(handle_unknown="ignore",sparse=False)) ]) print(pd.DataFrame(kategorik_islem.fit_transform(X_train[kategorik_degiskenler])).shape) kategorik_islem.fit_transform(X_train[kategorik_degiskenler])
Kodun çıktısı aşağıdadır. 6 adet kategorik değişkenimiz vardı. OneHotEncoder ile 19 adet değişken üretmiş olduk.
Numerik değişkenler ise SimpleImputer ile ortalamaları ile dolduruldu.
numerik_islem=Pipeline([("num_imputer",SimpleImputer(strategy="mean"))]) print(pd.DataFrame(numerik_islem.fit_transform(X_train[numerik_degiskenler])).shape) numerik_islem.fit_transform(X_train[numerik_degiskenler])
kategorik_numerik_birlesim=ColumnTransformer(remainder='passthrough', transformers=[("numerik",numerik_islem,numerik_degiskenler), ("kategorik",kategorik_islem,kategorik_degiskenler) ]) kategorik_numerik_birlesim
Koddaki "transformers" parametresinin işlevini anlattım. "remainder" ise eğer işlem yapmadığım bir değişken var ise onu da direkt olarak almamı sağlayan bir parametre.
Yukarıda da hangi değişkene hangi işlemi yaptığını daha net görebilirsiniz.
Değişkenleri modele hazır hale getirdik ancak modelimizi daha pipeline kurarak eklemedik. Bir sonraki kodda da modeli ekleyelim.
Yazının başında modele kendimizin de bir sınıf ekleyeceğini belirtmiştim. Bu nedenle farkı görebilmek için model_normal ve model_yenidegiskenli olmak üzere 2 pipeline ekliyorum.
model_yenidegiskenli, modele yeni bir değişken ekleyeceği için önce model_normal ile modeli kurup sonuçları göreceğim. Sonradan yeni değişkeni ekleyerek değişkenin etkisine bakacağım.
model_normal=Pipeline([("degisken_islemleri",kategorik_numerik_birlesim), ("classifier",RandomForestClassifier(random_state=24)) ])
Modelin diagram görünümü ;
set_config(display='diagram') display(model_normal)
skorlar_normal = cross_val_score(model_normal, X_train, y_train, cv=5) print("skorlar",skorlar_normal) print("skorların ortalaması ",skorlar_normal.mean()) print("skorlarıd std. sapması ",skorlar_normal.std())
Önce GridSearch için parametreleri belirleyelim.
param_grid = { 'classifier__n_estimators': [100,500], 'classifier__max_features': ['auto', 'sqrt', 'log2'], 'classifier__max_depth': [4,5,6], 'classifier__min_samples_leaf': [4, 8], 'classifier__criterion': ['entropy', 'gini'], }
Parametreleri belirledik ancak Pipeline yapısı kurduğumuz için parametreleri belirlerken normale göre bir değişiklik yaptık. Bu değişiklik "classifier__" değişikliğidir. Neden "classifier__" dediğimi soruyorsanız "model_normal" Pipeline'ını tanımlarken RandomForestClassifier'a bu ismi vermiştim.
Parametreleri belirledik toplam 5 parametre için sırasıyla 2,3,3,2,2 değer verdik. Tüm bunların kombinasyonunu alacağı için 2x3x3x2x2 olacak şekilde 72 farklı kombinasyon deneyecek.
Aşağıdaki kod ile bu işlemleri gerçekleştiriyoruz.
grid_normal = GridSearchCV(model_normal, param_grid=param_grid, scoring="accuracy", cv=5) grid_normal.fit(X_train, y_train)
Modelimizin belirlediğimiz değerler için en iyi parametresini bulduktan sonra tekrar çıktılarına bakabiliriz.
skor_grid_normal = cross_val_score(grid_normal.best_estimator_, X_train, y_train, cv=5) print("skor_grid_normal" ,skor_grid_normal) print("ortalama skor_grid_normal" ,skor_grid_normal.mean()) print("std sapma skor_grid_normal" ,skor_grid_normal.std())
Bu parametreler ile default parametrelere göre daha iyi bir "accuracy" sonucu bulmuş olduk. Peki modele hiç göstermediğimiz test setinde sonuç nedir ?
print("Train set en iyi skor: "+str(grid_normal.best_score_)) print("En iyi parametreler: "+str(grid_normal.best_params_)) print("Test seti sonucu: "+str(grid_normal.score(X_test,y_test)))
Train setimizde %81.8 , test setimizde %78.9 olarak geldi sonuçlar. Overfit olabilir ancak bu yazının konusu olmadığı için üzerinde durmayacağım.
Varolan değişkenler ile modeli oluşturduk. Şimdi de modele yeni bir değişken ekleyelim ve modeli nasıl etkilediğine bakalım. Bunu yapmak için bir içerisinde fit ve transform metodları olan bir Class yapısı oluşturmamız gerekiyor.
class degisken_ekle(BaseEstimator): def __init__(self): pass def fit(self, x_dataset, y=None): return self def transform(self, x_dataset): x_dataset['TotalIncome'] = x_dataset['ApplicantIncome']+x_dataset['CoapplicantIncome'] return x_dataset
Basit olması açısından "ApplicantIncome" ve "CoApplicantIncome" değerlerini toplayıp "TotalIncome" değişkenini oluşturuyorum. Şimdi bu Classı, Pipeline'a eklememiz gerekiyor. Bunun için yeni bir Pipeline oluşturdum.
model_yenidegiskenli=Pipeline([("degisken_ekleme",degisken_ekle()), ("degisken_islemleri",kategorik_numerik_birlesim), ("classifier",RandomForestClassifier(random_state=24)) ])
Pipeline'ın görünümü;
set_config(display='diagram') display(model_yenidegiskenli)
Yeni değişkenli modeli default parametreleri çalıştırısak ;
skorlar_yenidegiskenli = cross_val_score(model_yenidegiskenli, X_train, y_train, cv=5) print("skorlar",skorlar_yenidegiskenli) print("skorların ortalaması ",skorlar_yenidegiskenli.mean()) print("skorların std. sapması ",skorlar_yenidegiskenli.std())
Aynı GridSearch parametreleri ile yeni modeli deniyoruz.
param_grid_yenidegiskenli = { 'classifier__n_estimators': [100,500], 'classifier__max_features': ['auto', 'sqrt', 'log2'], 'classifier__max_depth': [4,5,6], 'classifier__min_samples_leaf': [4, 8], 'classifier__criterion': ['entropy', 'gini'], } grid_yenidegiskenli = GridSearchCV(model_yenidegiskenli, param_grid=param_grid_yenidegiskenli, scoring="accuracy", cv=5) grid_yenidegiskenli.fit(X_train, y_train)
Tekrar modelin en iyi parametreleri ile deneme yaptığımızda sonuçlar ;
skor_grid_yenidegiskenli = cross_val_score(grid_yenidegiskenli.best_estimator_, X_train, y_train, cv=5) print("skor_grid_yenidegiskenli" ,skor_grid_yenidegiskenli) print("ortalama skor_grid_yenidegiskenli" ,skor_grid_yenidegiskenli.mean()) print("std sapma skor_grid_yenidegiskenli" ,skor_grid_yenidegiskenli.std())
En iyi parametreler ile çıktıya tekrar baktığımızda ise ;
print("Train set en iyi skor: "+str(grid_yenidegiskenli.best_score_)) print("En iyi parametreler: "+str(grid_yenidegiskenli.best_params_)) print("Test seti sonucu: "+str(grid_yenidegiskenli.score(X_test,y_test)))
Train setimizde %82, test setimizde %78.9 olarak geldi sonuçlar.
Pipeline modelimizi "joblib" kütüphanesinin yardımı ile kaydedebilir ve çıktılarımızı direkt olarak alabiliriz.
#modeli kaydetmek için joblib.dump(grid_yenidegiskenli,"pipeline_yenidegisken.joblib") #kaydettiğimiz modeli kullanmak için pipeline_yeni = joblib.load("pipeline_yenidegisken.joblib")
Aynı sonuçların çıktığını göstermek amacıyla tekrar X_test ile test skor değerini alıyorum.
pipeline_yeni.score(X_test,y_test)
Sonuç yine 0.7891891891892 çıkmaktadır.
X_test tablonuzun sonuçlarını görmek isterseniz;
pipeline_yeni.predict(X_test)
Okuduğunuz için teşekkür ederim. Diğer yazılarımda görüşmek üzere. Hoşçakalın...
Yorumlar
Yorum Gönder