Kreuzvalidierung Neuronales Netz

mit matplotlib, NumPy, pandas, SciPy, SymPy und weiteren mathematischen Programmbibliotheken.
Antworten
doerflia
User
Beiträge: 20
Registriert: Freitag 3. Mai 2019, 14:49

Hi ihr,

ich bin neu im Bereich "Data Science" und habe festgestellt,
dass ich zu wenig Datensätze für ein Training eines einfachen Neuronalen Netzes besitze,
weshalb ich hierfür die Kreuzvalidierung als Lösung gefunden habe.

Meine erste Frage: habe ich die Kreuzvalidierung korrekt als Code umgesetzt?

Code: Alles auswählen

# -*- coding: utf-8 -*-
"""
Created on Wed Apr  3 16:26:14 2019

@author: mattdoe
"""

from data_preprocessor_db import data_storage # validation data
from sklearn.model_selection import StratifiedKFold
# from keras.utils import to_categorical
from keras.models import Sequential
from keras.layers import Dense
from keras.utils import normalize
from numpy import mean
from numpy import std



# create and evaluate a single multi-layer-perzeptron
def evaluate_model(Train, Test, Target_Train, Target_Test):
    # define model
    model = Sequential()
    # input layer automatically created
    model.add(Dense(9, input_dim=9, activation='relu')) # 1st hidden layer
    model.add(Dense(9, activation='relu')) # 2nd hidden layer
    model.add(Dense(9, activation='softmax')) #output layer

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

    # fit model
    model.fit(Train, Target_Train, epochs=50, verbose=0)
    # if not running: try to_categorical(Target_Train)
    
    # evaluate the model
    test_loss, test_acc = model.evaluate(Test, Target_Test, verbose=0)
    # if not running: try to_categorical(Target_Test)

    return model, test_acc



# for seperation of data_storage
# Link_ID = []
Input = []
Output = []

# list all results of k-fold cross-validation
scores, members = list(), list()

# seperate data_storage in Input and Output data
for items in data_storage:
    # Link_ID = items[0] # identifier not needed
    Input = (items[1], items[2], items[3], items[4], items[5], items[6], items[7], items[8]) # Input: all characteristics
    Output = items[9] # Output: scenario_class 1 to 8

# normalize Data
Input = normalize(Input)
Output = normalize(Output)

# prepare k-fold cross-validation
kfold = StratifiedKFold(n_splits=15, random_state=1, shuffle=True)

for train_ix, test_ix in kfold.split(Input, Output):
    # select samples
    Train, Target_Train = Input[train_ix], Output[train_ix]
    Test, Target_Test = Input[test_ix], Output[test_ix]
    
    # evaluate model
    model, test_acc = evaluate_model(Train, Test, Target_Train, Target_Test)
    
    # display each evalution result
    print('>%.3f' % test_acc)
    
    # add result to list
    scores.append(test_acc)
    members.append(model)

# summarize expected performance
print('Estimated Accuracy %.3f (%.3f)' % (mean(scores), std(scores)))



# save model // trained neuronal network
model.save('neuronal_network_1.h5')
Zum Code: Datastorage ist eine Struktur wie folgt:

Code: Alles auswählen

item[0], item[1], item[2], item[3], ... item[9]
....
item[0], item[1], item[2], item[3], ... item[9]
Die Daten werden data_preprocessor_db.py aus einer postgres-DB abgerufen und
sind alle numerisch, jedoch noch nicht normalisiert.

Noch ist die DB-Tabelle "learn_data" nicht fertig gefüllt, weshalb ich den Code auch noch nicht austesten konnte.
Die Tabelle besteht jedoch nicht nur aus Integer, sondern auch aus Float-Werten.
Ein Problem beim Normalisieren?

Meine zweite Frage: Kombination mit "early stopping" sinnvoll?

Letzten Endes erhalte ich mit meinem bisherigen Code ja immer bei jeden Funktionsaufruf den letzten Wert,
den mein Netz gespeichert hat, aber nicht den "vermeintlich" besten.
Dafür könnte doch der Ansatz des "early sopping's" helfen oder
widerspricht dieses den Grundsätzen der Kreuzvalidierung?

Meine dritte Frage: bedeutet Modell speichern, dass die Gewichte gespeichert werden?
Oder wird so nur die "Struktur" des NN gesichert?

Ich möchte das Modell später ja auch anwenden und brauche dafür die Gewichte...


Vielen Dank schonmal an alle Data Scienec und NN-Experten ;-)
Finde das Thema spannend und hoffe, ihr könnt mir da stückweit auch mein Verständnis für das für mich neue Thema schärfen.
Benutzeravatar
ThomasL
User
Beiträge: 1366
Registriert: Montag 14. Mai 2018, 14:44
Wohnort: Kreis Unna NRW

Hallo doerflia,
1) wie hast du denn festgestellt, dass du zu wenig Daten hast?

2) hier läuft was falsch:

Code: Alles auswählen

Input = []
Output = []

