Veri Bilimi Sınıflandırma Model Çıktılarını Değerlendiren Metrikler (Confusion Matrix, Accuracy, ROC-AUC, Log Loss, MCC) Python Uygulaması
Veri bilimi gözetimli (supervised) makine öğrenmesinde iki tür modelleme vardır. Bunlardan biri hedef değişken sürekli olduğunda regresyon, diğeri ise hedef değişken kesikli yani kategorik olduğunda ise sınıflandırma (classification) olarak geçmektedir.
Bu yazıda ise eğitilen bir sınıflandırma algoritmasının performans ölçümlemesi için hangi metriklerin nasıl kullanıldığından ve bunların birbirleri ile nasıl karşılaştırıldığından bahsetmekteyim.
Sınıflandırma (classification) algoritmaları ikili (binary) ya da ikiden fazla (multiclass) kategorik değerleri sınıflandırmayı amaçlamaktadır. Bu amaç doğrultusunda ortaya çıkan hataların değerlendirilmesi ve doğruluk oranlarının en efektif biçimde ortaya konulabilmesi için aşağıda yer alan başlıca metrikler kullanılmaktadır. Genellikle bu metrikler arasında Accuracy ön planda yer almaktadır. Ancak accuracy %99 olduğu durumlarda bile model başarısında emin olamayız. Özellikle dengesiz/imbalance dağılan hedef (target) değişken için farklı metrikler üzerinde durulabilir.
Confusion Matrix (Karmaşıklık Matrisi)
Kurulan bir sınıflandırma modelinin sonuçlarını yorumlayabilmek, gerçek ve tahmin edilen değerler arasında yer alan ilişkideki hataları çapraz olarak inceleyebilmek için confusion matrix kullanılır.
Yukarıdaki tablo, ikili (binary) sınıflandırma için kurulmuş olan bir modelin çıktısının confusion matrix’idir. Bu matriste yer alan Pozitif ve Negatif terimleri doğruluk veya yanlışlığı değil ayrıştırılmak istenen sınıfları (örn: evli/bekar) temsil etmektedir.
Örneğin bir veri seti üzerinden müşterilerin evli ya da bekar olup olmadığını tahmin etmeye çalışan bir model oluşturulsun. Kurulan sınıflandırma modeli sonucu değerlendirirken matrise göre pozitifleri evli, negatifleri ise bekar olacak şekilde birer sınıf olarak düşünebiliriz. Karşımıza ise değerlendirebileceğimiz dört parametre (TP, TN, FN, FP) çıkmaktadır. Bu parametreleri daha net anlamak adına Python üzerinden kodlayarak örnek gerçek ve model tahmin değerleri yaratıp confusion matriste inceleyelim.
Evli = 1, Bekar = 0 olmak üzere; tahmin edilmek istenen değişkende 5 adet evli ve 5 adet bekar olduğunu varsayalım.
import pandas as pd
import numpy as np
import sklearn.metrics as mt
import matplotlib.pyplot as plt
# Gerçek değerler
actual = np.array((1, 1, 1, 1, 1, 0, 0, 0, 0, 0))
# Tahmin edilen değerler, Tahminde array sonuna bilerek bir hatalı değer eklenmiştir
predict = np.array((1, 1, 1, 1, 1, 0, 0, 0, 0, 1))
mt.confusion_matrix(actual, predict)
# ÇIKTI:
# array([[5, 1],
# [0, 4]], dtype=int64)
Kod üzerinde görüldüğü üzere gerçek (actual) verilerde 5 adet evli ve 5 adet bekar bulunmaktayken tahmin edilen verilerde ise 6 adet evli ve 4 bekar değeri yer almaktadır. Confusion matrix üzerinden bakalım
TP & TN: Sınıfların doğru tahmin edildiği rakamı verir. Örnekte yer aldığı üzere evli olanların hepsi doğru tahmin edilirken bekar olanların 4 tanesi doğru bir şekilde eşleşip tahmin edilmiştir.
FN & FP: Sınıfların birbirleri ile olan yanlış tahmin adetlerini verir. Örnekte yer aldığı üzere gerçek veri setinde bekar sınıfında yer alan değer tahmin veri setinde evli olarak göründüğü için FN değeri 1 çıkmıştır.
Yukarıdaki örnekte ikili sınıf için confusion matrix oluşturulmuştur. İkiden fazla sınıf olduğu taktirde aşağıdaki gibi bir matris oluşturulabilir.
Confusion matristen elde edilen sonuçlar ile model başarısını ölçen Accuracay, Recall, Precision, F1-Score, ROC/AUC değerlerinin nasıl hesaplandığını ve hangi durumlarda kullanıldığından bahsedelim.
Accuracy
Accuracy, anlaşılması ve yorumlanması en basit ölçütlerden birisidir. Makine öğrenmesi sınıflandırma algoritmalarının testlerinde sıklıkla kullanılır. Accuracy skoru aşağıdaki gibi hesaplanır. Accuracy skoru 0 ve 1 arasında olup 1’e yaklaşan skorlarda model başarılı kabul edilir.
Accuracy = (TP + TN) / (TP + FP + TN + FN)
# Gerçek değerler
actual = np.array((1, 1, 1, 1, 1, 0, 0, 0, 0, 0))
# Tahmin edilen değerler
predict = np.array((1, 1, 1, 1, 1, 0, 0, 0, 0, 1))
# Accuracy Oranı Hesaplama
tn, fp, fn, tp = mt.confusion_matrix(actual, predict).ravel()
accuracy = (tp + tn) / (tn + fp + fn + tp)
print(f"Accuracy: {accuracy}")
# ÇIKTI:
# Accuracy: 0.9
# sklearn üzerinden hesaplama
print(mt.accuracy_score(actual, predict))
# ÇIKTI: 0.9
Dikkat edilmesi gereken husus model çıktısının (target) dengeli dağıldığı zamanlarda kullanılması gerekliliğidir. Örneğin medeni durum tahminlemesinde elinizdeki hedef değişkenin 99’u evli, 1’i bekar ve o bekar olan yanlış tahmin edilse de accuracy skoru %99 yönünde başarılı çıkacaktır. Bu yüzden dengesiz (imbalance) dağılan target değişkenler için aşağıdaki diğer ölçütleri de denemenizde fayda var.
Precision, Recall & Specificity
Bu ölçütler ile confusion matrisin derinlerine dalıp model başarısını daha ince görebilirsiniz. Yukarıda bahsettiğim accuracy’nin dengesiz dağılan target değişken üzerindeki illüzyonunu recall, precision ve specificity yöntemleri ile kullanarak çözümleyebilirsiniz. Şimdi veri setimizde eşit olarak dağılan evli ve bekar sayılarını dengesizleştirip yeniden confusion matrix ve accuracy sonuçlarına bakalım.
Gerçek veride evli = 2 adet, bekar = 8 adet iken modelin tahmin ettiği veride evli = 1 adet, bekar = 9 adettir.
# Gerçek değerler
actual = np.array((1, 1, 0, 0, 0, 0, 0, 0, 0, 0))
# Tahmin edilen değerler
predict = np.array((1, 0, 0, 0, 0, 0, 0, 0, 0, 0))
# Confusion Matrix
print(mt.confusion_matrix(actual, predict))
# ÇIKTI:
# [[8 0]
# [1 1]]
# tn, fp, fn, tp hesaplama
tn, fp, fn, tp = mt.confusion_matrix(actual, predict).ravel()
print(f"TN {tn}, FP {fp}, FN {fn}, TP {tp}")
# ÇIKTI:
# TN 8, FP 0, FN 1, TP 1
# Accuracy Oranı Hesaplama
print(mt.accuracy_score(actual, predict))
# ÇIKTI: 0.9
Kodda görüldüğü üzere model 2 evli sınıfından bir tanesini doğru tahmin edememesine rağmen accuracy skoru %90'lık bir başarı gösteriyor. O halde diğer ölçütleri de işin içene katarak onların skorlarını çıkaralım. Accuracy genel olarak tüm doğru ve yanlış tahminlerine odaklanırken diğer ölçütler farklı noktalara temas ederek skor üretirler. Aşağıdaki formülleri incelediğinizde aralarındaki farkın neyi amaçladıklarına göre değiştiklerini göreceksiniz.
Recall = TP / (TP + FN)
Precision = TP / (TP + FP)
Specificity = TN / (TN + FP)
Yukarıdaki dengesiz dağılan örnekten yola çıkarak bu ölçütleri kullanıp skorlarını hesaplayalım.
# Gerçek değerler
actual = np.array((1, 1, 0, 0, 0, 0, 0, 0, 0, 0))
# Tahmin edilen değerler
predict = np.array((1, 0, 0, 0, 0, 0, 0, 0, 0, 0))
# Recall
print(mt.recall_score(actual, predict))
# ÇIKTI: 0.5
# Precision
print(mt.precision_score(actual, predict))
# ÇIKTI: 1.0
# specificity
tn, fp, fn, tp = mt.confusion_matrix(actual, predict).ravel()
specificity = tn / (tn+fp)
print(specificity)
# ÇIKTI: 1.0
Yukarıda görüldüğü üzere her bir ölçütün farklı bir skoru bulunuyor. Dolayısıyla neye göre hangi skora karar vereceğimiz konusu burada ön plana çıkıyor. Burada kurulan modelin amacına göre aslında ölçütümüzün belirlenmesi gerekmektedir. Eğer ki bizim için bekarların (8 adet) tahmin edilmesi evli olanlara göre daha değerli ise accuracy skorunu benimseyebiliriz. Ancak evli olanlar öncelikli ise recall’a bir bakmakta fayda var diyebiliriz. Örneğin fabrikada çalışan makineler için bir bakım algoritması kurduğunuzda sizin için sayısı az alan veri daha değerli ise confusion matrix’e bakıp ona göre bir ölçümleme stratejisi oluşturabilirsiniz.
F1-Score
Bu ölçüt bir nebze de olsa kafa karışıklığını ortadan kaldırmak için precision ve recall skorlarının ortalamasını alarak bir denge skoru üretir. Kodlayarak sonucuna bir bakalım.
F1-score = 2 * Precision * Recall / (Precision + Recall)
# Gerçek değerler
actual = np.array((1, 1, 0, 0, 0, 0, 0, 0, 0, 0))
# Tahmin edilen değerler
predict = np.array((1, 0, 0, 0, 0, 0, 0, 0, 0, 0))
# F1-Score
print(mt.f1_score(actual, predict))
# ÇIKTI: 0.666
F1-Score, precision ve recall metriklerinin harmonik ortalaması olarak düşünülebilir. Eğer ki F1-Score’u precision değerine göre ağırlığını değiştirmek istersek aşağıdaki formülde olduğu gibi beta katsayısı ile oynayabiliriz.
# Gerçek değerler
actual = np.array((1, 1, 0, 0, 0, 0, 0, 0, 0, 0))
# Tahmin edilen değerler
predict = np.array((1, 0, 0, 0, 0, 0, 0, 0, 0, 0))
# F1 Beta = 2
print(mt.fbeta_score(actual, predict, beta = 0.2))
# ÇIKTI: 0.96296
Yukarıdaki kodda görüldüğü üzere Beta parametresine vereceğimiz değer ile precision etkisini belirlemiş oluyoruz.
ROC AUC Eğrisi
ROC, işlem karakteristik eğrisi (Receiver Operating Characteristics) olup AUC (Area Under The Curve) ise bu eğrinin altında kalan alan anlamına gelmektedir. Ayrıca bu iki terim birleşik olarak AUROC (Area Under the Receiver Operating Characteristics) şeklinde de ifade edilebilmektedir.
ROC-AUC eğtisi özellikle makine öğrenmesi modellerinin performansını görsel anlamda değerlendirilmesinde etkili bir yöntemdir. Birden fazla sınıflı (multi-class) problemlerinde model sonuçlarındaki parametreleri (TP, TN vs.) hassaslık (threshold) derecesinde grafikleştirir. ROC, bir olasılık eğrisidir, AUC ise parametrelerin ayrıştırılabilir ölçüsünü veya derecesini temsil eder. Bu grafik sayesinde model sonucu tahmin edilmek istenen sınıflar ne ölçüde farklılaştıkları izlenebilmektedir. Yüksek AUC derecesi sınıflar (Evli, Bekar) arasındaki ayrışmanın ne derece geçerli olduğunu göstermektedir.
Örnek üzerinden konuyu pratikte anlatalım. Bunun için sıfırdan basit bir model oluşturdum. Çünkü tahmin edilen değerin (0, 1) olasılık sonucunu (0.2, 0.8) ROC-AUC eğrisinde kullanmamız gerekiyor.
import numpy as np
import sklearn.metrics as mt
from sklearn.datasets import make_classification
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import train_test_split
import matplotlib.pyplot as plt
import scikitplot.metrics as splt
# Sınıflandırmaya yönelik olarak
# Sentetik veri oluşturulması
x, y = make_classification(n_samples = 1000,
n_features = 20,
n_informative = 2,
n_redundant = 10,
shuffle = False)
# Veri seti train ve test olarak ayrılması
X_train, X_test, y_train, y_test = train_test_split(x, y, test_size = 0.33, random_state = 0)
# Logistic regresyon ile model eğitimi
lgr = LogisticRegression()
lgr.fit(X_train,y_train)
# Gerçek veriler
actual = y_test
# Tahmin edilen verilerin bulunması
predicted = lgr.predict(X_test)
# Olasılık değerlerinin hesaplanması
predicted_proba = lgr.predict_proba(X_test)
Kodda görüldüğü üzere veri setini sentetik olarak oluşturduk. Sentetik veri içinde işaretli gelen target ve diğer değişkenleri train ve test olarak ayırıp model eğitimi için hazır hale getirdik. Model çıktılarını karşılaştırmalı olarak değerlendirebilmek için gerçek verilere [actual], tahmin edilen verilere [predicted] ve tahmin edilen değerlerin olasılığına ise [predicted_proba] değişkenlerine atadık. Yukarıda gördüğümüz accuracy, recall gibi metrikleri tek bir rapor halinde gösteren rapor fonksiyonunu çalıştıralım.
# Genel metriklerin bir özeti
print(mt.classification_report(actual, predicted))
Tabloda görüldüğü üzere [classification_report] adlı fonksiyon sayesinde yukarıda bahsettiğimiz metrikleri bir arada görebiliyoruz. Genel anlamda model başarısı iyi görünüyor çünkü target değişken zaten dengeli dağıldığı için accuracy skorunu baz alabiliriz. Bunun yanı sıra recall ve precision değerleri birbirlerine uzak görünmektedir.
ROC-AUC eğrisine geçmeden önce Python’daki scikit-plot kütüphanesinden bahsetmek isterim. Bu kütüphanenin en büyük özelliği içinde bulundurduğu matplotlib fonksiyonları ile model sonuçlarının yorumlanabilmesi için hazır tasarlanmış grafikleri barındırmaktadır. Örneğin confusion matrisi bu kütüphaneden yararlanarak çizdirelim.
# confusion matrix
splt.plot_confusion_matrix(actual, predicted)
Yukarıda renklendirilmiş confusion matrix üzerinden doğru ve hataların dağılımlarını görebilirsiniz. Şimdi sıra geldi ROC-AUC eğrisini çizdirmeye.
# Roc curve
splt.plot_roc(actual, predicted_proba)
Grafiğe baktığımızda 0.8'in üzerine hızlıca çıkan iki sınıfın (class = 0, class = 1) doğruluk oranlarının yüksek ve birbirlerine paralel olduğunu söyleyebiliriz. Aynı zamanda sınıfların geçtiği eğrinin altında yer alan bölüm AUC bölgesi yüksek yer kapladığından model başarısının yüksek olduğunu görebiliriz. AUC değerini skor olarak kod üzerinden bakalım;
# ROC AUC Skor
print(mt.roc_auc_score(actual, predicted_proba))
# ÇIKTI: 0.946
Log (Logaritmik) Loss
Log loss, tahmindeki olasılık değerlerine dayanan sınıflandırma için önemli bir ölçüttür. Gözlem boyutunda log loss’u yorumlamak zordur ancak değer ne kadar düşük olursa model başarısı o kadar yüksek demektir. Yukarıdaki örnekten yola çıkarak hesaplayalım.
# Log Loss
print(mt.log_loss(actual, predicted_proba))
# ÇIKTI: 0.1505
Çıktının düşük bir değerde olması model başarısını göstermektedir. Bu ölçüm yöntemi özellikle Kaggle yarışmalarında sıklıkla bir performans değerlendirme kriteri olarak kullanılmaktadır.
Matthews Correlation Coefficient (MCC)
Yukarıda bahsedilen popüler metriklerin (accuracy, recall, precision vs.) dengesiz dağılan veri setleri için tam anlamıyla bir sonuç verememektedir. Temelde gerçek veriler ile tahmin edilen veriler arasındaki korelasyon (phi-coefficient) ilişkisine bakarak değerlendirme yapan MCC ölçütü, aşağıdaki formülde görüldüğü üzere confusion matrix’de yer alan tüm parametreleri kullanmaktadır.
MCC değeri aynı korelasyon değerlendirmesinde olduğu gibi 1’e yaklaşan sonuçlarda ilişkinin boyutunu güçlü olarak kabul edebiliriz. Aşağıdaki karşılaştırmada MCC değeri diğer metrikler ile karşılaştırılıp yorumlanmıştır.
Metriklerin Karşılaştırılarak Yorumlanması
Yeniden dağınık veri setimize geri dönerek tüm metrikleri hesaba katıp karşılaştırma yapalım.
actual = np.array((1,1,0,0,0,0,0,0,0,0), np.uint8)
predict = np.array((1,1,1,0,0,0,0,0,0,0), np.uint8)
x = [actual, predict]
print("Confussion Matrix: \n", metrics.confusion_matrix(x[0], x[1]))
tn, fp, fn, tp = metrics.confusion_matrix(x[0], x[1]).ravel()
print(f"\ntn {tn}, fp {fp}, fn {fn}, tp {tp}")
print("\nAccuracy Score \n", metrics.accuracy_score(x[0], x[1]))
print("\nf1_score Score \n", metrics.f1_score(x[0], x[1]))
print("\nPrecision Score \n", metrics.precision_score(x[0], x[1]))
print("\nfbeta Score \n", metrics.fbeta_score(x[0], x[1], beta = 0.5))
print("\nROC AUC Score \n", metrics.roc_auc_score(x[0], x[1]))
print("\nRecall Score \n", metrics.recall_score(x[0], x[1]))
print("\nlog_loss Score \n", metrics.log_loss(x[0], x[1], eps=1e-15))
print("\nMCC Score \n", metrics.matthews_corrcoef(x[0], x[1]))
Çıktılar;
Confussion Matrix:
[[7 1]
[0 2]]
tn 7, fp 1, fn 0, tp 2
Accuracy Score
0.900
f1_score Score
0.800
Precision Score
0.666
fbeta Score
0.793
ROC AUC Score
0.937
Recall Score
1.000
log_loss Score
3.453
MCC Score
0.763
Sonuçları incelediğimiz zaman oldukça basit ve dengesiz dağılan veri seti için accuracy %90 oranında oldukça yüksek olmakla birlikte precision düşük ve recall ise oldukça yüksek görülmektedir. Recall ve precision değerlerinin hormanik ortalamasından oluşan F1-score ise daha objektif bir sonuç vermiş diyebiliriz. Precision ağırlığı düşürülerek yeniden bakılan F1-score, beta = 0.5 için doğruluk oranı %79 çıkmıştır. Log loss sonucu beklenenden yukarıda olduğundan model başarısını orta göstermektedir. ROC AUC skoru ise neredeyse accuracy sonucunu aştığı için yüksek durmaktadır. MCC skoru ise dengesiz dağılan veri setine göre objektif bir sonuç çıkararak model başarısını orta düzeyde göstermektedir.
Sonuç
Veri bilimi makine öğrenmesi sınıflandırma algoritmalarında modelin değerlendirilme aşamasının belki de en can alıcı kısımıdır. Çünkü hangi açıdan bakarsanız modelin başarım oranını o şekilde kabul edebilirsiniz. Burada en önemli husus veri setinin dengesiz dağılıp dağılmadığı konusudur. Hangi problem olursa olsun tüm metriklerin denenmesi veya gözlemlenmesinde bir sakınca olmadığı gibi modelin gelişimine de katkı sağlayacağı aşikardır.