Praxiseinstieg Machine Learning mit Scikit-Learn, Keras und TensorFlow. Aurélien Géron
Rufen Sie die Methode decision_function() auf, werden Sie sehen, dass zehn Scores pro Instanz zurückgegeben werden (statt nur einer). Das ist ein Score pro Kategorie:
>>> some_digit_scores = svm_clf.decision_function([some_digit])
>>> some_digit_scores
array([[ 2.92492871, 7.02307409, 3.93648529, 0.90117363, 5.96945908,
9.5 , 1.90718593, 8.02755089, -0.13202708, 4.94216947]])
Der höchste Wert ist in der Tat derjenige für die Kategorie 5:
>>> np.argmax(some_digit_scores)
5
>>> svm_clf.classes_
array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9], dtype=uint8)
>>> svm_clf.classes_[5]
5
|
Beim Trainieren eines Klassifikators wird die Liste der Zielkategorien im Attribut classes_ nach Werten sortiert gespeichert. In diesem Fall entspricht der Index jeder Kategorie im Array classes_ bequemerweise der Kategorie selbst (beispielsweise ist die Kategorie beim Index 5 auch die Kategorie 5), aber normalerweise ist dies nicht so. |
Wenn Sie Scikit-Learn dazu bringen möchten, das One-versus-One- oder das One-versus-the-Rest-Verfahren zu verwenden, nutzen Sie dazu die Klassen OneVs OneClassifier bzw. OneVsRestClassifier. Erstellen Sie einfach eine Instanz, der Sie im Konstruktor einen Klassifikator übergeben (es muss nicht einmal ein binärer Klassifikator sein). Der folgende Code erzeugt beispielsweise einen Klassifikator mit mehreren Kategorien mithilfe der OvR-Strategie und einem SVC:
>>> from sklearn.multiclass import OneVsRestClassifier
>>> ovr_clf = OneVsRestClassifier(SVC())
>>> ovr_clf.fit(X_train, y_train)
>>> ovr_clf.predict([some_digit])
array([5], dtype=uint8)
>>> len(ovr_clf.estimators_)
10
Einen SGDClassifier (oder einen RandomForestClassifier) zu verwenden, ist genauso einfach:
>>> sgd_clf.fit(X_train, y_train)
>>> sgd_clf.predict([some_digit])
array([5], dtype=uint8)
Diesmal musste Scikit-Learn weder OvR noch OvO anwenden, da SGD-Klassifikatoren Datenpunkte direkt mehreren Kategorien zuordnen können. Die Methode decision_function() liefert nun einen Wert pro Kategorie zurück. Schauen wir uns den Score an, den der SGD-Klassifikator jeder Kategorie zugewiesen hat:
>>> sgd_clf.decision_function([some_digit])
array([[-15955.22628, -38080.96296, -13326.66695, 573.52692, -17680.68466,
2412.53175, -25526.86498, -12290.15705, -7946.05205, -10631.35889]])
Sie sehen auch, dass sich der Klassifikator seiner Vorhersage sehr sicher ist: So gut wie alle Scores sind deutlich negativ, aber die Kategorie 5 besitzt einen Score von 2412,5. Bei Kategorie 3 ist sich das Modell nicht sicher, da sie einen Score von 573,5 hat. Diese Klassifikatoren sollten wir nun natürlich auch auswerten. Wie gewöhnlich setzen wir die Kreuzvalidierung ein. Beurteilen wir die Genauigkeit des SGD-Classifier mit der Funktion cross_val_score():
>>> cross_val_score(sgd_clf, X_train, y_train, cv=3, scoring="accuracy")
array([0.8489802 , 0.87129356, 0.86988048])
In sämtlichen Test-Folds erhalten wir mehr als 84%. Mit einem zufälligen Klassifikator hätten wir eine Genauigkeit von 10% erhalten. Dies ist also kein schlechtes Ergebnis. Es geht aber noch viel besser. Beispielsweise erhöht ein einfaches Skalieren der Eingabedaten (wie in Kapitel 2 besprochen) die Genauigkeit auf über 89%:
>>> from sklearn.preprocessing import StandardScaler
>>> scaler = StandardScaler()
>>> X_train_scaled = scaler.fit_transform(X_train.astype(np.float64))
>>> cross_val_score(sgd_clf, X_train_scaled, y_train, cv=3, scoring="accuracy")
array([0.89707059, 0.8960948 , 0.90693604])
Fehleranalyse
In einem echten Projekt würden Sie an dieser Stelle natürlich die Schritte auf der Checkliste für Machine-Learning-Projekte abarbeiten (siehe Anhang B): Optionen zur Datenaufarbeitung abwägen, mehrere Modelle ausprobieren, die besten in eine engere Auswahl ziehen, deren Hyperparameter mit GridSearchCV optimieren und so viel wie möglich automatisieren. Wir nehmen an dieser Stelle an, dass Sie ein vielversprechendes Modell gefunden haben und nach Verbesserungsmöglichkeiten suchen. Eine Möglichkeit ist, die Arten von Fehlern zu untersuchen, die das Modell begeht.
Sie können sich zunächst die Konfusionsmatrix ansehen. Dazu müssen Sie als Erstes über die Funktion cross_val_predict() Vorhersagen treffen und anschließend die bereits gezeigte Funktion confusion_matrix() aufrufen:
>>> y_train_pred = cross_val_predict(sgd_clf, X_train_scaled, y_train, cv=3)
>>> conf_mx = confusion_matrix(y_train, y_train_pred)
>>> conf_mx
array([[5578, 0, 22, 7, 8, 45, 35, 5, 222, 1],
[ 0, 6410, 35, 26, 4, 44, 4, 8, 198, 13],
[ 28, 27, 5232, 100, 74, 27, 68, 37, 354, 11],
[ 23, 18, 115, 5254, 2, 209, 26, 38, 373, 73],
[ 11, 14, 45, 12, 5219, 11, 33, 26, 299, 172],
[ 26, 16, 31, 173, 54, 4484, 76, 14, 482, 65],
[ 31, 17, 45, 2, 42, 98, 5556, 3, 123, 1],
[ 20, 10, 53, 27, 50, 13, 3, 5696, 173, 220],
[ 17, 64, 47, 91, 3, 125, 24, 11, 5421, 48],
[ 24, 18, 29, 67, 116, 39, 1, 174, 329, 5152]])
Dies ist eine Menge Zahlen. Es ist oft bequemer, sich eine Konfusionsmatrix über die Matplotlib-Funktion matshow() als Diagramm anzeigen zu lassen:
plt.matshow(conf_mx, cmap=plt.cm.gray)
plt.show()
Diese Konfusionsmatrix sieht recht gut aus, da die meisten Bilder auf der Hauptdiagonalen liegen. Dies bedeutet, dass sie korrekt zugeordnet wurden. Die 5en sehen etwas dunkler als die anderen Ziffern aus. Dies könnte bedeuten, dass es weniger Bilder von 5en im Datensatz gibt oder dass der Klassifikator bei den 5en nicht so gut wie bei den übrigen Ziffern abschneidet. Es lässt sich zeigen, dass hier beides der Fall ist.
Heben wir im Diagramm nun die Fehler hervor. Zunächst müssen Sie jeden Wert in der Konfusionsmatrix durch die Anzahl der Bilder in der entsprechenden Kategorie teilen, sodass Sie den Anteil der Fehler statt der absoluten Anzahl Fehler vergleichen können (Letzteres würde die häufigen Kategorien übertrieben schlecht aussehen lassen):
row_sums = conf_mx.sum(axis=1, keepdims=True)
norm_conf_mx = conf_mx / row_sums
Nun füllen wir die Diagonale mit Nullen auf, um nur die Fehler zu betrachten, und plotten das Ergebnis:
np.fill_diagonal(norm_conf_mx,