<jupyter_start><jupyter_text>Laden der DatenIm ersten Schritt bereiten wir alles vor und laden unseren Datensatz mit handgeschriebene Ziffern. Unser Ziel ist es, das Bild in usner Programm zu laden und zu klassifizieren. Klassifizieren bedeutet, zu erkennen um welche Ziffer es sich handelt. Ist es eine *0* oder doch eine *9*?Ein kleiner Hinweis  signalisiert ein Kommentar im Code, damit notieren sich Programmiererinnen Hinweise um Codezeilen leichter zu verstehen ;-)<jupyter_code># TensorFlow and tf.keras
import tensorflow as tf
from tensorflow import keras
import numpy as np
import matplotlib.pyplot as plt
import os
print(tf.__version__)

# Wir laden den Datensatz
mnist = keras.datasets.mnist
(train_images, train_labels), (test_images, test_labels) = mnist.load_data()

# Wir normieren das Bild, sodass es Werte von 0 - 1 beinhaltet. Das ist ein besserer Input für das NN.
train_images = np.expand_dims(train_images / 255.0, -1)
test_images = np.expand_dims(test_images / 255.0, -1)<jupyter_output><empty_output><jupyter_text>Visualisieren - Veranschaulichen - bildlich darstellenIm nächsten Schritt laden wir eine *0* und eine *9* aus unserem Trainingsdatensatz und veranschaulichen die zwei Ziffern.<jupyter_code># Lade eine 0 aus den Trainingsdaten
indicies_von_allen_0en = (np.where(test_labels == 0))[0]
bild_mit_ziffer_0 = test_images[indicies_von_allen_0en[0]]

# Lade eine 9 aus den Trainingsdaten
indicies_von_allen_9en = (np.where(test_labels == 9))[0]
bild_mit_ziffer_9 = test_images[indicies_von_allen_9en[0]]

# Visualisieren (= anzeigen) der Bilder, damit wir auch sehen ob wir das ganze richtig geladen haben 
plt.figure()
plt.imshow(bild_mit_ziffer_0[:,:,0], cmap=plt.cm.binary)
plt.title("Das ist eine 0")
plt.show()

plt.figure()
plt.imshow(bild_mit_ziffer_9[:,:,0], cmap=plt.cm.binary)
plt.title("Das ist eine 9")
plt.show()<jupyter_output><empty_output><jupyter_text>Neuronales Netz definierenAls nächstes müssen wir die Architetkur unseres neuronalen Netzes definieren. Wie viele Layer solltes haben, wie viele Neuronen haben diese Layer.Wir entscheiden uns als erstes für foglende Architektur:*   Input Layer: 28x28 (so groß sind unsere Bilder!)*   3 x Convolution Layer mit Pooling Layer *   Fully Connected Network (FCN) Layer (heißt *dense* in TF!) mit 128 Neuronen und einer ReLU Aktivierung*   Output sind 10 Neuronen (wir haben 10 Ziffern die wir klassifizieren wollen)<jupyter_code># Netzwerk Architektur
model = keras.Sequential([
    keras.layers.Conv2D(8, (3, 3), activation='relu', input_shape=(28,28,1)),
    keras.layers.MaxPooling2D((2, 2)),
    keras.layers.Conv2D(16, (3, 3), activation='relu'),
    keras.layers.MaxPooling2D((2, 2)),
    keras.layers.Conv2D(16, (3, 3), activation='relu'),
    keras.layers.Flatten(),
    keras.layers.Dense(128, activation='relu'),
    keras.layers.Dense(10)
])
# Lassen TF unser Netzwerk bauen
model.compile(optimizer='adam',
              loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True),
              metrics=['accuracy'])<jupyter_output><empty_output><jupyter_text>Neuronales Netz trainierenIm nächsten Schritt traineiren wir unser Netz mit den Daten, die wir oben geladen haben. Trainieren wird auch *fitten* genannt, da beim Trainieren die Gewichte der Neuronen angepasst werden, also gefitted werden. Das Wort kommt aus dem Englischen! Wir müssen TF natürlcih auch noch sagen, wie lange das Netzwerk traineirt werden soll. Das drückt man aus, wie oft dem Netzwerk die Trainingsdaten gezeigt werden sollen. * 1 x alle Trainingsdaten zeigen = 1 Epoche* 2 x alle Trainingsdaten zeigen = 2 Epochen<jupyter_code># Trainiere das Netzwerk für 5 Epochen
