Neuronale Netzwerke

Dr.-Ing. Erhard Henkes, Stand: 05.09.2024 


Ein praktischer Einstieg mit Python


Was braucht man eigentlich, um in die Themengebiete "Deep Learning", "Neuronale Netzwerke" und "Artificial Intelligence" praktisch einzusteigen?

Die Antworten sind vielfältig. Wollen wir selbst mit KI experimentieren, so nehmen wir Python als Programmiersprache zusammen mit der Bibliothek Keras, basierend auf Tensorflow, und legen los.


Welche Tools brauchen wir konkret?


Mein Vorschlag ist, Anaconda3 (individuelle Distrbution) und die dazu passende Spyder Version zu installieren. Ich empfehle als Verzeichnis C:\anaconda3 und Verwendung des Anaconda Prompt als Administrator, damit es keine Zugriffsprobleme gibt.

Nach der erfolgreichen Installation von Anaconda3 kann man mittels "Anaconda3 Prompt" im Terminal die wichtige Arbeitsumgebung "Spyder" installieren:

pip install
spyder
 

Zumeist kommt es zu Warnungen und Fehlermeldungen. Ich empfehle aufgrund der Komplexität die Verwendung von KI wie ChatGPT-4o als Partner, der geduldig und konsequent die Ausgaben erklärt und neue Kommandos zur Eingabe in den "Anaconda Prompt" ausspuckt. 

Nun haben wir hoffentlich Python am Start. Mit Spyder haben wir eine Kombination aus Editor, Interpreter und Aus-/Eingabekonsole an der Hand. Nun können wir loslegen. Bitte sicherstellen, dass tensorflow mit keras installiert ist.

KI wird durch Keras nun eben von Python beherrscht. Das sollte man akzeptieren und nicht weiter murren. Auf geht's! ;-)
Wir geben als Test folgende Codezeilen, die Bibiotheken in unser Programm importieren, testweise in den Spyder Editor ein:

import tensorflow as tf
from tensorflow import keras
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Activation, Dense
from tensorflow.keras.optimizers import Adam
from tensorflow.keras.metrics import categorical_crossentropy

Nun einfach F5 drücken oder oben den grünen Pfeil.
Dies steht für "Run", also ab geht die Post!
Wenn ihr in der Konsole nun nicht mit Errors gequält werdet, dann kann es losgehen. Ansonsten KI befragen.

Ihr habt sicher schon gehört, dass NN gerne eure GPU zum Kochen bringen. Die neuen NNUE verzichten darauf und begnügen sich mit eurer CPU.


Bei mir erschien vor einigen Jahren u.a. z.B. die Meldung:
Could not load dynamic library 'cudart64_110.dll etc., allerdings nur als Warnung/Hinweis. Wer unbedingt seine GPU mit CUDA verwenden möchte, kann sich um die korrekte CUDA-Installation kümmern.
Hierbei muss die CUDA-Version zur Tensorflow Version passen. Wie erhält man seine Tensorflow Version?
Anaconda Prompt ist hier die richtige Adresse. Wir geben ein:
pip show tensorflow und erhalten als Antwort viel mehr, als wir eigentlich wissen wollen:

Name: tensorflow
Version: 2.17.0
Summary: TensorFlow is an open source machine learning framework for everyone.
Home-page: https://www.tensorflow.org/
Author: Google Inc.
Author-email: packages@tensorflow.org
License: Apache 2.0
Location: C:\Users\Erhard Henkes\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.12_qbz5n2kfra8p0\LocalCache\local-packages\Python312\site-packages
Requires: tensorflow-intel
Required-by:

spy
Dieses Tensorflow ist unser Backend, wobei Keras auch andere Backends unterstützt. Hier erfährt man Details: https://de.wikipedia.org/wiki/TensorFlow
Wir setzen es einfach ein und freuen uns über unsere bequeme High-Level-Ebene Keras.
Damit haben wir die Grundlagen gelegt für unsere ersten NN Experimente.

Wir fangen an! Zunächst brauchen wir Daten. Ein beliebtes Beispiel zum Testen ist folgende Datenbank mit handgeschriebenen Zahlen: http://yann.lecun.com/exdb/mnist/
MNIST ist dabei ein Teil der Datenbank NIST. Hier erfahrt ihr mehr: https://www.nist.gov/srd/nist-special-database-19
NIST steht übrigens für "National Institute of Standards and Technology".

OK, unsere Daten haben wir also schon mal griffbereit.

