In dieser Session wollen wir uns mit der Modellentwicklung und dem Trainieren eines Modells befassen. Dafür schauen wir uns ein einfaches Klassifikationsbeispiel an.
Vor dem Training eines ML-Modells müssen alle Daten in Trainings- und Testdaten aufgespaltet werden.
Ziel: Ein möglichst generalisierendes Modell auf Basis der vorhandenen Daten
Damit das Modell später auch die richtigen Entscheidungen für ungesehene Daten trifft, nehmen wir einen Teil der Daten beiseite. Diese Daten verwenden wir dann zur Evaluierung des Modells. Dadurch können wir überprüfen, ob das Modell generalisiert.
Zunächst müssen wir dafür einen passenden Datensatz herunter laden. Zu Übungszwecken nehmen wir hierfür den bekannten Iris-Datensatz. Der Iris-flower-Datensatz besteht aus jeweils 50 Beobachtungen dreier Arten von Schwertlilien (Iris) (Iris Setosa, Iris Virginica und Iris Versicolor), an denen jeweils vier Attribute der Blüten erhoben wurden: Die Länge und die Breite des Sepalum (Kelchblatt) und des Petalum (Kronblatt). (Quelle: wikipedia.org)
Zunächst laden wir das Datenset mit dem uns bekannten Framework pandas
.
import pandas as pd
import numpy as np
file_path = "iris.csv"
df = pd.read_csv(file_path)
df.sample(5).T
Bekannte Datensätzen werden oft von den ML Frameworks wie z.B. sklearn
, tensorflow
bereitgestellt.
Dadurch kann der eigene Prototyp schnell getestet werden.
from sklearn.datasets import load_iris
# return_X_y:
# - True: return only data and targets (labels)
# - False: return dictionary with data, targets, target_names etc.
# as_frame:
# - True: return as pandas dataframe
# - False: return as NumPy arrays
X,y = load_iris(return_X_y=True, as_frame=True)
X.sample(5)
from tensorflow.keras.datasets import fashion_mnist
import matplotlib.pyplot as plt
class_names = ['T-shirt/top', 'Trouser', 'Pullover', 'Dress', 'Coat',
'Sandal', 'Shirt', 'Sneaker', 'Bag', 'Ankle boot']
(train_images, train_labels), (test_images,
test_labels) = fashion_mnist.load_data()
plt.figure(figsize=(10,10))
for i in range(25):
plt.subplot(5,5,i+1)
plt.axis("off")
plt.grid(False)
plt.imshow(train_images[i], cmap=plt.cm.binary)
label = class_names[train_labels[i]]
plt.title(label)
plt.show()
TODO 1: Was schätzt ihr? In welchem Verhätlnis werden Trainings- und Testdaten normalerweise gesplittet?
Wir unterteilen die Daten klassischerweise folgendermaßen:
Die Daten werden dabei zufällig aus dem vorhandenen Datensatz gezogen. Vereinzelte Datensätze für Benchmarks oder Wettbewerbe geben einen Train-Test-Split bereits vor. Dies wird in der Realität aber so nicht vorkommen.
Wenn wir unsere Daten nicht splitten haben wir keine Möglichkeit zu überprüfen, ob und wie unser Modell auf ungesehenen Daten funktioniert. Zum Beispiel können wir nicht feststellen, ob unser Modell "overfittet". Overfitting ist, wenn sich unser Modell zu sehr auf die Trainingsdaten spezialisiert und nicht generalisiert. Das heißt es funktioniert sehr gut auf Daten, die es bereits kennt, aber sehr schlecht auf Daten, die es noch nie gesehen hat.
Source: [What is underfitting and overfitting in machine learning and how to deal with it.](https://medium.com/greyatom/what-is-underfitting-and-overfitting-in-machine-learning-and-how-to-deal-with-it-6803a989c76)
Da wir dies verhindern wollen, sollten wir jetzt unsere Daten in Test- und Trainingsset aufteilen.
TODO 2: Implementiere eine Funktion train_test_split
, die aus einem DataFrame einen Train und einen Test Dataframe macht. Das Prozentsatz an Trainingsdaten wird als Parameter split
angegeben, z.B. split=0.8
bedeutet, dass 80% des Datensatz zum Training verwendet werden.
Tipp:
df.sample
zufällig Elemente aus dem ursprünglichen DataFrame zu nehmen und als Trainings-DataFrame zu verwenden. Hier kann für das Argument frac
der Wert von split
übergeben werden. df.index
df.drop(index)
. Hier wird also eine Liste von Indices übergeben# Complete this function
def train_test_split(df, split):
df_train = pd.DataFrame()
df_test = pd.DataFrame()
return df_train, df_test
train_split = 0.8
df_train, df_test = train_test_split(df, train_split)
print(f"Train samples: {df_train.size}\nValidation samples: {df_test.size}")
len(df_train), len(df_test), df_train.size/df.size
Für ein schnelle Visualisierung des Train-Test-Splits verwenden wir die Bibliothek matplotib.
import matplotlib.pyplot as plt # common import shortening
train_counts = df_train["species"].value_counts(normalize=True)
test_counts = df_test["species"].value_counts(normalize=True)
labels = train_counts.keys()
x = np.arange(len(labels)) # the label locations
width = 0.35 # the width of the bars
fig, [ax1, ax2] = plt.subplots(1, 2, figsize=(12, 4))
rects1 = ax1.bar(x - width/2, train_counts.values, width, label='Train')
rects2 = ax1.bar(x + width/2, test_counts.values, width, label='Test')
# Add some text for labels, title and custom x-axis tick labels, etc.
ax1.set_ylabel('Anteil')
ax1.set_title('Anteil der Klassen im Datensatz')
ax1.set_xticks(x)
ax1.set_xticklabels(labels)
ax1.legend()
# scatter plot of sepal length and with
label1 = "sepal_length"
label2 = "sepal_width"
ax2.scatter(df_train[label1], df_train[label2], label="Train")
ax2.scatter(df_test[label1], df_test[label2], label="Test")
ax2.set_title('Sample Punkte in Trainings- und Testdatensatz')
ax2.set_ylabel(label2)
ax2.set_xlabel(label1)
ax2.legend()
plt.show()
sepal_length
, sepal_width
) aufgezeichnet.Nachdem wir unsere Daten nun geladen haben, wird es Zeit das Modell zu bauen.
Um ein passendes Modell zu wählen, müssen wir uns überlegen, was wir in das Modell hineingeben und was am Ende genau vorhergesagt werden soll. Dementsprechend passen wir unseren In- und Output des Modells an.
Als Modell nehmen wir ein neuronales Netzwerk mit fully-connected Layers. Dies ist typisch für strukturelle/tabellarische Daten, wie wir sie hier vor uns haben.
Der Input in unser Modell sind die folgenden vier Features:
# drop species since it is the label here
label_col = "species"
features = [*df_train.drop(label_col, axis=1).columns]
num_features = len(features)
print(f"Features:{features}")
print(f"Anzahl der Features: {num_features}")
In anderen Datensätzen haben wir mehr Inputfeatures. Diese erfordern dann auch komplexere Modelle.
Das Ziel des Modells ist es vorherzusagen, zu welcher Spezies die Blume gehört. Es gibt die folgenden Spezies (Labels):
labels = df_train["species"].unique()
num_labels = len(labels)
print(f"Labels: {labels}")
print(f"Anzahl der Labels: {num_labels}")
Unser neuronales Netz muss also den folgenden Aufbau haben:
Dazwischen können wir beliebig viele versteckte (hidden) Layer einbauen.
TODO 3: Warum können wir nicht einfach einen Outputknoten haben, der direkt die Klasse vorher sagt?
Nachdem wir nun gesehen haben, was der In- und Output unseres Modells ist, können wir es nun bauen.
Dazu importieren wir zunächst die nötigen Libraries, die uns dabei unterstützen. Wir möchten schließlich nicht die darunterliegende Mathematik von neuem implementieren.
import tensorflow as tf
from tensorflow.keras import Sequential, Input
from tensorflow.keras.layers import Dense, InputLayer
from tensorflow.keras.losses import CategoricalCrossentropy
from tensorflow.keras.optimizers import Adam, SGD
from tensorflow.keras.callbacks import TensorBoard
from tensorflow.keras.metrics import Precision, Recall, CategoricalAccuracy
import os
Lasst uns kurz überprüfen, ob die richtige Tensorflow-Version installiert ist.
assert tf.__version__.startswith("2"), "Die falsche Version ist installiert"
"Ok!"
Nun definieren wir unser ML Modell mit Hilfe von Tensorflow's Keras.
keras.Sequential
. keras.layers
Ein weiterer wichtiger Bestandteil von neuronalen Netzen sind die Aktivierungsfunktionen. Am populärsten sind ReLU
(rectified linear unit) und die softmax
-Funktion.
Diese Aktivierungsfunktionen werden gebraucht, um sog. Nicht-Linearitäten zu erzeugen, d.h. eine Entscheidungsgrenze zwischen den Klassen zu schaffen, die nicht nur eine gerade Linie ist. Ohne diese entspräche das Modell einer einfachen linearen Klassifizierung der Form $y = Wx+t$.
Aktivierungsfunktion | Anwendung | Formel |
---|---|---|
Softmax | Klassifizierung | $$\sigma(\mathbf{z})_{i}=\frac{e^{z_{i}}}{\sum_{j=1}^{K} e^{z_{j}}}$$ |
ReLU | Hidden layer | $$\sigma(\mathbf{z})_{i}=\max\{0,z_i\}$$ |
Linear/ keine | Regression | $$\sigma(\mathbf{z})_{i}=z_i$$ |
model = Sequential([
InputLayer(input_shape=(num_features,), name="input_features"),
# hier können wir später noch mehr Layers hinzufügen
Dense(num_labels, activation="softmax", name="prediction"),
])
Dafür legen wir die Loss-Funktion fest:
Für eine Klassifizierung mit mehreren Klassen nehmen wir den CategoricalCrossEntropy
-Loss.
Alternativen:
BinaryCrossEntropy
(Binäre Klassifizierung)MeanSquaredError
(Regression): $\text{MSE}(y, \hat{y}) = \frac{1}{N}\sum_i||y_i-\hat{y_i}||^2 $Dazu benutzen wir einen sog. Optimizer.
(Vorsicht: Es folgt ein bisschen Mathe!)
https://ml-cheatsheet.readthedocs.io/en/latest/gradient_descent.html
from datetime import datetime
model.compile(optimizer=SGD(learning_rate=0.01),
loss=CategoricalCrossentropy(),
metrics=[CategoricalAccuracy(name="acc")])
time_str = datetime.utcnow().strftime("%y%m%d-%H%M%S")
model.summary()
Zum Training selbst können wir jetzt noch einige Parameter festlegen, die das Training selbst betreffen.
epochs = 50
batch_size = 8
TODO 4 : Anhand der Epochen, der Menge der Trainingsdatensätze und der Batch Size können wir die Gesamtanzahl der Update-Steps und die Anzahl der Update-Steps pro Epoche berechnen.
# Reminder: Number of Training Samples
len(df_train)
num_train_samples = len(df_train)
# Berechne diese beiden Variablen
update_steps_per_epoch = 0
update_steps_total = 0
update_steps_per_epoch, update_steps_total
(15, 750)
sollte hier das Ergebnis sein.
Wie bekommen wir nun aus den eingelesenen DataFrames die Werte mit denen wir das Modell trainieren können?
X_train = df_train.drop(label_col, axis=1).values
y_train = pd.get_dummies(df_train[label_col]).values
X_val = df_test.drop(label_col, axis=1).values
y_val = pd.get_dummies(df_test[label_col]).values # convert pd.Series into numpy array
TODO 5: Finde heraus was die Methode pd.get_dummies(df)
macht.
# Platz zum Ausprobieren von pd.get_dummies()
log_dir = os.path.join(".", "logs", "iris", time_str)
tensorboard_callback = TensorBoard(log_dir=log_dir,
histogram_freq=1,
write_images=True
)
history = model.fit(X_train, y_train,
batch_size=batch_size,
epochs=epochs,
validation_data=(X_val, y_val),
callbacks=[tensorboard_callback])
model.save("models\iris_model")
# tf.keras.models.load_model("models\iris_model")
os.listdir("models\iris_model")
TODO 6: Verpacke den Modellbau in eine Funktion.
build_classifier
. Die Funktion erhält als ParameterInputLayer, Sequential
Dense
SGD
ReLU, Softmax
CategoricalCrossentropy
.compile
)train_and_evaluate
aufrufst. Diese ist darunter definiert.def build_classifier(hidden_layers, num_labels, num_features, optim="sgd"):
# hidden_layers: List of integers, indicating the neurons per layer
# num_labels: number of output neurons
# num_features: number of input values
if len(hidden_layers) == 0:
# Logistic Regression, no hidden layers
model = Sequential([
Dense(num_labels, input_shape=(
num_features,),
activation="softmax"),
])
else:
# Vervollständige diesen Block
layers = []
# Definiere die notwendigen layers (input layer, hidden layer, output layer)
model = Sequential(layers)
# Make the model ready for training.
learning_rate = 0.01
# Definiere den Optimizer
# Kompiliere das Modell
print(model.summary())
return model
model_sgd = build_classifier(hidden_layers=[8], num_labels=num_labels,
num_features=num_features)
Oft bietet es sich an, den Trainings- und Evaluierungsprozess in einer Funktion zusammenzufassen, um z.B.
def train_and_evaluate(df_train, df_test, model):
# prepare data
X_train = df_train.drop("species", axis=1).values
y_train = pd.get_dummies(df_train["species"]).values
# convert pd.Series into numpy array
X_val = df_test.drop("species", axis=1).values
y_val = pd.get_dummies(df_test["species"]).values
time_str = datetime.utcnow().strftime("%y%m%d-%H%M%S")
log_dir = os.path.join(".", "logs", "iris", time_str)
# prepare training
batch_size = 8
epochs = 50
tensorboard_callback = TensorBoard(log_dir=log_dir,
histogram_freq=1,
write_images=True
)
# train the model
train_history = model.fit(X_train, y_train,
batch_size=batch_size,
epochs=epochs,
validation_data=(X_val, y_val),
callbacks=[tensorboard_callback])
# evaluate the model
eval_dict = model.evaluate(X_val, y_val, batch_size=batch_size)
return train_history, eval_dict
history_sgd, eval_sgd = train_and_evaluate(df_train, df_test, model_sgd)
dict(zip(["loss", "acc", "prec", "recall"], eval_sgd))
Die Ergebnisse sollten in etwa folgendermaßen aussehen: $$\text{Loss} <= 0.5$$ $$\text{Accuracy} >= 0.8$$ $$\text{Precision} >= 0.8$$ $$\text{Recall} >= 0.0$$
model = build_classifier([8], num_labels=num_labels,
num_features=num_features,
optim="adam")
history, eval = train_and_evaluate(df_train, df_test, model)
dict(zip(["loss", "acc", "prec", "recall"], eval))
Nachdem das Training abgeschlossen ist, wollen wir uns jetzt ansehen wie das Training verlaufen ist.
Dafür benutzen wir das sog. Tensorboard.
Dieses kann im Terminal über tensorboard --logdir "./logs"
gestartet werden.
Anschließend findet sich das Tensorboard im Browser unter http://localhost:6006.
Alternativ kann der Befehl auch hier im Notebook ausgeführt werden.
%load_ext tensorboard
#%reload_ext tensorboard # in case it doesnt load, uncomment this line
%tensorboard --logdir ".\logs\iris" --port 6007
Wenn wir nicht das Tensorboard benutzen wollen, bietet sich mit matplotlib auch ein sehr mächtiges Tool um unsere Ergebnisse zu visualisieren.
import matplotlib.pyplot as plt
Unsere Loss-Kurve lässt sich zum Beispiel mit den folgenden einfachen Zeilen darstellen:
plt.plot(np.arange(epochs),history.history["loss"])
plt.show()
So sieht das nur noch ein bisschen schwach aus. Was heißt jetzt was in dieser Grafik? Lasst uns doch die Achsen beschriften.
plt.plot(np.arange(epochs), history.history["loss"])
plt.xlabel("Epoche")
plt.ylabel("Loss")
plt.show()
TODO 7: Erstelle einen Plot für die Accuracy-Kurve.
color
-parameter der Funktion plt.plot()
)plt.grid()
.plt.legend()
)# Platz für den Code
Wenn wir alle relevanten Kurven in einer Grafik darstellen wollen, könnten wir das z.B. so machen.
fig, ax1 = plt.subplots()
ax1.set_xlabel('Epochen')
color = 'tomato'
ax1.plot(np.arange(epochs),history.history["val_acc"], color=color, label="Val")
color = 'tab:red'
ax1.set_ylabel('Accuracy', color=color)
ax1.plot(np.arange(epochs),history.history["acc"], color=color, label="Train")
ax1.tick_params(axis='y', labelcolor=color)
ax1.legend()
ax2 = ax1.twinx() # instantiate a second axes that shares the same x-axis
color = 'lightblue'
ax2.plot(np.arange(epochs),history.history["val_loss"], color=color, label="Val")
color = 'tab:blue'
ax2.set_ylabel('Loss', color=color) # we already handled the x-label with ax1
ax2.plot(np.arange(epochs),history.history["loss"], color=color, label="Train")
ax2.tick_params(axis='y', labelcolor=color)
ax2.legend()
plt.show()
Ob diese etwas unübersichtliche Darstellungsweise hilft das Training zu analysieren, sei an dieser Stelle dem Leser überlassen.
TODO 8: Warum fällt die Accuracy manchmal wieder, obwohl der Loss kontinuierlich fällt?
Für die Analyse der Performance unseres Modells benutzen wir die sog. Confusion Matrix.
Zunächst brauchen wir die Vorhersagen von unserem Modell.
y_pred = model.predict(X_val)
y_pred[:5]
Das sind jetzt allerdings nur die Wahrscheinlichkeiten. Die Klassenvorhersage bekommen wir folgendermaßen:
y_pred.argmax(axis=1)
Die annotierten Labels müssen wir auch in diese Repräsentation umwandeln.
y_val.argmax(axis=1)
df_pred = pd.DataFrame()
df_pred["pred"]= y_pred.argmax(axis=1)
df_pred["gt"] = y_val.argmax(axis=1)
df_pred.sample(5)
Nun müssen wir anhand der Vorhersagen die einzelnen Elemente der Confusion Matrix berechnen:
TODO 9: True Positive ist auch bei mehreren Klassen klar definiert. Aber was ist ein False Negative bei mehreren Klassen?
def true_positives(df_pred, label):
label, = np.where(labels==label)[0]
return ((df_pred["gt"]==label) & (df_pred["pred"] == label)).sum()
TPs={l:true_positives(df_pred,l) for l in labels}
TPs
def false_negatives(df_pred, label):
label, = np.where(labels==label)[0]
return ((df_pred["gt"]==label) & (df_pred["pred"] != label)).sum()
FNs={l:false_negatives(df_pred,l) for l in labels}
FNs
TODO 10: Schreibe die verblieben Funktionen false_positives
und true_negatives
, die dir die Anzahl der False Positives/True Negatives im Multi-Klassen-Szenario zurückgeben. Du kannst dich dabei an den Implementierungen von oben orientieren.
Vervollständige dazu die Vergleichsoperatoren in den beiden Funktionen. (Ersetze ...
mit !=, ==, >=, >, <, <=
).
(Zur Erinnerung: False Positives sind alle die, die als eine Klasse vorausgesagt wurden, aber zu einer anderen gehören, z.B. alle Samples die als "setosa" vorhergesagt wurden, aber zu "virginica" oder "versicolor" gehören.
def false_positives(df_pred, label):
# fill in the signs
# convert string label to number
label, = np.where(labels==label)[0]
print(label)
# mark false positives
FP_list = ((df_pred["gt"] ... label) & (df_pred["pred"] ... label))
print(FP_list)
# sum of all false positives
FP = FP_list.sum()
return FP
FPs={l:false_positives(df_pred,l) for l in labels}
FPs
def true_negatives(df_pred, label):
# fill in the signs for the '...'
label, = np.where(labels==label)[0]
TN = ((df_pred["gt"] ... label) & (df_pred["pred"] ... label)).sum()
return TN
TNs = {l: true_negatives(df_pred, l) for l in labels}
TNs
def confusion_matrix3(df_pred):
TPs = [true_positives(df_pred, l) for l in labels]
FPs = [false_positives(df_pred, l) for l in labels]
FNs = [false_negatives(df_pred, l) for l in labels]
cm = np.array([[TPs[0], FNs[0], FNs[0]],
[FPs[0], TPs[1], FNs[1]],
[FPs[0], FPs[1], TPs[2]]
])
return cm
confusion_matrix3(df_pred)
Wir können uns es auch viel einfacher machen und sklearn
benutzen.
from sklearn.metrics import confusion_matrix
confusion_matrix(df_pred["gt"],df_pred["pred"])
Das NN liefert für jeden beliebigen, numerischen Input drei numerische Werte zwischen 0 und 1, die die Wahrscheinlichkeit für die Klasse angeben. In den meisten Fällen ist der Input des NNs hochdimensional (mehrere 100 Inputfeatures) und eine Visualisierung der Decision Boundaries ergibt wenig Sinn oder ist nicht so einfach möglich. Da wir hier mit einem relativ einfachem Datensatz arbeiten, können wir uns jedoch die Decision Boundaries mit einem kleinen Trick ansehen.
Der Singular Value Decomposition (SVD) Algorithmus erlaubt es uns die Daten auf eine 2-dimensionale Ebene zu projezieren. Hier verlieren wir zwar Informationen, aber im Fall dieses Datensets ist der Verlust minimal. Diese 2-dimensionale Projektion können wir graphisch darstellen.
Die unten dargestellte Grafik zeigt unsere Trainingsdaten (Punkte) und die Wahrscheinlichkeit für jede Klasse (Hintergrundfarbe). Die schwarzen Linien stellen die Decision Boundaries dar, also die Stellen, an dem sich die Klasse mit der höchsten Wahrscheinlichkei ändert.
Wichtig: Wir sehen hier nur einen vereinfachte Projektion der Entscheidungsgrenzen (Decision Boundaries), da diese eigentlich 4-dimensional sind
from sklearn.decomposition import TruncatedSVD
svd = TruncatedSVD()
data = df_train.drop("species", axis=1).values
svd.fit(data) # ermitteln der optimalen Projektionsebene
# Projection der Daten auf eine 2-dimensionale Ebene
transformed = svd.transform(data)
res = 200
# Auswertung des Modells für jeden sichtbaren Punkt
grid = np.stack(np.meshgrid(np.linspace(transformed[:, 0].min(), transformed[:, 0].max(), res),
np.linspace(transformed[:, 1].min(), transformed[:, 1].max(), res)), axis=2)
# projektion der sichtbaren 2d Punkte ins 4-dimensionale
t = svd.inverse_transform(grid.reshape(-1, 2))
# Ermitteln der Grenzen des Datensatzes
min_sepal_length = df_train["petal_length"].min()
max_sepal_length = df_train["petal_length"].max()
min_sepal_width = df_train["petal_width"].min()
max_sepal_width = df_train["petal_width"].max()
mean_petal_length = df_train["sepal_length"].mean()
mean_petal_width = df_train["sepal_width"].mean()
class_map = model.predict(t).reshape((*grid.shape[:2], 3))
class_map_borders = class_map.argmax(axis=2)
fig, ax = plt.subplots(figsize=(8, 8))
ax.imshow(class_map, extent=[transformed[:, 0].min(), transformed[:, 0].max(
), transformed[:, 1].min(), transformed[:, 1].max()], alpha=.5, aspect='auto', origin="lower")
ax.contour(-class_map_borders, extent=[transformed[:, 0].min(), transformed[:, 0].max(
), transformed[:, 1].min(), transformed[:, 1].max()], colors='k', alpha=.3)
plt.scatter(transformed[:, 0], transformed[:, 1], c=y_train.argmax(axis=1))
ax.set_title("Decision Boundary")
plt.show()
Ein Beispiel:
from tensorflow.keras.callbacks import EarlyStopping
Wir setzen die Anzahl der Epochen sehr hoch an, um zu sehen, dass das Training schon vorher unterbrochen wird.
# important parameters
# the prefix "val_" ensures monitoring the validation metric, instead of the
monitor = "val_"+"loss"
mode = "min" # whether the goal is to min- or maximize the metric stated in monitor
patience = 10 # wait epochs until the stopping kicks in
early_stopping_callback = EarlyStopping(
monitor=monitor,
verbose=2,
patience=patience,
mode=mode, # change to "min" for loss
restore_best_weights=True
)
epochs = 1000
# build new model
model_early_stopping = build_classifier([8], num_labels=num_labels,
num_features=num_features)
# train the model
history = model_early_stopping.fit(X_train, y_train,
batch_size=batch_size,
epochs=epochs,
validation_data=(X_val, y_val),
callbacks=[early_stopping_callback])
eval_es = model_early_stopping.evaluate(X_val,y_val)
dict(zip(["loss", "acc", "prec", "recall"],eval_es))
from tensorflow.keras.callbacks import ModelCheckpoint
# important parameters
ckpt_path = os.path.join("ckpts", "iris")
os.makedirs(ckpt_path, exist_ok=True)
# the prefix "val_" ensures monitoring the validation metric, instead of the
monitor = "val_"+"acc"
mode = "max" # whether the goal is to min- or maximize the metric stated in monitor
# how often is the model saved
period = 2
# Saves storage
save_best_only = True # False
ckpt_callback = ModelCheckpoint(
ckpt_path,
monitor=monitor,
verbose=2,
save_best_only=save_best_only,
mode=mode,
save_freq="epoch",
period=period,
)
epochs = 20
# build new model
model = build_classifier([8], num_labels=num_labels,
num_features=num_features)
print(batch_size)
# train the model
history = model.fit(X_train, y_train,
batch_size=batch_size,
epochs=epochs,
validation_data=(X_val, y_val),
callbacks=[ckpt_callback])
Ich wollte schon immer mal wissen, ob/wie/was...?
Session 6: Hyperparameter-Tuning