model.fit(train_images, train_labels, epochs=5)

#Speichere mein modell ab
# 1) zuerst in meinem obersten drive Folder einen Folder / Verezeichnis mit Namen modul_2_cnn anlegen --> 'rechte Maustaste -> neuer Folder -> modul_2_cnn'
# from google.colab import drive
# drive.mount('/content/drive')
# tf.saved_model.save(model, '/content/drive/My Drive/modul_2_cnn/model')<jupyter_output><empty_output><jupyter_text>Überprüfen wie gut das Netzwerk istWir haben das Netzwerk trainiert, jetzt wollen wir auch wissen wie gut es funktioniert. Wir sagen auch, wir *evaluieren* das Netzwerk jetzt. Evaluiert wird mit den Testdaten. Wir fragen, wie viele der Testdaten richtig klassifieziert werden, das heißt wie oft das Netzwerk die Zahl richtig erkennt.<jupyter_code># Testen des Netzwerkes
test_loss, test_acc = model.evaluate(test_images,  test_labels, verbose=0)
print('Unser Ergebnis:')
print('Von den ', test_images.shape[0], ' wurden ', int(test_acc * test_images.shape[0]), ' richtig erkannt. Das sind {:.2f}% der Daten'.format(test_acc * 100.0))<jupyter_output><empty_output><jupyter_text>Können wir darstellen wie es im NN aussieht?Was passiert eigentlcih in diesen *convolutional layers*? Wie sehen solche Filter aus? Das visualisieren wir im nächsten Code Block.<jupyter_code>conv1_layer_weight = model.layers[0].get_weights()[0][:,:,0,:]

import matplotlib.gridspec as gridspec
# Erster convolution layer --> 8 Filter
fig = plt.figure(figsize=(10,10))
gs1 = gridspec.GridSpec(2,4, wspace=0.05, hspace=0.05)
for idx in range(8):
    a = plt.subplot(gs1[idx])
    a.axis('off')
    imgplot = plt.imshow(conv1_layer_weight[:,:,idx], cmap=plt.cm.binary)
conv2_layer_weight = model.layers[2].get_weights()[0][:,:,0,:]

import matplotlib.gridspec as gridspec
# Zweiter convolution layer --> 16 Filter
fig = plt.figure(figsize=(10,10))
gs1 = gridspec.GridSpec(4,4, wspace=0.05, hspace=0.05)
for idx in range(16):
    a = plt.subplot(gs1[idx])
    a.axis('off')
    imgplot = plt.imshow(conv2_layer_weight[:,:,idx], cmap=plt.cm.binary)
conv3_layer_weight = model.layers[4].get_weights()[0][:,:,0,:]

import matplotlib.gridspec as gridspec
# Dritter convolution layer --> 16 Filter
fig = plt.figure(figsize=(10,10))
gs1 = gridspec.GridSpec(4,4, wspace=0.05, hspace=0.05)
for idx in range(16):
    a = plt.subplot(gs1[idx])
    a.axis('off')
    imgplot = plt.imshow(conv3_layer_weight[:,:,idx], cmap=plt.cm.binary)<jupyter_output><empty_output><jupyter_text>Können wir darstellen was mit dem Bild in dabei passiert?In den nachfolgenden code snippets visualisieren wir was mit der 0 so passiert wenn wir sie durch das Netz laufen lassen.<jupyter_code>print(model.summary())

layer_outputs = [layer.output for layer in model.layers[:6]] 
cnn_model = tf.keras.Model(inputs=model.inputs, outputs=layer_outputs)
aktivierung_der_null = cnn_model.predict(np.expand_dims(bild_mit_ziffer_0, axis=0))
print(len(aktivierung_der_null))


