Schichten
Kurz gesagt: Schichten verbinden Neuronen hintereinander: Eine verborgene Schicht lässt das Netz Regeln lernen, die eine einzelne Gerade nicht zeichnen kann, zum Beispiel XOR.
Problem
Die Regel, die eine Gerade nicht zeichnen kann
Das Betrugsteam will eine neue Regel: Eine Zahlung ist verdächtig, wenn das Land der Karte und das Land der Verbindung (nach IP-Adresse) nicht übereinstimmen. Eine bulgarische Karte von einer ausländischen IP: Jemand benutzt im Ausland eine gestohlene Karte. Eine ausländische Karte von einer bulgarischen IP: Eine gestohlene Karte wird hier benutzt. Eine bulgarische Karte aus Bulgarien und eine ausländische Karte aus dem Ausland (ein Kunde, der dort lebt) sind in Ordnung.
Es gibt zwei Merkmale, je 0 oder 1, wobei 1 Ausland bedeutet. Es gibt vier Fälle, und (1, 0) und (0, 1) sind verdächtig. Das Programm probiert 15.000 Neuronen aus: jedes Gewicht von −6 bis 6 in Schritten von 0,5 und einen Bias von −5,75 bis 5,75. Für jedes zählt es, wie viele Fälle bei einem Schwellenwert von 0,5 richtig sind:
const cases = [[0, 0], [1, 0], [0, 1], [1, 1]];const labels = [0, 1, 1, 0]; // 1 = verdächtiglet tried = 0;let best = 0;let bestW = [0, 0];let bestB = 0;for (let i = -12; i <= 12; i++) { for (let j = -12; j <= 12; j++) { for (let k = -12; k <= 11; k++) { const w = [i / 2, j / 2]; const b = k / 2 + 0.25; tried++; let right = 0; cases.forEach((x, n) => { const guess = neuron(x, w, b) >= 0.5 ? 1 : 0; if (guess === labels[n]) right++; }); if (right > best) { best = right; bestW = w; bestB = b; } } }}console.log(`probierte Neuronen: ${tried}, bestes Neuron: ${best} von 4`);console.log(`w = [${bestW[0].toFixed(2)}, ${bestW[1].toFixed(2)}], b = ${bestB.toFixed(2)}`);cases.forEach((x, n) => { const p = neuron(x, bestW, bestB); const right = (p >= 0.5 ? 1 : 0) === labels[n]; console.log(`${x[0]} ${x[1]}: p = ${p.toFixed(3)} ${right ? "richtig" : "falsch"}`);});probierte Neuronen: 15000, bestes Neuron: 3 von 4w = [-6.00, 0.50], b = -0.250 0: p = 0.438 richtig1 0: p = 0.002 falsch0 1: p = 0.562 richtig1 1: p = 0.003 richtigDas beste trifft 3 von 4, und es nennt eine ausländische Karte von einer bulgarischen IP „ok“. Keines der 15.000 trifft alle vier. Und das liegt nicht daran, dass das Raster der Gewichte zu grob ist: Die nächste Karte zeigt, warum.
Die Idee
Warum eine Gerade nicht reicht
Zeichne die Fälle als Punkte in einem Quadrat: Die Karte ist die waagerechte Achse, die Verbindung die senkrechte. Die verdächtigen (1, 0) und (0, 1) liegen in zwei gegenüberliegenden Ecken, die anderen beiden sind in Ordnung.
Das Neuron sagt „ja“, wenn z über null liegt. Die Grenze z = 0, also w₁·Karte + w₂·IP + b = 0, ist eine Gerade. Auf der einen Seite sagt das Neuron „ja“, auf der anderen „nein“. Und eine Gerade, die die beiden verdächtigen Ecken auf einer Seite lässt, lässt mindestens eine der anderen beiden bei ihnen. Probier es mit einem Bleistift: Es geht nicht.
Die logistische Regression aus der Lektion über Klassifikation hat dieselbe Grenze, also kann sie es auch nicht. Es braucht zwei Geraden.
Die Idee
Zwei Neuronen in der Mitte
Zwischen Eingabe und Ausgabe setzen wir zwei Neuronen. Jedes zieht seine eigene Gerade:
- h1: „mindestens eins kommt aus dem Ausland“. Gewichte 6 und 6, Bias −3: z liegt über null, sobald mindestens eine Eingabe 1 ist.
- h2: „beide kommen aus dem Ausland“. Gewichte 6 und 6, Bias −9: z liegt nur über null, wenn beide Eingaben 1 sind.
Die Ausgabe liest h1 und h2 mit den Gewichten 10 und −10 und einem Bias von −5: „mindestens eins, aber nicht beide“. Der verdächtige Bereich ist der Streifen zwischen den beiden Geraden.
Die Neuronen in der Mitte sind eine verborgene Schicht. Verborgen, weil ihnen niemand eine richtige Antwort vorgibt: Die Eingabe kommt aus den Daten, die Ausgabe hat ein Label, und was die dazwischen berechnen, entscheidet das Netz selbst. Hier sind die Gewichte von Hand gewählt, damit sie etwas bedeuten, das du in Worte fassen kannst. Gelernte lassen sich meist nicht so leicht in Worte fassen.
Von links nach rechts zu rechnen, Schicht für Schicht, ist der Vorwärtsdurchlauf:
const W = [[6, 6], [6, 6]];const B = [-3, -9];const V = [10, -10];const C = -5;
function forward(x) { // mindestens eins kommt aus dem Ausland const h1 = neuron(x, W[0], B[0]); // beide kommen aus dem Ausland const h2 = neuron(x, W[1], B[1]); const h = [h1, h2]; // die verborgene Schicht return neuron(h, V, C);}Sieh es dir an
Der Vorwärtsdurchlauf, Zahl für Zahl
Ein Fall: eine ausländische Karte von einer bulgarischen IP, (1, 0). Jeder Schritt gibt sein z und seine Ausgabe aus:
const x = [1, 0]; // eine ausländische Karte, eine IP aus Bulgarienconst z1 = weightedSum(x, W[0], B[0]);const h1 = sigmoid(z1);console.log(`h1: z = ${z1.toFixed(2)}, h1 = ${h1.toFixed(4)}`);const z2 = weightedSum(x, W[1], B[1]);const h2 = sigmoid(z2);console.log(`h2: z = ${z2.toFixed(2)}, h2 = ${h2.toFixed(4)}`);const h = [h1, h2];const z = weightedSum(h, V, C);const p = sigmoid(z);console.log(`Ausgabe: z = ${z.toFixed(2)}, p = ${p.toFixed(4)}`);h1: z = 3.00, h1 = 0.9526h2: z = -3.00, h2 = 0.0474Ausgabe: z = 4.05, p = 0.9829h1: 6 × 1 + 6 × 0 − 3 = 3, und die Sigmoid-Funktion ergibt 0,9526. h2: 6 × 1 + 6 × 0 − 9 = −3, das sind 0,0474. Die Ausgabe: 10 × 0,9526 − 10 × 0,0474 − 5 = 4,05, und die Sigmoid-Funktion ergibt 0,9829. Verdächtig.
Die vier Fälle mit forward:
const cases = [[0, 0], [1, 0], [0, 1], [1, 1]];const fmt = (v, d, n = 0) => v.toFixed(d).padStart(n);console.log("Karte IP p");for (const x of cases) { const p = forward(x); const verdict = p >= 0.5 ? "verdächtig" : "ok"; console.log(`${fmt(x[0], 0, 5)} ${fmt(x[1], 0, 3)} ${fmt(p, 4, 7)} ${verdict}`);}Karte IP p 0 0 0.0107 ok 1 0 0.9829 verdächtig 0 1 0.9829 verdächtig 1 1 0.0107 okVier von vier. Dieselben Gewichte, dieselbe Sigmoid-Funktion, nur eine Schicht mehr.
Sieh es dir an
Zwei Geraden und das Signal durch das Netz
const W = [[6, 6], [6, 6]];const B = [-3, -9];const V = [10, -10];const C = -5;
function forward(x) { // mindestens eins kommt aus dem Ausland const h1 = neuron(x, W[0], B[0]); // beide kommen aus dem Ausland const h2 = neuron(x, W[1], B[1]); const h = [h1, h2]; // die verborgene Schicht return neuron(h, V, C);}Schritt für Schritt, in Worten
- Vier Fälle: Karte und IP, 1 bedeutet Ausland. (1, 0) und (0, 1) sind verdächtig.
- Das beste einzelne Neuron zieht eine Gerade und trifft 3 von 4: (1, 0) bleibt auf der falschen Seite.
- Die verborgene Schicht: zwei Geraden, h1 „mindestens eins“ und h2 „beide“. Das Verdächtige liegt dazwischen.
- Der Vorwärtsdurchlauf für eine ausländische Karte von einer bulgarischen IP: x = (1, 0).
- h1: 6 × 1 + 6 × 0 − 3 = 3,00, σ ergibt 0,9526. h2: 6 × 1 + 6 × 0 − 9 = −3,00, σ ergibt 0,0474.
- Die Ausgabe liest h: 10 × 0,9526 − 10 × 0,0474 − 5 = 4,05, σ ergibt 0,9829.
- 0,9829 liegt über 0,5: verdächtig.
- Die vier Fälle durch dasselbe Netz: 0,0107; 0,9829; 0,9829; 0,0107. Vier von vier.
Oben stehen die vier Fälle. Eine Gerade lässt immer einen falsch. Die zwei Geraden der verborgenen Schicht schließen einen Streifen, und die Ausgabe sagt nur darin „verdächtig“.
Probier es aus
Sag die Ausgabe voraus
Dieselbe Regel mit ReLU statt der Sigmoid-Funktion: h1 = ReLU(Karte + IP), h2 = ReLU(Karte + IP − 1), Ausgabe h1 − 2·h2. Was wird für die vier Fälle ausgegeben?
const cases = [[0, 0], [1, 0], [0, 1], [1, 1]];const out = cases.map((x) => { const h1 = relu(x[0] + x[1]); const h2 = relu(x[0] + x[1] - 1); return h1 - 2 * h2;});console.log(out.join(" "));(0, 0): 0 − 0 = 0. (1, 0) und (0, 1): h1 = 1, h2 = ReLU(0) = 0, die Ausgabe ist 1. (1, 1): h1 = 2, h2 = 1, die Ausgabe ist 2 − 2 = 0. Mit ReLU kommt die Regel genau heraus, ohne Rundung. Ohne h2 wäre die letzte Zahl 2.
Probier es aus
Finde den Fehler
Ein Kollege schreibt forward selbst. Das Netz antwortet wie ein einzelnes Neuron: drei von vier. In welcher Zeile steckt der Fehler?
Wähle die Zeile mit dem Fehler.
Die Ausgabe liest die Eingabe x, nicht die verborgene Schicht h. Die verborgenen Neuronen werden berechnet und nicht benutzt, und das Netz fällt zu einem Neuron mit seiner einen Geraden zusammen. Die Ausgabe muss h bekommen.
Probier es aus
Ergänze
Was bekommt das Ausgabeneuron?
const h = [h1, h2];
return neuron(, V, C);Die Ausgabe liest h: die Ausgaben der verborgenen Schicht. Jede Schicht bekommt die Ausgaben der vorherigen.
Die Idee
Wann eine verborgene Schicht hilft und wann sie schadet
- Sie hilft, wenn die Merkmale sich gegenseitig beeinflussen. „Ausländische Karte“ allein bedeutet nichts, und „ausländische IP“ auch nicht. Die Kombination schon. Eine verborgene Schicht lernt solche Kombinationen, ohne dass man es ihr sagt.
- Wenn du die Kombination kennst, schreib sie hin. Das Merkmal „Karte ≠ Verbindung“ ist eine Zeile Code. Damit reicht ein Neuron oder ein
if. Ein Netz, das etwas lernt, das du hinschreiben könntest, ist langsamer, braucht Daten und macht mehr Fehler. - Mehr Neuronen sind mehr Gewichte. Hier sind es 9 Zahlen. Eine verborgene Schicht mit 100 Neuronen über 20 Merkmalen sind 2.201 Zahlen. Mehr Gewichte bedeuten mehr Daten, oder das Netz lernt auswendig.
- Jede Schicht kostet bei jeder Zahlung. Der Vorwärtsdurchlauf geht bei jeder Anfrage durch alle Gewichte. Bei einem großen Netz und vielen Zahlungen sind das Zeit und Geld.
- Verborgene Neuronen müssen nichts bedeuten. Hier tun sie es, weil die Gewichte von Hand gewählt sind. Gelernte sind meist eine Mischung, die sich nicht in Worte fassen lässt.
Genauer, für Neugierige
Mit Formeln. Die verborgene Schicht ist h = σ(W·x + b): W ist eine Tabelle (eine Matrix) aus 2 × 2 Gewichten, b enthält die zwei Biases, und σ wird auf jedes Element angewendet. Die Ausgabe ist p = σ(v·h + c). Der Vorwärtsdurchlauf sind diese zwei Zeilen hintereinander. Deshalb ist eine Schicht in echten Bibliotheken eine Matrix mal ein Vektor.
Deep Learning. Ein Netz mit vielen verborgenen Schichten heißt tief. Jede Schicht baut auf der vorherigen auf: Die ersten erfassen einfache Kombinationen, die nächsten kombinieren diese.
Wie viele Neuronen reichen. Es ist bewiesen, dass eine verborgene Schicht mit genug Neuronen jede stetige Funktion (auf einem beschränkten Bereich) beliebig genau annähern kann. Aber „genug“ kann sehr viel sein, und niemand sagt, wie man die Gewichte findet. Deshalb gibt es in der Praxis mehrere Schichten.
Ein bisschen Geschichte. 1969 zeigten Minsky und Papert, dass eine einzelne Schicht (ein Perzeptron) genau diese Regel (XOR, „das eine oder das andere“) nicht lernen kann. Das Interesse an neuronalen Netzen sank für Jahre. Die Lösung mit einer verborgenen Schicht war bekannt, nicht aber, wie man ihre Gewichte lernt. Das kam mit der Backpropagation.
Übung
Eine Schicht für alle
Löse es in deinem Editor.
Das forward der Lektion weiß, dass es zwei verborgene Neuronen gibt. Schreibe ein allgemeines layer(x, w, b): Es bekommt die Eingaben, eine Tabelle von Gewichten (eine Zeile für jedes Neuron) und die Biases und gibt ein Array mit der Ausgabe jedes Neurons zurück. Baue dann dasselbe Netz als zwei Schichten: die verborgene mit den Gewichten aus der Lektion und die Ausgabeschicht mit einem einzigen Neuron (Gewichte 10 und −10, Bias −5). Lass die vier Fälle durch layer(layer(x, w1, b1), w2, b2) laufen.
Karte IP p 0 0 0.0107 ok 1 0 0.9829 verdächtig 0 1 0.9829 verdächtig 1 1 0.0107 okDieselbe Tabelle wie in der Lektion: layer ist der allgemeine Teil, und das Netz sind zwei Zeilen mit Gewichten.
Weitere Aufgaben
- Ein drittes verborgenes Neuron. Füge der verborgenen Schicht ein Neuron mit den Gewichten 0 und 0 und einem Bias von 0 hinzu (und ein Gewicht von 0 dafür an der Ausgabe). Die Ausgabe darf sich nicht ändern.
- ReLU in der verborgenen Schicht. Gib
layerdie Aktivierung als Parameter und baue das Netz aus „Sag die Ausgabe voraus“.
Test
Teste dich
Verwandte Lektionen
Abschnitt „Verwandte Lektionen“- Baut auf: Pfeilfunktionen · Was maschinelles Lernen ist · Klassifikation: Betrug oder nicht · Das Neuron
- Geht weiter in: Backpropagation · Das Training von Anfang bis Ende · Die Ziffern auf dem Kassenbon
- Aufgaben zum Thema: Ein neuronales Netz von innen