Die Mathematik hinter KI: Lassen Sie uns einfach anfangen
Wie würden Sie normalerweise ein Programm schreiben, um zwei Zahlen unter 100 zu addieren? Ganz einfach, oder? Normalerweise würden Sie etwas Einfaches schreiben wie:
#include <iostream>
using namespace std;
int main() {
int a, b, sum;
cout << "Geben Sie die erste Zahl ein: ";
cin >> a;
cout << "Geben Sie die zweite Zahl ein: ";
cin >> b;
sum = a + b;
cout << "Die Summe ist: " << sum << endl;
return 0;
}
Aber lassen Sie uns einen aufregenderen Weg einschlagen – lassen Sie uns einer Maschine beibringen, selbstständig zu lernen, wie man addiert, und zwar mit einem neuronalen Netzwerk. Neuronale Netzwerke stehen im Zentrum der modernen KI und treiben alles an, von der Bilderkennung und Sprachübersetzung bis hin zu fortgeschrittenen Systemen wie ChatGPT.
In diesem Artikel zeigen wir, wie ein neuronales Netzwerk "lernen" kann, zwei Zahlen durch Training zu addieren, nicht indem es einer fest kodierten Regel folgt. Sie werden sehen, wie ein neuronales Netzwerk lernt zu "addieren", indem es Muster erkennt. Wir werden sogar die tatsächlichen Zahlen (die Magie hinter dem Vorhang) enthüllen, die dies ermöglichen.
Hier ist ein Blick auf die interne Struktur eines trainierten neuronalen Netzwerks:
// Gewichte und Biases der versteckten Schicht
double weight1_node1 = 0.658136;
double weight2_node1 = 0.840666;
double bias_node1 = -0.893218;
double weight1_node2 = -0.720667;
double weight2_node2 = -0.369172;
double bias_node2 = 0.036762;
double weight1_node3 = 0.512252;
double weight2_node3 = 0.292342;
double bias_node3 = -0.0745917;
// Gewichte und Bias der Ausgabeschicht
double weight_out_node1 = 1.93108;
double weight_out_node2 = -0.718584;
double weight_out_node3 = 0.589741;
double bias_out = -0.467899;
Auf den ersten Blick könnten diese Werte zufällig erscheinen, aber sie repräsentieren ein winziges künstliches Gehirn, das in der Lage ist, zu lernen, wie man zwei Zahlen addiert.
Verwenden Sie diese Formel, um die Ausgabe zu berechnen. Keine Sorge, wenn es jetzt kompliziert erscheint – folgen Sie einfach mit:
Hier sind x1
und x2
die beiden Zahlen, die Sie addieren möchten (normalisiert durch Division durch 100):
double x1 = 0.3; // 30 / 100
double x2 = 0.5; // 50 / 100
// Versteckte Schicht
double z1 = x1 * weight1_node1 + x2 * weight2_node1 + bias_node1;
double h1 = sigmoid(z1);
double z2 = x1 * weight1_node2 + x2 * weight2_node2 + bias_node2;
double h2 = sigmoid(z2);
double z3 = x1 * weight1_node3 + x2 * weight2_node3 + bias_node3;
double h3 = sigmoid(z3);
// Ausgabeschicht
double sum = (h1 * weight_out_node1 + h2 * weight_out_node2 + h3 * weight_out_node3 + bias_out) * 200.0f;
Lassen Sie uns aufschlüsseln, warum wir mit 200 multiplizieren: Wir haben unsere Eingaben und Ausgaben zunächst skaliert, um zwischen 0 und 1 zu passen, um das Training zu erleichtern. Da die maximale Summe von zwei Zahlen unter 100 200 beträgt, skalieren wir die Ausgabe zurück, indem wir sie mit 200 multiplizieren.
Sigmoid-Funktion:
double sigmoid(double x) {
return 1.0 / (1.0 + exp(-x));
}
Lassen Sie es uns mit einigen echten Zahlen ausprobieren:
Lassen Sie uns x1 = 30
und x2 = 50
verwenden. Wir normalisieren sie zuerst:
x1 = 30 / 100 = 0.3
x2 = 50 / 100 = 0.5
Jetzt gehen wir die Mathematik durch:
z1 = (0.3 × 0.658136) + (0.5 × 0.840666) - 0.893218
≈ 0.1974408 + 0.420333 - 0.893218
≈ -0.2754442
h1 = sigmoid(z1) ≈ 0.431844
z2 = (0.3 × -0.720667) + (0.5 × -0.369172) + 0.036762
≈ -0.2162001 - 0.184586 + 0.036762
≈ -0.3640241
h2 = sigmoid(z2) ≈ 0.409872
z3 = (0.3 × 0.512252) + (0.5 × 0.292342) - 0.0745917
≈ 0.1536756 + 0.146171 - 0.0745917
≈ 0.2252549
h3 = sigmoid(z3) ≈ 0.556078
// Endausgabeschicht
output = (h1 × 1.93108) + (h2 × -0.718584) + (h3 × 0.589741) - 0.467899
≈ (0.834389) + (-0.294320) + (0.328013) - 0.467899
≈ 0.400183
Vorhergesagte Summe = 0.400183 × 200 ≈ 80.0366
// Warum mit 200 multiplizieren? Weil wir während des Trainings alle Eingaben und Ausgaben normalisiert haben, um zwischen 0 und 1 zu bleiben, um die Lernstabilität zu verbessern. Da die maximale Summe von zwei Eingaben 100 + 100 = 200 beträgt, skalieren wir die Ausgabe des Netzwerks zurück, indem wir sie mit 200 multiplizieren.
Boom! Das Ergebnis ist ungefähr 80, was die korrekte Summe von 30 und 50 ist.
Wenn Sie nicht glauben, dass dieses kleine Netzwerk wirklich addieren kann, lassen Sie es uns erneut mit anderen Zahlen testen: 20 und 30.
Wir normalisieren die Eingaben zuerst:
x1 = 20 / 100 = 0.2
x2 = 30 / 100 = 0.3
Dann die Berechnungen:
z1 = (0.2 × 0.658136) + (0.3 × 0.840666) - 0.893218
≈ 0.1316272 + 0.2521998 - 0.893218
≈ -0.509391
h1 = sigmoid(z1) ≈ 0.375241
z2 = (0.2 × -0.720667) + (0.3 × -0.369172) + 0.036762
≈ -0.1441334 - 0.1107516 + 0.036762
≈ -0.218123
h2 = sigmoid(z2) ≈ 0.445714
z3 = (0.2 × 0.512252) + (0.3 × 0.292342) - 0.0745917
≈ 0.1024504 + 0.0877026 - 0.0745917
≈ 0.1155613
h3 = sigmoid(z3) ≈ 0.528860
// Endausgabeschicht
output = (h1 × 1.93108) + (h2 × -0.718584) + (h3 × 0.589741) - 0.467899
≈ (0.724688) + (-0.320095) + (0.311644) - 0.467899
≈ 0.248338
Vorhergesagte Summe = 0.248338 × 200 ≈ 49.6676
Es sagte etwa 49.67 voraus. Das ist super nah an der tatsächlichen Summe: 50!
Ziemlich cool, oder?
Also... Was ist ein neuronales Netzwerk überhaupt?
Kurze Einführung: Ein neuronales Netzwerk ist wie eine mathematische Version Ihres Gehirns. Es besteht aus Einheiten, die Neuronen genannt werden und Eingaben verarbeiten, indem sie sie mit Gewichten multiplizieren, einen Bias hinzufügen und dann eine Aktivierungsfunktion anwenden. Eine gängige Aktivierungsfunktion ist das Sigmoid:
sigmoid(x) = 1 / (1 + e^(-x))
Dies komprimiert jede Eingabe in einen Wert zwischen 0 und 1, sodass das Netzwerk komplexe Muster erfassen kann.
Dies ist das Diagramm der Sigmoidfunktion, das deutlich zeigt, wie Eingaben sanft in Werte zwischen 0 und 1 umgewandelt werden:

