Gerade-ungerade Unterscheiden Lernen mit Naive Bayes

Aufgabe

KI lernt gerade / ungerade Zahlen zu unterscheiden

Methode

Categorical Naive Bayes - ein Machine Learning Modell auf Basis der "Naive Bayes" Data Analytics Methode

Links

1. Problem Definition

Features: M0, M1, ... Dezimalstellen einer ganzen (nicht-negativen) Zahl. Hier beschränkt auf Läge m = 6. Einer-Stelle rechts (M5).

Kategorien: Für jede Dezimalstelle a in 0,1,...,9

Response: R, Klassen-Labels: 0 Zahl gerade, 1 Zahl ungerade

Data sets: Input 6 Ziffern, Response 1 Ziffer binär. Dataset aufgeteilt in Training- und Testdaten

Die notwendigen Imports

In [5]:
# Von numpy
import numpy as np
import numpy.random as rd
np.set_printoptions(precision=3,suppress=True)
# Von sklearn
from sklearn.naive_bayes import CategoricalNB
from sklearn import metrics
from sklearn.model_selection import train_test_split

2. Die Problem-Dimensionen

In [24]:
n_feat = 6    # M0,M1,M2,M3,M4,M5 - M5 definiert die Einer-Stellen
n_attr = 10   # 0,1,...9 Dezimalziffern
n_labl = 2    # Ergebnis-Klassen 0 (gerade), 1 (ungerade)
N      = 200  # Umfang des Datasets

3. Parameter für Problem-Variationen

Standard: div=2 für die Gerade/Ungerade Lernaufgabe

In [25]:
# Parameter für Problem-Variationen    
div = 2       # Division durch div mit Rest zur Erzeugung von Trainings-Responses
# div = 5     # Variante 2: Teilbarkeit durch 5

4. Generierung des Datasets

N Ganze Zahlen und gerade/ungerade Klassifikation

In [26]:
# Features: Ganze Zahlen Zufallswahl

A = rd.randint(n_attr,size=(N,n_feat)) 

# Zugehörige Response: gerade (0) oder ungerade (1) per Rest modulo 2 

low_digit = 5           # Position der Einerstelle in a ist M5
y = list()
y = np.fmod(A[:,low_digit],div) # R = y generiert als Rest modulo div aus der Einer-Stelle

Ansicht der ersten 20 generierten Daten

In [27]:
# Ausgabe der ersten 20 Daten

print(' M0........M5    y     ')
print('--------------------------')

for i in range(20):
    print(A[i,:],'   ',y[i],)
 M0........M5    y     
--------------------------
[1 0 6 4 7 9]     1
[3 1 8 5 8 9]     1
[0 5 2 5 9 8]     0
[0 9 7 5 9 9]     1
[9 3 2 2 1 2]     0
[4 7 0 1 5 3]     1
[5 8 8 7 6 1]     1
[8 2 1 1 0 7]     1
[5 4 8 3 2 8]     0
[8 8 7 3 7 7]     1
[5 3 0 1 9 0]     0
[5 0 4 7 7 7]     1
[0 1 2 9 7 0]     0
[6 7 3 7 0 4]     0
[7 4 2 5 0 5]     1
[3 9 4 8 4 1]     1
[0 1 5 7 5 0]     0
[0 3 0 7 6 3]     1
[6 8 7 7 1 4]     0
[3 5 1 2 8 8]     0

5. Datenset Aufteilung in Trainings- und Testdaten

In [30]:
# Aufteilung in Training- und Test-Set: 30% für Test, zufällig ausgewählt

A_train, A_test, y_train, y_test = train_test_split(A,y,test_size=0.3,random_state=109) 

6. Das Categorical NaiveBayes Modell

In [32]:
# Definition und Training des NB Modells aus sklearn

g_u_model = CategoricalNB(alpha=0) # alpha=0: Keine Glättung für missing values
g_u_model.fit(A_train,y_train)     # "Training" des Modells mittels Trainingsdatenset
C:\Users\bernh\Anaconda3-5\lib\site-packages\sklearn\naive_bayes.py:509: UserWarning: alpha too small will result in numeric errors, setting alpha = 1.0e-10
  'setting alpha = %.1e' % _ALPHA_MIN)
Out[32]:
CategoricalNB(alpha=0)

7. Ergebnisse, Tests und Accuracy des trainierten Modells

In [33]:
# 7.1 Gerade-Ungerade-Klassifikation für ein paar Einzel-Testzahlen
print('Ein paar Testzahlen:')
print('Testzahl      y_pred y_true')
for i in range(10):
    test_zahl = A_test[i]
    pred = g_u_model.predict([test_zahl])
    print(test_zahl,pred,'   ',y_test[i])
Ein paar Testzahlen:
Testzahl      y_pred y_true
[3 1 8 5 8 9] [1]     1
[2 1 7 8 6 0] [0]     0
[9 1 7 2 5 9] [1]     1
[4 5 4 4 4 7] [1]     1
[3 8 4 6 4 7] [1]     1
[4 7 0 1 5 3] [1]     1
[5 8 1 1 4 4] [0]     0
[1 8 2 1 9 1] [1]     1
[6 9 6 1 5 8] [0]     0
[1 2 3 8 9 2] [0]     0
In [34]:
# 7.2 Accuracy über alle Trainingsdaten und Testdaten:
# Vergleich predictions mit y_train bzw. y_test

