Bernd Thomas
Beck et al.
Published in
7 min readDec 3, 2019

--

6.2 NN-Modelle lernen Wurzeln und Quadrate

Die Quadratwurzel einer Zahl ist eine stetige Funktion für Werte x > 0. Kann ein NN lernen, welche Zahlen Quadratzahlen sind? Die Frage lässt sich durch eine NN-Approximation der Wurzelfunktion (sqrt) ausdrücken: Kann ein NN trainiert werden, dass es die Wurzelfunktion soweit annähert, dass man damit aus Quadratzahlen die zugehörige ganze Zahl bestimmen kann. Mehr noch, könnte man damit Zahlen danach klassifizieren, ob sie Quadratzahlen (von ganzen Zahlen) sind - wie z.B. die 4 von 16? Das wäre eine "praktische Anwendung" für die Approximation, wie im vorigen Abschnitt erwähnt.

Wir verwenden für die Approximation die gleichen Code-Teile wie in 6, oben, setzen aber statt cubic() die neue Funktion sqroot() für die Datengenerierung ein. (Prinzipiell sollte man natürlich aus X,y eine Zufallsauswahl treffen. Das ist für das Beispiel hier aber nicht relevant.)

# Generate Data
def cubic(x):
return x**3 + x**2 - x - 1
def sqroot(x):
return np.sqrt(x)

func = sqroot
X,y = get_func_data(func,0,100,0.1)
Abb. 1: Plot der Wurzelfunktion

Wir verwenden die gleiche Modelldefinition wie für die kubische Funtion in 6, experimentieren aber ein wenig mit der Anzahl Neuronen und ggf. einer weiteren Hidden Schicht. Als Aktivierungsfunktion erweist sich sigmoid am besten.

# Sequential model with keras for learning the square rootmf = Sequential()mf.add(Dense(7, activation='sigmoid',input_shape=(1,)))
#mf.add(Dense(4,activation='relu')) # Testing an additional layer
mf.add(Dense(1, activation='linear'))

Das Training erfolgt ebenso wie in 6, wir beschränken uns aber auf 100 Epochen.

Abb. 2: Hidden Layer mit 7 Neuronen — Konvergenz (loss)

Wir können nun mit der predict Methode prüfen, ob das NN für die - jedem Kind bekannten - ganzzahligen Quadratzahlen zwischen 0 und 100 die richtig Wurzel (ganzzahlig) kennt:

args = np.array([s*s for s in range(1,11)])
int_pred = mf.predict(args)
for i in range(len(int_pred)):
print(args[i],np.round(int_pred[i]))
0 [2.]
1 [2.]
4 [2.]
9 [3.]
16 [4.]
25 [5.]
36 [7.]
49 [8.]
64 [9.]
81 [9.]
100 [9.]
121 [10.]

Die Ausgabe zeigt, welchen Wert das NN-Modell für die bekannten ersten Quadratzahlen “vorhersagt”. Das Ergebnis ist für einige Zahlen im mittleren Bereich korrekt, wird aber “zu den Rändern hin” schlechter.

Das lässt sich unmittelbar verstehen, wenn man sich nun die Plots von Funktion (grün) und NN-Approximation (rot) ansieht: Eine deutliche Annäherung als Ergebnis des Trainings aber für “praktische Zwecke” sicher genug.

Abb. 3: Die trainierte NN-Näherung (rot)

Umgekehrt lässt sich aber mit diesem Modell keine Klassifikation der Zahlen in Quadratzahlen und Nicht-Quadratzahlen trainieren. Die Nachbarzahlen einer Quadratzahl liefern den gleichen gerundeten Wurzelwert wie die eigentliche Quadratzahl. Man kann hier nicht einmal davon ausgehen, dass die “Vorhersage” des Modells für eine Quadratzahl ihrer Wurzel näher kommt als die für Nachbarzahlen. Die Beispiel-Ausgabe zeigt dies für die Bereiche um 16 bzw. um 49:

x  y_pred     y_round        x  y_pred     y_round
14 [3.7081015] [4.] 46 [7.8075314] [8.]
15 [3.8487484] [4.] 47 [7.8913507] [8.]
16 [3.991071] [4.] 48 [7.971839] [8.]
17 [4.1348233] [4.] 49 [8.049056] [8.]
18 [4.2797465] [4.] 50 [8.123067] [8.]
19 [4.4255743] [4.] 51 [8.193947] [8.]

Nimmt man die Vorhersagen des trainierten Modells mit in den Plot auf, zeigt sich eine Stufenfunktion (blau), d.h. der “Wurzelwert” ist für mehrere x-Werte gleich.

Abb. 4: Prediction (rot) mit Rundung auf ganze Zahlen (blau)

Approximationsgüte und Versuche mit größerer Neuronenzahl

Generell zeigt sich, dass die Approximationsgüte sich nicht weiter verbessert, wenn man die Anzahl der Epochen erhöht. Sie ist damit nicht mit der Konvergenz des Trainings (loss-Werte) verbunden.

Die Vergrößerung des Hidden Layers hat dagegen sehr wohl einen Verbesserungseffekt. Wir zeigen hier die Plots für Modellvarianten mit 32, 64 und 100 Neuronen. Anders als im kubischen Beispiel in 6, oben, haben wir hier keine analytisch verifizierbaren Gewichte. Der approximierende Zustand des Modells wird hier nur durch Training erreicht. Wir zeigen die Ergebnisse nach diesem Schema für die 32-, 64-, 100-Neuronenmodelle.

Abb. 5: Entwicklung der Approximationsgüte mit Anzahl Neuronen im Hidden Layer: n=32 (links), n=64 (mitte), n=100 (rechts)

Dabei wird die Approximation mit größerer Neuronenzahl so gut, dass man die Erkennung von Quadratzahlen unter den Trainingsdaten versuchen kann: Man bestimmt zu jedem gerundeten Ausgabewert (q = rnd_pred) den Index derjenigen Eingabe, die den geringsten Abstand zwischen nicht-gerundetem Ausgabewert (pred) und q ergibt. Die Hoffnung ist, dass ein gut approximierendes, trainiertes NN den geringsten Abstand genau zwischen Wurzel und zugehöriger Quadratzahl aufweist (und nicht einer benachbarten Eingabezahl). Beispiel: Die Eingabewerte 14.0 - 18.0 liefern als gerundete Vorhersage q = 4.0. Die errechneten Ausgabewerte des Modells für 14.0 - 18.0 haben ihren minimalen Abstand aber genau für den Eingabewert 16.0. Damit ist die 16.0 als Quadratzahl von 4.0 erkannt und als solche klassifizierbar.

x  y_pred     rnd_pred
======================
n = 32
0 [0.8455908] [1.] 0
1 [1.1324232] [1.] 1
4 [1.9546225] [2.] 2
9 [3.0129554] [3.] 3
16 [3.9794862] [4.] 4
26 [5.0427504] [5.] 5
36 [5.9919] [6.] 6
48 [7.004981] [7.] 7
62 [7.997052] [8.] 8
81 [9.0220175] [9.] 9
100 [9.732203] [10.] 10
4 Fehler
n = 64
0 [0.7862625] [1.] 0
1 [1.0792389] [1.] 1
4 [1.9185824] [2.] 2
9 [3.0393558] [3.] 3
15 [3.9558372] [4.] 4
25 [5.0353847] [5.] 5
36 [6.032816] [6.] 6
48 [7.012378] [7.] 7
62 [8.014762] [8.] 8
79 [9.019726] [9.] 9
100 [9.957708] [10.] 10
5 Fehler
n = 100
0 [0.8125498] [1.] 0
1 [1.0890824] [1.] 1
4 [1.8860375] [2.] 2
9 [2.9820263] [3.] 3
16 [4.037427] [4.] 4
25 [4.992529] [5.] 5
37 [6.038187] [6.] 6
49 [6.969966] [7.] 7
64 [8.0021] [8.] 8
81 [8.99117] [9.] 9
100 [9.875802] [10.] 10
2 Fehler

