Ana içeriğe atla

PYTHON MAKİNE ÖĞRENMESİ - PIPELINE VE COLUMNTRANSFORMER


Merhabalar, bu yazımda makine öğrenmesi modelleme sürecinde yazdığınız kodların kalitesini ve okunabilirliğini nasıl artıracağınızı anlatacağım. Bunun için Pipeline ve ColumnTransformer fonksiyonlarını kullanacağız. Kodlara geçmeden önce bu fonksiyonların tanımı ve birbirinden ne gibi farkları olduğuna kısaca değineceğim. 

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])


Değişken gruplarına hangi işlemlerin uygulanacağını belirledik. Şimdi bunları birleştirerek veri setini modele hazır hale getirmemiz gerekiyor. İşte tam bu noktada ColumnTransformer kullanılıyor.


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)


Modeli ekledikten sonra çaprak doğrulama metriğimiz olan "cross_val_score" fonksiyonuna parametre olarak oluşturduğumuz Pipeline'ı , train verilerimizi verdik. cv=5 değeri, X_train'i 5 parçaya ayıracak 4 parçayı ile train edecek 1 parçasını test edecek ve bu parçaları kendi içinde değiştirecek bir fonksiyondur. Uzun lafın kısası "skorlar_normal" değişkeninde 5 adet skor göreceğiz. Bu modelin sonuçlarını görmek için aşağıdaki kodları yazıyorum. 

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())


Modelimizi kurduk ancak default parametreleri ile kullandık. En iyi sonucu veren parametreleri nedir diye bakmak için hyper paramater optimization yapmamız gerekiyor. Projede basit olması açısından GridSearchCV i kullandım. GridSearchCV verdiğiniz parametreleri, tüm kombinasyonları ile deneyen bir algoritmadır. CV ile yukarıda da bahsettiğim 4 e 1 bölünmeyi yaparak bu işlemi uyguluyor. 

Ö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

Most Popular

PYTHON İLE VERİ GÖRSELLEŞTİRME

Merhabalar, Bu yazımda data analiz aşamasında işimizi oldukça kolaylaştıran bir yöntem olan veri görselleştirme üzerinde duracağım. Grafik türlerini göstermeye çalışırken aklıma gelen tüm özellikleri grafiklere koymaya çalıştım. Sizler grafikleri kullanırken işinize yaramayan kodları çıkartabilirsiniz.  Aynı satırda/sütunda birden fazla grafik çizdirmemizi ve ilişkileri daha rahat görmemizi sağlayan subplot konusuna ayrı değineceğim. Subplot için buraya tıklayabilirsiniz.  İncelemek istediğiniz grafiğin adına tıklayarak grafiğin kodunun yazımına doğrudan ilerleyebilirsiniz. İlerlediğiniz sayfadan tekrar menüye dönmek için grafik adına tıklamanız yeterlidir.  Bu yazımda değineceğim grafik türleri: 1)LINE PLOT 2)BAR CHART      2.1) STACKED BAR CHART      2.2) GROUPED BAR CHART 3)BOX PLOT 4)PIE CHART 5)HEATMAP  6)TREE MAP  7)SCATTER PLOT Grafik türleri görmeye başlamadan önce bazı görselleştirme kütüphanelerini import etmek...

PYTHON İLE VERİ GÖRSELLEŞTİRME- SUBPLOTS

Merhabalar, Bu yazımda,  PYTHON İLE VERİ GÖRSELLEŞTİRME yazımda değindiğim subplot konusunu ele alacağım. Subplot; satır veya sütunda bir veya daha çok grafiği çizdirmemizi sağlayan bir grafik türüdür. Bu grafikler aynı tür ve boyutta olabileceği gibi farklı tür ve boyutlarda da olabilir. Kullanıcının istediği tasarımı verebilmesi ve verinin hikayesini daha akıcı bir şekilde anlatabilmesi nedeniyle oldukça kullanışlıdır.  Farklı subplotlar göstermek için araştırma yaparken, çok kolay bir şekilde istediğiniz tasarımı oluşturmayı sağlayan bir fonksiyon ile karşılaştım. Bu fonksiyon matplotlib kütüphanesinden subplot_mosaic . Sizlere bu fonksiyonu anlatabilmek ve çeşitli grafik türlerini tek bir figure altında nasıl oluşturabileceğinizi göstermek amacıyla bir veri seti oluşturdum. Veri setinin amacı görselleştirme çalışması olduğu için lütfen anlam çıkarmaya çalışmayın:)  Kodların anlamlarını tek tek açıklayacağım. En sonda ise hepsini birleştireceğim. B uraya tıklayarak ...