Nano-Neuron - 7 einfache JavaScript-Funktionen, die zeigen, wie die Maschine "lernen" kann

Ein Nano-Neuron ist eine vereinfachte Version eines Neurons aus dem Konzept eines neuronalen Netzwerks. Nano-Neuron erfüllt die einfachste Aufgabe und ist darauf trainiert, Temperaturen von Grad Celsius in Grad Fahrenheit umzuwandeln.


Der Code von NanoNeuron.js besteht aus 7 einfachen JavaScript-Funktionen, die das Lernen, Trainieren, Vorhersagen sowie die direkte und Rückwärtsausbreitung des Modellsignals umfassen. Der Zweck des Schreibens dieser Funktionen bestand darin, dem Leser eine minimale, grundlegende Erklärung (Intuition) zu geben, wie eine Maschine schließlich "lernen" kann. Der Code verwendet keine Bibliotheken von Drittanbietern. Wie das Sprichwort sagt, funktioniert nur einfaches "Vanille" JavaScript.


Diese Funktionen sind keine vollständige Anleitung zum maschinellen Lernen. Viele maschinelle Lernkonzepte fehlen oder sind vereinfacht! Diese Vereinfachung dient einzig und allein dem Zweck, dem Leser das grundlegendste Verständnis und die Intuition dafür zu vermitteln, wie eine Maschine im Prinzip "lernen" kann, so dass sich "MAGIE des maschinellen Lernens" für den Leser immer mehr als "MATHEMATIK des maschinellen Lernens" anhört.


Nanoneuron


Was unser Nano-Neuron "lernen" wird


Möglicherweise haben Sie im Zusammenhang mit neuronalen Netzen von Neuronen gehört. Ein Nano-Neuron ist eine vereinfachte Version desselben Neurons. In diesem Beispiel schreiben wir die Implementierung von Grund auf neu. Der Einfachheit halber werden wir kein Netzwerk von Nano-Neuronen aufbauen. Wir werden uns darauf konzentrieren, ein einziges Nano-Neuron zu erzeugen und ihm beizubringen, wie man die Temperatur von Grad Celsius in Grad Fahrenheit umwandelt. Mit anderen Worten, wir werden ihm beibringen , die Temperatur in Grad Fahrenheit basierend auf der Temperatur in Grad Celsius vorherzusagen .


Übrigens lautet die Formel zum Umrechnen von Grad Celsius in Grad Fahrenheit wie folgt:


Celsius in Fahrenheit


Aber im Moment weiß unser Nano-Neuron nichts über diese Formel ...


Nano-Neuronen-Modell


Beginnen wir mit der Erstellung einer Funktion, die das Modell unseres Nano-Neurons beschreibt. Dieses Modell ist eine einfache lineare Beziehung zwischen x und y , die so aussieht: y = w * x + b . Einfach ausgedrückt, unser Nano-Neuron ist ein Kind, das im XY Koordinatensystem eine gerade Linie zeichnen kann.


Die Variablen w und b sind Modellparameter. Ein Nano-Neuron kennt nur diese beiden Parameter einer linearen Funktion. Genau diese Parameter lernt unser Nano-Neuron während des Trainingsprozesses.


Das einzige, was ein Nano-Neuron in dieser Phase tun kann, ist die Simulation linearer Beziehungen. Er tut dies in der predict() -Methode, die eine Variable x am Eingang und die Variable y am Ausgang voraussagt. Keine Magie.


 function NanoNeuron(w, b) { this.w = w; this.b = b; this.predict = (x) => { return x * this.w + this.b; } } 

_ (... warte ... lineare Regression bist du, oder was?) _


Berechne Grad Celsius in Grad Fahrenheit


Die Temperatur in Grad Celsius kann nach der Formel in Grad Fahrenheit umgerechnet werden: f = 1.8 * c + 32 , wobei c die Temperatur in Grad Celsius und f die Temperatur in Grad Fahrenheit ist.


 function celsiusToFahrenheit(c) { const w = 1.8; const b = 32; const f = c * w + b; return f; }; 