Bitte nun folgenden Code eingeben in den Editor von Spyder:

import tensorflow as tf
# http://yann.lecun.com/exdb/mnist/  <-- Handgeschriebene Zahlen
mnist = tf.keras.datasets.mnist 
# Aufteilen in Sets für Training und Test
(x_train, y_train),(x_test, y_test) = mnist.load_data()
x_train, x_test = x_train / 255.0, x_test / 255.0 
# NN zur Bilderkennung einrichten
model = tf.keras.models.Sequential([
  tf.keras.layers.Flatten(input_shape=(28, 28)),
  tf.keras.layers.Dense(512, activation=tf.nn.relu),
  tf.keras.layers.Dropout(0.2),
  tf.keras.layers.Dense(10, activation=tf.nn.softmax)
])
# NN Aufbau zeigen
model.summary()
# Compilieren
model.compile(optimizer='adam', loss='sparse_categorical_crossentropy', metrics=['accuracy'])
# Training
print("\nTraining")
model.fit(x_train, y_train, epochs=2)
# Test
print("\nTest")
model.evaluate(x_test, y_test)

Bei mir erhalte ich (beim ersten Durchgang) folgende Ausgabe in der Konsole:

Model: "sequential"
┌─────────────────────────────────┬────────────────────────┬───────────────┐
│ Layer (type)                    │ Output Shape           │ Param #       │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ flatten (Flatten)               │ (None, 784)            │ 0             │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ dense (Dense)                   │ (None, 512)            │ 401,920       │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ dropout (Dropout)               │ (None, 512)            │ 0             │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ dense_1 (Dense)                 │ (None, 10)             │ 5,130         │
└─────────────────────────────────┴────────────────────────┴───────────────┘
Total params: 407,050 (1.55 MB)
Trainable params: 407,050 (1.55 MB)
Non-trainable params: 0 (0.00 B)

Training
Epoch 1/2
C:\Users\Erhard Henkes\AppData\Roaming\Python\Python312\site-packages\keras\src\layers\reshaping\flatten.py:37: UserWarning: Do not pass an `input_shape`/`input_dim` argument to a layer. When using Sequential models, prefer using an `Input(shape)` object as the first layer in the model instead.
super().__init__(**kwargs)
1875/1875 ━━━━━━━━━━━━━━━━━━━━ 5s 2ms/step - accuracy: 0.8892 - loss: 0.3730
Epoch 2/2
1875/1875 ━━━━━━━━━━━━━━━━━━━━ 4s 2ms/step - accuracy: 0.9686 - loss: 0.1033

Test
313/313 ━━━━━━━━━━━━━━━━━━━━ 1s 1ms/step - accuracy: 0.9656 - loss: 0.1052


Das war es schon! Das erste "Neural Network" (NN) hat gearbeitet.

ChatGPT-4o meint hierzu:

Das Modell ist ein einfaches neuronales Netzwerk, das für die Bilderkennung auf dem MNIST-Datensatz (handgeschriebene Ziffern) verwendet wird. Es besteht aus den folgenden Schichten:

  1. Flatten (Flatten-Schicht)

  2. Dense (Vollverbundene Schicht)

  3. Dropout (Dropout-Schicht)

  4. Dense (Vollverbundene Schicht)

Zusammenfassung der Parameter

Trainings- und Testergebnisse



Die Handschrifterkennung soll besser werden? 96,5 % ist nicht genug?
Na dann lassen wir unser NN härter trainieren. Wir erhöhen von
epochs=2 auf epochs=20.

Was passiert nun? Es dauert länger, und es hilft. Die Genauigkeit der Erkennung steigt in den letzten Trainings-Durchläufen auf über 99,5%.

