Scikit-learn ile Kendi Tahminleyicilerimizi Yazmak

Algoritmalarınızı scikit-learn yapısına uygun olarak yazın ve bu zengin kütüphanenin gücünü tam olarak kullanın
data science
Author

Kaan Öztürk

Published

November 25, 2023

Python’la yapay öğrenme modelleri çalıştıran her veri bilimcinin aşina olduğu bir kütüphanedir scikit-learn. Son derece zengin bir algoritma koleksiyonunu barındırır. Çoğu zaman o algoritmaları olduğu gibi kullanmak yeterlidir.

Ama bazen çok özelleşmiş bir algoritmaya ihtiyaç duyabilirsiniz. Bunu scikit-learn arayüzüne uygun şekilde yazarsanız, kütüphanenin sağladığı birçok başka kolaylığa da rahatça erişebilirsiniz.

Bu yazıda, kendi regresyon veya sınıflandırıcı modelinizi nasıl scikit-learn modülünün parçasıymış gibi yazabileceğinizi anlatacağım. Önce, scikit-learn sisteminin nasıl kullanıldığına bir bakalım.

scikit-learn arayüzü

Aşina olduğumuz scikit-learn yapısında üç aşama vardır: Tahminleyici nesnesini yaratmak, veriyle eğitmek, sonra eğitilmiş modelden tahmin üretmek. Mesela:

knn = KNeighborsClassifier()  # (1)
knn.fit(X_train, y_train)     # (2)
y_pred = knn.predict(X_test)  # (3)
  1. scikit-learn içindeki her algoritma bir estimator nesne sınıfı olarak kodlanmıştır. Regresyon, sınıflayıcı ve diğer tahminleyici türlerinin hepsi temel BaseEstimator sınıfından türetilmiştir.
  2. Eğitme işi fit() metodu ile sağlanır. Buradaki eğitim, sözgelişi, regresyon parametrelerini belirlemek, PCA bileşenlerini hesaplamak, veya bir ölçekleme yapmakta kullanılacak parametreleri bulmak olabilir.
  3. Eğitilen modeli kullanarak yeni veri ile kestirim yapmak için predict() metodu kullanılır.

Bunlar en temel işlemler. Bunların yanı sıra, sınıflayıcı (classifier) tahminleyicilerde predict_proba() veye decision_function() metodları bulunabilir. Bunlar her bir tahmin sınıfı için evet/hayır cevabı yerine daha “yumuşak” puanlar sağlarlar. Ayrıca regresyon tahminleyicilerinde score() gibi başarı ölçüsü veren metotlar olabilir.

Bazı algoritmalar ise veriyi dönüştürme amaçlı kullanılırlar. Örneğin ölçekleme veya PCA işlemleri. Bu tür tahminleyicilerde transform() metodu bulunur.

scaler = StandardScaler()
scaler.fit(X_train)
X_trf = scaler.transform(X_train)

Bir çok durumda birkaç tahminleyici ve dönüştürücü birbirlerinin ardı sıra eklenerek kullanılırlar. scikit-learn bunu otomatikleştirmek için bir Pipeline (veri hattı) nesnesi sağlar. Bir Pipeline nesnesinin kendine ait fit() ve predict() metodları bulunur, böylece kendi başına bir tahminleyici imiş gibi kullanılabilir.

Örneğin, aşağıdaki adımlar, verinin önce ölçeklenip sonra k-NN ile sınıflandırıldığı bir işlem sağlar.

pipe = Pipeline(steps=[('scale', StandardScaler), ('knn', knn)])
pipe.fit(X_train, y_train)
y_pred = pipe.predict(X_test)

Yeni bir tahminleyici yazacağımız zaman, burada özetlenen arayüzle tutarlı olmasını isteriz. scikit-learn bize bunun için bir standart ve ona uymak için kullanılacak araçlar sunuyor.

Resmi belgeler

Buradaki örnekler ve açıklamalar kaçınılmaz olarak eksik kalacak. Tam bir başvuru kaynağı olarak resmi dökümantasyona (Developing scikit-learn estimators) bakabilirsiniz.

Neden?

