KAPITEL 3
Klassifikation
In Kapitel 1 haben wir erwähnt, dass die am häufigsten zu findenden Aufgaben beim überwachten Lernen Regression (die Vorhersage von Werten) und Klassifikation (die Vorhersage von Kategorien) sind. In Kapitel 2 haben wir uns eine Regressionsaufgabe genauer angesehen, bei der wir den Wert von Immobilien mit unterschiedlichen Algorithmen wie linearer Regression, Entscheidungsbäumen und Random Forests vorhergesagt haben (diese Methoden werden in späteren Kapiteln genauer vorgestellt). Wenden wir uns nun Systemen zur Klassifikation zu.
MNIST
In diesem Kapitel werden wir den MNIST-Datensatz verwenden, eine Sammlung von 70.000 kleinen Bildern handschriftlicher Ziffern, die von Oberschülern und Mitarbeitern des US Census Bureaus aufgeschrieben wurden. Jedes Bild ist mit der dargestellten Ziffer gelabelt. Dieser Datensatz ist so intensiv untersucht worden, dass er oft als »Hello World« des Machine Learning bezeichnet wird: Wann immer jemand ein neues Klassifikationsverfahren entwickelt, möchte man wissen, wie es auf MNIST abschneidet. Jeder, der Machine Learning lernt, beschäftigt sich früher oder später mit MNIST.
Scikit-Learn enthält viele Hilfsfunktionen zum Herunterladen verbreiteter Datensätze. MNIST ist einer davon. Der folgende Code besorgt den MNIST-Datensatz:1
>>> from sklearn.datasets import fetch_openml
>>> mnist = fetch_openml('mnist_784', version=1)
>>> mnist.keys()
dict_keys(['data', 'target', 'feature_names', 'DESCR', 'details',
'categories', 'url'])
Die von Scikit-Learn heruntergeladenen Datensätze sind für gewöhnlich Dictionaries mit einer ähnlichen Struktur, bestehend aus folgenden Schlüsseln:
Der Schlüssel DESCR beschreibt den Datensatz.
Der Schlüssel data enthält ein Array mit einer Zeile pro Datenpunkt und einer Spalte pro Merkmal.
Der Schlüssel target enthält ein Array mit den Labels.
Betrachten wir die beiden Arrays:
>>> X, y = mnist["data"], mnist["target"]
>>> X.shape
(70000, 784)
>>> y.shape
(70000,)
Es gibt 70.000 Bilder, und jedes davon hat 768 Merkmale. Das liegt daran, dass jedes Bild aus 28 x 28 Pixeln besteht und jedes Merkmal einfach die Intensität eines Pixels von 0 (weiß) bis 255 (schwarz) enthält. Betrachten wir eine Ziffer aus dem Datensatz. Dazu müssen wir lediglich den Merkmalsvektor eines Datenpunkts herausgreifen, zu einem Array mit den Abmessungen 28 x 28 umformatieren und mit der Funktion imshow() aus Matplotlib darstellen:
import matplotlib as mpl
import matplotlib.pyplot as plt
some_digit = X[0]
some_digit_image = some_digit.reshape(28, 28)
plt.imshow(some_digit_image, cmap="binary")
plt.axis("off")
plt.show()
Dieses Bild sieht wie eine 5 aus, was uns das Label auch bestätigt:
>>> y[0]
'5'
Beachten Sie, dass das Label ein String ist. Die meisten ML-Algorithmen erwarten Zahlen, daher wollen wir y in einen Integer casten:
>>> y = y.astype(np.uint8)
Abbildung 3-1 zeigt einige weitere Bilder aus dem MNIST-Datensatz, um Ihnen ein Gefühl für die Komplexität dieser Klassifikationsaufgabe zu geben.
Einen Moment noch! Sie sollten stets einen Testdatensatz erstellen und ihn vor dem genaueren Betrachten der Daten beiseitelegen. Der MNIST-Datensatz ist bereits in Trainingsdaten (die ersten 60.000 Bilder) und Testdaten (die letzten 10.000 Bilder) unterteilt:
X_train, X_test, y_train, y_test = X[:60000], X[60000:], y[:60000], y[60000:]
Abbildung 3-1: Ziffern aus dem MNIST-Datensatz
Die Trainingsdaten sind schon für uns gemischt, was gut ist, denn damit stellen wir sicher, dass bei der Kreuzvalidierung sämtliche Folds einander ähnlich sind (Sie möchten nicht, dass einige Ziffern in einem Fold fehlen). Außerdem reagieren ein paar Lernalgorithmen sensibel auf die Reihenfolge der Trainingsdatenpunkte und schneiden schlechter ab, wenn sie viele ähnliche Datenpunkte nacheinander erhalten. Das Mischen des Datensatzes sorgt dafür, dass dies nicht passiert.2
Trainieren eines binären Klassifikators
Für den Anfang werden wir die Aufgabe vereinfachen und lediglich versuchen, eine Ziffer zu erkennen – beispielsweise die Ziffer 5. Dieser »5-Detektor« ist ein Beispiel für einen binären Klassifikator, mit dem sich genau zwei Kategorien unterscheiden lassen, 5 und nicht-5. Erstellen wir also die Zielvektoren für diese Klassifikationsaufgabe:
y_train_5 = (y_train == 5) # True bei allen 5en, False bei allen anderen Ziffern.
y_test_5 = (y_test == 5)
Nun wählen wir einen Klassifikator aus und trainieren diesen. Ein guter Ausgangspunkt ist der Klassifikator für das stochastische Gradientenverfahren (SGD), dem die Klasse SGDClassifier in Scikit-Learn entspricht. Dieser Klassifikator hat den Vorteil, sehr große Datensätze effizient zu bearbeiten. Dies liegt daran, dass SGD die Trainingsdatenpunkte einzeln und nacheinander abarbeitet (wodurch SGD außerdem für das Online-Learning gut geeignet ist). Erstellen wir zunächst einen SGDClassifier und trainieren wir diesen auf dem gesamten Trainingsdatensatz:
from sklearn.linear_model import SGDClassifier
sgd_clf = SGDClassifier(random_state=42)
sgd_clf.fit(X_train, y_train_5)
|
Der SGDClassifier arbeitet beim Trainieren zufallsbasiert (daher der Name »stochastisch«). Wenn Sie reproduzierbare Ergebnisse erhalten möchten, sollten Sie den Parameter random_state setzen. |
Nun können Sie mit dem Klassifikator Bilder mit der Nummer 5 erkennen:
>>> sgd_clf.predict([some_digit])
array([ True])
Der Klassifikator meint, dass dieses Bild eine 5 darstellt (True). Es sieht so aus, als läge er in diesem Fall richtig! Werten wir nun die Qualität dieses Modells aus.