Verhalten der XOR-Verknüpfung erlernen

Anders als beim Ampelbeispiel korrelieren die Traingingsdaten z.B. bei der XOR-Verknüpfung nicht direkt,

XOR-Verknüpfung

Trainingsdaten                        Druck auf die Gewichte

0 0        0               0 0        0
0 1   ->   1               0 +   ->   1
1 0        1               + 0        1
1 1        0               - -        0

denn auf alle Gewichte wirkt die gleiche Anzahl von Druckkräften nach oben und nach unten.

Ein- und Ausgabeschicht alleine reichen nicht

Wird der Code des Ampelbeispiels Training mit Datenmengen entsprechend angepasst ergibt sich folgernder Code:

 1  
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import numpy as np
weights = np.array([0.5,0.48])
alpha = 0.1

inputs = np.array( [ [ 0, 1 ],    # without 0, 0 
                     [ 1, 0 ],
                     [ 1, 1 ], ] )

outputs = np.array( [ 1, 1, 0 ] )

for iteration in range(40):
    error_for_all = 0
    for row_index in range(len(outputs)):
        input = inputs[row_index]
        goal_prediction = outputs[row_index]
        
        prediction = input.dot(weights)
        
        error = (goal_prediction - prediction) ** 2
        error_for_all += error
        
        delta = prediction - goal_prediction
        weights = weights - (alpha * (input * delta))	
        print("Prediction:" + str(prediction))
    print("Error:" + str(error_for_all) + "\n")
print(weights)

Listung1: Erlernen der gesamten Datemenge (ausgenommen [0, 0])

Ausgabe:

Prediction:0.48
Prediction:0.5
Prediction:1.082
Error:1.691124

Prediction:0.4238
Prediction:0.4418
Prediction:0.9790400000000001
Error:1.6021130016000005

...
Prediction:0.2855325799130818
Prediction:0.28589753998570977
Prediction:0.7142871079089124
Error:1.5306120902889961

Prediction:0.28555061113088237
Prediction:0.28587907519624756
Prediction:0.714286717694417
Error:1.530612139572686

[0.2858625  0.28556688]

Das Netzwerk kann so nicht konvergieren.

Hidden Layer

Es kann nun aber versucht werden, mittels Hidden Layer eine temporäre Datenmenge zu erzeugen, die eine begrenzte Korrelation mit der Eingabeschicht aufweist. Die nächste Schicht, im folgenden Beispiel bereits die Ausgabeschicht (3. Schicht), kann dann eventuell mit dieser temporären Datenmenge korrelieren und damit richtige Vorhersagen machen. Es wird versucht eine indirekte Korrelation zu erlernen.

KNN mit 3 Schichten
KNN mit 3 Schichten

Aktivierungsfunktion

Zusätzlich muss eine nichtlineare Aktivierungsfunktion angewandt werden. Im Abschnitt Vom biologischen zum künstlichen Neuron wurde bereits erwähnt, dass die unterschiedlich gewichten und aufsummierten "Erregungen" nur wenn ein bestimmtes Schwellenpotential überschritten wird, das Neuron seinerseits "feuern" lässt. (Bislang haben in den bisherigen Beispielen die künstlichen Neuronen proportional zum Input "gefeuert", sich also linear verhalten. Das funktioniert nicht mit mehreren Schichten. Die Begründung findet sich hierfür findet sich auf der Seite Exkurs: Aktivierungsfunktionen.)

Sigmoid-Funktion

Dieses Verhalten versucht man mathematisch z.B. mit der sogenannten Sigmoid-Funktion nachzubilden:

Sigmoid-Funktion mit Ableitung
Sigmoid-Funktion mit ihrer Steigung (1. Ableitung)

Die Sigmoid-Funktion ist nichtlinear. Das künstliche Neuron beginnt um den Nullpunkt herum aktiv zu werden, dort ist die Steigung der Funkion auch am größten.

Für die Voraussagen müssen mit der relativ komplexen Sigmoid-Funktion s(x) die Aktivierungen berechnet werden (Forward Propagation). (Alle diese Werte werden als Matrix zwischengespeichert):

Sigmoid-Funktion: \( s(x)= {\frac {1}{1+e^{-x}}} \)

Die Ableitung der Sigmoid-Funktion zur Berechnung der Deltas

Für das Training, also die Aktualisierung der Gewichte müssen aber auch die Änderungen der Aktivierungsfunktion berücksichtigt werden. Diese Änderungen entsprechen der Steigung der Funktion. Es wird daher die 1. Ableitung der Sigmoid-Funktion benötigt:

