Praxiseinstieg Machine Learning mit Scikit-Learn, Keras und TensorFlow. Aurélien Géron
in_test_set = ids.apply(lambda id_: test_set_check(id_, test_ratio))
return data.loc[~in_test_set], data.loc[in_test_set]
Leider gibt es im Immobiliendatensatz keine Identifikatorspalte. Die Lösung ist, den Zeilenindex als ID zu nutzen:
housing_with_id = housing.reset_index() # fügt die Spalte `index` hinzu
train_set, test_set = split_train_test_by_id(housing_with_id, 0.2, "index")
Wenn Sie den Zeilenindex als eindeutigen Identifikator verwenden, müssen Sie sicherstellen, dass die neuen Daten am Ende des Datensatzes angehängt werden und nie eine Zeile gelöscht wird. Falls das nicht möglich ist, können Sie immer noch versuchen, einen eindeutigen Identifikator aus den stabilsten Merkmalen zu entwickeln. Beispielsweise werden geografische Länge und Breite garantiert für die nächsten paar Millionen Jahre stabil bleiben, daher könnten Sie diese folgendermaßen zu einer ID kombinieren:15
housing_with_id["id"] = housing["longitude"] * 1000 + housing["latitude"]
train_set, test_set = split_train_test_by_id(housing_with_id, 0.2, "id")
Scikit-Learn enthält einige Funktionen, die Datensätze auf unterschiedliche Weise in Teildatensätze aufteilen. Die einfachste Funktion darunter ist train_test_split(), die so ziemlich das Gleiche tut wie die oben definierte Funktion split_train_test(), aber einige zusätzliche Optionen bietet. Erstens gibt es den Parameter random_state, der den Seed-Wert des Zufallszahlengenerators festlegt. Und zweitens können Sie mehrere Datensätze mit einer identischen Anzahl Zeilen übergeben, die anhand der gleichen Indizes aufgeteilt werden (das ist sehr nützlich, z.B. wenn Sie ein separates DataFrame mit den Labels haben):
from sklearn.model_selection import train_test_split
train_set, test_set = train_test_split(housing, test_size=0.2, random_state=42)
Bisher haben wir ausschließlich zufallsbasierte Methoden zur Stichprobenauswahl betrachtet. Wenn Ihr Datensatz groß genug ist (insbesondere im Vergleich zur Anzahl der Merkmale), ist daran nichts auszusetzen. Ist er aber nicht groß genug, besteht das Risiko, ein erhebliches Stichproben-Bias zu verursachen. Wenn ein Umfrageunternehmen 1.000 Personen anruft, um diesen Fragen zu stellen, wählt es nicht einfach nur zufällig 1.000 Probanden aus dem Telefonbuch aus. Beispielsweise besteht die Bevölkerung der USA aus 51,3% Frauen und 48,7% Männern, also sollte eine gut aufgebaute Studie in den USA dieses Verhältnis auch in der Stichprobe repräsentieren: 513 Frauen und 487 Männer. Dies bezeichnet man als stratifizierte Stichprobe: Die Bevölkerung wird in homogene Untergruppen, die Strata, aufgeteilt, und aus jedem Stratum wird die korrekte Anzahl Datenpunkte ausgewählt. Damit ist garantiert, dass der Testdatensatz die Gesamtbevölkerung angemessen repräsentiert. Würde die Stichprobe rein zufällig ausgewählt, gäbe es eine 12%ige Chance, dass die Stichprobe verzerrt ist und entweder weniger als 49% Frauen oder mehr als 54% Frauen im Datensatz enthalten sind. In beiden Fällen wären die Ergebnisse mit einem erheblichen Bias behaftet.
Nehmen wir an, Experten hätten Ihnen in einer Unterhaltung erklärt, dass das mittlere Einkommen ein sehr wichtiges Merkmal zur Vorhersage des mittleren Immobilienpreises ist. Sie möchten sicherstellen, dass der Testdatensatz die unterschiedlichen im Datensatz enthaltenen Einkommensklassen gut repräsentiert. Da das mittlere Einkommen ein stetiges numerisches Merkmal ist, müssen Sie zuerst ein kategorisches Merkmal für das Einkommen generieren. Betrachten wir das Histogramm des Einkommens etwas genauer (siehe Abbildung 2-8): Die meisten mittleren Einkommen liegen bei 1,6 bis 6 (also 15.000 bis 60.000 USD), aber einige mittlere Einkommen liegen deutlich über 6. Es ist wichtig, dass Ihr Datensatz für jedes Stratum eine genügende Anzahl Datenpunkte enthält, andernfalls liegt ein Bias für die Schätzung der Wichtigkeit des Stratums vor. Das heißt, Sie sollten nicht zu viele Strata haben, und jedes Stratum sollte groß genug sein. Der folgende Code nutzt die Funktion pd.cut(), um ein kategorisches Merkmal für das Einkommen mit fünf Kategorien zu erzeugen (mit den Labels 1 bis 5): Kategorie 1 reicht von 0 bis 1,5 (also weniger als 15.000 USD), Kategorie 2 von 1,5 bis 3 und so weiter:
housing["income_cat"] = pd.cut(housing["median_income"],
bins=[0., 1.5, 3.0, 4.5, 6., np.inf],
labels=[1, 2, 3, 4, 5])
Diese Einkommenskategorien sind in Abbildung 2-9 dargestellt:
housing["income_cat"].hist()
Abbildung 2-9: Histogramm der Einkommenskategorien
Nun sind wir so weit, eine stratifizierte Stichprobe anhand der Einkommenskategorie zu ziehen. Dazu können Sie die Klasse StratifiedShuffleSplit aus Scikit-Learn verwenden:
from sklearn.model_selection import StratifiedShuffleSplit
split = StratifiedShuffleSplit(n_splits=1, test_size=0.2, random_state=42)
for train_index, test_index in split.split(housing, housing["income_cat"]):
strat_train_set = housing.loc[train_index]
strat_test_set = housing.loc[test_index]
Prüfen wir, ob das wie erwartet funktioniert hat. Zunächst einmal können Sie sich die Anteile der Einkommenskategorien im Testdatensatz ansehen:
>>> strat_test_set["income_cat"].value_counts() / len(strat_test_set)
3 0.350533
2 0.318798
4 0.176357
5 0.114583
1 0.039729
Name: income_cat, dtype: float64
Mit einer ähnlichen Codezeile lassen sich die Anteile der Einkommenskategorien im vollständigen Datensatz bestimmen. In Abbildung 2-10 werden die Anteile der Einkommenskategorien im gesamten Datensatz, im als stratifizierte Stichprobe generierten Testdatensatz und in einem rein zufälligen Testdatensatz miteinander verglichen. Wie Sie sehen, sind die Anteile der Einkommenskategorien in der stratifizierten Stichprobe beinahe die gleichen wie im Gesamtdatensatz, während der als zufällige Stichprobe erzeugte Testdatensatz recht verzerrt ist.
Nun sollten Sie das Merkmal income_cat entfernen, damit die Daten wieder in ihrem Ursprungszustand sind:
for set_ in (strat_train_set, strat_test_set):
set_.drop("income_cat", axis=1, inplace=True)
Wir haben uns aus gutem Grund eine Menge Zeit für das Erstellen des Testdatensatzes genommen – ist es doch ein oft vernachlässigter, aber entscheidender Teil eines Machine-Learning-Projekts. Viele der hier vorgestellten Ideen werden noch nützlich sein, sobald wir die Kreuzvalidierung besprechen. Nun ist es an der Zeit, mit dem nächsten Abschnitt fortzufahren: dem Erkunden der Daten.
Abbildung 2-10: Vergleich des Stichproben-Bias einer stratifizierten und einer zufälligen Stichprobe
Erkunde und visualisiere die Daten, um Erkenntnisse zu gewinnen
Bisher haben wir nur einen kurzen Blick auf die Daten geworfen, um ein allgemeines Verständnis von der Art der verarbeiteten Daten zu erhalten. Nun ist unser Ziel, etwas mehr in die Tiefe zu gehen.
Zunächst sollten Sie sicherstellen, dass Sie den Testdatensatz beiseitegelegt haben und nur noch den Trainingsdatensatz erkunden. Wenn Ihr Trainingsdatensatz