import matplotlib.gridspec as gridspec
# Erster convolution layer --> 8 Filter
output_conv_1 = aktivierung_der_null[0]
print('Aktivierungen der ersten Layer mit Groesse ', output_conv_1.shape)
fig = plt.figure(figsize=(10,10))
gs1 = gridspec.GridSpec(2,4, wspace=0.05, hspace=0.05)
for idx in range(8):
    a = plt.subplot(gs1[idx])
    a.axis('off')
    imgplot = plt.imshow(output_conv_1[0,:,:,idx], cmap=plt.cm.binary)

# Zweiter convolution layer --> 16 Filter
output_conv_2 = aktivierung_der_null[2]
print('Aktivierungen der zweiten Layer mit Groesse ', output_conv_2.shape)
fig = plt.figure(figsize=(10,10))
gs1 = gridspec.GridSpec(4,4, wspace=0.05, hspace=0.05)
for idx in range(16):
    a = plt.subplot(gs1[idx])
    a.axis('off')
    imgplot = plt.imshow(output_conv_2[0,:,:,idx], cmap=plt.cm.binary)
# Dritter convolution layer --> 16 Filter
output_conv_3 = aktivierung_der_null[4]
print('Aktivierungen der dritten Layer mit Groesse ', output_conv_3.shape)
fig = plt.figure(figsize=(10,10))
gs1 = gridspec.GridSpec(4,4, wspace=0.05, hspace=0.05)
for idx in range(16):
    a = plt.subplot(gs1[idx])
    a.axis('off')
    imgplot = plt.imshow(output_conv_3[0,:,:,idx], cmap=plt.cm.binary)<jupyter_output><empty_output><jupyter_text>Könnt ihr folgendes herausfinden?*   Trainingszeit (=Epochen) des neuronalen Netzwerkes:  * Was passiert wenn man nur ganz kurz trainiert (z.B: 1 Epoche)? Wie viele der Testdaten werden dann noch richtig erkannt?  * Was passiert wenn man gaaaaanz  lange trainiert (z.B: 1000 Epochen) Wie viele der Testdaten werden dann richtig erkannt? Was könnt ihr dabei beobachten?  * **Tipp**: Findet die Stelle im Code wo trainiert wird und ändere die Anzahl der Epochen entsprechend. *   Was passiert wenn man die Input Zahl leicht nach links verschiebt? Wird sie dann noch immer richtig erkannt? Probiert doch einfach das Beispiel aus und beschriebt was ihr da seht. Findet ihr eine Erklärung dafür?* Was passiert wenn man die Input Zahl leicht verrauscht? Wird sie dann noch immer richtig erkannt? Probiert doch einfach das Beispiel aus und beschriebt was ihr da seht. Findet ihr eine Erklärung dafür? Woher könnte Rauschen zum Beispiel kommen, findet ihr Beispiele dafür?<jupyter_code># Beispiel nach links verschobene Zahl

verschobene_neun = np.zeros_like(bild_mit_ziffer_9) # wir erstellen ein leeres Bild mit der gleichen Größe wie unsere 9
verschobene_neun[:, :15] = bild_mit_ziffer_9[:, 5:20]

plt.figure()
plt.imshow(bild_mit_ziffer_9[:,:,0], cmap=plt.cm.binary)
plt.title("Das ist eine richtige 9")
plt.show()

plt.figure()
plt.imshow(verschobene_neun[:,:,0], cmap=plt.cm.binary)
plt.title("Das ist eine verschobene 9")
plt.show()

from scipy.special import softmax
logits_des_nn_fuer_neun = model.predict(np.expand_dims(bild_mit_ziffer_9, 0))
wahrscheinlichkeiten_des_nn_fuer_neun = softmax(logits_des_nn_fuer_neun)[0]
erkannte_klasse_des_nn_fuer_neun = np.argmax(wahrscheinlichkeiten_des_nn_fuer_neun)
print('Das NN erkennt die Neun als ', erkannte_klasse_des_nn_fuer_neun, ' mit einer Wahrscheinlikeit von ', wahrscheinlichkeiten_des_nn_fuer_neun[erkannte_klasse_des_nn_fuer_neun])