Training
Epoch 1/20
1875/1875 ━━━━━━━━━━━━━━━━━━━━ 5s 2ms/step - accuracy: 0.8953 - loss: 0.3605
Epoch 2/20
1875/1875 ━━━━━━━━━━━━━━━━━━━━ 4s 2ms/step - accuracy: 0.9705 - loss: 0.0979
Epoch 3/20
1875/1875 ━━━━━━━━━━━━━━━━━━━━ 4s 2ms/step - accuracy: 0.9787 - loss: 0.0686
Epoch 4/20
1875/1875 ━━━━━━━━━━━━━━━━━━━━ 4s 2ms/step - accuracy: 0.9839 - loss: 0.0509
Epoch 5/20
1875/1875 ━━━━━━━━━━━━━━━━━━━━ 4s 2ms/step - accuracy: 0.9866 - loss: 0.0416
Epoch 6/20
1875/1875 ━━━━━━━━━━━━━━━━━━━━ 4s 2ms/step - accuracy: 0.9905 - loss: 0.0294
Epoch 7/20
1875/1875 ━━━━━━━━━━━━━━━━━━━━ 4s 2ms/step - accuracy: 0.9912 - loss: 0.0262
Epoch 8/20
1875/1875 ━━━━━━━━━━━━━━━━━━━━ 4s 2ms/step - accuracy: 0.9906 - loss: 0.0278
Epoch 9/20
1875/1875 ━━━━━━━━━━━━━━━━━━━━ 4s 2ms/step - accuracy: 0.9921 - loss: 0.0220
Epoch 10/20
1875/1875 ━━━━━━━━━━━━━━━━━━━━ 4s 2ms/step - accuracy: 0.9930 - loss: 0.0214
Epoch 11/20
1875/1875 ━━━━━━━━━━━━━━━━━━━━ 4s 2ms/step - accuracy: 0.9932 - loss: 0.0199
Epoch 12/20
1875/1875 ━━━━━━━━━━━━━━━━━━━━ 4s 2ms/step - accuracy: 0.9944 - loss: 0.0178
Epoch 13/20
1875/1875 ━━━━━━━━━━━━━━━━━━━━ 4s 2ms/step - accuracy: 0.9948 - loss: 0.0164
Epoch 14/20
1875/1875 ━━━━━━━━━━━━━━━━━━━━ 4s 2ms/step - accuracy: 0.9936 - loss: 0.0177
Epoch 15/20
1875/1875 ━━━━━━━━━━━━━━━━━━━━ 4s 2ms/step - accuracy: 0.9953 - loss: 0.0141
Epoch 16/20
1875/1875 ━━━━━━━━━━━━━━━━━━━━ 4s 2ms/step - accuracy: 0.9954 - loss: 0.0147
Epoch 17/20
1875/1875 ━━━━━━━━━━━━━━━━━━━━ 4s 2ms/step - accuracy: 0.9960 - loss: 0.0124
Epoch 18/20
1875/1875 ━━━━━━━━━━━━━━━━━━━━ 4s 2ms/step - accuracy: 0.9964 - loss: 0.0117
Epoch 19/20
1875/1875 ━━━━━━━━━━━━━━━━━━━━ 4s 2ms/step - accuracy: 0.9955 - loss: 0.0142
Epoch 20/20
1875/1875 ━━━━━━━━━━━━━━━━━━━━ 4s 2ms/step - accuracy: 0.9956 - loss: 0.0140

Test
313/313 ━━━━━━━━━━━━━━━━━━━━ 1s 1ms/step - accuracy: 0.9797 - loss: 0.1274


Mit diesem einfachen, nur wenige Zeile umfassenden Programm zeigen wir mittels "Keras" die wesentlichen Schritte:
1) Daten bereitstellen
2) Neuronales Netzwerk einrichten

3) Trainieren
4) Testen (=Anwenden)

Hoffentlich klappt es bei euch. Es finden sich nun viele neue Begriffe im Programmcode. Für die wissbegierigen und experimentierfreudigen Coder sei folgendes Kapitel eines wirklich guten Tutorials empfohlen: https://www.tutorialspoint.com/keras/keras_layers.htm

Zunächst sollte man wissen, dass in Keras der Begriff "model" für unser NN verwendet wird. Unser NN besteht aus mehreren Schichten ("Layers"). Innerhalb dieser Schichten finden sich Neuronen, sprich unsere digitalen "Nervenzellen", die in definierter Weise miteinander verflochten werden und somit ein neuronales Netzwerk (engl.: neural network) ergeben.

Beim Input Layer treten unsere Eingangsdaten hinein und beim Output Layer kommen die Ergebnisse heraus.
Dazwischen werden Signale über die Neuronen unserer Hidden Layers gesendet.

Das Training sorgt dafür, dass die Ergebnisse immer besser mit der Erwartung übereinstimmen, z.B. bei Rechenaufgaben, bzw. dass bei bestimmten Eingangsdaten ein idealer Output als Handlungsanweisung erfolgt, z.B. die beste Aktion in einer bestimmten Situation. Ich denke hier als engagierter Schachspieler an Schach Engines, die inzwischen erfolgreich neuronale Netzwerke verwenden, um von einer Position (Input) zu einem Zug (Output) zu gelangen.