for items in data_storage:
    Input = (items[1], items[2], items[3], items[4], items[5], items[6], items[7], items[8]) # Input: all characteristics
    Output = items[9] # Output: scenario_class 1 to 8
Egal wieviele items du in data_storage hast, nach durchlaufen der Schleife hast du in Input ein Tuple mit 8 Werten und in Output einen Wert.
Das ist nicht das was du willst, sondern eine Liste von Listen.

3) Du verwendest "categorical_crossentropy", dazu dieser Auszug:
Note: when using the categorical_crossentropy loss, your targets should be in categorical format
(e.g. if you have n classes, the target for each sample should be a n-dimensional vector
that is all-zeros except for a 1 at the index corresponding to the class of the sample).
In order to convert integer targets into categorical targets, you can use the Keras utility to_categorical
When using the sparse_categorical_crossentropy loss, your targets should be integer targets.
If you have categorical targets, you should use categorical_crossentropy.
Das Target wird daher auf keinen Fall normalisiert und muss ein Integerwert (zB von 1 bis 8) sein.

Early Stopping wird verwendet um ein unnötiges Trainieren abzubrechen, wenn das Modell anfängt zu "overfitten".
Macht also immer Sinn.

model.save() speichert das komplette Model inkl. Gewichten.
Es gibt mMn auch ein save_weights() wo nur die Gewichte gespeichert werden.

Ohne Daten und damit ohne Möglichkeit den Code zu testen ist das alles etwas schwierig.
So wie oben gezeigt läuft der nicht wirklich.
Ich bin Pazifist und greife niemanden an, auch nicht mit Worten.
Für alle meine Code Beispiele gilt: "There is always a better way."
https://projecteuler.net/profile/Brotherluii.png
doerflia
User
Beiträge: 20
Registriert: Freitag 3. Mai 2019, 14:49

Hi,

zu 1)
Ich habe eine Berechnungsformel gefunden, die abhängig von Anzahl der Eingangs-, Ausgangsneuronen und den Neuronen in der ersten und zweiten Schicht eine Mindestanzahl von Datensätze für das Training berechnet.
Da ich eine Strukur von [9, 9, 9, 9] habe, wobei ich mich ja frage, ob das überhaupt sinnvoll ist, sind 30 Datensätze die per Hand gelabbelt sind, wohl eindeutig zu wenig.
Aber 15 Durchläufe mit Kreuzvalidierung sollte dann doch schon ausreichen.

zu 2)
Hier versuche ich einen Split von data_storage.
Das ist aber noch nicht die Trennung der Datensätze in Trainings- und Testdaten,
sondern von "Merkmalen" und "gelabelte Klasse".

Also Output soll ja ein Klassen-Wert sein (gelabbelte Klasse),
Input alle 8 Informationen haben. Jede Information soll einem Eingangsneuron zu geschoben werden.
--> dabei merke ich: meine Netzstruktur passt so nicht ;-)

Da ich auch neu mit Python programmiere, habe ich da meine Schwierigkeiten mit...
Tuples sind für mich ganz neu...

So speichere ich ja nur 1 Datensatz richtig?
Dann mit .append jedes mal einen 8er/1er Satz Daten in der Liste anfügen, oder?

Also etwas in der Form:

Code: Alles auswählen

for items in data_storage:
    Input.append(items[1], items[2], items[3], items[4], items[5], items[6], items[7], items[8]) # Input: all characteristics
    Output .append(items[9]) # Output: scenario_class 1 to 8
Neue Frage:
Können die nacholgend genutzten Funktionen StratifiedKFold und kfold.split Listen mit Tuples überhaupt verarbeiten???

zu 3)
Ok, Theorie verstanden. Die Eingangsinformationen sollte ich aber normalisieren, oder?
D. h. ich habe

Code: Alles auswählen

Input = normalize(Input)
nur noch folgende Zeile und lasse output unverändert.

"categorical_crossentropy" ist der richtige Ansatz bei so vielen Ausgangsklassen, richtig?

Ich sollte aber dann im Modell folgendes noch ändern (to_categorical einfügen):

Code: Alles auswählen

    # fit model
    model.fit(Train, to_categorical(Target_Train), epochs=50, verbose=0)
    
    # evaluate the model
    test_loss, test_acc = model.evaluate(Test, to_categorical(Target_Test), verbose=0)
Wird das to_categorical auch bei "Input"/"Train bzw. Test" (Source) benötigt?
Oder nur beim Target?




Danke für die Erklärung von Early Stopping und dem Modell speichern.
Early Stopping werde ich mal versuchen umzusetzen, sobald alle Fehler hier draußen sind.

Ja, muss langsam mal voran kommen, meine Daten aufzubereiten...
Das ist in meiner Anwendung leider nicht trivial :)
Viel Datenbank und Sensorarbeit...

Vorher macht ein Test auch nur wenig Sinn.
Nur musste ich irgendwie die Zeit überbrücken (bin auf Zulieferer angewiesen)
und habe mich eben mal in ein neues Thema rangewagt ;-)

Dir vielen Dank schon mal für deine bisherige Hilfe :)
Antworten