Zum Inhalt
wannacode

Lineare Regression

Kurz gesagt: Die lineare Regression sagt eine Zahl mit einer Geraden y = w · x + b voraus, und der mittlere quadratische Fehler (MSE) sagt in einer Zahl, wie gut die Gerade zu den Daten passt.

Problem

Zwei Kollegen, zwei Geraden

Das Lager will Stunden versprechen, nicht Tage. Hier sind 10 frühere Bestellungen: die Entfernung und wie viele Stunden sie gedauert haben.

// frühere Bestellungen: Entfernung in km und Stunden bis zur Lieferung
double[] km = [30, 50, 80, 100, 130, 160, 200, 240, 270, 300];
double[] hours = [10, 11, 17, 17, 23, 24, 31, 33, 40, 44];

Zwei Kollegen schauen sich die Punkte an und schlagen jeweils eine Regel vor:

  • Gerade A: Jeder Kilometer gibt 0,08 Stunden dazu, plus 12 Stunden im Lager;
  • Gerade B: Jeder Kilometer gibt 0,12 Stunden dazu, plus 6 Stunden im Lager.

Im Diagramm sehen beide ordentlich aus. Die Kollegen streiten den ganzen Nachmittag. Der Streit endet erst, wenn sie eine Zahl haben, die sagt, wie gut eine Gerade ist.

Die Idee

Die Gerade als Modell

Beide Vorschläge sind von derselben Art: Stunden = w · km + b.

  • w ist die Steigung: wie viele Stunden jeder Kilometer dazugibt;
  • b ist der Achsenabschnitt: die Stunden bei 0 km, also die Bearbeitung im Lager.

Ein Modell, das eine Zahl mit einer Geraden vorhersagt, heißt lineare Regression. Das Modell zu trainieren heißt, w und b zu wählen.

// die Gerade: Stunden = w · km + b
static double Predict(double w, double b, double km) =>
w * km + b;

Anders als die k nächsten Nachbarn merkt sich die Gerade die Bestellungen nicht. Es sind zwei Zahlen, und eine Vorhersage ist eine Multiplikation und eine Addition. Und sie setzt ihre Steigung fort: Für 500 km sagt sie 0,12 · 500 + 6 = 66 Stunden, während die Nachbarn von 500 km die Bestellungen von 300 km sind.

Die Idee

Der Fehler jeder Bestellung

Für jede Bestellung sagt die Gerade irgendeine Zahl. Der Fehler ist der Unterschied: was die Gerade sagt minus was wirklich passiert ist. Er ist positiv, wenn die Gerade mehr verspricht, und negativ, wenn sie weniger verspricht.

Gerade B, Bestellung für Bestellung:

double w = 0.12, b = 6;
for (var i = 0; i < km.Length; i++)
{
var guess = Predict(w, b, km[i]);
Console.WriteLine($"{km[i]} km: tatsächlich {hours[i]} Std., Gerade {guess:F2} Std., Fehler {guess - hours[i]:F2}");
}
Console.WriteLine($"MSE: {Mse(w, b):F2}");
Ausgabe
30 km: tatsächlich 10 Std., Gerade 9.60 Std., Fehler -0.40
50 km: tatsächlich 11 Std., Gerade 12.00 Std., Fehler 1.00
80 km: tatsächlich 17 Std., Gerade 15.60 Std., Fehler -1.40
100 km: tatsächlich 17 Std., Gerade 18.00 Std., Fehler 1.00
130 km: tatsächlich 23 Std., Gerade 21.60 Std., Fehler -1.40
160 km: tatsächlich 24 Std., Gerade 25.20 Std., Fehler 1.20
200 km: tatsächlich 31 Std., Gerade 30.00 Std., Fehler -1.00
240 km: tatsächlich 33 Std., Gerade 34.80 Std., Fehler 1.80
270 km: tatsächlich 40 Std., Gerade 38.40 Std., Fehler -1.60
300 km: tatsächlich 44 Std., Gerade 42.00 Std., Fehler -2.00
MSE: 1.83

Zehn Fehler, manche plus, manche minus. Wir brauchen eine Zahl für die ganze Gerade.

Die Idee

Eine Zahl: der mittlere Quadratwert

Jeder Fehler wird quadriert, die Quadrate werden addiert und durch die Zahl der Bestellungen geteilt. Das ist der mittlere quadratische Fehler, kurz MSE. Eine Zahl, die sagt, wie schlecht ein Modell ist, heißt Verlustfunktion (Verlust). Weniger ist besser, und 0 heißt, die Gerade geht durch jeden Punkt.

// der Mittelwert der quadrierten Fehler (MSE)
double Mse(double w, double b)
{
double sum = 0;
for (var i = 0; i < km.Length; i++)
{
var error = Predict(w, b, km[i]) - hours[i];
sum += error * error;
}
return sum / km.Length;
}

Warum ein Quadrat? Vergleiche drei Geraden. C ist die flache Gerade durch den Mittelwert, 25 Stunden für jede Bestellung:

// der mittlere Fehler ohne Quadrat
double MeanError(double w, double b)
{
double sum = 0;
for (var i = 0; i < km.Length; i++)
{
sum += Predict(w, b, km[i]) - hours[i];
}
return sum / km.Length;
}
string[] names = ["A", "B", "C"];
double[] ws = [0.08, 0.12, 0];
double[] bs = [12, 6, 25];
for (var i = 0; i < names.Length; i++)
{
Console.WriteLine($"Gerade {names[i]}: MSE {Mse(ws[i], bs[i]):F2}, mittlerer Fehler {MeanError(ws[i], bs[i]):F2}");
}
Ausgabe
Gerade A: MSE 17.35, mittlerer Fehler -0.52
Gerade B: MSE 1.83, mittlerer Fehler -0.28
Gerade C: MSE 124.00, mittlerer Fehler 0.00

Ohne das Quadrat hat Gerade C einen mittleren Fehler von 0,00: Sie liegt genauso oft über den Punkten wie darunter, und die Plusse heben die Minusse auf. Die schlechteste Gerade „gewinnt“. Mit dem Quadrat hebt sich nichts auf: C ist 124, A ist 17,35 und B ist 1,83. Der Kollege mit Gerade B gewinnt den Streit.

Das Quadrat tut noch etwas anderes: Ein großer Fehler wiegt mehr. Ein Fehler von 4 Stunden ergibt 16, zwei Fehler von je 2 Stunden ergeben zusammen 8. Eine Gerade, die überall ein bisschen falsch liegt, ist besser als eine, die bei der Hälfte der Bestellungen genau ist und bei der anderen Hälfte sehr falsch.

Sieh es dir an

Die Fehler als Quadrate

static double Predict(double w, double b, double km) =>
w * km + b;
double Mse(double w, double b)
{
double sum = 0;
for (var i = 0; i < km.Length; i++)
{
var error = Predict(w, b, km[i]) - hours[i];
sum += error * error;
}
return sum / km.Length;
}
Schritt für Schritt, in Worten
  1. 10 frühere Bestellungen: wie viele km und nach wie vielen Stunden sie ankamen.
  2. Gerade A: 0,08 Stunden pro km plus 12 Stunden.
  3. Der Fehler jeder Bestellung: was die Gerade sagt minus was wirklich war. Manche Punkte liegen über der Geraden, manche darunter.
  4. Jeder quadrierte Fehler ist ein Quadrat, dessen Seite der Fehler ist. Die mittlere Fläche ist der MSE: 17,35.
  5. Gerade B: 0,12 Stunden pro km plus 6 Stunden. Die Gerade dreht sich, und die Quadrate schrumpfen.
  6. Der MSE von B ist 1,83, gegen 17,35 bei A.
  7. Die Gerade mit dem kleineren MSE liegt näher an den Punkten. Eine Zahl statt eines Streits.

Jedes Quadrat ist ein quadrierter Fehler: Seine Seite ist der Abstand vom Punkt zur Geraden. Der MSE ist ihre mittlere Fläche. Gerade B ist besser, weil ihre Quadrate kleiner sind, nicht weil jemand es so findet.

Probier es aus

Sage die Ausgabe voraus

Der Kurier berechnet 2 € pro Kilogramm. Drei Pakete von 1, 2 und 3 kg kosten 3 €, 5 € und 7 €. Was gibt das Programm aus?

double[] kg = [1, 2, 3];
double[] price = [3, 5, 7];
double sum = 0;
for (var i = 0; i < 3; i++)
{
var error = 2 * kg[i] - price[i];
sum += error * error;
}
Console.WriteLine($"{sum / 3:F2}");
Antwort

Probier es aus

Ergänze

Was wird für jede Bestellung zur Summe addiert?

var error = Predict(w, b, km[i]) - hours[i];
sum += ;

Probier es aus

Finde den Fehler

Ein Kollege schreibt die Verlustfunktion und vergleicht die Geraden. Sein Programm sagt, die beste sei die flache Gerade C. In welcher Zeile steckt der Fehler?

Wähle die Zeile mit dem Fehler.

Die Idee

Wenn die Gerade lügt

Die Gerade ist das einfachste Modell und reicht oft aus. Aber:

  • Die Welt ist nicht gerade. Wenn die Bestellung nach 300 km über ein anderes Lager läuft und noch einen Tag wartet, bilden die Punkte eine Stufe. Die Gerade verläuft zwischen den beiden Teilen und liegt bei beiden falsch.
  • Ein seltsamer Punkt. Eine Bestellung, die eine Woche verloren war, ist ein Fehler von Hunderten Stunden, und sein Quadrat wiegt so viel wie alle anderen zusammen. Die Gerade neigt sich zu ihm und fängt an, über die normalen Bestellungen zu lügen. Solche Punkte prüft man und entfernt sie, oder man misst mit dem mittleren absoluten Fehler.
  • Weit außerhalb der Daten. Die Bestellungen reichen bis 300 km. Für 2.000 km sagt die Gerade 246 Stunden, aber niemand hat geprüft, ob es eine solche Lieferung überhaupt gibt.
  • Ein Zusammenhang ist keine Ursache. Die Gerade sagt, dass weiter entfernte Bestellungen länger unterwegs sind. Sie sagt nicht, warum. Wenn die weiter entfernten Städte einfach von einem langsameren Kurier beliefert werden, zerstört ein schnellerer Kurier dort die Gerade.