1. Ableitung der Sigmoid-Funktion: \( s'(x)=s(x)(1-s(x)) \)

Der Quellcode sieht z.B. so aus:


 1  
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
import numpy as np
import matplotlib.pyplot as plt

def sigmoid(x):               # sigmoid function
    return 1/(1 + np.exp(-x))
def sigmoidDerived(output):   # derived sigmoid (derive = dt. ableiten)
    return output * (1 - output)

alpha = 0.8

inputs = np.array( [ [ 0, 0 ],
                     [ 0, 1 ],    
                     [ 1, 0 ],
                     [ 1, 1 ], ] )

outputs = np.array( [0, 1, 1, 0 ] ).T

hidden_size = 3 # hidden layer with 3 neurons

weights_0_1 = 2 * np.random.random((2, hidden_size)) - 1   # random -1 <-> +1
weights_1_2 = 2 * np.random.random((hidden_size, 1)) - 1
print("Randomly initialized weights:")
print(weights_0_1)
print(weights_1_2)

iterationList = []
layer_2_errorList = []

for iteration in range(200):
    layer_2_error = 0
    for i in range(len(inputs)):
        layer_0 = inputs[i:i+1]
        z = np.dot(layer_0, weights_0_1)
        layer_1 = sigmoid(np.dot(layer_0, weights_0_1))  
        layer_2 = np.dot(layer_1, weights_1_2)
        layer_2_delta = (layer_2 - outputs[i:i+1])
        layer_1_delta = layer_2_delta.dot(weights_1_2.T * sigmoidDerived(layer_1))

        weights_1_2 -= alpha * layer_1.T.dot(layer_2_delta)
        weights_0_1 -= alpha * layer_0.T.dot(layer_1_delta)

        layer_2_error += np.sum(layer_2_delta **2)

    if iteration % 10 == 9:
        print("Error:" + str(layer_2_error))
        iterationList.append(iteration)                   # collect plotting data
        layer_2_errorList.append(layer_2_error)
        plt.plot(iterationList, layer_2_errorList,'r')
        plt.xlabel('iteration')
        plt.ylabel('error')

print("Trained weights:")
print(weights_0_1)
print(weights_1_2)

plt.show()

Listung 2: Erlernen der gesamten Datemenge mit Hidden Layer (3 Neuronen) und Sigmoid-Aktivierungsfunktion

Randomly initialized weights:
[[-0.19884562 0.45738208 0.21236538]
[-0.1227856 0.13775044 0.98627854]]
[[ 0.8831812 ]
[-0.03806979]
[ 0.29838438]]
Error:1.7637972725930073
Error:1.674706921462746
...
Error:0.02910377270016944
Error:0.025159140619482802
Trained weights:
[[-3.06230537 -3.81043791 -0.13769899]
[-0.23922823 -3.99645417 -3.14814029]]
[[ 2.20159747]
[-4.27237435]
[ 2.08803124]]

Konvergenz mit graphischer Ausgabe

In Listing 2 wird der quadratische Fehler in Abhängigkeit von der Iteration graphisch ausgegeben. Man sieht sehr schnell und besser als bei reinen Zahlenreihen, wie gut das KNN konvergiert:

Der Fehler verringert sich, das KNN konvergiert.
Der Fehler verringert sich, das KNN konvergiert.


Nachtrag: Die mathematische Ableitung der Sigmoid-Funktion

\( s(x)= {\frac {1}{1+e^{-x}}}\\ Quotientenregel: f(x) = \frac{g(x)}{h(x)} \quad \rightarrow \quad f'(x)=\frac{h(x) \cdot g'(x) - g(x) \cdot h'(x)}{\left[h(x)\right]^2}\\ g(x)=1\rightarrow g'(x)=0 \\ h(x)={1+e^{-x}} \rightarrow h'(x)=-e^{-x} (Kettenregel: f(x) = g(h(x)) \rightarrow f'(x) = g'(h(x)) \cdot h'(x) =e^{-x} \cdot (-1)) \\ s'(x)= \frac{(1+e^{-x}) \cdot 0 - 1 \cdot (-e^{-x})) }{(1+e^{-x})^2} = \frac{e^{-x}}{(1+e^{-x})^2} \\ \)

Mathematisch reicht das eigentlich, aber um hohen Rechenaufwand bei KNNs zu vermeiden, lässt sich die Formel noch vereinfachen:

\( s'(x)= \frac{e^{-x}}{(1+e^{-x})^2}\\ in \: 2 \: Terme \: aufteilen:\\ s'(x)= \frac{\color{red}-1\color{black}+\color{blue}1+e^{-x}}{(1+e^{-x})^2} \\ s'(x)= \color{blue}\frac{1+e^{-x}}{(1+e^{-x})^2} \color{red}- \frac{1}{(1+e^{-x})^2} \\ kürzen:\\ s'(x)= \frac{1}{(1+e^{-x})} - \frac{1}{(1+e^{-x})^2} \\ ausklammern:\\ s'(x)= \frac {1}{1+e^{-x}}(1-{\frac {1}{1+e^{-x}}}) \\ Wegen \: s(x)= {\frac {1}{1+e^{-x}}}\: gilt:\\ \textbf{s'(x)=s(x)(1-s(x))}\\ \)

Für das Training also die Aktualisierung der Gewichte (Backward Propagation), werden die zwischengespeicherten Werte verwendet und lediglich von 1 subtrahiert und mit dem Wert der jeweiligen Aktivierung selbst multipliziert werden. Es ist also keine weitere aufwendige Berechnung nötig.






Last modified: Monday, 7 November 2022, 2:38 PM