Zum Inhalt
wannacode

Was maschinelles Lernen ist

Kurz gesagt: Maschinelles Lernen ist ein Weg, auf dem das Programm die Regel selbst aus Beispielen (Merkmale und ein Label) holt, statt dass ein Programmierer sie von Hand mit ifs schreibt.

Problem

Wie viele Tage braucht die Bestellung

Die Produktseite muss „kommt in 2 Tagen an“ sagen. Ein Kollege schreibt die Regel nach Kilometern: Bis 100 km sind es 1 Tag, bis 250 km 2 Tage, darüber 3. Dann lässt er sie über 12 frühere Bestellungen laufen, deren Reisezeit wir kennen:

// die Regel des Kollegen: nach Kilometern
static int rule(double km) {
if (km < 100) return 1;
if (km < 250) return 2;
return 3;
}
int right = 0;
for (int i = 0; i < ORDERS.length; i++) {
int guess = rule(ORDERS[i][0]);
if (guess == DAYS[i]) right++;
else System.out.println("Bestellung " + (i + 1) + ": die Regel sagt " + guess + ", tatsächlich " + DAYS[i]);
}
System.out.println("die Regel trifft " + right + " von " + ORDERS.length);
Ausgabe
Bestellung 2: die Regel sagt 1, tatsächlich 2
Bestellung 5: die Regel sagt 2, tatsächlich 3
Bestellung 6: die Regel sagt 2, tatsächlich 3
Bestellung 9: die Regel sagt 3, tatsächlich 4
Bestellung 10: die Regel sagt 3, tatsächlich 4
Bestellung 12: die Regel sagt 3, tatsächlich 5
die Regel trifft 6 von 12

Sechs von zwölf. Der Kollege fügt zwei Regeln hinzu: Ein schweres Paket (über 10 kg) braucht einen Tag länger, ebenso ein ausgelasteter Kurier (über 35 Pakete an dem Tag). Dieselbe Schleife, mit der neuen Regel:

// zwei weitere Regeln: ein schweres Paket und ein ausgelasteter Kurier
static int rule(double km, double kg, double parcels) {
int days = km < 100 ? 1 : km < 250 ? 2 : 3;
if (kg > 10) days++;
if (parcels > 35) days++;
return days;
}
Ausgabe
Bestellung 2: die Regeln sagen 1, tatsächlich 2
Bestellung 12: die Regeln sagen 4, tatsächlich 5
die Regeln treffen 10 von 12

Zehn von zwölf, aber mit drei neuen Zahlen nach Augenmaß: 10 kg, 35 Pakete und „einen Tag länger“. Warum nicht 8 kg? Warum +1 und nicht +0,5? Und nächsten Monat wechselt der Shop den Kurier, und alle Zahlen stimmen wieder nicht. Bei 50.000 Bestellungen und zehn Dingen, die zählen, kann sie niemand von Hand abstimmen.

Die Idee

Die Regel aus den Beispielen

Dreh es um. Statt die Regel zu schreiben und an den früheren Bestellungen zu prüfen, gib dem Programm die früheren Bestellungen und lass es die Regel selbst finden. Das heißt maschinelles Lernen: ein Programm, das die Regel aus Beispielen ableitet, statt dass jemand sie schreibt.

Jedes Beispiel hat zwei Teile:

  • Merkmale: was du über die Bestellung weißt, bevor sie losgeht. Hier sind es drei: der Abstand, das Gewicht und wie viele Pakete der Kurier an dem Tag hat;
  • Label: die Antwort, die du erraten willst. Hier ist es die Anzahl der Tage, die die Bestellung gebraucht hat.
// frühere Bestellungen: km, kg, Pakete des Kuriers an dem Tag
static final double[][] ORDERS = {
{15, 1.2, 18}, {40, 0.8, 35}, {60, 3.5, 20},
{120, 2.0, 22}, {150, 12.0, 25}, {180, 1.5, 40},
{220, 0.5, 15}, {250, 8.0, 30}, {300, 2.5, 45},
{320, 18.0, 20}, {380, 1.0, 28}, {400, 6.0, 42},
};
// wie viele Tage jede gebraucht hat
static final int[] DAYS = {1, 2, 1, 2, 3, 3, 2, 3, 4, 4, 3, 5};

Was du aus den Beispielen bekommst und was für eine neue Bestellung eine Antwort gibt, heißt Modell. Es gibt viele Arten von Modellen. Wir beginnen mit dem einfachsten, bei dem du jede Zahl sehen kannst.

Die Idee

Die nächsten Nachbarn