Daher möchten wir, dass unser Nano-Neuron diese spezielle Funktion simuliert. Er muss erraten (lernen), dass der Parameter w = 1.8 und b = 32 ohne es vorher zu wissen.


So sieht die Konvertierungsfunktion im Diagramm aus. Das ist es, was unser nano-neuronales "Baby" lernen muss, zu "zeichnen":


Celsius in Fahrenheit umwandeln


Datengenerierung


In der klassischen Programmierung kennen wir die Eingabedaten ( x ) und den Algorithmus zur Konvertierung dieser Daten (Parameter w und b ), aber die Ausgabedaten ( y ) sind unbekannt. Die Ausgabe wird basierend auf der Eingabe unter Verwendung eines bekannten Algorithmus berechnet. Im Gegensatz dazu sind beim maschinellen Lernen nur die Eingabe- und Ausgabedaten ( x und y ) bekannt, der Algorithmus zum Umschalten von x auf y unbekannt (Parameter w und b ).


Es ist die Erzeugung von Input und Output, die wir jetzt tun werden. Wir müssen Daten zum Trainieren unseres Modells und Daten zum Testen des Modells generieren. Die celsiusToFahrenheit() hilft uns dabei. Jeder der Trainings- und Testdatensätze ist ein Satz von Paaren x und y . Wenn beispielsweise x = 2 , dann ist y = 35,6 und so weiter.


In der realen Welt werden die meisten Daten wahrscheinlich gesammelt und nicht generiert . Bei solchen gesammelten Daten kann es sich beispielsweise um ein Paar von "Gesichtsfotos" -> "Name der Person" handeln.

Wir werden den TRAINING-Datensatz verwenden, um unser Nano-Neuron zu trainieren. Bevor er erwachsen wird und in der Lage ist, selbst Entscheidungen zu treffen, müssen wir ihm beibringen, was „wahr“ und was „falsch“ ist, indem wir „korrekte“ Daten aus einem Trainingssatz verwenden.


Übrigens wird hier das Lebensprinzip „Müll am Eingang - Müll am Ausgang“ deutlich nachvollzogen. Wenn ein Nano-Neuron eine „Lüge“ in das Trainingskit wirft, dass 5 ° C in 1000 ° F umgewandelt werden, dann wird er dies nach vielen Trainingsiterationen glauben und alle Temperaturwerte mit Ausnahme von 5 ° C korrekt konvertieren. Wir müssen mit den Trainingsdaten, die wir täglich in unser neuronales Gehirnnetzwerk laden, sehr vorsichtig sein.

Abgelenkt. Lass uns weitermachen.


