Ocena modelu, testowanie klasyfikatora¶
Zadanie uczenia klasyfikatora powinno zapewnić jak najmniejszy błąd rzeczywisty, obliczony na podstawie całej populacji. Jednak podczas uczenia dysponujemy jedynie małym podzbiorem \(X_{train}\) całej populacji. Ponadto każdy klasyfikator posiada pewne parametry, które musimy ustalić. Od wyboru parametrów zależy sprawność klasyfikatora. Niewłaściwy ich wybór może doprowadzić do zwiększenia błędu rzeczywistego. Według [Stapor2005] możemy zdefiniować pewne podstawowe miary błędu klasyfikowania.
Poniżej omawiamy eksperymentalne metody szacowania błędu. Ocenę jego wartości dokonuje się w odniesieniu do zbioru testowego \(X_{test}\), który nie był częścią zbioru uczącego \(X_{train}\), użytego w celu uczenia klasyfikatora.
Definicja 26
Łącznym błędem klasyfikacji nazywamy, wyrażoną w procentach, wartość \(E\) określoną następująco
gdzie \(n_{err}\) oznacza liczbę błędnie sklasyfikowanych próbek w zbiorze testowym, natomiast \(n_{test}\) określa liczebność tego zbioru.
Drugą miarą błędu klasyfikatora jest tzw sprawność klasyfikatora.
Definicja 27
Sprawnością klasyfikatora nazywamy, wyrażoną w procentach, wartość \(Eff\) określoną następująco
gdzie \(n_{prop}\) oznacza liczbę poprawnie sklasyfikowanych próbek.
Nie są to jedyne miary błędu klasyfikacji. W celu oceny sprawności klasyfikacji dla klasyfikatorów binarnych definiujemy cztery wartości \(TP, FP, TN, FN\) w sposób następujący:
- \(TP\) (ang. true positive) - ilość próbek zaklasyfikowanych poprawnie do klasy \(C_1\)
- \(FN\) (ang. false negative) - ilość próbek zaklasyfikowanych niepoprawnie do klasy \(C_1\)
- \(TN\) (ang. true negative)- ilość próbek poprawnie odrzuconych z klasy \(C_1\)
- \(FP\) (ang. false positive)- ilość próbek niepoprawnie odrzuconych z klasy \(C_1\)
Na podstawie powyższych wartości definiujemy następujące miary: zwrotu, precyzji (wrażliwości) oraz specyficzności.
Definicja 28
Zwrotem klasyfikatora nazywamy wartość \(Recall\) określoną następująco
Definicja 29
Precyzją (wrażliwością) klasyfikatora nazywamy wartość \(Sens\) określoną następująco
Definicja 30
Specyficznością klasyfikatora nazywamy wartość \(Spec\) określoną następująco
Miary te są szczególnie często stosowanie w diagnostyce medycznej, gdzie zachodzi problem klasyfikacji pacjentów do grupy osób chorych albo zdrowych. Ponadto opracowano szereg dodatkowych miar oceny, które pozwalają scharakteryzować jakość procesu klasyfikacji. Są to: miara \(f\) czyli tzw. fall-out, dokładność (ang. accuracy) \(Acc\) oraz błąd \(Err\) (ang. error). Miary te są zdefiniowane w sposób następujący
gdzie \(n=TP+TN+FP+FN\). Miara \(f\) określa nam procent błędnych próbek. Natomiast miara \(Acc\) określa nam dokładność klasyfikacji, czyli stosunek sumy próbek poprawnie przydzielonych i poprawnie odrzuconych dla danej kategorii do całkowitej liczby próbek. Błąd \(Err\) natomiast to suma próbek niepoprawnie przydzielonych i niepoprawnie odrzuconych dla danej kategorii do całkowitej liczby próbek. Powyższe miary często są stosowane w zadaniu kategoryzacji tekstu (por. [Manning2008])
Czasami wprowadzone powyżej miary mogą nie być dokładne. Przykładowo, jeśli klasyfikator zaklasyfikuje każdą próbkę do wszystkich możliwych kategorii, wówczas miara \(Recall=100\%\) ale precyzja \(Sens\) jest na niskim poziomie. Odwrotnie, jeśli klasyfikator odrzuci każdą z próbek ze wszystkich grup wówczas prezycja \(Sens\) oraz \(f\) są bardzo dobre natomiast \(Recall\) jest niezadowalające. Zazwyczaj wysoka wartość miary \(Recall\) wiąże się z niską wartością miary \(Sens\). Jeśli jednak uda się tak dobrać parametry klasyfikatora, że miary \(Recall\) i \(Sens\) mają równe wartości wówczas taka wartość jest nazywana BEP (ang. break-even point) [Lewis1994]. Wartość BEP jest często wykorzystywana w ocenie klasyfikatora dla problemu kategoryzacji tekstu. Jeśli niemożliwy jest dobór parametrów klasyfikatora taki aby wartości \(Recall\) i \(Sens\) były sobie równe, wówczas wartość średnia najbliższych sobie wartość \(Recall\) i \(Sens\) jest wykorzystywana jako interpolowane BEP. Jednak jeśli te najbliższe wartości mimo wszystko są stosunkowo dalekie od siebie wówczas wartość BEP może nie odzwierciedlać sprawności systemu.
Inną stosowaną miarą sprawności jest zaproponowana przez van Rijsbergena [Rijsbergen1979] miara \(F_1\).
Definicja 31
Miara \(F_1\) dana jest następującym wzorem
gdzie \(Recall\) i \(Sens\) są dane odpowiednio wzorami (1) i (2)
Miara \(F_1\) jest często używana jako kryterium optymalizacyjne w doborze optymalnych parametrów dla binarnych decyzji. Wartość miary \(F_1\) jest maksymalna kiedy wartości miar \(Recall\) i \(Sens\) są sobie równe. Zauważmy, że BEP to faktycznie wartość miary \(F_1\). Z definicji BEP \(Recall=Sens\) wówczas \(F_1=2Recall^2 / 2Recall=Recall=Sens\). Miarę \(F_1\) można uogólnić wprowadzając współczynnik \(\gamma\). Otrzymujemy wówczas miarę \(F_{\gamma}\) jako średnią ważoną miar \(Recall\) i \(Sens\).
Definicja 32
Miara \(F_{\gamma}\) dana jest następującym wzorem
gdzie \(Recall\) i \(Sens\) są dane odpowiednio wzorami (1) i (2)
Przykład 16
Rozważmy zbiór danych iris. W tym przykładzie ograniczamy liczbę cech obiektów do 2. Zbiór iris dzielimy, w sposób losowy na dwie części: uczącą i testową w stosunku 70:30. Następnie przeprowadzamy na zbiorze uczącym proces uczenia klasyfikatora kNN dla parametru \(k=9\) i testujemy klasyfikator na zbiorze testowym. Dla wybranego parametru \(k\) klasyfikator kNN osiągnął sprawność \(80\%\), błąd \(20\%\). Szczegółowe wartości miar błędu klasyfikatora przedstawia poniższa tabela.
>>>
Sprawność: 0.800000
Błąd: 0.200000
F1: [ 1. 0.64 0.66666667]
F_0.5: [ 1. 0.6557377 0.65217391]
precision recall f1-score support
setosa 1.00 1.00 1.00 19
versicolor 0.67 0.62 0.64 13
virginica 0.64 0.69 0.67 13
avg / total 0.80 0.80 0.80 45
>>>
Poniżej prezentujemy skrypt w języku python realizujący powyższą procedurę.
import numpy as np
from sklearn.cross_validation import train_test_split
from sklearn import svm, cross_validation, datasets, neighbors
from sklearn.metrics import classification_report, f1_score
from sklearn.metrics import fbeta_score
#importujemy zbiór danych
iris = datasets.load_iris()
#Zbiór obiektów
X = iris.data[:, :2]
#Wektor poprawnej klasyfikacji obiektów
y = iris.target
target_names=iris.target_names
#Dzielimy losowo zbiór na dwie części
train, test, train_targets, test_targets = train_test_split(X, y,
test_size=0.30, random_state=42)
#ustalamy parametr k
k = 9
#Tworzymy klasyfikator.
#Jako metrykę przyjmujemy metrykę euklidesową.
clf = neighbors.KNeighborsClassifier(k, weights='uniform',
metric='euclidean')
clf.fit(train, train_targets)
test_labels = clf.predict(test)
print "Sprawność: %f" %clf.score(test, test_targets)
print "Błąd: %f" %(1-clf.score(test, test_targets))
print "F1: ", f1_score(test_targets, test_labels, average=None)
print "F_0.5: ", fbeta_score(test_targets, test_labels,
average=None, beta=0.5)
print classification_report(test_targets, test_labels,
target_names=target_names)
W celu wyznaczenia optymalnych wartości parametrów klasyfikatora wykorzystujemy dodatkowy zbiór \(X_{val}\) zwany zbiorem walidacyjnym. Zbiór ten służy do oceny ,,tymczasowego klasyfikatora’‘. Najczęściej uzyskujemy go z wejściowego zbioru uczącego \(X_{train}\) poprzez odpowiedni podział tego zbioru. Istnieje wiele metod podziału zbioru. Omówimy tutaj dwie często stosowane metody: metodę k-krotnej walidacji krzyżowej oraz metodę bootstrap.
W metodzie k-krotnej walidacji krzyżowej dobór parametru \(k\) w oczywisty sposób zależy od rozmiaru zbioru walidacyjnego i powinien być dobrany tak, aby można było podzielić zbiór walidacyjny na \(k\) równych podzbiorów. W skrajnym przypadku możemy przyjąć \(k=n_{val}\), gdzie \(n_{val}\) jest ilością próbek w zbiorze walidacyjnym. Metodę k-krotnej walidacji krzyżowej w skrócie oznaczamy literami \(CV\), natomiast błąd estymowany za pomocą tej metody oznaczamy symbolem \(E_{CV}\). Metodę k-krotnej walidacji krzyżowej przedstawiamy na poniższym schemacie
Algorytm 15 Algorytm estymacji błędu metodą k-krotnej walidacji krzyżowej (CV)
Dane: zbiór walidacyjny \(X_{val}\), parametr \(k\).
Wyniki: błąd \(E_{CV}\).
- Podziel losowo zbiór \(X_{val}\) na \(k\) równych części
- \(\mathbf{for} \ i=1 \ \mathbf{to} \ k \ \mathbf{do}\)
- \(\qquad\) Naucz klasyfikator na zbiorze \(\left(X_{val}-X_{val}^k\right)\).
- \(\qquad\) Testuj tak nauczony klasyfikator na zbiorze \(X_{val}^k\).
- \(\qquad\) Oblicz błąd \(E_i\).
- Oblicz średni błąd jako \(E_{CV}=\frac{1}{k}\sum_{i=1}^kE_i\).
Ustalając parametr \(k\) należy zwrócić uwagę, że duże wartości \(k\) powodują, iż uzyskany estymator błędu ma małe obciążenie, lecz dużą wariancję i na odwrót małe wartości \(k\) powodują, że uzyskany estymator błędu ma duże obciążenie i małą wariancję. Szczególnym przypadkiem metody k-krotnej walidacji krzyżowej jest metoda leave-one-out (LOO). W tej metodzie parametr \(k=n\). To oznacza, że zbiór testowy w każdym z \(n\) klasyfikatorów jest jednoelementowy. Metoda ta wymaga budowy aż \(n\) klasyfikatorów, co zwiększa jej złożoność obliczeniową. Taki estymator jest jednak estymatorem nieobciążonym, jednak ma największą wariancję spośród wszystkich klasyfikatorów k-krotnej walidacji krzyżowej.
Przykład 17
Rozważmy zbiór danych iris. Wykorzystujemy procedurę k-krotnej walidacji krzyżowej do estymacji błędu liniowego klasyfikatora SVM z parametrem C=1. Parametr k ustalamy na 10 tzn zbiór danych dzielimy na 10 części. Szczegółowe wyniki przedstawia poniższa tabela.
>>>
Wyniki dla każdego podzbioru:
[ 1. 0.93333333 1. 1. 0.86666667 1.
0.93333333 1. 1. 1. ]
Średnia sprawność klasyfikatora
0.973333333333
>>>
Poniżej prezentujemy skrypt w języku python realizujący powyższą procedurę.
import numpy as np
from sklearn import cross_validation
from sklearn import datasets
from sklearn import svm
from sklearn import metrics
#importujemy zbiór danych
iris = datasets.load_iris()
#Zbiór obiektów, ograniczamy ilość cech do 2
X = iris.data
#Wektor poprawnej klasyfikacji obiektów
y = iris.target
#Wykorzystujemy liniowy klasyfikator SVM z parametrem C=1
#i obliczamy błąd klasyfikacji metodą k-krotnej walidacji krzyzowej
#dla parametru k=10
clf = svm.SVC(kernel='linear', C=1)
scores = cross_validation.cross_val_score(clf, X, y, cv=10)
print "Wyniki dla każdego podzbioru: "
print scores
predicted = cross_validation.cross_val_predict(clf, X, y, cv=10)
print "Średnia sprawność klasyfikatora"
print metrics.accuracy_score(iris.target, predicted)
Drugą często stosowaną metodą estymacji błędu klasyfikatora jest metoda bootstrap. Jest ona stosowana dla zbiorów o małej liczebności próbek. Polega ona na losowaniu pewnej ilości razy (powiedzmy \(k\)) \(n\) próbek z pełnego zbioru walidacyjnego \(X_{val}\). Otrzymujemy w ten sposób \(k\) n-elementowych zbiorów, w których niektóre próbki mogą się powtarzać. Oznaczmy te zbiory (tak, jak w poprzedniej metodzie) poprzez \(X_{val}^k\). Każdy z podzbiorów tworzy zbiór uczący klasyfikatora, zaś reszta stanowi zbiór testowy dla danego zbioru uczącego. Tak, jak poprzednio łączny błąd obliczamy jako średnią arytmetyczną błędów dla każdego z \(k\) podzbiorów. Schemat algorytmu przedstawiamy poniżej (Algorytm 16). Błąd estymowany za pomocą metody bootstrap oznaczamy symbolem \(E_{B}=\sum_{i=1}^jE_i\).
Algorytm 16 Algorytm estymacji błędu metodą Bootstrap (B)
Dane: zbiór walidacyjny \(X_{val}\), parametr \(k\), parametr \(n\).
Wyniki: błąd \(E_{B}\).
- \(\mathbf{for} \ i=1 \ \mathbf{to} \ k \ \mathbf{do}\)
- \(\qquad\) Wylosuj ze zwracaniem \(n\) próbkę z pełnego zbioru próbek \(X_{val}\) i oznacz jako \(X_{val}^k\).
- \(\qquad\) Naucz klasyfikator na zbiorze \(X_{val}^k\).
- \(\qquad\) Testuj tak nauczony klasyfikator na zbiorze \(\left(X_{val}\setminus X_{val}^k\right)\).
- \(\qquad\) Oblicz błąd \(E_i\).
- Oblicz średni błąd jako \(E_{B}=\frac{1}{k}\sum_{i=1}^kE_i\).
Przykład 18
Rozważmy zbiór danych iris. Wykorzystujemy procedurę jackknife do estymacji błędu liniowego klasyfikatora SVM z parametrem C=1. Parametr k określający ilość losowań próbki ze zbioru ustalamy na 1000, natomiast parametr \(n\) oznaczający ilość elementów w zbiorze uczącym określamy na \(90\). Otrzymany wynik to średnia z klasyfikatora we wszystkich 1000 iteracjach i wynosi \(3,20\%\). Poniżej prezentujemy skrypt w języku python realizujący powyższą procedurę.
import numpy as np
from sklearn import cross_validation
from sklearn import datasets
from sklearn import svm, neighbors
from sklearn import metrics
from sklearn import cross_validation
#importujemy zbiór danych
iris = datasets.load_iris()
#Zbiór obiektów, ograniczamy ilość cech do 2
X = iris.data
#Wektor poprawnej klasyfikacji obiektów
y = iris.target
#ilości obiektów w zbiorze uczącym i testowym
test_size=60
train_size=90
#dzielimy zbiór
bs = cross_validation.Bootstrap(X.shape[0], n_iter=1000,
train_size=train_size,
test_size=test_size, random_state=0)
#Wykorzystujemy liniowy klasyfikator SVM z parametrem C=1
#i obliczamy błąd klasyfikacji metodą jackknife
clf = svm.SVC(kernel='linear', C=1)
scores=[]
for train_index, test_index in bs:
clf.fit(X[train_index,:] , y[train_index])
scores.append(clf.score(X[test_index,:], y[test_index]))
print "Błąd estymowany metodą Jackknife: %f" %(1-sum(scores)
/np.shape(scores))
Oszacowane powyższymi metodami wartości błędu klasyfikatora wykorzystywane są do obliczenia średniego błędu klasyfikatora oraz przedziału ufności. W tym celu dokonujemy estymacji rozkładu błędu poprzez \(n_{CI}\)-krotne obliczenie wartości \(E_J\) lub \(E_{CV}\). Dla ustalonego poziomu istotności \(\alpha\) przedział ufności określamy jako \(\frac{\alpha}{2}\) oraz \(1-\frac{\alpha}{2}\) percentyl z rozkładu. Zatem przedział ufności \(CI\) ma postać
gdzie \(p_{\frac{\alpha}{2}}\) jest \(\frac{\alpha}{2}\) percentylem. Poniżej podajemy, zaczerpnięty z [Ripley1996], schemat doboru optymalnych parametrów klasyfikatora oraz jego oceny.
Algorytm 17 Schemat wyboru modelu i jego oceny
- Wybór architektury klasyfikatora i parametrów do optymalizacji.
- Uczenie klasyfikatora na zbiorze uczącym.
- Ocena ,,tymczasowego’’ klasyfikatora na zbiorze walidacyjnym.
- Powtarzanie kroków \(1\)-\(3\) dla różnych wartości parametrów klasyfikatora, wybór najlepszego modelu.
- Ocena nauczonego klasyfikatora na zbiorze testowym.