logits_des_nn_fuer_verschobene_neun = model.predict(np.expand_dims(verschobene_neun, 0))
wahrscheinlichkeiten_des_nn_fuer_verschobene_neun = softmax(logits_des_nn_fuer_verschobene_neun)[0]
erkannte_klasse_des_nn_fuer_verschobene_neun = np.argmax(wahrscheinlichkeiten_des_nn_fuer_verschobene_neun)
print('Das NN erkennt die verschobene Neun als ', erkannte_klasse_des_nn_fuer_verschobene_neun, ' mit einer Wahrscheinlikeit von ', wahrscheinlichkeiten_des_nn_fuer_verschobene_neun[erkannte_klasse_des_nn_fuer_verschobene_neun])

# Beispiel einer verrauschten Zahl

verrauschten_neun = np.copy(bild_mit_ziffer_9) # wir kopieren das Bild mit der Ziffer 9
rauschen = np.zeros_like(bild_mit_ziffer_9) # wir erstellen ein leeres bild
bild_koordinaten = [np.random.randint(0, i - 1, 50) for i in rauschen[:,:,0].shape]
rauschen[bild_koordinaten] = 1
verrauschten_neun += rauschen
bild_koordinaten = [np.random.randint(0, i - 1, 50) for i in rauschen[:,:,0].shape]
rauschen[bild_koordinaten] = -1
verrauschten_neun += rauschen
verrauschten_neun = np.clip(verrauschten_neun,0,1)


plt.figure()
plt.imshow(bild_mit_ziffer_9[:,:,0], cmap=plt.cm.binary, vmin=0, vmax=1)
plt.title("Das ist eine richtige 9")
plt.show()

plt.figure()
plt.imshow(verrauschten_neun[:,:,0], cmap=plt.cm.binary, vmin=0, vmax=1)
plt.title("Das ist eine verrauschten 9")
plt.show()

from scipy.special import softmax
logits_des_nn_fuer_neun = model.predict(np.expand_dims(bild_mit_ziffer_9, 0))
wahrscheinlichkeiten_des_nn_fuer_neun = softmax(logits_des_nn_fuer_neun)[0]
erkannte_klasse_des_nn_fuer_neun = np.argmax(wahrscheinlichkeiten_des_nn_fuer_neun)
print('Das NN erkennt die Neun als ', erkannte_klasse_des_nn_fuer_neun, ' mit einer Wahrscheinlikeit von ', wahrscheinlichkeiten_des_nn_fuer_neun[erkannte_klasse_des_nn_fuer_neun])

logits_des_nn_fuer_verrauschten_neun = model.predict(np.expand_dims(verrauschten_neun, 0))
wahrscheinlichkeiten_des_nn_fuer_verrauschten_neun = softmax(logits_des_nn_fuer_verrauschten_neun)[0]
erkannte_klasse_des_nn_fuer_verrauschten_neun = np.argmax(wahrscheinlichkeiten_des_nn_fuer_verrauschten_neun)
print('Das NN erkennt die verrauschten Neun als ', erkannte_klasse_des_nn_fuer_verrauschten_neun, ' mit einer Wahrscheinlikeit von ', wahrscheinlichkeiten_des_nn_fuer_verrauschten_neun[erkannte_klasse_des_nn_fuer_verrauschten_neun])<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.10164857923949963, 'fraction_numerical': 0.023858835224919227, 'mean_word_length': 6.397058823529412, 'pattern_counts': {'":': 0, '<': 33, '<?xml version=': 0, '>': 42, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '8', 'dir': 'jupyter-structured-clean-dedup', 'id': '324726', 'license': 'MIT', 'n_tokens_mistral': 4807, 'n_tokens_neox': 4728, 'n_words': 1082, 'path': 'modul_2_mnist-digits_cnn.ipynb', 'repo_name': 'hacker-school/juml', 'size': '12071'}