Hauspreise schätzen mit Machine Learning – Lineare Regression
Der erste Algorithmus in Python wartet auf unsere gesammelten Hausdaten von ImmobilienScout24. Wie im letzten Beitrag erwähnt werden wir nun als Baseline-Modell eine lineare Regression anwenden. Mit dieser Methode legen wir in den n-dimensionalen Raum aus erklärenden Variablen eine Hyperebene, die als Funktion aller Eigenschaften der Immobilien den dazugehörigen Preis schätzt. Doch genug mit dem Fachchinesisch, los geht’s!
Einleitung
Nachdem wir im letzten Artikel die gesammelten Daten grundlegend gesäubert haben, können wir jetzt den ersten Algorithmus austesten. Lineare Regression ist ein guter Einstieg in die Welt des Machine Learning. Sie ist oft nicht der präziseste Algorithmus, allerdings lassen sich die Ergebnisse und Koeffiezenten der Schätzfunktion wunderbar interpretieren. Wenn du noch mehr über die Grundlagen linearer Regression erfahren willst, lies dir einfach diesen Beitrag durch.
Daten für lineare Regression vorbereiten
Die Daten der über 61.000 Häuser, die wir hier gesammelt haben, liegen schon als Pandas DataFrame namens df vor. Nun brauchen wir nur noch die zwei unten stehenden weitere Funktionen, um das Modell bauen und testen zu können. Beide sind in der Bibliothek scikit-learn enthalten, welche für Statistik und Machine Learning oft unverzichtbar ist.
from sklearn.linear_model import LinearRegression from sklearn.model_selection import train_test_split
Um die ursprünglichen Daten nicht zu verändern, schreiben wir sie in den neuen DataFrame df2. In diesem Schritt entfernen wir außerdem alle nicht numerischen Werte aus dem DataFrame.
df2 = df.select_dtypes(exclude=["object"])
Außerdem gibt es noch andere Werte, die aus dem Modell ausgeschlossen werden sollen. Nämlich solche, die erst ausgehend vom Preis berechnet werden. Diese dürfen nicht in die lineare Regression einfließen, da sie das Modell auf „unehrliche“ Weise präziser machen.
df2 = df2.drop(["purchasePriceRange", "pricetrend"],axis=1)
Als nächstes definieren wir das Zielmerkmal und splitten den DataFrame senkrecht in eine Matrix X aus erklärenden Variablen und einen Vektor y mit der Zielvariable purchasePrice – dem Kaufpreis.
Zielmerkmal = "purchasePrice" y = df2.fillna(0)[Zielmerkmal].values X = df2.fillna(0).drop(Zielmerkmal,axis=1).values
Im Arbeitsschritt oben füllen wir gleichzeitig alle leeren Zellen mit Nullen, da der Algorithmus mit leeren Einträgen nicht umgehen kann. Außerdem sind X und y keine DataFrames mehr, sondern Numpy-Arrays ohne Spaltenüberschriften. Das wird erreicht mit dem Attribut „values“. Nun können wir die Daten in einen Trainings- und einen Testdatensatz aufteilen. Dabei trainieren wir das Modell mit 70% der Daten und halten die restlichen 30% für die spätere Validierung zurück.
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size = 0.3)
Lineare Regression – das Modell aufstellen
Jetzt können wir endlich das Modell bauen. Zuerst weisen wir die Funktion LinearRegression() der Variable linreg zu.
linreg = LinearRegression()
Die Argumente innerhalb der Funktion belassen wir bei ihren Default-Werten. Alle Eigenschaften dieser Funktion und was du hier genau an deine Bedürfnisse anpassen kannst, findest du auf dieser Seite.
Nun trainieren wir den Algorithmus.
linreg.fit(X_train,y_train)
Während des Trainings versucht der Algorithmus, durch eine lineare Kombination aller einbezogenen Merkmale eine Hyperebene zu bauen, welche den Zusammenhang zwischen den erklärenden Variablen und der Zielvariable so gut wie möglich repräsentiert. Bei „normaler“ linearer Regression heißt dies, dass die Summe der quadrierten Abweichungen zwischen der berechneten Ebene und den wahren Datenpunkten minimiert werden soll.
In der Regel dauert das Training dieser Art von Algorithmus nur wenige Sekunden. Wenn Python damit fertig ist, können wir mit Hilfe des Modells und des Testdatensatzes die Zielvariablen eben dieser Daten schätzen. Wir trainieren das Modell also mit einem Teil der Daten, lassen es auf die neuen Daten los und schauen uns an, wie es performt.
Das Modell testen
Erstellen wir also einen Schätzvektor y_pred, der für jedes Haus in den Testdaten den geschätzten Kaufpreis enthält.
y_pred = linreg.predict(X_test)
Den Vektor y_pred als Reihe von geschätzten Hauspreisen können wir nun mit dem Vektro y_test der echten Preise im Testdatensatz vergleichen. Dafür müssen wir noch die geeigneten Metriken importieren.
from sklearn.metrics import mean_squared_error,mean_absolute_error
Die Funktion mean_squared_error ermittelt den mittleren quadratischen Schätzfehler, der vom Algorithmus standardmäßig genutzt wird, um das Modell zu optimieren. Die zweite Funktion mean_absolute_error berechnet den einfachen mittleren Schätzfehler – also den Betrag, um den sich das Modell im Schnitt irrt. Beide Metriken haben Vor- und Nachteile. Bei der zuerst genannten Metrik fallen große Abweichungen aufgrund der Quadrierung überproportional ins Gewicht. Das kann in Fällen, wo große Fehler besonders fatal sind, sehr nützlich sein. Dafür lässt sich der Wert nicht so gut interpretieren. Die zuletzt genannte Metrik lässt sich schon besser interpretieren. Probieren wir mal beide aus.
mean_squared_error(y_pred,y_test)
Der mittlere quadrierte Fehler beträgt also ca. 51,5 Billionen. Gut zu wissen, schlecht zu interpretieren.
mean_absolute_error(y_pred,y_test)
Der einfache durchschnittliche Schätzfehler beträgt rund 271.301. Das Modell liegt also im Schnitt etwa 270.000 Euro neben dem echten Kaufpreis eines Hauses. Je nachdem, wie teuer ein Haus wirklich ist, kann ein Fehler von 270.000 gut oder schlecht sein. Bei einer Immobilie, die 500.000 Euro angeboten wird, den Wert nur auf gut die Hälfte des Preise zu schätzen, ist ziemlich suboptimal. Bei einer Villa, die 5 Mio. Euro kosten soll, würde die Schätzung bei solch einem Fehler allerdings nur gut 5% neben der Wahrheit liegen. Schauen wir uns doch mal in einem Scatterplot die Beziehung zwischen den geschätzten und den echten Immobilienpreisen an.
import matplotlib.pyplot as plt plt.scatter(y_test,y_pred,alpha=0.1) plt.plot( [min(min(y_test),min(y_pred)),max(max(y_test),max(y_pred))], [min(min(y_test),min(y_pred)),max(max(y_test),max(y_pred))], c="red", alpha=0.2,label="zero error line") plt.xlim(0,max(max(y_test),max(y_pred))/300) plt.ylim(0,max(max(y_test),max(y_pred))/300) plt.xlabel("y_test") plt.ylabel("y_pred") plt.xticks(rotation=45) plt.legend() plt.style.use("default") plt.show()
Würden alle Datenpunkte auf der roten Linie liegen, hätten wir ein perfektes Modell. Leider ist dem nicht so und die Daten streuen in hohem Maße. Die lineare Regression ist zwar eines der einfacheren statistischen Modelle, jedoch oft nicht das präziseste. Schauen wir uns dazu noch die Verteilung der Abweichungen an.
plt.hist((y_pred-y_test),bins=[x*10000 for x in range(-100,100)]) plt.xticks(rotation=45) plt.show()
Auch hier siehst du, dass das Modell relativ breit streut, der Schätzfehler also oft relativ groß ist. Aber es ist ja auch „nur“ ein Baseline-Modell, von dem aus wir uns verbessern wollen. In den nächsten Beiträgen lernst du noch Random Forests, Gradient Boosting und neuronale Netze kennen. Bei Fragen und Feedback kannst du gern die Kommentarfunktion nutzen. Viel Spaß beim Coden und bis zum nächsten Mal!