Wie würdest du raten? Du würdest nach früheren Bestellungen suchen, die der neuen ähneln: ein ähnlicher Abstand, ein ähnliches Gewicht, eine ähnliche Auslastung. Wenn sie 3 Tage gebraucht haben, braucht die neue wahrscheinlich genauso lange.

Das ist ein Modell: k nächste Nachbarn (k-NN). Es behält alle früheren Bestellungen. Für eine neue findet es die k nächsten und gibt den Durchschnitt ihrer Tage zurück. Hier ist k = 3.

„Nah“ heißt ein kleiner Abstand zwischen zwei Bestellungen. Die Differenz jedes Merkmals wird quadriert, die Quadrate werden addiert, und du ziehst die Quadratwurzel aus der Summe, wie beim Abstand zwischen zwei Punkten auf einer Karte.

Es gibt eine Falle. Kilometer liegen in den Hundertern, Kilogramm im einstelligen Bereich. Addierst du die Differenzen, wie sie sind, wiegen 10 kg so viel wie 10 km, also nichts, und die Kilometer entscheiden allein. Deshalb wird jede Differenz durch den Bereich ihres Merkmals geteilt (der größte minus der kleinste Wert in den Daten). Dann liegt jedes Merkmal auf einer Skala: 1 heißt „von einem Ende zum anderen“.

// wie weit die Werte jedes Merkmals auseinanderliegen: max − min
static double[] ranges(double[][] rows) {
double[] range = new double[rows[0].length];
for (int j = 0; j < range.length; j++) {
double min = rows[0][j], max = rows[0][j];
for (double[] row : rows) {
min = Math.min(min, row[j]);
max = Math.max(max, row[j]);
}
range[j] = max - min;
}
return range;
}
static double distance(double[] a, double[] b) {
double sum = 0;
for (int j = 0; j < a.length; j++) {
double d = (a[j] - b[j]) / RANGE[j];
sum += d * d;
}
return Math.sqrt(sum);
}

Sieh es dir an

Die drei nächsten zur neuen Bestellung

static Integer[] nearest(double[] order) {
Integer[] near = new Integer[ORDERS.length];
double[] dist = new double[ORDERS.length];
for (int i = 0; i < ORDERS.length; i++) {
near[i] = i;
dist[i] = distance(order, ORDERS[i]);
}
Arrays.sort(near,
Comparator.comparingDouble(i -> dist[i]));
return near;
}
static double predict(double[] order, int k) {
Integer[] near = nearest(order);
double sum = 0;
for (int n = 0; n < k; n++) {
sum += DAYS[near[n]];
}
return sum / k;
}
Schritt für Schritt, in Worten
  1. Eine neue Bestellung: 140 km, 10 kg, 30 Pakete des Kuriers. Unten stehen die 12 früheren Bestellungen und wie viele Tage jede gebraucht hat.
  2. Der Abstand zu jeder früheren Bestellung. Jede Differenz wird durch den Bereich ihres Merkmals geteilt: km übertönen kg nicht.
  3. Dann sortiert es die Bestellungen nach Abstand: Die ähnlichsten stehen zuerst.
  4. Es nimmt die ersten k = 3: Bestellung 5, Bestellung 8, Bestellung 4. Die übrigen zählen nicht.
  5. Die Vorhersage ist der Durchschnitt ihrer Tage: (3 + 3 + 2) / 3 = 2,7 Tage.
  6. Niemand hat eine Regel geschrieben. Die Antwort kommt von den früheren Bestellungen, die der neuen am meisten ähneln.

Das Modell misst den Abstand zu jeder früheren Bestellung, sortiert sie und nimmt die ersten k:

static Integer[] nearest(double[] order) {
Integer[] near = new Integer[ORDERS.length];
double[] dist = new double[ORDERS.length];
for (int i = 0; i < ORDERS.length; i++) {
near[i] = i;
dist[i] = distance(order, ORDERS[i]);
}
Arrays.sort(near,
Comparator.comparingDouble(i -> dist[i]));
return near;
}
static double predict(double[] order, int k) {
Integer[] near = nearest(order);
double sum = 0;
for (int n = 0; n < k; n++) {
sum += DAYS[near[n]];
}
return sum / k;
}

Die neue Bestellung hat 140 km, 10 kg und 30 Pakete beim Kurier:

double[] order = {140, 10.0, 30};
System.out.printf("die 3 nächsten zu %.0f km, %.1f kg, %.0f Pakete:%n", order[0], order[1], order[2]);
Integer[] near = nearest(order);
for (int n = 0; n < 3; n++) {
double[] o = ORDERS[near[n]];
System.out.printf(" Bestellung %d: %.0f km, %.1f kg, %.0f Pakete → %d Tage (Abstand %.2f)%n",
near[n] + 1, o[0], o[1], o[2], DAYS[near[n]], distance(order, o));
}
System.out.printf("Vorhersage: %.1f Tage%n", predict(order, 3));
Ausgabe
die 3 nächsten zu 140 km, 10.0 kg, 30 Pakete:
Bestellung 5: 150 km, 12.0 kg, 25 Pakete → 3 Tage (Abstand 0.20)
Bestellung 8: 250 km, 8.0 kg, 30 Pakete → 3 Tage (Abstand 0.31)
Bestellung 4: 120 km, 2.0 kg, 22 Pakete → 2 Tage (Abstand 0.53)
Vorhersage: 2.7 Tage

Am nächsten liegt das schwere Paket aus 150 km. Bestellung 8 ist 100 km weiter weg, aber sie ist schwer und an einem ebenso vollen Tag, deshalb kommt sie an zweiter Stelle. Die leichte Bestellung aus 120 km ist die dritte. Der Durchschnitt von 3, 3 und 2 Tagen ist 2,7.

Probier es aus

Sag die Ausgabe vorher

Für eine Bestellung mit 230 km, 3 kg und 24 Paketen sind die drei nächsten Bestellung 4 (2 Tage), Bestellung 7 (2 Tage) und Bestellung 8 (3 Tage), in dieser Reihenfolge. Was druckt das Programm?

double[] order = {230, 3.0, 24};
System.out.printf("%.1f %.1f%n", predict(order, 1), predict(order, 3));
Antwort

Die Idee

Eine Vorhersage für neue Bestellungen

Das Modell ist fertig, sobald es die Daten hat. Neue Bestellungen:

double[][] fresh = {{140, 10.0, 30}, {90, 1.0, 20}, {350, 4.0, 40}};
for (double[] order : fresh) {
System.out.printf("%.0f km, %.1f kg, %.0f Pakete: %.1f Tage%n",
order[0], order[1], order[2], predict(order, 3));
}
Ausgabe
140 km, 10.0 kg, 30 Pakete: 2.7 Tage
90 km, 1.0 kg, 20 Pakete: 1.3 Tage
350 km, 4.0 kg, 40 Pakete: 4.0 Tage

Wie gut es rät, sagen diese drei Zahlen nicht. Die ehrliche Prüfung läuft an Bestellungen, die das Modell nicht gesehen hat, und sie steht in der Lektion über die Auswertung.

Wie wählst du k? Bei k = 1 sind die Tage einer einzigen Bestellung die Vorhersage, und die kann eine Ausnahme sein: ein Feiertag, ein verlorenes Paket. Ist k gleich allen Bestellungen, ist die Vorhersage der Durchschnitt von allem, und das Modell unterscheidet nicht mehr zwischen 15 km und 400 km. Meist probierst du ein paar Werte aus und nimmst den, der bei ungesehenen Bestellungen am seltensten daneben liegt.

Probier es aus

Ergänze

Die Vorhersage ist der Durchschnitt der Tage der k nächsten. Wodurch wird die Summe geteilt?

double sum = 0;
for (int n = 0; n < k; n++) {
    sum += DAYS[near[n]];
}
return sum / ;

Probier es aus

Finde den Fehler

Ein Kollege schreibt den Abstand um. Das Programm läuft, aber die Nachbarn der Bestellung mit 140 km und 10 kg sind jetzt die Bestellungen 5, 4, 6: Die schwere Bestellung 8 vom selben vollen Tag ist herausgefallen, und ein leichtes Paket hat ihren Platz genommen. Für die Bestellung mit 90 km wird die Vorhersage 1.7 Tage statt 1.3. In welcher Zeile steckt der Fehler?

Wähle die Zeile mit dem Fehler.

Die Idee

Wann maschinelles Lernen das falsche Werkzeug ist

Ein Modell ist eine Regel, die niemand geschrieben hat. Das ist seine Stärke und sein Preis.

  • Die Regel ist bekannt und stabil. Die Liefergebühr beträgt 4,90 € bis 2 kg, 6,90 € über 2 kg, ab 50 € ist sie frei. Das ist ein if, kein Modell. Ein Modell würde sie nur ungefähr lernen und läge manchmal um ein paar Cent daneben.
  • Du brauchst eine exakte Antwort oder eine Erklärung. Steuer, ein Rabatt aus einem Vertrag, das Rückgaberecht. „Meistens ist es so“ reicht dort nicht, und „das Modell hat entschieden“ ist für den Kunden keine Erklärung.
  • Es gibt keine Daten. Ein neuer Shop ohne frühere Bestellungen hat nichts, woraus er lernen kann. Dann ist eine Regel nach Augenmaß ein ehrlicher Anfang, und die Daten kommen später.
  • Die Welt hat sich verändert. Das Modell kennt nur die Vergangenheit. Ein neuer Kurier, Feiertage, ein Streik: Vorhersagen aus den alten Bestellungen lügen, bis das Modell neu trainiert wird.
  • Ein Fehler ist teuer. Ein Modell, das in 1 von 20 Fällen danebenliegt, taugt für „kommt in 2 Tagen an“ und taugt nicht für „die Zahlung durchlassen“. Dort steht eine Regel oder ein Mensch daneben.