Es gibt auch andere Aktivierungsfunktionen wie ReLU (Rectified Linear Unit), tanh und softmax, jede mit ihren eigenen Anwendungsfällen und Verhaltensweisen. Aber Sigmoid ist ein großartiger Ausgangspunkt, weil es einfach und glatt ist – perfekt für kleine neuronale Netzwerke wie unseres.
Die Einrichtung: Unserem Netzwerk das Addieren beibringen
Für dieses Experiment haben wir ein kleines Netzwerk mit folgendem erstellt:
- 2 Eingabeknoten (für die beiden Zahlen, die addiert werden sollen)
- 3 versteckte Knoten (die die schwere Arbeit mit Sigmoid-Magie erledigen)
- 1 Ausgabeknoten (der uns die Summe gibt)
Hier ist ein Diagramm, das die Struktur des neuronalen Netzwerks zeigt, das wir verwenden, um zwei Zahlen zu addieren. Sie können sehen, wie jeder Eingabeknoten mit allen Knoten der versteckten Schicht verbunden ist, wie Gewichte und Biases angewendet werden und wie alles zur endgültigen Ausgabe fließt:

Und ja, wir haben alles skaliert. Da unsere Zahlen unter 100 liegen, teilen wir die Eingaben durch 100 und skalieren die Ausgabe am Ende wieder auf 200.
Das ist der Code, den wir schreiben, um zwei Zahlen mit dem neuronalen Netzwerk zu addieren:
#include <iostream>
#include <cmath>
using namespace std;
float sigmoid(float x) {
return 1.0f / (1.0f + exp(-x));
}
float sigmoid_derivative(float y) {
return y * (1 - y); // y = sigmoid(x)
}
int main() {
float weight1_node1 = 0.5f, weight2_node1 = 0.5f, bias_node1 = 0.0f;
float weight1_node2 = -0.5f, weight2_node2 = -0.5f, bias_node2 = 0.0f;
float weight1_node3 = 0.25f, weight2_node3 = -0.25f, bias_node3 = 0.0f;
float weight_out_node1 = 0.5f, weight_out_node2 = 0.5f, weight_out_node3 = 0.5f, bias_out = 0.0f;
float learning_rate = 0.01f;
int epochs = 1000;
for (int epoch = 0; epoch < epochs; ++epoch) {
float total_loss = 0.0f;
for (int a = 0; a < 100; ++a) {
for (int b = 0; b < 100; ++b) {
float x1 = a / 100.0f;
float x2 = b / 100.0f;
float target = (a + b) / 200.0f; // Ziel normalisieren auf den Bereich 0-1
// Vorwärtsdurchlauf für die versteckte Schicht
float z1 = x1 * weight1_node1 + x2 * weight2_node1 + bias_node1;
float h1 = sigmoid(z1);
float z2 = x1 * weight1_node2 + x2 * weight2_node2 + bias_node2;
float h2 = sigmoid(z2);
float z3 = x1 * weight1_node3 + x2 * weight2_node3 + bias_node3;
float h3 = sigmoid(z3);
// Ausgabeschicht mit linearer Aktivierung
float y_pred = h1 * weight_out_node1 + h2 * weight_out_node2 + h3 * weight_out_node3 + bias_out;
float error = target - y_pred;
total_loss += error * error;
// Rückwärtsdurchlauf (Ausgabeschicht - linear)
float delta_out = error;
float grad_out_w1 = delta_out * h1;
float grad_out_w2 = delta_out * h2;
float grad_out_w3 = delta_out * h3;
float grad_out_b = delta_out;
// Rückwärtsdurchlauf (versteckte Schicht)
float delta_h1 = delta_out * weight_out_node1 * sigmoid_derivative(h1);
float delta_h2 = delta_out * weight_out_node2 * sigmoid_derivative(h2);
float delta_h3 = delta_out * weight_out_node3 * sigmoid_derivative(h3);
float grad_w1_node1 = delta_h1 * x1;
float grad_w2_node1 = delta_h1 * x2;
float grad_b_node1 = delta_h1;
float grad_w1_node2 = delta_h2 * x1;
float grad_w2_node2 = delta_h2 * x2;
float grad_b_node2 = delta_h2;
float grad_w1_node3 = delta_h3 * x1;
float grad_w2_node3 = delta_h3 * x2;
float grad_b_node3 = delta_h3;
// Gewichte aktualisieren
weight_out_node1 += learning_rate * grad_out_w1;
weight_out_node2 += learning_rate * grad_out_w2;
weight_out_node3 += learning_rate * grad_out_w3;
bias_out += learning_rate * grad_out_b;
weight1_node1 += learning_rate * grad_w1_node1;
weight2_node1 += learning_rate * grad_w2_node1;
bias_node1 += learning_rate * grad_b_node1;
weight1_node2 += learning_rate * grad_w1_node2;
weight2_node2 += learning_rate * grad_w2_node2;
bias_node2 += learning_rate * grad_b_node2;
weight1_node3 += learning_rate * grad_w1_node3;
weight2_node3 += learning_rate * grad_w2_node3;
bias_node3 += learning_rate * grad_b_node3;
}
}
if ((epoch + 1) % 100 == 0 || epoch == 0)
cout << "[Zusammenfassung] Epoche " << epoch + 1 << ": Verlust = " << total_loss << endl;
}
// Modell testen
float a, b;
cout << "\nTestvorhersage der Summe (a + b)\nGeben Sie a ein: ";
cin >> a;
cout << "Geben Sie b ein: ";
cin >> b;
float x1 = a / 100.0f;
float x2 = b / 100.0f;
float h1 = sigmoid(x1 * weight1_node1 + x2 * weight2_node1 + bias_node1);
float h2 = sigmoid(x1 * weight1_node2 + x2 * weight2_node2 + bias_node2);
float h3 = sigmoid(x1 * weight1_node3 + x2 * weight2_node3 + bias_node3);
float y_pred = h1 * weight_out_node1 + h2 * weight_out_node2 + h3 * weight_out_node3 + bias_out;
float predicted_sum = y_pred * 200.0f;
cout << "w1_node1: " << weight1_node1 << ", w2_node1: " << weight2_node1
<< ", w1_node2: " << weight1_node2 << ", w2_node2: " << weight2_node2
<< ", w1_node3: " << weight1_node3 << ", w2_node3: " << weight2_node3
<< ", out_w1: " << weight_out_node1 << ", out_w2: " << weight_out_node2
<< ", out_w3: " << weight_out_node3 << ", bias_out: " << bias_out
<< ", bias_node1: " << bias_node1 << ", bias_node2: " << bias_node2 << ", bias_node3: " << bias_node3 << endl;
cout << "Vorhergesagte Summe: " << predicted_sum << "\nTatsächliche Summe: " << (a + b) << endl;
return 0;
}
Also... Wie hat es gelernt?
Hier kommt der coole Teil: Wir haben nicht die Regeln für die Addition kodiert. Wir haben dem Netzwerk einfach eine Menge von Zahlenpaaren und deren Summen gegeben, und es hat langsam diese Gewichte und Biases angepasst, bis die Vorhersagen immer besser wurden.
Es ist eine Art Versuch und Irrtum, aber intelligenter – mit einem Hauch von Kalkül.
Hat es wirklich gelernt zu addieren?
Eh, nicht genau wie wir. Es versteht nicht, was "plus" bedeutet. Aber es lernt eine super gute Annäherung. Es sieht Muster in Zahlen und sagt Summen voraus, die oft genau sind.
Es ist mehr wie Lernen durch das Erspüren von Mustern, anstatt Regeln zu kennen. Wenn Sie neugierig sind, können Sie die Eingaben ändern und es testen. Je mehr Sie spielen, desto mehr werden Sie verstehen, wie diese kleinen Gehirne funktionieren.
Zusammenfassung
Einem neuronalen Netzwerk das Addieren von zwei Zahlen beizubringen, mag zunächst albern erscheinen, aber es ist eine großartige Möglichkeit, einen Blick in den Geist der KI zu werfen. Hinter all dem Hype steckt nur Gewicht, Bias und ein wenig kluge Mathematik.
Im nächsten Teil werden wir tiefer eintauchen, wie neuronale Netzwerke funktionieren: Wir werden Schichten, Aktivierungsfunktionen, Ableitungen und was während des Trainings wirklich passiert, erkunden.