Neden tahminleyici algoritmamızı scikit-learn yapısına uydurmak için uğraşalım? Eğitim ve kestirim adımlarını iki ayrı fonksiyon halinde tutmakla, veya tek bir nesne sınıfı yaratıp içine fit() ve predict() metotlarını koymakla niye yetinmeyelim? Haklı sorular.

scikit-learn standardına uymanın en bariz yararı, kütüphanenin sunduğu üst seviye araçları kolayca kullanabilme imkânı. Örneğin, algoritmanız önceden verilerin ölçeklenmesini gerektiriyorsa, bunu kendiniz kodlamak zorunda kalmazsınız. scikit-learn içinde bulunan dönüştürücü ile beraber bir pipeline kurabilirsiniz.

Keza scikit-learn içindeki çapraz doğrulama (cross-validation), parametre uzayı tarama (grid search) gibi işlemleri de kolayca yapabilirsiniz. Otomatik model seçme işlemlerine kendi algoritmanızı pürüzsüz bir şekilde entegre edebilirsiniz.

Üstelik, içinde fit() ve predict() metotları bulunan bir nesne sınıfı yaptıktan sonra, bunu scikit-learn standardına uygun hale getirmenin zahmetsiz bir iş olduğunu göreceğiz. Yani az bir çabayla, büyük bir güç elde ediyoruz.

Kod kalıpları

Yeni tahminleyiciler geliştirirken örnek alabileceğiniz kod kalıpları mevcut. Sıfırdan başlamak yerine uygun bir kalıbı alıp içini uygun şekilde doldurabilirsiniz.

Tekerleği baştan icat etmeyin

İstediğiniz dönüşümü yapabilen bir scikit-learn fonksiyonu varsa onu kullanın. Kütüphanenin dökümantasyonuna göz gezdirin, aşina olun.

Kestirim

İlk örnek olarak, regresyon kestirimi yapan bir sınıf yaratalım. Algoritmik ayrıntılara boğulmamak için basit tutalım: Tahmin olarak eğitim kümesinin ortalamasını veya ortancasını veren bir kestirici olsun.

import numpy as np
from sklearn.base import BaseEstimator, RegressorMixin
from sklearn.utils import check_X_y, check_array
from sklearn.utils.validation import check_is_fitted

class SimpleRegressor(BaseEstimator, RegressorMixin):   # (1)
    def __init__(self, method="mean"):                  # (2)
        self.method = method
        
    def fit(self, X, y):                                # (3)
        if self.method not in ["mean", "median"]:       # (4)
            raise ValueError("method must be 'mean' or 'median'")
        X, y = check_X_y(X, y)
        if self.method=="mean":
            self.coef_ = np.mean(y)                     
        if self.method=="median":
            self.coef_ = np.median(y)                   # (5)
        self.n_features_in_ = X.shape[1]
        return self                                     # (6)
    
    def predict(self, X):
        X = check_array(X)                              # (7)
        check_is_fitted(self, attributes=["coef_"])
        return np.ones(X.shape[0])*self.coef_           # (8)

Bunu satır satır açıklayalım.

(1) Miras alınan sınıflar
Kestirici sınıfımıza SimpleRegressor adını veriyoruz. Bu sınıf BaseEstimator sınıfında tanımlanmış elemanları miras alıyor. Ayrıca RegressorMixin sınıfını ebeveyn olarak ekliyoruz. Böylece hem bunun bir regresyon kestiricisi olarak tanınmasını, hem de score() metodunun miras alınmasını sağlıyor.

(2) Başlatıcı
Sınıf inşacısının (constructor) basit olması istenir. Kestiriciyi çağırırken, algoritmayla ilgili hiperparametreleri burada veririz.

Dikkat edilecek noktalar:

  • Her parametre, kendisiyle tıpatıp aynı isimde bir nesne değişkenine dönüştürülmelidir (method ve self.method gibi). Tahminleyicilerin get_param() ve set_param() metodlarının doğru çalışması buna bağlıdır.
  • Bütün parametrelerin varsayılan (default) değerleri olmalıdır. Bir tahminleyici hiç bir parametre verilmeden de makul bir şekilde çalışabilmeli. Örneğin reg = SimpleRegressor().
  • Veri denetlemesi veya hata düzeltmesi gibi işlemler sadece fit() içinde yapılmalıdır. __init__ içinde atama dışında bir işlem bulunmamalıdır. Aksi takdirde, hiperparametre ataması için kullanılan set_param() gibi alternatif yollar (mesela grid search işleminde) doğru çalışmaz.