pred_train = g_u_model.predict(A_train)
print('Accuracy Trainingset: %6.2f' % metrics.accuracy_score(y_train,pred_train))

pred_test = g_u_model.predict(A_test)
print('Accuracy Testset:     %6.2f' % metrics.accuracy_score(y_test,pred_test))
Accuracy Trainingset:   0.99
Accuracy Testset:       1.00
In [35]:
# 7.3 Einige NaiveBayes-interne Ergebnisse des trainierten Modells

print('Classes          :',g_u_model.classes_)
print('Class count      :',g_u_model.class_count_,np.sum(g_u_model.class_count_))
logprior = g_u_model.class_log_prior_
print('Prior classes    :',np.exp(logprior))   

print()
print('N_features       :',g_u_model.n_features_)

print('N_Categories     :',g_u_model.n_categories_)

print('Category count  :\n',g_u_model.category_count_)
logprob = g_u_model.feature_log_prob_  
prob = np.exp(logprob)
print('Probability of features per class:\n',prob)
Classes          : [0 1]
Class count      : [68. 72.] 140.0
Prior classes    : [0.486 0.514]

N_features       : 6
N_Categories     : [10 10 10 10 10 10]
Category count  :
 [array([[ 9.,  9.,  5.,  6.,  8.,  5.,  7.,  5.,  7.,  7.],
       [18.,  8.,  3.,  8.,  6., 10.,  4.,  5.,  6.,  4.]]), array([[ 6.,  9.,  3.,  9.,  5.,  9.,  5.,  9.,  6.,  7.],
       [ 9.,  6., 11.,  8.,  6.,  8.,  5.,  4.,  8.,  7.]]), array([[ 6., 11.,  9.,  5.,  4.,  4.,  8.,  8.,  8.,  5.],
       [ 7.,  4.,  6.,  9.,  9.,  3.,  8.,  8., 13.,  5.]]), array([[ 6.,  6.,  5.,  8.,  2.,  7.,  6., 12.,  7.,  9.],
       [ 6., 10.,  3.,  9.,  6., 10.,  7.,  9.,  9.,  3.]]), array([[ 5., 10.,  2., 10.,  5.,  7.,  5.,  9.,  6.,  9.],
       [ 7.,  5., 10.,  5.,  7.,  2.,  9., 12.,  7.,  8.]]), array([[15.,  1., 10.,  0., 13.,  0., 17.,  0., 12.,  0.],
       [ 1., 13.,  1., 11.,  0., 16.,  0., 16.,  0., 14.]])]
Probability of features per class:
 [[[0.132 0.132 0.074 0.088 0.118 0.074 0.103 0.074 0.103 0.103]
  [0.25  0.111 0.042 0.111 0.083 0.139 0.056 0.069 0.083 0.056]]

 [[0.088 0.132 0.044 0.132 0.074 0.132 0.074 0.132 0.088 0.103]
  [0.125 0.083 0.153 0.111 0.083 0.111 0.069 0.056 0.111 0.097]]

 [[0.088 0.162 0.132 0.074 0.059 0.059 0.118 0.118 0.118 0.074]
  [0.097 0.056 0.083 0.125 0.125 0.042 0.111 0.111 0.181 0.069]]

 [[0.088 0.088 0.074 0.118 0.029 0.103 0.088 0.176 0.103 0.132]
  [0.083 0.139 0.042 0.125 0.083 0.139 0.097 0.125 0.125 0.042]]

 [[0.074 0.147 0.029 0.147 0.074 0.103 0.074 0.132 0.088 0.132]
  [0.097 0.069 0.139 0.069 0.097 0.028 0.125 0.167 0.097 0.111]]

 [[0.221 0.015 0.147 0.    0.191 0.    0.25  0.    0.176 0.   ]
  [0.014 0.181 0.014 0.153 0.    0.222 0.    0.222 0.    0.194]]]

8. Variationen der Aufgabenstellung

Die folgenden Zellen sind je nach Aufgabenvariation im Standard-Ablauf oben zwischen zu schalten

8.1 Robustheit: Einfügen von zufälligen Fehlern in den Trainingsdaten

8.2 Vielfache von 5

In [31]:
# 8.1 Variation 1:
# Fehler-Generierung für Trainingsdaten: Zufällig Änderung von y_train
# --- Diese Zelle starten im Anschluß an Zelle 5. "Train-Test-Split" ---
# --- Dann weiter mit Zelle 6. "Categorical NaiveBayes Modell"

err_level = 0.05 # 5% Zufalls-Fehler in Trainingsdaten 
rd_count = 0
print(y_train[:20])

for i in range(len(y_train)):
    if rd.rand()<err_level:
        y_train[i] = rd.choice([r for r in range(div)])
        rd_count += 1

print(y_train[:20])
print('Errors:',rd_count)
[0 0 1 0 0 0 1 1 1 0 1 1 0 0 1 1 1 1 0 1]
[0 0 1 0 0 0 1 1 1 1 1 1 0 0 1 1 1 1 0 1]
Errors: 9
In [23]:
# 8.2 Variation 2:

# Vielfache von 5 lernen
# --- In Zelle 3. "Parameter für Problem-Variationen" Parameter div=5 setzen ---
# --- Dann weiter in normaler Abfolge ---