1.2 Podział zbioru danych na uczący i testowy

W problemie klasyfikacji, naszym zadaniem jest stworzenie algorytmu (programu), który na podstawie znanych sobie, opisanych wzorców, będzie w stanie efektywnie rozpoznawać wzorce nieopisane i dotąd sobie nieznane.

Potrzebujemy więc znaleźć sposób, aby zapewnić sobie to nieznane i nieopisane niezbędne przeprowadzenia procesu klasyfikacji. Na tę chwilę dysponujemy jedynie spójnym, jednolitym zbiorem danych. Poznajmy więc najpopularniejsze sposoby na jego podział na część wykorzystywaną w uczeniu klasyfikatora (zbiór uczący) i późniejszej klasyfikacji na już wyuczonym klasyfikatorze (zbiór testowy). Wszystkie przydatne nam metody podziału są zaimplementowane w module model_selection pakietu sklearn. Zaimportujmy więc w naszym skrypcie ten moduł.

from sklearn import model_selection

Podział prosty

Najprostszym możliwym podejściem jest prosty, losowy podział zbioru na dwie części z zachowaniem jakiejś proporcji pomiędzy nimi. Służy do tego metoda train_test_split.

Wywołanie jej rozpoczynamy od przekazania parametrów tablic, które chcemy podzielić (X i y). Atrybutem test_size możemy przekazać proporcję pomiędzy podziałami. Dodatkowo, aby zapewnić powtarzalność podziału przy kolejnym uruchomieniu skryptu, warto przekazywać ziarno losowości, w atrybucie random_state.

splits = model_selection.train_test_split(X, y, test_size=.333, random_state=0)

Podział zwraca nam krotkę zawierającą już docelowe podzbiory. Odłóżmy je do odpowiednich zmiennych i wyświetlmy na ekranie ich wielkości.

X_train, X_test, y_train, y_test = splits
print X_train.shape, X_test.shape, y_train.shape, y_test.shape
(100, 4) (50, 4) (100,) (50,)

Jak widać w parze X_train, y_train znalazły się 2/3, a w X_test, y_test — 1/3 wczytanego zbioru danych.

Walidacja krzyżowa k-fold

Podział prosty pozwala nam na co prawda na uzyskanie zbioru uczącego i testowego, ale nie możemy mieć przy nim pewności, że losowość nie dokonała się w nim w szczęśliwy dla klasyfikatora sposób, który wykaże jego wyższą jakość. Dla bezpieczeństwa i większej rzeczowości naszych badań musimy dokonać wielokrotnego podziału. Teoretycznie moglibyśmy wielokrotnie dokonać tego rodzaju podziału, aby zdobyć wiele par testowych i uczących, ale w takim podejściu istnieje duże prawdopodobieństwo występowania tych samych próbek w różnych zbiorach uczących. Metodą, która niweluje tego rodzaju problem jest k-fold cross-validation.

W podejściu krzyżowym dzielimy losowo zbiór danych na k możliwie równych sobie podzbiorów. Uzyskujemy dzięki temu k par, w których każdy z podzbiorów raz występuje jako zbiór uczący, a pozostała, połączona część zbioru jest wykorzystywana jako zbiór testowy.

Deklarujemy parametr k będący liczbą docelowych podziałów. Zwyczajowo stosuje się k równie 10 lub 30, w zależności od większości zbioru. Inicjalizujemy nim obiekt podziału cv. Następnie w pętli iterujemy ten obiekt i wyświetlamy wielkość podzielonych zbiorów oraz zawartość zmiennej train.

k = 10
cv = model_selection.StratifiedKFold(n_splits=k)

fold = 0
for train, test in cv.split(X_train, y_train):
    print "Fold %i (%i w TS, %i w VS)" % (fold, len(train), len(test))
    print train
    fold += 1
Fold 0 (15 w TS, 135 w VS)
[  0   1   2 ... 102 103 104]
Fold 1 (15 w TS, 135 w VS)
[  5   6   7 ... 107 108 109]
Fold 2 (15 w TS, 135 w VS)
[ 10  11  12 ... 112 113 114]
Fold 3 (15 w TS, 135 w VS)
[ 15  16  17 ... 117 118 119]
Fold 4 (15 w TS, 135 w VS)
[ 20  21  22 ... 122 123 124]
Fold 5 (15 w TS, 135 w VS)
[ 25  26  27 ... 127 128 129]
Fold 6 (15 w TS, 135 w VS)
[ 30  31  32 ... 132 133 134]
Fold 7 (15 w TS, 135 w VS)
[ 35  36  37 ... 137 138 139]
Fold 8 (15 w TS, 135 w VS)
[ 40  41  42 ... 142 143 144]
Fold 9 (15 w TS, 135 w VS)
[ 45  46  47 ... 147 148 149]

Zauważ, że w zmiennych test i train nie zawierają się skompletowane podzbiory a jedynie indeksy wzorców, które powinny się w nich znaleźć.

Last updated