Datenanalyse

Neuronales Netz: Erkennung von handgeschriebenen Ziffern

Als Anwendung eines neuronalen Netzes ist im Folgenden die Erkennung von hand­geschriebenen Ziffern beschrieben. Das Programm ist in der Programmier­sprache Python als jupyter-Notebook implementiert.

Handgeschriebene Ziffern

Eine Standardanwendung für ein neuronales Netz ist die Erkennung von hand­geschriebenen Ziffern. Die Datei mnist_train.csv enthält die Daten von 60.000 gescannten hand­geschriebenen Ziffern. Mit diesen Daten trainieren Sie zunächst das neuronale Netz. Anschließend testen Sie das neuronale Netz mit weiteren 10.000 hand­geschriebenen Ziffern, die sich in der Datei mnist_test.csv befinden. Die Daten entstammen der öffentlich zugänglichen MNIST-Daten­sammlung.

Jede der beiden CSV-Dateien enhält eine Tabelle. Jede Zeile der Tabelle besteht aus 785 Zahlenwerten, dabei gibt der erste Wert die Ziffer an, die dargestellt ist, und die restlichen 784 Zahlenwerte stellen Graustufen eines 28×28-Pixel-Bildes dar, das die hand­geschriebene Ziffer zeigt. Als Beispiel sehen Sie in Bild 1 eine hand­geschriebene Ziffer 6.

 

Bild 1: Handgeschriebene Ziffer 6 als 28×28-Pixel-Bild 

Bild 1: Handgeschriebene Ziffer 6 als 28×28-Pixel-Bild

 

 

Eingabedaten aufbereiten

Zunächst lesen Sie die Trainings­daten, die sich in der CSV-Datei mnist_train.csv befinden, mit folgender Funktion ein:

def readInput(filename):
    datafile=open(filename, 'r')
    datalist=datafile.readlines()
    datafile.close()
    return datalist

Das Array datalist besteht aus 60.000 Strings, die jeweils Zeilen von durch Kommas getrennten Werten darstellen. Mit einer Funktion prepareInput bereiten Sie die Daten für die weitere Verwendung auf.

Im ersten Schritt wandeln Sie jede der durch Kommas unterteilten Zeilen in eine Liste um. Die Listeneinträge sind aber noch Strings, daher wandeln Sie diese als nächstes in Zahlen um. Dann trennen Sie die erste Zahl ab, diese entspricht dem Wert der dar­gestellten Ziffer, und speichern diee in der Variablen d. Die restlichen Zahlen vermindern Sie um 128, sodass sie in den Bereich {-128., ..., +127} fallen. Das Ergebnis ist der spätere Eingabe­vektor x, auf den in der Eingabe­schicht ja die Funktion σ angewandt wird. So erreichen Sie, dass der Wertebereich von σ ausgeschöpft wird.

Den Zielvektor y bilden Sie folgender­maßen. Zunächst erzeugen Sie einen Vektor mit zehn gleichen Werten von 0.1. In diesem Vektor ändern Sie an Index­position d den Wert in 0.9. Das neuronale Netz soll später so trainiert werden, dass bei Eingabe einer hand­geschriebenen Ziffer d das Neuron an Index­position d der Ausgabe­schicht einen hohen Wert liefert und alle anderen Neuronen der Ausgabe­schicht einen niedrigen Wert. Die Ausgabewerte des neuronalen Netzes liegen stets zwischen 0 und 1, da sie als Fiúnktionswerte der Funktion σ zustande kommen.

Eingabe­vektor und zugehöriger Zielvektor werden miteinander verkettet, und alle so aufbereiteten Zeilen werden zum Schluss zu einer zwei­dimensionalen Liste r zusammengefügt.

 

def prepareInput(datalist):
    r=[]
    for z in datalist:
        v=z.split(',')
        x=[int(s) for s in v]
        d=x[0]
        x=[t-128 for t in x[1:]]
        y=[0.1]*10
        y[d]=0.9
        r+=[y+x]
    return r

In ent­sprechender Weise werden auch die Testdaten aus der Datei mnist_test.csv eingelesen und aufbereitet.

Programm

Es folgt das fertige Programm. Es beginnt mit den Definitionen der Funktionen readInput und prepareInput. Dann kommt die Definition der Funktion sigma. Die Definitionen der eigentlichen Funktionen des neuronalen Netzes, nämlich propagate, backpropagate, train und test schließen sich an. Die Theorie dazu, wie die Funktionen propagate und backpropagate zustande kommen, finden Sie in dem Artikel über Neuronale Netze.

Im Haupt­programm definieren Sie zunächst das neuronale Netz, indem Sie pro Schicht die Anzahl der Neuronen angeben. Hier etwa definieren Sie ein neuronales Netz mit drei Schichten, wobei die Eingabe­schicht 28 · 28 = 784 Neuronen umfasst, die innere Schicht 100 Neuronen und die Ausgabe­schicht 10 Neuronen. Alternativ, hier im Programmtext auskommentiert, können Sie auch ein neuronales Netz mit vier Schichten definieren.

Überraschender­weise hat die Initialisierung der Gewichtungsmatrizen erheblichen Einfluss auf die Erkennungsrate des neuronalen Netzes. Hier ist eine Initialisierung gewählt, die normalverteilte Zufallswerte enthält.

Für die im Verlauf der Berechnung erforder­lichen Vektoren erzeugen Sie zunächst Platzhalter, sodass Sie später per Indizierung darauf zugreifen können (zum Beispiel muss e[0] vorhanden sein, wenn Sie ihm ein Wert zuweisen).

