Bernd Thomas
Beck et al.
Published in
4 min readNov 11, 2019

--

5.1 Pre-Training: Ein einfaches Perzeptron lernt, die Quersumme einer Zahl zu berechnen.

Als Vorbereitung werden die Parameter der Lern-Aufgabe gesetzt und die Konvertierungsfunktion to_digits definiert. Als Stellenzahl wählen wir wieder 4, d.h. der Zahlenraum geht von 0 bis 9999.

5.1.0 Generierung der Data Sets und Klassifikationen

# Imports

import numpy as np
import random as rd
import matplotlib.pyplot as plt
from keras.models import Sequential
from keras.layers import Dense
from keras.optimizers import sgd, adam
# Optimizers: stochastic gradient descent or adam

# Paramters
n_pos = 4
nmin,nmax = 0,10000
n_cases = 200
prange = range(10) # print output typical range
# Helpers

def pad_zeros(n_pos,s):
''' Padding string s with zeros at high end
to achieve total length of string of n_pos
'''
t = str(s)
while len(t) < n_pos:
t = '0' + t
return t # returns string of length n_pos

v_pad = np.vectorize(pad_zeros)

def to_string(X,n_pos):
''' Convert 1d-array of integer numbers to
strings of n_pos characters
using Python function str()
X 1d-array, n_pos length of string
padding zeros at high end if needed
returns 1d-array
'''
return v_pad(n_pos,np.array([str(x) for x in X]))

def to_digits(X,n_pos):
''' Convert integer numbers to a sequence of
digits (i.e. 1-digit numbers)
using to_string for intermediate representation
as a string of characters
X 1darray, n_pos max length of numbers
returns 1d-array
'''
Z = to_string(X,n_pos)
print(Z[:10])

return np.array([list(map(int,list(z))) for z in Z])
# Generate Data Sets and convert to digits

X = np.random.randint(nmin,nmax,size=(n_cases))
X = to_digits(X,n_pos)
print(X[:10])

# We now have a dataset X that consists of the digits
# of the random numbers generated above
# For y calculate 1st order checksum from list x
y = np.array(list(sum(x) for x in X))
print(list((list(X[i]),y[i]) for i in prange))

# Aufteilung in Traings- und Testdaten ( 25%)
# Simple partitioning 25% Test data
X_train = X[:150]
y_train = y[:150]
X_test = X[150:]
y_test = y[150:]
['8391' '7460' '1764' '4558' '6630' '8299' '2915' '6792' '7792' '1866']
[[8 3 9 1]
[7 4 6 0]
[1 7 6 4]
[4 5 5 8]
[6 6 3 0]
[8 2 9 9]
[2 9 1 5]
[6 7 9 2]
[7 7 9 2]
[1 8 6 6]]
[([8, 3, 9, 1], 21), ([7, 4, 6, 0], 17), ([1, 7, 6, 4], 18), ([4, 5, 5, 8], 22), ([6, 6, 3, 0], 15), ([8, 2, 9, 9], 28), ([2, 9, 1, 5], 17), ([6, 7, 9, 2], 24), ([7, 7, 9, 2], 25), ([1, 8, 6, 6], 21)]

5.1.1 Entwurf und Training des Modells für Lernen der Quersumme

Ein simples lineares Perzeptron reicht, wie man sich theoretisch überlegen kann.

Entwurf:

# Design keras Model

mqs = Sequential()
mqs.add(Dense(1,activation='linear',use_bias=False, input_shape=(4,)))
# Zum besseren Verständnis ohne bias Parameter

ms = mqs
ms.summary()
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
dense_2 (Dense) (None, 1) 4
=================================================================
Total params: 4
Trainable params: 4
Non-trainable params: 0
_________________________________________________________________

Compilation: Es werden die initialen Gewichte gezeigt.

# Compile keras model specificaionsgd1 = sgd(lr= 0.01)                           # sgd with learning rate
ms.compile(optimizer=sgd1,loss='mean_squared_error',metrics=['accuracy'])
ms.get_weights()
[array([[-0.7744944 ],
[-0.37460202],
[-0.63680476],
[ 0.10898578]], dtype=float32)]

Training:

# Train checksum model, saving epoch history

hist = ms.fit(X_train,y_train,epochs=200,batch_size=n_cases,verbose=0)
print([hist.history['loss'][i] for i in range(0,200,20)])
[788.5260009765625, 45.09532928466797, 2.5909037590026855, 0.14886048436164856, 0.008552571758627892, 0.0004914271994493902, 2.8228132578078657e-05, 1.6185658751055598e-06, 9.367367681534233e-08, 5.363701482963279e-09]

Die loss-Funktion zeigt rasche Konvergenz nach ca. 60 Epochen.

# Show trained weights
ms.get_weights()
[array([[0.9999999 ],
[0.99999976],
[0.99999976],
[0.9999999 ]], dtype=float32)]

Die Gewichte entwickeln sich zur theoretischen Lösung (1.0, 1.0, 1.0, 1.0).

5.1.2 Überprüfung des trainierten NN mit den Testdaten

# Evaluation - Check training quality on test data y_pred = list(ms.predict(X_test))print('Zahl      y  y_pred')
acc = 0 # Accuracy counter
for i in range(len(y_pred)):
y_class = int(round(y_pred[i][0]))
print(X_test[i], y_test[i],y_class)
acc += 1 if y_test[i] == y_class else 0
if acc == len(y_pred):
print('All',acc,'predicitons correct')
else:
print(acc,' predictions correct out of',len(y_pred),'test caes. Accuracy:',acc/len(y_pred))
Zahl y y_pred
[2 7 4 3] 16 16
[3 7 5 1] 16 16
[1 0 1 7] 9 9
[0 2 9 3] 14 14
[4 2 8 2] 16 16
[9 9 4 2] 24 24
[7 8 9 0] 24 24
[6 6 3 9] 24 24
[0 8 1 4] 13 13
[3 5 5 0] 13 13
...
[8 4 4 7] 23 23
[9 8 3 2] 22 22
[3 0 2 6] 11 11
[1 4 5 8] 18 18
All 50 predicitons correct

Perfekt! — Das pre-trained Modell mqs für die einfache Quersummenbildung ist erstellt und getestet.

Es könnte jetzt abgespeichert werden und bei Bedarf mit den trainierten Gewichten verwendet werden. Verwendung bedeutet, ein Set von zu klassifizierenden Zahlen als Input vorzugeben (ohne Klassifikation) und das Modell mit model.predict(X) aufzurufen. Dabei ist vorher das Eingabeset mit to_digits zu konvertieren: X = to_digits(X,n_pos) (n_pos ist die gewünschte fixe Stellenanzahl).

Zur Erinnerung, die mathematische Regel ist: eine Zahl ist Vielfaches von 3, wenn ihre Quersumme ein Vielfaches von 3 ist. Herrlich rekursiv! Und direkt in einen Berechnungsalgorithmus umzusetzen. Als Aufgabe für Machine Learning aber …

Weiter lesen: 5.2 Transfer Learning Recurrent Modell und Training eines Gesamt-Modells für Vielfache von 3

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