(3) fit() metodu
Bu metodun her zaman X (veri matrisi) ve y (hedef değer vektörü) alması gerekir.

Note

Gözetimsiz (unsupervised) algoritmalarda y alınması gerekmez, yine de API tutarlılığı açısından parametre listesinde bulunur, varsayılan değer olarak None atanır: def fit(self, X, y=None).

(4) Doğrulama kontrolleri
Hiperparametrelerin doğru aralıkta olması, verilerin uygun biçimde olması gibi testler burada yapılmalıdır. Bunun için sklearn.utils modülünde çeşitli fonksiyonlar mevcut. Burada check_X_y ile verilerin uygun biçim ve boyutta olduğunu kontrol ediyoruz.

Numpy kullanın

scikit-learn algoritmaları pandas veri tabloları alabilir, ama bunları kendi içinde Numpy dizilerine dönüştürür. Siz de tahminleyicilerinizi kodlarken sadece Numpy yapılarını kullanın.

(5) Model parametreleri Seçilen yönteme göre, hedef vektörü y’nin ortalama veya ortancasını hesaplayıp, coef_ isimli bir nesne değişkeninde tutuyoruz. Bu isim istediğiniz gibi seçilebilir. Birçok regresyon tahmincisi coef_ kullandığı için biz de burada aynısını kullandık.

Veri kullanılarak hesaplanan değişkenlerin alt çizgiyle bitmesi gerekir.

n_features_in_ değişkeni veride kaç öznitelik olduğunun hesabını tutmamıza yarar. Kestirme için kullanılan verinin de aynı sayıda özniteliği olduğunu kontrol etmek için kullanılır.

(6) fit() bir tahminleyici döndürür
fit() metodu her zaman self döndürmelidir. Böylece

y_pred = SimpleRegressor().fit(X_train, y_train).predict(X_test)

gibi metot zincirleri kurmak mümkün olur.

(7) Kestirim aşaması kontrolleri
check_array() fonksiyonu girdinin uygun biçimde bir veri yapısı olup olmadığını denetler.

Tahminleyicinin fit() metodu bir kere çalıştırılıp model eğitilmeden kestirim yapmak anlamsız olacaktır. check_is_fitted() fonksiyonuyla bunun denetimini yaparız. Fonksiyon buradaki haliyle coef_ değişkeninin mevcut olup olmadığına bakar. attributes parametresini kullanmasaydık, altçizgi (_) ile biten herhangi bir değişken olup olmadığını kontrol ederdi.

(8) predict() bir dizi (array) döndürür
Model tahmini için gerekli işlemler yapıldıktan sonra, test verisindeki satır sayısı uzunluğunda bir boyutlu bir dizi (array) elde ederiz.

Genel amaçlı tahminleyiciler yazın

Özel bir probleme odaklanıyor olsanız da, çözümün işe yarar en genel halinin ne olabileceğini düşünün ve tahminleyicinizi ona göre yazın. Tahminleyicinizi bambaşka bir bağlam ve problemde de kullanılabilecek şekilde tasarlayın. Yazdığınız kod, veri hakkında çok fazla varsayıma bağlı olmasın. Veri biçimlendirme işlemlerini tahminleyiciye koymayın. Ya önceden yapın, ya da veri akış hattının başına ayrı bir işlem olarak koyun.

Şimdi bu kestiriciyi diğer scikit-learn kestiricileri gibi kullanabiliriz. Önce rastgele veri üretelim:

np.random.seed(29101923)
X_train, X_test = np.random.rand(10,2), np.random.rand(5,2)
y_train, y_test = np.random.randn(10), np.random.randn(5)
y_train.mean(), np.median(y_train)
(0.3662107108568314, 0.2861502525347002)

Basit regresyon tahminleyicimiz, eğitim kümesinin hedef değişkeninin ortalaması hesaplayacak ve her yeni kestirim için aynı değeri verecek:

SimpleRegressor().fit(X_train, y_train).predict(X_test)
array([0.36621071, 0.36621071, 0.36621071, 0.36621071, 0.36621071])

Tahminleyicinin ortanca değer kullanmasını istersek:

SimpleRegressor(method="median").fit(X_train, y_train).predict(X_test)
array([0.28615025, 0.28615025, 0.28615025, 0.28615025, 0.28615025])

RegressorMixin sınıfından miras alınan score metodunu kullanarak, kestirimimizin \(R^2\) puanını hesaplatabiliriz.

SimpleRegressor().fit(X_train, y_train).score(X_test,y_test)
-0.6810225522666027

Standartlara uyum kontrolü

Yazdığımız tahminleyicinin scikit-learn standartlarına uyup uymadığını kontrol etmek için utils.estimator_checks.check_estimator fonksiyonunu kullanırız.

check_estimator, yaptığı kontrollerin yanı sıra, mevcut veri kümelerinde tahminleyicimizin ne kadar isabetli olduğunu da test eder. Ancak, mecvut haliyle kestiricimiz testi geçemiyor:

from sklearn.utils.estimator_checks import check_estimator
check_estimator(SimpleRegressor())
AssertionError                            Traceback (most recent call last)
...
   2277     if not regressor._get_tags()["poor_score"]:
-> 2278         assert regressor.score(X, y_) > 0.5
   2279 
   2280 

AssertionError: 

Bunun nedeni, çok basit bir kestirici yaratmış olmamız ve hatasının yüksek oluşu.

Kötü tahmin yapan bir kestiriciyi scikit-learn’e kabul ettirmek mümkün. Bunun için estimator tag özelliklerini kullanırız. Bu etiketlerin ne olduklarının açıklaması ve tam bir listesi için belgelere bakabilirsiniz. Buradaki amacımız için poor_score etiketini True değeriyle kestiricimize eklemeliyiz. Bunun için kestirici sınıfına _more_tags isimli bir metod eklemek gerekli.

from sklearn.utils.estimator_checks import check_estimator
def _more_tags(self):
    return {'poor_score': True}
SimpleRegressor._more_tags = _more_tags
check_estimator(SimpleRegressor())

Şimdi hiç bir hata mesajı almadık. Tahminleyicimiz testleri geçti.

Sınıflandırma

Şimdi de basit bir sınıflandırıcı (classifier) yaratalım. Her nokta için tahmin edilen sııf, ona en yakın komşusunun sınıfı olsun (1-NN algoritması).

Proje kalıpları içeren project-template reposunda tam da bu işi yapan bir kod kalıbı mevcut. Küçük değişikliklerle kullanalım:

Code
import numpy as np
from sklearn.base import BaseEstimator, ClassifierMixin
from sklearn.utils import check_X_y, check_array
from sklearn.utils.validation import check_is_fitted
from sklearn.utils.multiclass import unique_labels
from sklearn.metrics import euclidean_distances

class TemplateClassifier(BaseEstimator,ClassifierMixin):
    """ An example classifier which implements a 1-NN algorithm.
    For more information regarding how to build your own classifier, read more
    in the User Guide.
    
    Parameters
    ----------
    demo_param : str, default='demo'
        A parameter used for demonstation of how to pass and store parameters.
        
    Attributes
    ----------
    X_ : ndarray, shape (n_samples, n_features)
        The input passed during fit().
    y_ : ndarray, shape (n_samples,)
        The labels passed during fit().
    classes_ : ndarray, shape (n_classes,)
        The classes seen at fit().
        
    Examples
    --------
    >>> X = [[0, 0], [1, 1]]
    >>> y = [0, 1]
    >>> clf = TemplateClassifier()
    >>> clf.fit(X, y)

    >>> rng = np.random.RandomState(13)
    >>> X_test = [[0.2,0.2], [0.4,0.4], [0.6, 0.6], [0.8, 0.8]]
    >>> clf.predict(X_test)
    array([0, 0, 1, 1])
    """
    def __init__(self, demo_param='demo'):
        self.demo_param = demo_param

    def fit(self, X, y):
        """A reference implementation of a fitting function for a classifier.
        Parameters
        ----------
        X : array-like, shape (n_samples, n_features)
            The training input samples.
        y : array-like, shape (n_samples,)
            The target values. An array of int.
        Returns
        -------
        self : object
            Returns self.
        """
        # Check that X and y have correct shape
        X, y = check_X_y(X, y)
        
        # Store the number of features
        self.n_features_in_ = X.shape[1]
        
        # Store the classes seen during fit
        self.classes_ = unique_labels(y)

        self.X_ = X
        self.y_ = y
        # Return the classifier
        return self

    def predict(self, X):
        """ A reference implementation of a prediction for a classifier.
        Parameters
        ----------
        X : array-like, shape (n_samples, n_features)
            The input samples.
        Returns
        -------
        y : ndarray, shape (n_samples,)
            The label for each sample is the label of the closest sample
            seen during fit.multiclass.
        """
        # Check is fit had been called
        check_is_fitted(self, ['X_', 'y_'])

        # Input validation
        X = check_array(X)

        closest = np.argmin(euclidean_distances(X, self.X_), axis=1)
        return self.y_[closest]

