Makine Öğrenmesinde Cross Validation ve Python Üzerinden K-Fold Uygulaması

Yiğit Şener
6 min readJul 27, 2020

--

Aşağıda linkini verdiğim yazımda neden veri setini train, validation ve test olarak ayırdığımızdan bahsetmiştim. Şimdiki yazımda ise bu 3 süreci de bir araya getiren sürekli validasyon sağlayabilen Cross Validation (cv) yöntemini Python’nun Sklearn kütüphanesinden yararlanarak anlatacağım.

Cross validation yani çapraz doğrulama anlamına gelen yöntem temelde tüm veri setini hem train hem de test verisi gibi kullanmaktadır.

Makine öğrenmesi modeli kurulurken belli bir oranda veriyi train ve test olarak ayırırız. Ancak buradaki handikap örneğin; modelin öğrenmesini istediğimiz bazı uç değerler belki test bölümünde kalmış olabilir. Bu değerler train veri seti üzerinde, modeli eğitirken olmayacağı için algoritmayı test edildiğinde sonuçlar beklendiği gibi çıkmayabilir.

O halde neye göre train veri setini ayırmamız gerekiyor ki test sırasında olabildiğince az sürpriz veri ile karşı karşıya kalabilelim. Tam bu noktada cross validation ile çapraz doğrulama yaparak train içinde yakalanabilecek en optimum değerleri yakalayabiliriz. Bu işlem sırasıyla train ve test veri setlerini parçalayarak her bir parça üzerinde model çalışması yapmaktadır. Aşağıdaki tablo ile açıklamaya çalışalım;

Tablo içinde örnek veri setimiz yer almaktadır. Burada input (x) değerleri bağımsız değişken ve target (y) değerleri ise bağımlı değişkendir. Normalde dikey olarak gözümüzde canlandırdığımız tabloda cross validation yöntemi daha kolay anlatılabilmesi için yatay olarak biçimlendirilmiştir. Veri setinin train ve test olarak 80% ve 20% olmak üzere ikiye bölündüğü durum iterasyon kısmının ilk bölümünde yer almaktadır. Genellikle bu aşamadan sonra ana model kurulur ve makine öğrenme algoritması belirlenmiş olurdu. Ancak cross validation yöntemi ile aynı veri seti üzerinde farklı test kesikleri seçilerek her bir seçilen test bölümü üzerinde model yeniden eğitilmektedir. Yukarıdaki tabloda sırasıyla seçilen her bir kesik gösterilmiştir. Veri her 5 iterasyon için 2 farklı parçaya bölünmüş ve her seferinde farklı bir iterasyon alanı alınarak modelin tüm yüzeylerde çalışması hedeflenmiştir. Böylece en iyi test skoruna göre modelimizi eğitebilmekteyiz. Şimdi bir uygulama üzerinden anlatalım.

Python’da Cross Validation (KFold) Uygulaması

Öncelikle klasik yöntem ile veri setini train ve test olarak ayırarak bir doğrusal regresyon analizi yapalım.

İlgili kütüphaneleri yüklüyoruz.

import numpy as np
import pandas as pd
from sklearn.datasets import make_regression
from sklearn.linear_model import LinearRegression
from sklearn.model_selection import KFold, train_test_split, cross_val_predict, cross_val_score
from sklearn import metrics
import matplotlib.pyplot as plt
import seaborn as sns

Bir bağımsız (input) değişkeni ve bir bağımlı (target) değişkeni olan sentetik (kurmaca) veri setimizi oluşturalım. Burada veri setindeki hata değerini kasten artırıyoruz ki normal bir veri seti gibi gözlemleyebilelim. Hatayı belirlemek için bias ve noise değerleri ile oynayabiliriz.

x, y = make_regression(n_samples = 678
,n_features = 1
,n_targets = 1
,bias = .7
,noise = 11
,shuffle = False
)

Oluşturduğumuz x (input) ve y (target) değişkenlerinden ilk 5 satıra bakalım.

df = pd.DataFrame({"x":x[:,0], "y":y},index=None)
df.head()
# ÇIKTI:
# x y
# 0 -1.607205 -21.500688
# 1 -0.222452 7.257665
# 2 -0.161832 6.868731
# 3 -0.081337 19.555520
# 4 0.583183 8.253384

Klasik yöntem ile veri setini train ve test olarak ikiye ayıralım. test_size parametresi ile %20'lik bölümü test için seçiyoruz.

x_train, x_test, y_train, y_test = train_test_split(x, y
,test_size=0.2
,shuffle = False
,random_state = 0)
veri_kesitleri = {"x_train" : x_train
,"x_test" : x_test
,"y_train" : y_train
,"y_test" : y_test}

for i in veri_kesitleri:
print(f"{i}: satır sayısı {veri_kesitleri.get(i).shape[0]}")

# ÇIKTI:
# x_train: satır sayısı 542
# x_test: satır sayısı 136
# y_train: satır sayısı 542
# y_test: satır sayısı 136

Ayırdığımız bölümleri scatter plot üzerinde görüntüleyelim. Kırmızı ile görünen noktalar test yeşil ile görünen noktalar ise train veri setini göstermektedir.

plt.scatter(x_train,y_train, color = "green", label = "Train")
plt.scatter(x_test,y_test, color = "red", label = "Test")
plt.xlabel("input x değerleri")
plt.ylabel("target y değerleri")
plt.legend()
plt.show()

En basit hali ile regresyon modelimizi train veri seti üzerinden çalıştırıp makine öğrenmesini gerçekleştirelim.