Die Gerade, die die Kollegen nach Augenmaß gewählt haben, ist nur eine von unzähligen Kombinationen aus w und b. Welche die beste ist und wie man sie ohne Raten findet, steht in der Lektion über den Gradientenabstieg.

Genauer, für Neugierige

Die Formel für die beste Gerade. Für eine Gerade gibt es eine exakte Formel (die Methode der kleinsten Quadrate). w ist, wie stark sich km und Stunden gemeinsam bewegen, geteilt durch die Bewegung der km allein. b ist so gewählt, dass die Gerade durch den Mittelpunkt der Daten geht:

w = Σ (km − Mittelwert km) · (Stunden − Mittelwert Stunden) / Σ (km − Mittelwert km)², b = Mittelwert Stunden − w · Mittelwert km.

var n = km.Length;
double meanKm = 0, meanHours = 0;
for (var i = 0; i < n; i++)
{
meanKm += km[i];
meanHours += hours[i];
}
meanKm /= n;
meanHours /= n;
double top = 0, bottom = 0;
for (var i = 0; i < n; i++)
{
top += (km[i] - meanKm) * (hours[i] - meanHours);
bottom += (km[i] - meanKm) * (km[i] - meanKm);
}
var w = top / bottom;
var b = meanHours - w * meanKm;
Console.WriteLine($"die beste Gerade: {w:F4} Std./km, {b:F2} Std.");
Console.WriteLine($"MSE: {Mse(w, b):F2}");
Ausgabe
die beste Gerade: 0.1241 Std./km, 5.64 Std.
MSE: 1.62

Die beste Gerade ist etwas besser als B (1,62 gegen 1,83). Der Kollege hat gut geurteilt.

Warum nicht immer die Formel? Sie gilt für eine Gerade mit dem quadratischen Fehler. Mit vielen Merkmalen, mit einer anderen Verlustfunktion und bei neuronalen Netzen gibt es keine exakte Formel, oder sie ist zu langsam. Deshalb nimmt man den Gradientenabstieg.

Mehr Merkmale. Die Gerade kann ein w pro Merkmal haben: Stunden = w₁ · km + w₂ · kg + w₃ · Pakete + b. Die Idee und der MSE sind dieselben.

Der mittlere absolute Fehler (MAE) addiert |Fehler| statt des Quadrats. Er bestraft einen großen Fehler nicht so stark und reagiert ruhiger auf seltsame Punkte. Der MSE ist glatter und deshalb für den Gradientenabstieg bequemer.

In den Bibliotheken: LinearRegression in scikit-learn berechnet genau diese Formel. In ML.NET ist es die Regression Sdca oder Ols.

Übung

Der Preis des Kuriers

Löse die Aufgabe in deinem Editor.

Der Kurier veröffentlicht seine Preisliste nicht, aber der Shop hat festgehalten, was er für acht Pakete gezahlt hat:

// Pakete: Gewicht in kg und der Preis des Kuriers in €
double[] kg = [0.5, 1.0, 1.5, 2.0, 3.0, 4.0, 5.0, 6.0];
double[] eur = [4.10, 4.60, 5.00, 5.60, 6.40, 7.50, 8.30, 9.40];

Die Kollegen schlagen drei Geraden für den Preis vor: A ist 0,90 € pro kg plus 3,80 €, B ist 1,10 € pro kg plus 3,20 €, C ist 0,70 € pro kg plus 4,60 €. Schreibe die Verlustfunktion (MSE), gib sie für alle drei aus und sage, welche die beste ist.

Ausgabe
Gerade A (0.90 €/kg + 3.80 €): MSE 0.0144
Gerade B (1.10 €/kg + 3.20 €): MSE 0.0769
Gerade C (0.70 €/kg + 4.60 €): MSE 0.2869
kleinster MSE: Gerade A

Weitere Aufgaben

  • Der mittlere absolute Fehler. Ersetze das Quadrat durch den Betrag. Ändert sich der Gewinner?
  • Die Formel. Berechne die beste Gerade mit der Formel aus „Genauer, für Neugierige“. Wie viel besser als A ist sie?

Test

Teste dich

1. Die Gerade lautet Stunden = 0,12 · km + 6. Was bedeutet die 6?
2. Warum werden die Fehler quadriert, bevor man sie addiert?
3. Gerade A hat einen MSE von 17,35 und Gerade B einen MSE von 1,83. Welche ist für diese Bestellungen besser?
4. Eine Bestellung geht verloren und kommt nach 200 Stunden statt nach 25 an. Was passiert mit der Geraden mit dem kleinsten MSE?
5. Warum kann die Gerade etwas über eine Bestellung von 500 km sagen, die k nächsten Nachbarn aber nicht?

Erstellt von Ivo Gergov im Rahmen der Kampagne von Evo Tech für zugängliche Programmierausbildung ·