Als “Fehler” sind hier die nach dem beschriebenen Verfahren fehl-klassifizierten Quadratzahlen gezählt. Der Null wird schon per Prediction (y_pred) falsch bestimmt (was auf die Unbeschränktheit der Ableitung der Wurzelfunktion bei 0 zurückzuführen ist). Obwohl die Trainingsdaten bei allen Trainings gleich sind, variiert das Trainingsergebnis, da wir keine (festen) Vorgaben für die initialen Werte der Gewichte gemacht haben (default initialization).

Die Loss-Funktion konvergiert— bei sonst gleichen Trainingsparametern — mit größerer Neuronenzahl im Hidden Layer auf geringere Konvergenz-Level. Die Abbildung zeigt die Werte für die Modelle mit 7, 32, 64 und 100 Neuronen (halb-logarithmische Skala).

Abb. 6: Loss-Konvergenz bei n=7, 32, 64, 100 (logarithmisch)

Wie schon in Teil 3 einmal diskutiert, ist es nicht verwunderlich, wenn durch massive Erhöhung der Parameteranzahl (hier: Gewichte) die Approximation den vorgegebenen Datensatz immer besser abbildet. Im Extremfall wird das trainierte NN schlicht zu einem (assoziativen) Speicher für die Datenpunkte.

Quadrate Lernen

Für das “Lernen” der Wurzelfunktion haben wir das gleiche NN-Modell verwendet, wie für das Besipiel des kubischen Polynoms. Das Trainingsziel wird, wie in Teil 2 gefordert, nur durch die Vorgabe der Trainingsdaten erreicht. Allerdings lässt sich starkes Lernen nur schwerlich realisieren, da wir hier wegen des Interpolationscharakters des Modells nur numerische Werte annähern und die auch nur im vorgegebenen Bereich. Die Güte der Approximation hängt dabei im Wesentlichen von der Datenmenge und der Anzahl Neuronen in der Hidden-Schicht und kann ab einem bestimmten Level nicht durch weitere Iterationen (Epochen) verbessert werden.

Als weiteres, einfaches Beispiel soll die Umkehrung der Wurzelfunktion trainiert werden, die Quadratfunktion. Dabei verwenden wir das gleiche, universelle Modell wie zuvor und variieren auch wieder die Neuronenzahl im Hidden Layer.

Dazu sind nur die Trainingsdaten neu zu generieren, hier im Intervall von -10 bis 10.

# Train for square(x) - Change in provisioning of training data 

def square(x):
return x*x

func = square

X,y = get_func_data(func,-10,10,0.1)
Abb. 7: Die Funktion y = x**2 von -10 bis 10

Die Modell-Defintion ist die gleiche wie bei der Wurzelfunktion, ebenso wie der Trainingsprozess mit gleicher Anzahl Trainingsepochen und gleicher Batch-Größe. Trainiert werden die Modelle mit 7, 32, und 64 Knoten in der ersten Schicht. Die “Evaluation” mit denselben Daten wie für das Training zeigt die zunehmend bessere Anpassung an den Funktionsgraphen mit wachsender Neuronenzahl.

Abb. 8: Training der Quadratfunktion mit n = 7 (links), 32 (mitte), 64 (rechts) Neuronen im Hidden Layer

Für n = 7 sieht der Approximationsgraph noch sehr “eigenwillig” aus, mit größerem n passt er sich aber schön an und sieht — zumindest innerhalb des Interpolationsbereichs — brauchbar aus. Darüber hinaus scheint ihn die Vorgabefunktion nicht weiter zu interessieren.

Mit diesen Fähigkeiten Neuronaler Netze sollte es möglich sein, eine etwas schwierigere Aufgabe zu lernen, die Pythagoras Formel.

Weiter lesen: 6.3 Pythagoras Lernen

Zurück auf Anfang

bernhard.thomas@becketal.com
www.becketal.com

--

--

Bernd Thomas
Beck et al.

Dr. Bernhard Thomas — Mathematics, Theor. Biology, Computational Sciences, AI and advanced Technologies for the Enterprise. Beck et al. Consultant