Praxis: Keras auf PlantVillage
Jetzt wird gestapelt: Dataset-Loading direkt aus den Ordnern, Augmentation als Teil des Modells, die Mini-Architektur aus der Bausteine-Lektion — und ein Training, das sich selbst stoppt, bevor es overfittet.
Anders als bei sklearn: Kein Feature-Schritt, kein Scaler. Das CNN bekommt Bilder als Tensoren (Höhe × Breite × 3) und lernt den Rest selbst. Dafür: Batches, Epochen, Callbacks.
1. Daten laden — die Ordnerstruktur zahlt sich aus
PYTHONcnn_daten.py
import tensorflow as tf
from tensorflow import keras
BILD_GROESSE = (128, 128)
BATCH = 32
train_ds, val_ds = keras.utils.image_dataset_from_directory(
"plantvillage dataset/color",
validation_split=0.2,
subset="both", # liefert (train, val) in einem Aufruf
seed=42, # gleicher Seed = konsistenter Split!
image_size=BILD_GROESSE,
batch_size=BATCH,
label_mode="int", # Labels als Klassen-Index 0..37
)
klassen = train_ds.class_names # 38 Ordnernamen, alphabetisch
# Performance: Bilder im Voraus laden, während die GPU rechnet
train_ds = train_ds.prefetch(tf.data.AUTOTUNE)
val_ds = val_ds.prefetch(tf.data.AUTOTUNE)2. Modell — Augmentation eingebaut
PYTHONcnn_modell.py
from tensorflow.keras import layers
modell = keras.Sequential([
keras.Input(shape=(128, 128, 3)),
# Augmentation: nur im Training aktiv, bei predict() automatisch aus
layers.RandomFlip("horizontal"),
layers.RandomRotation(0.1),
layers.RandomZoom(0.1),
layers.Rescaling(1.0 / 255), # Pixel 0..255 → 0..1
layers.Conv2D(32, 3, padding="same", activation="relu"),
layers.MaxPooling2D(),
layers.Conv2D(64, 3, padding="same", activation="relu"),
layers.MaxPooling2D(),
layers.Conv2D(128, 3, padding="same", activation="relu"),
layers.MaxPooling2D(),
layers.Flatten(),
layers.Dropout(0.3), # gegen Overfitting im dichten Teil
layers.Dense(128, activation="relu"),
layers.Dense(38, activation="softmax"),
])
modell.compile(
optimizer=keras.optimizers.Adam(1e-3),
loss="sparse_categorical_crossentropy", # int-Labels, keine One-Hots
metrics=["accuracy"],
)
modell.summary() # IMMER ansehen: stimmen Shapes + Parameterzahl?3. Training mit Notbremse
PYTHONcnn_training.py
stop = keras.callbacks.EarlyStopping(
monitor="val_loss",
patience=3, # 3 Epochen ohne Besserung → Schluss
restore_best_weights=True, # zurück zum besten Stand
)
verlauf = modell.fit(
train_ds,
validation_data=val_ds,
epochs=30,
callbacks=[stop],
)
# Lernkurven ansehen — Pflicht, nicht Kür:
# verlauf.history["loss"] vs. verlauf.history["val_loss"]
test_loss, test_acc = modell.evaluate(val_ds)
print(f"Validation Accuracy: {test_acc:.3f}")Schon dieses kleine Netz erreicht auf PlantVillage nach wenigen Epochen typischerweise über 90 % Accuracy — mehr als die getunten klassischen Verfahren auf Histogramm-Features. Die Lernkurven verraten dabei mehr als die Endzahl: Laufen Training- und Validation-Loss auseinander, lernt das Netz auswendig.
Warum eigentlich? — Warum Augmentation als Modell-Schicht?
Random-Flip, -Rotation und -Zoom erzeugen in jeder Epoche leicht andere Versionen jedes Bildes — das Netz kann sich einzelne Pixel nicht merken und muss Muster lernen. Als Schicht im Modell statt als separater Vorverarbeitungsschritt hat das zwei Vorteile: Sie läuft automatisch nur im Training (bei
predict/evaluate deaktiviert sie sich selbst), und sie kann nicht versehentlich auf das Validation-Set angewendet werden — eine ganze Fehlerklasse ist konstruktionsbedingt weg.Häufiger Denkfehler — Rescaling vergessen — oder doppelt
Die zwei häufigsten stillen CNN-Bugs: (1) Pixel gehen als 0–255 ins Netz (Rescaling vergessen) — der Loss startet hoch und das Training kriecht, weil die Aktivierungen sofort sättigen. (2) Doppelt normalisiert — z.B. Rescaling-Layer und später
preprocess_input eines vortrainierten Netzes. Beides läuft ohne Fehlermeldung. Debugging-Reflex: einmal print(batch.numpy().min(), batch.numpy().max()) direkt vor dem Netz — die Wertebereiche müssen zur ersten Schicht passen.Tiefer rein — Epochen, Batches, Adam — das Trainings-Vokabular
Eine Epoche = einmal alle Trainingsbilder gesehen. Pro Batch (32 Bilder) wird ein Gradient berechnet und ein Schritt gemacht — bei 43.000 Bildern also ~1.350 Updates pro Epoche. Das ist Stochastic Gradient Descent: Der Batch-Gradient ist eine verrauschte Schätzung des echten, aber 1.350 verrauschte Schritte schlagen einen exakten. Adam verfeinert das mit Momentum (Schwung über Batches hinweg) und pro Parameter angepassten Lernraten — deshalb funktioniert die Default-Lernrate 0,001 fast immer als Start.