Unser "model" (das ist unser NN) ist "Sequential". Das bedeutet, wir verwenden eine lineare Komposition von Keras Layers. Diese NN sind einfach und daher für den Einstieg empfehlenswert.

Wir können den Code (ohne Kommentare) übrigens auch wie folgt schreiben:

import tensorflow as tf
from tensorflow import keras
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Flatten, Activation, Dense, Dropout

mnist = tf.keras.datasets.mnist
 
(x_train, y_train),(x_test, y_test) = mnist.load_data()
x_train, x_test = x_train / 255.0, x_test / 255.0
 
model = Sequential()
model.add(Flatten(input_shape=(28, 28)))
model.add(Dense(512, activation=tf.nn.relu))
model.add(Dropout(0.2))
model.add(Dense(10, activation=tf.nn.softmax))

model.summary()

model.compile(optimizer='adam', loss='sparse_categorical_crossentropy', metrics=['accuracy'])

print("\nTraining")
model.fit(x_train, y_train, epochs=20)

print("\nTest")
model.evaluate(x_test, y_test)

Vielleicht gefällt dieser Stil besser. Das Ergebnis ist gleich. Man muss hier mehr "importieren", damit man es im Code ohne vorgestellte Module verwenden kann. Die add-Methode (eine Methode ist eine public Funktion einer Klasse) zeigt deutlicher den Aufbauprozess des Netzwerks, bei dem wir Schicht hinter Schicht anfügen.

Was bedeuten die Layers (Schichten) mit den Bezeichnungen
Flatten - Dense - Dropout - Dense ?

"Flatten" wird im Eingangsbereich verwendet, um mehrdimensionale in eindimensionale Arrays zu wandeln.
Immer einer nach dem anderen, nicht drängeln oder gar zwei auf einmal, wie eine Drehtür für nur eine Person. Aus dem Format 28, 28 wird dann eben 56.

"Dense" bedeutet, dass
alle Neuronen miteinander vebunden sind. Es gibt hier zahlreiche Parameter (siehe Link).

"Dropout" schaltet beim Training eine gewisse Anzahl Neuronen (bei uns rate = 0.2 = 20%) aus, um die Gefahr der Überanpassung (overfitting) zu vermeiden. Damit bekämpft man das Rauschen, sprich schlechte Daten. Dies sind die Parameter:
Dropout(rate, noise_shape = None, seed = None)

Man sollte sich als Einsteiger in NN theoretisch und praktisch behutsam einarbeiten und begleitend die vorhandenen Erklärungen in Tutorials lesen. Hinter der sanften Oberfläche von Keras stecken unzählige mathematische Monster.

model.summary() zeigt die Architektur unseres NN im Überblick.

Nun zum Compiler: Welchen "Loss" soll man wählen? Bekannt ist z.B. "
mean_squared_error", die bekannte "mittlere quadratische Abweichung". Für unseren Fall ist der gewählte Loss allerdings deutlich zielführender. Ausprobieren!
Genau so wichtig ist die Wahl des Optimizers. "Adam" steht hier für "Adaptive Moment Estimation
" und gehört zur ersten Wahl.
Bei den Metrics ist accuracy ein guter Griff.

Bei all diesen Themen kann man sich endlos in mathematischen Details verlieren. Wichtig für den Einstieg ist zunächst, dass es "klappt", "läuft" und Ergebnisse liefert. Hoffentlich auch auf euren Maschinen.
;-)


 


 

Ein weiteres Beispiel verwendet ein Convolutional Neural Network (CNN). CNNs sind für Bilderkennung optimiert und bieten oft bessere Ergebnisse als einfache neuronale Netze.

import tensorflow as tf

# MNIST-Datensatz laden
mnist = tf.keras.datasets.mnist
(x_train, y_train), (x_test, y_test) = mnist.load_data()

# Daten normalisieren
x_train = x_train.reshape(-1, 28, 28, 1).astype('float32') / 255
x_test = x_test.reshape(-1, 28, 28, 1).astype('float32') / 255

# CNN-Modell definieren
model = tf.keras.models.Sequential([
tf.keras.layers.Conv2D(32, (3, 3), activation='relu', input_shape=(28, 28, 1)),
tf.keras.layers.MaxPooling2D((2, 2)),
tf.keras.layers.Conv2D(64, (3, 3), activation='relu'),
tf.keras.layers.MaxPooling2D((2, 2)),
tf.keras.layers.Conv2D(64, (3, 3), activation='relu'),
tf.keras.layers.Flatten(),
tf.keras.layers.Dense(64, activation='relu'),
tf.keras.layers.Dense(10, activation='softmax')
])