Bu örnek, regresyon için hazırladığımız örnekten daha karmaşık değil. Uzunluğu, daha ayrıntılı açıklamalar içermesinden kaynaklanıyor. Sizin de kendi sınıflayıcılarınızı yazarken, bu örnekte olduğu gibi, değişkenlerin ne tip olduğu ve ne anlama geldiği, nesne değişkenlerinin (attributes) listesi ve açıklaması, tahminleyicinin kullanımına örnekler, ayrıca fit() ve predict() metodlarının aldığı girdilerin ve döndürdüğü değerlerin açıklamaları gibi bilgileri koymanız gerekir.

Sınıflandırıcılarda hedef değerler ayrık kategorilerdir. Bunlar tam sayı veya dize (string) olarak kullanılmalı. Kategori listesi, classes_ isimli sıralanmış bir dizi olarak nesne değişkeni olarak saklanmalıdır.

Verilerin kategorilerini baştan bilmiyorsanız, utils.multiclass.unique_labels() fonksiyonu ile bunları verilerden alabilirsiniz.

Buradaki basit sınıflandırıcı (1-NN) verilen bir tahmin noktasına, eğitim kümesindeki en yakın komşunun değerini atar. Bu yüzden eğitim kümesi kestirim (predict()) adımında erişilebilir olmalıdır. Bu yüzden fit() içinde X_ ve y_ nesne değişkenleri yaratılır ve bunlar daha sonra predict() içinde tahmin yapmak için kullanılırlar.

Algoritmanın özelliğine bağlı olarak, sınıflandırıcıya decision_function(), predict_proba() ve predict_log_proba() metodları da eklenebilir.

Sınıflandırıcının testlerini yapalım:

check_estimator(TemplateClassifier())

Kullanım örneği:

X = [[0, 0], [1, 1]]
y = [0, 1]
clf = TemplateClassifier()
clf.fit(X, y)
TemplateClassifier()
In a Jupyter environment, please rerun this cell to show the HTML representation or trust the notebook.
On GitHub, the HTML representation is unable to render, please try loading this page with nbviewer.org.
X_test = [[0.2,0.2], [0.4,0.4], [0.6, 0.6], [0.8, 0.8]]
y_test = [0, 1, 1, 1]
clf.predict(X_test)
array([0, 0, 1, 1])

Modelin doğruluğunu (accuracy) bulmak için score() metodunu kullanabiliriz.

clf.score(X_test, y_test)
0.75

Veri dönüşümü

Dönüştürücü (transformer) bir veri kümesini alıp başka bir biçime çevirir. Yine bir fit() metodu vardır, ama ardından predict() yerine transform() yapılır.

Hareketli ortalama

Örnek olarak, verilerin hareketli ortalamasını veren bir dönüştürücü hazırlayalım. Böyle bir işlem için bir dönüştürücü yazmak aşırı gelebilir, ama yararlı olacağı durumlar vardır. Sözgelişi, modelin eğitim kümesini hazırlarken bir adımda hareketli ortalama alıyor olabiliriz. Önceden bu işlemi bir dönüştürücü haline getirirsek, bütün eğitim ve kestirim sürecini bir pipeline içine koymamız, çapraz doğrulamaları aksamadan yapmamız mümkün olur.