Wir werden den TEST-Datensatz verwenden, um zu bewerten, wie gut unser Nano-Neuron trainiert hat, und können korrekte Vorhersagen für neue Daten treffen, die er während seines Trainings nicht gesehen hat.


 function generateDataSets() { // xTrain -> [0, 1, 2, ...], // yTrain -> [32, 33.8, 35.6, ...] const xTrain = []; const yTrain = []; for (let x = 0; x < 100; x += 1) { const y = celsiusToFahrenheit(x); xTrain.push(x); yTrain.push(y); } // xTest -> [0.5, 1.5, 2.5, ...] // yTest -> [32.9, 34.7, 36.5, ...] const xTest = []; const yTest = []; //   0.5    1,       //   ,       . for (let x = 0.5; x < 100; x += 1) { const y = celsiusToFahrenheit(x); xTest.push(x); yTest.push(y); } return [xTrain, yTrain, xTest, yTest]; } 

Vorhersagefehlerschätzung


Wir brauchen eine bestimmte Metrik (Messung, Anzahl, Bewertung), die zeigt, wie nahe die Vorhersage eines Nano-Neurons an der Wahrheit liegt. Mit anderen Worten, diese Zahl / Metrik / Funktion sollte zeigen, wie richtig oder falsch das Nano-Neuron ist. Es ist wie in der Schule, ein Schüler kann eine Note von 5 oder 2 für seine Kontrolle bekommen.


Im Fall eines Nano-Neurons wird sein Fehler (Fehler) zwischen dem wahren Wert von y und dem vorhergesagten Wert der prediction durch die Formel erzeugt:


Vorhersagekosten


Wie aus der Formel hervorgeht, betrachten wir den Fehler als einen einfachen Unterschied zwischen den beiden Werten. Je näher die Werte beieinander liegen, desto geringer ist der Unterschied. Wir verwenden hier die Quadratur, um das Zeichen zu entfernen, sodass (1 - 2) ^ 2 am Ende (2 - 1) ^ 2 . Die Division durch 2 erfolgt nur, um die Bedeutung der Ableitung dieser Funktion in der Formel für die Rückausbreitung eines Signals zu vereinfachen (mehr dazu weiter unten).


Die Fehlerfunktion sieht in diesem Fall folgendermaßen aus:


 function predictionCost(y, prediction) { return (y - prediction) ** 2 / 2; // ie -> 235.6 } 

Direkte Signalausbreitung


Direkte Signalausbreitung durch unser Modell bedeutet, Vorhersagen für alle Paare aus dem xTrain und yTrain Trainingsdatensatz zu yTrain und den durchschnittlichen Fehler (Fehler) dieser Vorhersagen zu berechnen.


Wir lassen unser Nano-Neuron einfach „sprechen“, damit es Vorhersagen treffen kann (Temperatur umwandeln). Gleichzeitig kann ein Nano-Neuron in diesem Stadium sehr falsch sein. Der Durchschnittswert des Vorhersagefehlers zeigt uns, wie weit unser Modell derzeit der Wahrheit entspricht. Der Wert des Fehlers ist hier sehr wichtig, da wir durch Ändern der Parameter w und b und direktes Weiterleiten des Signals auswerten können, ob unser Nano-Neuron mit neuen Parametern „intelligenter“ geworden ist oder nicht.


Der durchschnittliche Vorhersagefehler eines Nano-Neurons wird mit der folgenden Formel berechnet:


Durchschnittskosten


Wobei m die Anzahl der Trainingskopien ist (in unserem Fall haben wir 100 Datenpaare).


So können wir dies in Code implementieren:


 function forwardPropagation(model, xTrain, yTrain) { const m = xTrain.length; const predictions = []; let cost = 0; for (let i = 0; i < m; i += 1) { const prediction = nanoNeuron.predict(xTrain[i]); cost += predictionCost(yTrain[i], prediction); predictions.push(prediction); } //     . cost /= m; return [predictions, cost]; } 

Signalumkehrung


Nun, da wir wissen, wie richtig oder falsch unsere Nano-Neuronen in ihren Vorhersagen sind (basierend auf dem Durchschnittswert des Fehlers), wie können wir die Vorhersagen genauer machen?


Die umgekehrte Signalausbreitung hilft uns dabei. Bei der Signalrückübertragung wird der Fehler eines Nano-Neurons bewertet und anschließend dessen Parameter w und b so b , dass die nächsten Vorhersagen des Nano-Neurons für den gesamten Satz von Trainingsdaten etwas genauer werden.


Hier wird maschinelles Lernen zur Magie. Das Schlüsselkonzept ist hier eine Ableitung der Funktion , die zeigt, welchen Größenschritt und welchen Weg wir gehen müssen, um uns dem Minimum der Funktion (in unserem Fall dem Minimum der Fehlerfunktion) zu nähern.


Das ultimative Ziel beim Training eines Nano-Neurons ist es, das Minimum der Fehlerfunktion zu finden (siehe Funktion oben). Wenn wir solche Werte von w und b bei denen der Durchschnittswert der Fehlerfunktion klein ist, bedeutet dies, dass unser Nano-Neuron mit Temperaturvorhersagen in Grad Fahrenheit gut zurechtkommt.


Derivate sind ein großes und eigenständiges Thema, das wir in diesem Artikel nicht behandeln werden. MathIsFun ist eine großartige Ressource, die ein grundlegendes Verständnis von Derivaten vermitteln kann.


Eine Sache, die wir aus dem Wesen einer Ableitung lernen müssen und die uns helfen wird zu verstehen, wie die Rückausbreitung eines Signals funktioniert, ist, dass die Ableitung einer Funktion an einem bestimmten Punkt x und y per Definition eine Tangente an die Kurve dieser Funktion bei x und ist y und zeigt uns die Richtung zum Minimum der Funktion an .


Ableitung Steigung


Bild aus MathIsFun genommen


In der obigen Grafik sehen Sie beispielsweise, dass am Punkt (x=2, y=4) Neigung der Tangente uns anzeigt, dass wir uns nach und bewegen , um zum Minimum der Funktion zu gelangen. Beachten Sie auch, dass wir uns umso schneller zum kleinsten Punkt bewegen müssen, je größer die Neigung der Tangente ist.


Die Ableitungen unserer durchschnittlichen Fehlerfunktion averageCost in averageCost auf die Parameter w und b sehen folgendermaßen aus:


dW


dB


Wobei m die Anzahl der Trainingskopien ist (in unserem Fall haben wir 100 Datenpaare).


Hier erfahren Sie ausführlicher, wie Sie die Ableitung komplexer Funktionen übernehmen.


 function backwardPropagation(predictions, xTrain, yTrain) { const m = xTrain.length; //           'w'  'b'. //      0. let dW = 0; let dB = 0; for (let i = 0; i < m; i += 1) { dW += (yTrain[i] - predictions[i]) * xTrain[i]; dB += yTrain[i] - predictions[i]; } //    . dW /= m; dB /= m; return [dW, dB]; } 

Model Training


Jetzt wissen wir, wie wir den Fehler der Vorhersagen unseres Nano-Neuron-Modells für alle Trainingsdaten abschätzen können (direkte Signalausbreitung). Wir wissen auch, wie man die Parameter w und b Nano-Neuronen-Modells (Rückausbreitung des Signals) anpasst, um die Genauigkeit der Vorhersagen zu verbessern. Das Problem ist, dass wenn wir das Signal nur einmal vorwärts und rückwärts ausbreiten, dies nicht ausreicht, damit unser Modell die Abhängigkeiten und Gesetze in den Trainingsdaten erkennt und lernt. Sie können dies mit dem eintägigen Schulbesuch eines Schülers vergleichen. Er / sie muss regelmäßig, Tag für Tag, Jahr für Jahr zur Schule gehen, um das gesamte Material zu lernen.


Wir müssen also die Vorwärts- und Rückwärtsausbreitung des Signals viele Male wiederholen . trainModel() Funktion. Sie ist wie eine "Lehrerin" für das Modell unseres Nano-Neurons:


  • Sie wird einige Zeit ( epochs ) mit unserem immer noch albernen Nano-Neuron verbringen und versuchen, ihn zu trainieren.
  • Sie wird spezielle Bücher ( xTrain und yTrain Datensätze) für das Training verwenden.
  • es ermutigt unseren „Schüler“, fleißiger (schneller) mit dem alpha Parameter zu lernen, der im Wesentlichen die Lerngeschwindigkeit steuert.

Ein paar Worte zum alpha Parameter. Dies ist nur ein Koeffizient (Multiplikator) für die Werte der Variablen dW und dB , die wir während der dW des Signals berechnen. Die Ableitung zeigte uns also die Richtung zum Minimum der Fehlerfunktion (die Vorzeichen der Werte von dW und dB sagen dies aus). Die Ableitung zeigte uns auch, wie schnell wir uns dem Minimum der Funktion dW müssen (die absoluten Werte von dW und dB sagen dies aus). Jetzt müssen wir die Schrittgröße mit alpha multiplizieren, um die Geschwindigkeit unserer Annäherung auf ein Minimum (die Gesamtschrittgröße) einzustellen. Wenn wir für alpha große Werte verwenden, können wir manchmal so große Schritte ausführen, dass wir einfach über das Minimum der Funktion springen und diese überspringen können.


In Analogie zur „Lehrerin“, je stärker sie unsere „Nano-Schülerin“ zum Lernen zwingen würde, desto schneller würde er lernen, ABER wenn Sie ihn sehr stark zwingen und unter Druck setzen, könnte unsere „Nano-Schülerin“ einen Nervenzusammenbruch erleben und völlige Apathie und er wird überhaupt nichts lernen.


Wir werden die Parameter unseres Modells w und b wie folgt aktualisieren:


w


b


Und so sieht das Training selbst aus:


 function trainModel({model, epochs, alpha, xTrain, yTrain}) { //     -.  . const costHistory = []; //    ()  for (let epoch = 0; epoch < epochs; epoch += 1) { //   . const [predictions, cost] = forwardPropagation(model, xTrain, yTrain); costHistory.push(cost); //   . const [dW, dB] = backwardPropagation(predictions, xTrain, yTrain); //    -,    . nanoNeuron.w += alpha * dW; nanoNeuron.b += alpha * dB; } return costHistory; } 

Alle Funktionen zusammenfassen


Es ist Zeit, alle zuvor erstellten Funktionen zusammen zu verwenden.


Erstellen Sie eine Instanz des Nano-Neuron-Modells. Derzeit weiß das Nano-Neuron nichts über die Parameter w und b . Setzen wir also w und b zufällig.


 const w = Math.random(); // ie -> 0.9492 const b = Math.random(); // ie -> 0.4570 const nanoNeuron = new NanoNeuron(w, b); 

Wir generieren Trainings- und Testdatensätze.


 const [xTrain, yTrain, xTest, yTest] = generateDataSets(); 

Versuchen wir nun, unser Modell in kleinen Schritten ( 0.0005 ) für 70000 Epochen zu trainieren. Sie können mit diesen Parametern experimentieren, sie werden empirisch ermittelt.


 const epochs = 70000; const alpha = 0.0005; const trainingCostHistory = trainModel({model: nanoNeuron, epochs, alpha, xTrain, yTrain}); 

Lassen Sie uns überprüfen, wie sich der Fehlerwert unseres Modells während des Trainings geändert hat. Wir erwarten, dass der Fehlerwert nach dem Training deutlich geringer sein sollte als vor dem Training. Dies würde bedeuten, dass unser Nano-Neuron klüger ist. Die umgekehrte Option ist auch möglich, wenn nach dem Training der Fehler der Vorhersagen nur zunimmt (zum Beispiel große Werte des Lernschritts alpha ).


 console.log('  :', trainingCostHistory[0]); // ie -> 4694.3335043 console.log('  :', trainingCostHistory[epochs - 1]); // ie -> 0.0000024 

Und so hat sich der Wert des Modellfehlers während des Trainings geändert. Auf der x Achse befinden sich Epochen (in Tausendern). Wir gehen davon aus, dass das Chart abnehmen wird.


Trainingsprozess


Schauen wir uns an, welche Parameter unser Nano-Neuron „gelernt“ hat. Wir erwarten, dass die Parameter w und b den gleichnamigen Parametern der Funktion celsiusToFahrenheit() ( w = 1.8 und b = 32 ) ähnlich sind, da ich versucht habe, ihr Nano-Neuron zu simulieren.


 console.log(' -:', {w: nanoNeuron.w, b: nanoNeuron.b}); // ie -> {w: 1.8, b: 31.99} 

Wie Sie sehen, ist das Nano-Neuron der Funktion celsiusToFahrenheit() sehr celsiusToFahrenheit() .


Nun wollen wir sehen, wie genau die Vorhersagen unseres Nano-Neurons für Testdaten sind, die er während des Trainings nicht gesehen hat. Der Vorhersagefehler für die Testdaten sollte in der Nähe des Vorhersagefehlers für die Trainingsdaten liegen. Dies bedeutet, dass das Nano-Neuron die richtigen Abhängigkeiten gelernt hat und seine Erfahrung korrekt aus zuvor unbekannten Daten abstrahieren kann (dies ist der gesamte Wert des Modells).


 [testPredictions, testCost] = forwardPropagation(nanoNeuron, xTest, yTest); console.log('   :', testCost); // ie -> 0.0000023 

Jetzt, da unser "Nano-Baby" in der "Schule" gut ausgebildet war und jetzt weiß, wie man Grad Celsius in Grad Fahrenheit genau umrechnet, selbst für Daten, die er nicht gesehen hat, können wir ihn als einigermaßen schlau bezeichnen. Jetzt können wir ihn sogar um Rat zur Temperaturumrechnung bitten, und das war der Zweck des gesamten Trainings.


 const tempInCelsius = 70; const customPrediction = nanoNeuron.predict(tempInCelsius); console.log(`- "",  ${tempInCelsius}°C   :`, customPrediction); // -> 158.0002 console.log('  :', celsiusToFahrenheit(tempInCelsius)); // -> 158 

Ganz in der Nähe! Wie die Menschen ist unser Nano-Neuron gut, aber nicht perfekt :)


Erfolgreich codieren!


Wie man ein Nano-Neuron laufen lässt und testet


Sie können das Repository klonen und das Nano-Neuron lokal ausführen:


 git clone https://github.com/trekhleb/nano-neuron.git cd nano-neuron 

 node ./NanoNeuron.js 

Verpasste Konzepte


Die folgenden Konzepte für maschinelles Lernen wurden zur Vereinfachung der Erläuterung weggelassen oder vereinfacht.


Trennung von Trainings- und Testdatensätzen


Normalerweise haben Sie einen großen Datensatz. Abhängig von der Anzahl der Exemplare in diesem Satz kann die Aufteilung in Trainings- und Testsätze im Verhältnis 70/30 erfolgen. Die Daten im Set müssen vor dem Teilen zufällig gemischt werden. Wenn die Datenmenge groß ist (z. B. Millionen), kann die Aufteilung in Test- und Trainingssätze in Anteilen nahe 90/10 oder 95/5 durchgeführt werden.


Online macht


Normalerweise werden Sie keine Fälle finden, in denen nur ein Neuron verwendet wird. Stärke liegt im Netzwerk solcher Neuronen. Ein neuronales Netzwerk kann viel komplexere Abhängigkeiten lernen.


Im obigen Beispiel ähnelt unser Nano-Neuron möglicherweise eher einer einfachen linearen Regression als einem neuronalen Netzwerk.


Eingangsnormalisierung


Vor dem Training ist es üblich, die Eingabedaten zu normalisieren .


Vektorimplementierung


Bei neuronalen Netzen sind Vektor- (Matrix-) Berechnungen viel schneller als Berechnungen in for Schleifen. Normalerweise wird die direkte und umgekehrte Signalausbreitung unter Verwendung von Matrixoperationen ausgeführt, die beispielsweise die Python Numpy- Bibliothek verwenden.


Minimale Fehlerfunktion


Die Fehlerfunktion, die wir für das Nano-Neuron verwendet haben, ist sehr vereinfacht. Es sollte logarithmische Komponenten enthalten. Eine Änderung der Formel für die Fehlerfunktion hat auch eine Änderung der Formeln für die Vorwärts- und Rückwärtsausbreitung des Signals zur Folge.


Aktivierungsfunktion


Normalerweise passiert der Ausgabewert des Neurons die Aktivierungsfunktion. Zur Aktivierung können Funktionen wie Sigmoid , ReLU und andere verwendet werden.

Source: https://habr.com/ru/post/de479220/


All Articles