Maschinelles Lernen ist für die Fälle, in denen die Regel zu viele Teile hat, als dass ein Mensch sie schreiben könnte, und es viele Beispiele gibt.

Genauer, für Neugierige

Was eine Vorhersage kostet. k-NN lernt nichts im Voraus: Für jede neue Bestellung vergleicht es mit allen früheren und sortiert sie. Bei n Bestellungen sind das mindestens n Abstände pro Vorhersage. Bei 12 Bestellungen ist das nichts, bei 10 Millionen ist es langsam. Deshalb benutzen echte Bibliotheken Strukturen wie einen k-d-Baum, und bei vielen Merkmalen suchen sie näherungsweise. Was „wächst mit n“ heißt, steht in der Lektion über Big-O.

Dieselbe Suche, aber für Text. Die Suche nach Bedeutung in Datenbanken ist k nächste Nachbarn über Vektoren: die Lektion über Vektoren im SQL-Kurs.

Das Teilen durch den Bereich heißt Min-Max-Normalisierung. Ein anderer Weg ist, den Mittelwert abzuziehen und durch die Standardabweichung zu teilen. Warum die Skala der Merkmale auch für andere Modelle wichtig ist, siehst du in der Lektion über den Gradientenabstieg.

Die echten Werkzeuge. Hier sind es Schleifen und Arrays, damit du jede Zahl siehst. In der Praxis wird maschinelles Lernen meist in Python gemacht: NumPy für Arrays, scikit-learn für die klassischen Modelle (dort ist k-NN KNeighborsRegressor), PyTorch für neuronale Netze. C# hat ML.NET, Java hat DJL und Tribuo, JavaScript hat TensorFlow.js und ONNX Runtime. Die Ideen sind dieselben.

Modelle trainieren und in Produktion betreiben (Trainingsserver, Deployment, beobachten, ob ein Modell veraltet) ist Infrastruktur und wird im DevOps-Kurs ZeroToOps gelehrt.

Übung

Wie viele Minuten das Verpacken dauert

Löse die Aufgabe in deinem Editor.

Das Lager will wissen, wie viele Minuten das Verpacken einer Bestellung dauert, damit es das Personal planen kann. Die Merkmale sind die Anzahl der Artikel und das Gesamtgewicht, das Label sind die Minuten. Hier sind zehn frühere Bestellungen:

// frühere Bestellungen: Anzahl der Artikel, kg
static final double[][] ORDERS = {
{1, 0.5}, {2, 1.0}, {3, 4.0}, {5, 2.0}, {6, 8.0},
{8, 3.0}, {10, 12.0}, {12, 5.0}, {15, 9.0}, {20, 6.0},
};
// Minuten zum Verpacken
static final int[] MINUTES = {4, 6, 9, 10, 15, 14, 22, 18, 25, 28};

Schreib k nächste Nachbarn mit k = 3 und Teilung durch den Bereich und sag zwei neue Bestellungen vorher: 4 Artikel und 1,5 kg sowie 11 Artikel und 10 kg.

Ausgabe
4 Artikel, 1.5 kg: 6.7 Min.
11 Artikel, 10.0 kg: 20.7 Min.

Weitere Aufgaben

  • Ohne den Bereich. Entferne die Teilung durch den Bereich und sieh nach, welche Nachbarn herauskommen. Welches Merkmal entscheidet jetzt?
  • Gewichte. Gib den Nachbarn ein Gewicht: Der nähere zählt mehr (zum Beispiel 1 / Abstand). Ändert sich die Vorhersage?

Test

Teste dich

1. Der Shop trainiert ein Modell, das sagt, wie viele Tage eine Bestellung braucht. Was ist das Label?
2. Warum teilt der Abstand jede Differenz durch den Bereich ihres Merkmals?
3. Was macht k nächste Nachbarn, wenn eine neue Bestellung eintrifft?
4. Die Liefergebühr beträgt 4,90 € bis 2 kg und 6,90 € über 2 kg, und ab 50 € ist sie frei. Wie berechnest du sie?
5. Warum ist k = 1 riskant?

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