import numpy as np
from sklearn.base import BaseEstimator, TransformerMixin
from sklearn.utils import check_array
from sklearn.utils.validation import check_is_fitted

class RollingMean(BaseEstimator, TransformerMixin):
    def __init__(self, n=15):
        self.n = n
    
    def moving_average_(self, a):
        return np.convolve(a, np.ones(self.n), "valid") / self.n
    
    def fit(self, X, y=None):
        X = check_array(X)
        self.n_features_in_ = X.shape[1]
        return self
    
    def transform(self, X):
        check_is_fitted(self, 'n_features_in_')
        X = check_array(X)
        # Moving average will have len(X) - n + 1 rows. For consistent size, fill the first n-1 rows with NaN.
        empty_rows = np.array([np.nan]*(X.shape[1]*(self.n-1))).reshape(self.n-1,X.shape[1])
        movav = np.apply_along_axis(self.moving_average_, axis=0, arr=X)
        return np.r_[empty_rows, movav]

Bu örnekte fit() herhangi bir işlem yapmıyor, sadece girdi için doğruluk denetimi yapıyor ve n_features_in_ nesne özelliğini yaratıyor. Daha sonra transform() çağrıldığında bu değişkenin var olup olmadığı denetleniyor.

Üç değişkenli bir rastgele yürüyüş verisi üretelim:

np.random.seed(29101923)
X = np.cumsum(np.random.rand(30).reshape(3,-1).T, axis=0)
X
array([[0.74977745, 0.96371498, 0.32437245],
       [1.71830174, 1.4121694 , 0.50916844],
       [2.10912041, 1.54142327, 0.77301929],
       [2.12452587, 1.97102509, 1.23124119],
       [2.34991983, 2.00229673, 1.87464155],
       [2.75549879, 2.71351979, 2.71059991],
       [2.87756103, 3.29451338, 3.27770838],
       [3.64506605, 3.47371139, 3.9280419 ],
       [4.18478839, 4.08826894, 4.22898858],
       [5.0129616 , 4.10754193, 4.32410028]])
RollingMean(n=3).fit(X).transform(X)
array([[       nan,        nan,        nan],
       [       nan,        nan,        nan],
       [1.5257332 , 1.30576922, 0.53552006],
       [1.98398267, 1.64153925, 0.83780964],
       [2.19452204, 1.83824836, 1.29296734],
       [2.40998149, 2.2289472 , 1.93882755],
       [2.66099322, 2.67010996, 2.62098328],
       [3.09270862, 3.16058152, 3.30545006],
       [3.56913849, 3.61883124, 3.81157962],
       [4.28093868, 3.88984076, 4.16037692]])

Bu örnekte fit() ayrı bir iş yapmadığı için, aynı işlem doğrudan fit_transform() ile de yapılabilir. Bu metot TransformerMixin sınıfından miras alınır, o yüzden bizim açıkça eklememize lüzum yok.

RollingMean(n=3).fit_transform(X)
array([[       nan,        nan,        nan],
       [       nan,        nan,        nan],
       [1.5257332 , 1.30576922, 0.53552006],
       [1.98398267, 1.64153925, 0.83780964],
       [2.19452204, 1.83824836, 1.29296734],
       [2.40998149, 2.2289472 , 1.93882755],
       [2.66099322, 2.67010996, 2.62098328],
       [3.09270862, 3.16058152, 3.30545006],
       [3.56913849, 3.61883124, 3.81157962],
       [4.28093868, 3.88984076, 4.16037692]])

Minimum-maksimum ölçekleme