Es folgt dann das Programm­stück zum Trainieren und Testen des neuronalen Netzes. Die 60.000 Trainings­datensätze geben Sie mehrfach in sogenannten Epochen in das Netz ein, um es zu trainieren. Im Anschluss daran bestimmen Sie mit den 10.000 Testdatensätzen jeweils die erzielte Erkennungsrate.

 

import numpy as np

# Daten aus Datei einlesen
def readInput(filename):
    datafile=open(filename, 'r')
    datalist=datafile.readlines()
    datafile.close()
    return datalist

# Daten für die weitere Verwendung aufbereiten
def prepareInput(datalist):
    r=[]
    for z in datalist:
        v=z.split(',')
        x=[int(s) for s in v]
        d=x[0]
        x=[t-128 for t in x[1:]]
        y=[0.1]*10
        y[d]=0.9
        r+=[y+x]
    return r

# Aktivierungsfunktion
def sigma(x):
    return 1.0/(1+np.exp(-x))

# Eingabevektor x durch das neuronale Netz schicken
def propagate(x):
    e[0]=np.array(x).reshape(1,m[0])  # 1 x m[0]-Matrix
    s[0]=sigma(e[0])
    for r in range(1, n):
        e[r]=np.matmul(s[r-1], w[r-1])
        s[r]=sigma(e[r])

# Zielvektor y durch das neuronale Netz zurückverbreiten
# und dabei die Gewichtungen anpassen
def backpropagate(y):
    y=np.array(y).reshape(1, m[n-1])  # 1 x m[n-1]-Matrix
    r=n-1
    f[r]=s[r] - y
    h[r]=f[r]*s[r]*(1-s[r])
    # Fehler zurückpropagieren
    for r in range(n-2, 0, -1):
        f[r]=np.matmul(h[r+1], w[r].T)
        h[r]=f[r]*s[r]*(1-s[r])
    # Gewichte anpassen
    for r in range(n-1, 0, -1):
        w[r-1] -= np.matmul(s[r-1].T, alpha*h[r])

# das neuronale Netz mit den Trainingsdaten trainieren
def train():
    for z in traindata:
        x=z[10:]
        propagate(x)
        y=z[:10]
        backpropagate(y)

# das neuronale Netz mit den Testdaten testen
# und die Erkennungsrate bestimmen
def test():
    cnt=0
    for z in testdata:
        x=z[10:]
        propagate(x)
        y=z[:10]
        p=np.argmax(y)
        q=np.argmax(s[n-1][0])  # [0] weil 1 x m_n-1-Matrix
        if p==q:
            cnt+=1
    return 100.0*cnt/len(testdata)  # Prozent richtig erkannt
       
# Neuronales Netz definieren
# Anzahl der Neuronen pro Schicht
m=[28*28,200,10]
#m=[28*28,300,80,10]
n=len(m)    # Anzahl der Schichten
# Liste mit Gewichtungsmatrizen:
w=[np.random.normal(0.0, pow(m[r-1], -0.5), (m[r-1], m[r])) for r in range(1,n)]
e=[0]*n  # Platzhalter für Eingabevektoren
s=[0]*n  # Platzhalter für Ausgabevektoren
f=[0]*n  # Platzhalter für Fehlervektoren
h=[0]*n  # Platzhalter für Fehlervektoren

# Trainingsdaten einlesen
traindata=prepareInput(readInput("mnist_train.csv"))
# Testdaten einlesen
testdata=prepareInput(readInput("mnist_test.csv"))

# Mehrere Epochen mit jeweils 60.000 Trainingsdatensätzen und
# anschließendem Test mit 10.000 Testdatensätzen durchlaufen,
# nach und nach die Lernrate verringern
epochs=6
for i in range(epochs):
    alpha=0.1*(epochs-i)  # Lernrate nach und nach verringern
    train()
    print("Training zu Ende")
    p=test()
    print('Erkennung: '+str(p)+ " %")

Der Programmtext steht in Form eines jupyter-Notebooks unter MultilayerNeuralNetwork-HandwrittenNumbers.ipynb zum Herunterladen zur Verfügung. Zusätzlich erforderlich sind die Dateien mnist_train.csv und mnist_test.csv. Kopieren Sie diese drei Dateien in ein Verzeichnis.

Probieren Sie das Programm mit unter­schiedlichen neuronalen Netzen mit unter­schiedlicher Anzahl von Schichten und Anzahl von Neuronen in den inneren Schichten aus. Die Anzahl der Neuronen in der Eingabe­schicht liegt fest, sie beträgt 28 · 28 = 784 entsprechend der Anzahl der Pixel der Eingabebilder. Die Anzahl der Neuronen der Ausgabe­schicht liegt ebenfalls fest, sie beträgt 10 entsprechend der Anzahl der zu erkennenden unter­schiedlichen Ziffern 0, ..., 9.

Leistungsfähigkeit

Das hier angegebene neuronale Netz erzielt nach der vierten Trainingsepoche eine Erkennungsrate von über 95 %. Tatsächlich wäre eine Erkennungsrate von 100 % auch gar nicht unbedingt sinnvoll, da manche Ziffer so unsauber geschrieben ist, dass eine Zuordnung zu einem "richtigen" Wert rein willkürlich wäre.

 

Weiter mit:   [up]

 


H.W. Lang   mail@hwlang.de   Impressum   Datenschutz
Created: 04.01.2021   Updated: 29.08.2025
Diese Webseiten sind größtenteils während meiner Lehrtätigkeit an der Hochschule Flensburg entstanden