lr = LinearRegression()
lr.fit(x_train,y_train)

Regresyon analizimizi değerlendirebileceğimiz fonksiyonumuzu yazalım. Burada fonksiyon kullanmamızın sebebi ise ileride aynı ölçümlemeyi cross validation’da da yapacağımız için yeniden kod yazmak yerine aynı fonksiyonu çağırabileceğiz.

def scoreResults(model, x_train, x_test, y_train, y_test):

y_train_predict = model.predict(x_train)
y_test_predict = model.predict(x_test)

r2_train = metrics.r2_score(y_train, y_train_predict)
r2_test = metrics.r2_score(y_test, y_test_predict)

mse_train = metrics.mean_squared_error(y_train, y_train_predict)
mse_test = metrics.mean_squared_error(y_test, y_test_predict)

return [r2_train, r2_test, mse_train, mse_test]

Fonksiyon içerisine 5 parametre almaktadır. Model parametresine train veri seti ile eğitilen algoritma verilirken geri kalan değişkenlere ise train ve test olarak ayrılmış veri setlerini tanımlanır.

Klasik yöntem ile bölerek oluşturulan regresyon modelinin sonuçlarını kurduğumuz fonksiyon üzerinden öğrenelim.

result_lr = scoreResults(model = lr, x_train = x_train, x_test = x_test, y_train = y_train, y_test = y_test)

print(f"Train R2 Score: {result_lr[0]:.4f} MSE: {result_lr[2]:.4f}")
print(f"Test R2 Score: {result_lr[1]:9.4f} MSE: {result_lr[3]:.4f}")

# ÇIKTI
# Train R2 Score: 0.5997 MSE: 119.6256
# Test R2 Score: 0.5718 MSE: 122.3637

Çıkan sonucu değerlendirdiğimizde R2 istatistiksel olarak anlamlı görülmekle birlikte MSE (hataların kare ortalaması) ise 120 civarlarında görülmektedir.

Sonuçları karşılaştırabilmek adına veri setini cross validation yöntemini kullanarak farklı iterasyonlarda çalıştıralım. Burada K-Fold yöntemini kullanacağız. K-Fold içinde geçen K değeri veri setini kaç kere iterasyondan geçirerek train ve test olarak farklı kesitler için bölmemiz gerektiğini söylediğimiz parametredir.

scores = []
lr_cv = LinearRegression()
k = 5
iter = 1
cv = KFold(n_splits=k, random_state = 0, shuffle=False)
for train_index, test_index in cv.split(x):
# print("Train Index: ", train_index, "\n")
# print("Test Index: ", test_index)
x_train, x_test, y_train, y_test = x[train_index], x[test_index], y[train_index], y[test_index]
lr_cv.fit(x_train, y_train)

result = scoreResults(model = lr_cv
,x_train = x_train
,x_test = x_test
,y_train = y_train
,y_test = y_test)

print(f"{iter}. veri kesiti")
print(f"Train R2 Score: {result[0]:.4f} MSE: {result[2]:.4f}")
print(f"Test R2 Score: {result[1]:9.4f} MSE: {result[3]:.4f}\n")
iter += 1
scores.append(lr_cv.score(x_test, y_test))

Yukarıdaki kodu tek tek açıklayarak inceleyelim. Scores ile boş olarak oluşturulan liste içine her bir iterasyon sonucunda belirlenen model skorlarını almaktadır. lr_cv regresyon modelinin atandığı değişkendir. k değeri K-Fold için belirlenen kaç iterasyon sağlanacağını söyleyen değişkendir. iter her bir döngüyü sayan ve artacak olan değerdir. cv, K-Fold fonksiyonun tanımlandığı değişken olup içerisine k değeri verilmektedir. Ardından gelen for döngüsünde ise k değeri ile belirlenmiş iterasyon kadar train ve test veri seti datanın her bir bölümü için ayrı ayrı seçilerek modellenir. Öğrenen model hem train hem de test için sınanarak yukarıda yazılan fonksiyon çağırılır ve sonuçlar her bir iterasyon için yazdırılır. Döngü içerisinde her bir iterasyon için olan sonuçlara bakalım.

# ÇIKTI:
# 1. veri kesiti
# Train R2 Score: 0.6140 MSE: 117.5215
# Test R2 Score: 0.5209 MSE: 130.4772

# 2. veri kesiti
# Train R2 Score: 0.5640 MSE: 123.5427
# Test R2 Score: 0.6937 MSE: 107.3702

# 3. veri kesiti
# Train R2 Score: 0.5974 MSE: 122.9611
# Test R2 Score: 0.5945 MSE: 108.8582

# 4. veri kesiti
# Train R2 Score: 0.6123 MSE: 115.4303
# Test R2 Score: 0.5357 MSE: 139.4282

# 5. veri kesiti
# Train R2 Score: 0.5973 MSE: 120.5744
# Test R2 Score: 0.5835 MSE: 118.4442

Klasik olarak ayırdığımız işlemde test R2 0.5718 ve MSE 122 olarak çıkmıştı. Yukarıdaki iterasyonlardan testi en yüksek olan iterasyonun skorlarına baktığımızda 2. iterasyonda R2 0.6937 ve MSE 107 olarak görülmektedir. Yani daha iyi bir R2 skoru ve daha düşük hata (MSE) değeri elde etmiş olduk.

Cross validation ile elde edilen skorları bir bar chart üzerinde gösterelim.

Sonuç

Cross validation yöntemi ile verilerinizin tüm uçlarına dokunabilir ve en optimimum kesitten en başarılı modeli seçebilirsiniz.

--

--