Başka bir örnek olarak, minimum-maksimum arası ölçekleme için bir dönüştürücü oluşturalım1. Bu örnekte fit() metodu boş durmuyor, eğitim kümesinin en büyük ve en küçük değerlerini bulup bir kenara yazıyor.

  • 1 Bunun için hazır bir dönüştürücü var ama örnek için yokmuş gibi yapalım.

  • class MinmaxScaler(BaseEstimator, TransformerMixin):
        def __init__(self):
            pass
        def fit(self, X, y=None):
            X = check_array(X)
            self.n_features_in_ = X.shape[1]
            self.max_ = X.max(axis=0)
            self.min_ = X.min(axis=0)
            return self
        def transform(self, X):
            check_is_fitted(self, "max_")
            X = check_array(X)
            return (X - self.min_) / (self.max_ - self.min_)

    Burada ölçekleme için herhangi bir parametre alınmadığından başlatıcı __init__ boş kalıyor (ama Python sentaksı gereği sınıf tanımında bulunmak zorunda).

    Bu dönüştürücünün bir öncekinden farkı, eğitim kümesine bağlı oluşu. Bu ölçekleyici, eğitim verisinin sütunlarının minimum ve maksimum değerlerini belirliyor. transform() işleminde ise, aldığı verileri minimum değer 0 ve maksimum değer 1 olacak şekilde lineer bir fonksiyonla dönüştürüyor.

    Bu dönüştürücüyü yine rastgele üretilmiş verilerle deneyelim:

    X_train = np.random.rand(20,3)
    X_test = np.random.rand(10,3)
    X_test
    array([[0.35628925, 0.66850959, 0.84267923],
           [0.69403501, 0.38350617, 0.62806853],
           [0.38789683, 0.53202186, 0.61269947],
           [0.51598942, 0.37764728, 0.36217848],
           [0.02539575, 0.26381172, 0.64366218],
           [0.91755454, 0.36221054, 0.50155528],
           [0.09683517, 0.71776499, 0.7394225 ],
           [0.81163167, 0.49544736, 0.63947337],
           [0.58945327, 0.50590908, 0.03767375],
           [0.09311029, 0.12597857, 0.72295531]])
    scaler = MinmaxScaler().fit(X_train)
    scaler.transform(X_test)
    array([[ 3.42904840e-01,  6.06674521e-01,  8.79083858e-01],
           [ 7.17503869e-01,  2.46825035e-01,  6.44693329e-01],
           [ 3.77961291e-01,  4.34343120e-01,  6.27907771e-01],
           [ 5.20030762e-01,  2.39427514e-01,  3.54297244e-01],
           [-2.40942497e-02,  9.56970773e-02,  6.61724192e-01],
           [ 9.65412838e-01,  2.19936867e-01,  5.06519853e-01],
           [ 5.51403047e-02,  6.68865111e-01,  7.66310361e-01],
           [ 8.47932141e-01,  3.88163622e-01,  6.57149313e-01],
           [ 6.01510650e-01,  4.01372740e-01, -1.15815402e-04],
           [ 5.10089822e-02, -7.83330700e-02,  7.48325452e-01]])

    Dönüştürücümüz check_estimator testlerinden de başarıyla geçiyor.

    check_estimator(MinmaxScaler())

    Pipeline kullanımı

    Tahminleyicilerimiz uygun şekilde hazırlandıysa artık bunları model seçimi, parametre arama (grid search), çapraz doğrulama (cross-validation), veri akışı (pipeline) işlemleri için kullanabilirsiniz.

    Bir tahminleyiciyi bir veri akışı içinde kullanırken dikkat etmeniz gereken şeyler var:

    • Bir Pipeline nesnesinin fit() metodu, içindeki her tahminleyicinin fit() metodunu sırayla çalıştırır. Hattın bir ucundan giren veriyi dönüştürüp bir sonraki tahminleyiciye aktarır.
    • Hattın en sonundaki hariç, tahminleyicilerin hepsinin bir fit() veya fit_transform() metodu bulunmalıdır. Eğitim kümesinden farklı bir veri alacaklarsa, transform() metodları olmalıdır.
    • Pipeline nesnesi, hattın son adımındaki tahminleyiciyle aynı metodlara sahiptir. Son tahminleyici bir sınıflandırıcıysa, veri hattı da sınıflandırıcı olarak çalışır. Son aşamada bir dönüştürücü varsa, veri hattı da bir dönüştürücüdür.

    Veri hatlarıyla ilgili daha fazla bilgi için ilgili belgelere bakabilirsiniz.