Keras & TensorFlow
Keras 3 fonctionne au-dessus de TensorFlow, JAX ou PyTorch. Le backend se choisit avant l'import.
import os
os.environ["KERAS_BACKEND"] = "tensorflow" # ou "jax", "torch"
import keras
Construire un modèle
from keras import layers
model = keras.Sequential([
keras.Input(shape=(128,)),
layers.Dense(256, activation="relu"),
layers.Dropout(0.2),
layers.Dense(10, activation="softmax"),
])
model.summary()
Model: "sequential"
┌─────────────────────────────────┬────────────────────────┬───────────────┐
│ Layer (type) │ Output Shape │ Param # │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ dense (Dense) │ (None, 256) │ 33,024 │
│ dropout (Dropout) │ (None, 256) │ 0 │
│ dense_1 (Dense) │ (None, 10) │ 2,570 │
└─────────────────────────────────┴────────────────────────┴───────────────┘
Total params: 35,594 (139.04 KB)
summary() est le premier outil de debug : une forme de sortie inattendue s'y voit
immédiatement.
API fonctionnelle, dès qu'il y a plusieurs entrées ou une branche :
inp = keras.Input(shape=(128,))
x = layers.Dense(256, activation="relu")(inp)
x = layers.Dropout(0.2)(x)
out = layers.Dense(10, activation="softmax")(x)
model = keras.Model(inp, out)
Compiler et entraîner
model.compile(
optimizer=keras.optimizers.AdamW(learning_rate=3e-4),
loss="sparse_categorical_crossentropy",
metrics=["accuracy"],
)
history = model.fit(
X_train, y_train,
validation_data=(X_val, y_val),
epochs=20,
batch_size=32,
callbacks=[
keras.callbacks.EarlyStopping(patience=3, restore_best_weights=True),
keras.callbacks.ReduceLROnPlateau(factor=0.5, patience=2),
keras.callbacks.ModelCheckpoint("best.keras", save_best_only=True),
],
)
Epoch 1/20
188/188 ━━━━━━━━━━━━━━━━━━━━ 2s 7ms/step - accuracy: 0.4123 - loss: 1.8342 - val_accuracy: 0.6210
Epoch 2/20
188/188 ━━━━━━━━━━━━━━━━━━━━ 1s 6ms/step - accuracy: 0.6890 - loss: 1.0021 - val_accuracy: 0.7455
Choisir la bonne perte
| Cible | Perte | Dernière couche |
|---|---|---|
Entiers de classe (0, 1, 2) |
sparse_categorical_crossentropy |
Dense(n, softmax) |
| One-hot | categorical_crossentropy |
Dense(n, softmax) |
| Binaire | binary_crossentropy |
Dense(1, sigmoid) |
| Régression | mse / mae |
Dense(1) sans activation |
Confondre sparse_ et la version one-hot est l'erreur la plus fréquente : elle produit un
message sur les formes plutôt qu'un mauvais score, donc elle se repère vite.
Évaluer et prédire
loss, acc = model.evaluate(X_test, y_test, verbose=0)
proba = model.predict(X_test)
classes = proba.argmax(axis=1)
Courbes d'apprentissage
import matplotlib.pyplot as plt
plt.plot(history.history["loss"], label="train")
plt.plot(history.history["val_loss"], label="val")
plt.legend(); plt.xlabel("epoch"); plt.ylabel("loss")
La val_loss qui remonte pendant que la loss descend est la signature du surapprentissage.
EarlyStopping(restore_best_weights=True) récupère automatiquement le meilleur état.
Sauvegarder
model.save("model.keras") # format natif, tout inclus
model = keras.models.load_model("model.keras")
model.save_weights("poids.weights.h5") # poids seuls
model.load_weights("poids.weights.h5")
Pipeline de données
import tensorflow as tf
ds = (
tf.data.Dataset.from_tensor_slices((X, y))
.shuffle(10_000)
.batch(32)
.prefetch(tf.data.AUTOTUNE)
)
model.fit(ds, epochs=10)
prefetch(AUTOTUNE) recouvre la préparation des données et le calcul — souvent le gain le
plus simple quand le GPU attend.
Transfer learning
base = keras.applications.EfficientNetB0(include_top=False, weights="imagenet", pooling="avg")
base.trainable = False # gel
model = keras.Sequential([base, layers.Dense(5, activation="softmax")])
model.compile(optimizer=keras.optimizers.Adam(1e-3), loss="sparse_categorical_crossentropy")
model.fit(train_ds, epochs=5)
base.trainable = True # dégel pour le fine-tuning
model.compile(optimizer=keras.optimizers.Adam(1e-5), loss="sparse_categorical_crossentropy")
model.fit(train_ds, epochs=5)
Le second compile avec un learning rate cent fois plus petit est obligatoire : dégeler
sans le baisser détruit les poids pré-entraînés dès le premier batch.
GPU
print(tf.config.list_physical_devices("GPU"))
[PhysicalDevice(name='/physical_device:GPU:0', device_type='GPU')]
Liste vide alors qu'un GPU existe : c'est presque toujours une incompatibilité entre les versions de TensorFlow, CUDA et cuDNN.