# Modell zusammenfassen
model.summary()

# Kompilieren
model.compile(optimizer='adam', loss='sparse_categorical_crossentropy', metrics=['accuracy'])

# Training
print("\nTraining")
model.fit(x_train, y_train, epochs=5)

# Test
print("\nTest")
model.evaluate(x_test, y_test)



  • Conv2D-Schichten:
    Diese Schichten erkennen Muster und Merkmale im Bild, indem sie Faltungsoperationen durchführen. Jede Conv2D-Schicht wendet Filter an, die verschiedene Aspekte des Bildes (wie Kanten oder Texturen) erkennen.
  • MaxPooling2D-Schichten:
    Diese Schichten reduzieren die Größe der Daten und helfen, die Berechnungen effizienter zu machen, indem sie wichtige Informationen aus den Faltungsoperationen extrahieren.
  • Dense-Schichten:
    Diese Schichten, die nach den Convolution-Schichten kommen, verarbeiten die extrahierten Merkmale und führen die finale Klassifikation durch.

  • Model: "sequential"
    ┌─────────────────────────────────┬────────────────────────┬───────────────┐
    │ Layer (type)                    │ Output Shape           │ Param #       │
    ├─────────────────────────────────┼────────────────────────┼───────────────┤
    │ conv2d (Conv2D)                 │ (None, 26, 26, 32)     │ 320           │
    ├─────────────────────────────────┼────────────────────────┼───────────────┤
    │ max_pooling2d (MaxPooling2D)    │ (None, 13, 13, 32)     │ 0             │
    ├─────────────────────────────────┼────────────────────────┼───────────────┤
    │ conv2d_1 (Conv2D)               │ (None, 11, 11, 64)     │ 18,496        │
    ├─────────────────────────────────┼────────────────────────┼───────────────┤
    │ max_pooling2d_1 (MaxPooling2D)  │ (None, 5, 5, 64)       │ 0             │
    ├─────────────────────────────────┼────────────────────────┼───────────────┤
    │ conv2d_2 (Conv2D)               │ (None, 3, 3, 64)       │ 36,928        │
    ├─────────────────────────────────┼────────────────────────┼───────────────┤
    │ flatten (Flatten)               │ (None, 576)            │ 0             │
    ├─────────────────────────────────┼────────────────────────┼───────────────┤
    │ dense (Dense)                   │ (None, 64)             │ 36,928        │
    ├─────────────────────────────────┼────────────────────────┼───────────────┤
    │ dense_1 (Dense)                 │ (None, 10)             │ 650           │
    └─────────────────────────────────┴────────────────────────┴───────────────┘
    Total params: 93,322 (364.54 KB)
    Trainable params: 93,322 (364.54 KB)
    Non-trainable params: 0 (0.00 B)

    Training
    Epoch 1/5
    C:\Users\Erhard Henkes\AppData\Roaming\Python\Python312\site-packages\keras\src\layers\convolutional\base_conv.py:107: UserWarning: Do not pass an `input_shape`/`input_dim` argument to a layer. When using Sequential models, prefer using an `Input(shape)` object as the first layer in the model instead.
    super().__init__(activity_regularizer=activity_regularizer, **kwargs)
    1875/1875 ━━━━━━━━━━━━━━━━━━━━ 38s 19ms/step - accuracy: 0.9013 - loss: 0.3226
    Epoch 2/5
    1875/1875 ━━━━━━━━━━━━━━━━━━━━ 35s 19ms/step - accuracy: 0.9844 - loss: 0.0481
    Epoch 3/5
    1875/1875 ━━━━━━━━━━━━━━━━━━━━ 36s 19ms/step - accuracy: 0.9901 - loss: 0.0315
    Epoch 4/5
    1875/1875 ━━━━━━━━━━━━━━━━━━━━ 35s 19ms/step - accuracy: 0.9919 - loss: 0.0269
    Epoch 5/5
    1875/1875 ━━━━━━━━━━━━━━━━━━━━ 35s 19ms/step - accuracy: 0.9937 - loss: 0.0202

    Test
    313/313 ━━━━━━━━━━━━━━━━━━━━ 2s 5ms/step - accuracy: 0.9870 - loss: 0.0420



    Wie man sieht, ist das Resultat etwas genauer.

    Mit diesem Einstieg, der hoffentlich erfolgreich war, ist das praktische Arbeiten mit Python, Tensorflow, Keras im Bereich NN gelungen.