🧠 Reti Neurali con TensorFlow: Riconoscimento Cifre MNIST
Impara Deep Learning costruendo reti neurali che riconoscono cifre scritte a mano
Introduzione
Benvenuto nel mondo affascinante delle reti neurali! In questa esercitazione costruirai la tua prima rete neurale da zero usando TensorFlow, la libreria più potente per il Deep Learning. Imparerai a costruire, addestrare e ottimizzare reti neurali per il riconoscimento di cifre scritte a mano usando il famoso dataset MNIST.
Lavorerai come Deep Learning Engineer per una startup che sviluppa sistemi di riconoscimento ottico per la digitalizzazione di documenti. Costruirai una rete neurale che riconosce automaticamente cifre scritte a mano con accuratezza superiore al 99%.
⚠️ ATTENZIONE: COPIA BLOCCATA
Il codice è protetto da sistema anti-copia. Dovrai SCRIVERE manualmente tutto il codice per imparare veramente! 🚫📋
Parte 1: Setup e Caricamento Dataset MNIST
▶ Importazione TensorFlow e Caricamento Dati
Crea una nuova cella su Google Colab e importa TensorFlow:
# IMPORT TENSORFLOW E LIBRERIE PER DEEP LEARNING
import tensorflow as tf
from tensorflow import keras
from tensorflow.keras import layers, models, callbacks
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
import random
import time
import warnings
warnings.filterwarnings('ignore')
# Configurazione TensorFlow per performance
tf.config.threading.set_inter_op_parallelism_threads(2)
tf.config.threading.set_intra_op_parallelism_threads(2)
# Configurazione visualizzazione
sns.set_style("whitegrid")
plt.rcParams['figure.figsize'] = (14, 8)
plt.rcParams['font.size'] = 12
print("✅ TensorFlow e librerie importate correttamente!")
print(f"📊 Versione TensorFlow: {tf.__version__}")
print(f"📊 Versione Keras: {keras.__version__}")
# Verifica GPU disponibile (su Google Colab)
gpu_devices = tf.config.list_physical_devices('GPU')
if gpu_devices:
print(f"🎮 GPU disponibile: {len(gpu_devices)} dispositivo(i)")
for device in gpu_devices:
print(f" • {device}")
else:
print("⚠️ Nessuna GPU disponibile, utilizzeremo la CPU")
print("\n🧠 Componenti TensorFlow disponibili:")
print(" • layers: Costruzione layer neurali")
print(" • models: Creazione modelli sequenziali/funzionali")
print(" • callbacks: Callback per monitoraggio training")
print(" • optimizers: Ottimizzatori (Adam, SGD, etc.)")
print(" • losses: Funzioni di loss")
print(" • metrics: Metriche di valutazione")
▶ Caricamento e Esplorazione Dataset MNIST
Carica il famoso dataset MNIST di cifre scritte a mano:
# 2. CARICAMENTO DATASET MNIST
print("🔢 CARICAMENTO DATASET MNIST - CIFRE SCRITTE A MANO")
print("="*60)
# Carica dataset MNIST da Keras
(X_train, y_train), (X_test, y_test) = keras.datasets.mnist.load_data()
print(f"✅ Dataset MNIST caricato con successo!")
print(f"\n📊 INFORMAZIONI DATASET:")
# Informazioni dataset
print(f" • X_train shape: {X_train.shape} → {X_train.shape[0]} immagini di {X_train.shape[1]}x{X_train.shape[2]} pixel")
print(f" • y_train shape: {y_train.shape} → {y_train.shape[0]} etichette")
print(f" • X_test shape: {X_test.shape} → {X_test.shape[0]} immagini di test")
print(f" • y_test shape: {y_test.shape} → {y_test.shape[0]} etichette di test")
# Informazioni pixel
print(f"\n🎨 INFORMAZIONI PIXEL:")
print(f" • Tipo dati: {X_train.dtype}")
print(f" • Valore minimo pixel: {X_train.min()}")
print(f" • Valore massimo pixel: {X_train.max()}")
print(f" • Valore medio pixel: {X_train.mean():.2f}")
# Distribuzione classi
print(f"\n🎯 DISTRIBUZIONE CLASSI (CIFRE 0-9):")
unique, counts = np.unique(y_train, return_counts=True)
distribuzione_train = dict(zip(unique, counts))
unique_test, counts_test = np.unique(y_test, return_counts=True)
distribuzione_test = dict(zip(unique_test, counts_test))
print(f"\nSet di Training:")
for cifra in range(10):
count = distribuzione_train[cifra]
percentuale = (count / len(y_train)) * 100
print(f" • Cifra {cifra}: {count:5d} immagini ({percentuale:5.1f}%)")
print(f"\nSet di Test:")
for cifra in range(10):
count = distribuzione_test[cifra]
percentuale = (count / len(y_test)) * 100
print(f" • Cifra {cifra}: {count:5d} immagini ({percentuale:5.1f}%)")
# Visualizzazione esempi dataset
print(f"\n🎨 VISUALIZZAZIONE ESEMPI DATASET...")
fig, axes = plt.subplots(3, 10, figsize=(20, 6))
fig.suptitle('Esempi Dataset MNIST - Cifre da 0 a 9', fontsize=16, fontweight='bold')
for cifra in range(10):
# Trova indici per questa cifra
indices = np.where(y_train == cifra)[0]
# Seleziona 3 immagini casuali per ogni cifra
sample_indices = np.random.choice(indices, 3, replace=False)
for row, idx in enumerate(sample_indices):
ax = axes[row, cifra]
ax.imshow(X_train[idx], cmap='gray', interpolation='nearest')
ax.set_title(f'Cifra: {cifra}', fontsize=10)
ax.axis('off')
# Mostra valore pixel in alto a sinistra
ax.text(0.05, 0.95, f'Pixel max: {X_train[idx].max()}',
transform=ax.transAxes, fontsize=8, verticalalignment='top',
bbox=dict(boxstyle='round', facecolor='white', alpha=0.8))
plt.tight_layout()
plt.show()
print("✅ Dataset esplorato e visualizzato correttamente!")
Parte 2: Preprocessing Dati per Deep Learning
▶ Normalizzazione e Preparazione Dati
Prepara i dati per le reti neurali:
# 3. PREPROCESSING DATI PER RETI NEURALI
print("🔧 PREPROCESSING DATI PER DEEP LEARNING")
print("="*60)
print("Applicando preprocessing in 4 passaggi...")
# 3.1 Normalizzazione pixel (da 0-255 a 0-1)
print("\n1. 🔢 NORMALIZZAZIONE PIXEL (0-255 → 0-1):")
print(f" Prima: pixel range = [{X_train.min()}, {X_train.max()}]")
X_train_normalized = X_train.astype('float32') / 255.0
X_test_normalized = X_test.astype('float32') / 255.0
print(f" Dopo: pixel range = [{X_train_normalized.min():.3f}, {X_train_normalized.max():.3f}]")
# 3.2 Reshape per reti neurali (aggiungi canale colore)
print("\n2. 📐 RESHAPE PER RETI NEURALI:")
print(f" Prima shape: {X_train_normalized.shape}")
# Aggiungi dimensione canale (28, 28) → (28, 28, 1) per CNN
X_train_reshaped = X_train_normalized.reshape(-1, 28, 28, 1)
X_test_reshaped = X_test_normalized.reshape(-1, 28, 28, 1)
print(f" Dopo shape: {X_train_reshaped.shape}")
print(f" • -1: dimensioni automatiche (numero immagini)")
print(f" • 28, 28: altezza, larghezza immagine")
print(f" • 1: canale (grayscale)")
# 3.3 One-hot encoding delle etichette
print("\n3. 🔤 ONE-HOT ENCODING ETICHETTE:")
print(f" Prima: y_train[0] = {y_train[0]} (tipo: {type(y_train[0])})")
# Converti etichette in one-hot encoding
y_train_onehot = keras.utils.to_categorical(y_train, 10)
y_test_onehot = keras.utils.to_categorical(y_test, 10)
print(f" Dopo: y_train_onehot[0] = {y_train_onehot[0]}")
print(f" • Lunghezza vettore: {len(y_train_onehot[0])}")
print(f" • Posizione 1: index {np.argmax(y_train_onehot[0])}")
# 3.4 Creazione validation set
print("\n4. 🎯 CREAZIONE VALIDATION SET:")
print(f" Training set originale: {X_train_reshaped.shape[0]} immagini")
# Usiamo 10% del training come validation
validation_split = 0.1
val_size = int(X_train_reshaped.shape[0] * validation_split)
# Mescola indici
indices = np.arange(X_train_reshaped.shape[0])
np.random.shuffle(indices)
# Split
X_val = X_train_reshaped[indices[:val_size]]
y_val = y_train_onehot[indices[:val_size]]
X_train_final = X_train_reshaped[indices[val_size:]]
y_train_final = y_train_onehot[indices[val_size:]]
print(f" • Training set finale: {X_train_final.shape[0]} immagini")
print(f" • Validation set: {X_val.shape[0]} immagini")
print(f" • Test set: {X_test_reshaped.shape[0]} immagini")
# 3.5 Verifica preprocessing
print(f"\n✅ PREPROCESSING COMPLETATO!")
print(f"\n📊 RIEPILOGO DATASET FINALE:")
datasets = {
'Training': (X_train_final, y_train_final),
'Validation': (X_val, y_val),
'Test': (X_test_reshaped, y_test_onehot)
}
for name, (X, y) in datasets.items():
print(f"\n{name} Set:")
print(f" • Immagini: {X.shape[0]}")
print(f" • Dimensione immagine: {X.shape[1:]} (altezza, larghezza, canali)")
print(f" • Etichette: {y.shape}")
print(f" • Range pixel: [{X.min():.3f}, {X.max():.3f}]")
# Distribuzione classi
if len(y.shape) > 1: # One-hot encoded
y_labels = np.argmax(y, axis=1)
else:
y_labels = y
unique, counts = np.unique(y_labels, return_counts=True)
print(f" • Distribuzione classi: {dict(zip(unique, counts))}")
# 3.6 Visualizzazione dati preprocessati
print(f"\n🎨 VISUALIZZAZIONE DATI PREPROCESSATI...")
fig, axes = plt.subplots(2, 5, figsize=(15, 6))
for i in range(5):
# Immagine originale
axes[0, i].imshow(X_train[i], cmap='gray')
axes[0, i].set_title(f'Originale\nCifra: {y_train[i]}', fontsize=11)
axes[0, i].axis('off')
# Immagine preprocessata
img_preprocessed = X_train_final[i].reshape(28, 28)
axes[1, i].imshow(img_preprocessed, cmap='gray')
axes[1, i].set_title(f'Preprocessata\nPixel: [{img_preprocessed.min():.2f}, {img_preprocessed.max():.2f}]', fontsize=11)
axes[1, i].axis('off')
plt.suptitle('Confronto Immagini Originali vs Preprocessate', fontsize=14, y=1.02)
plt.tight_layout()
plt.show()
print("✅ Dati pronti per le reti neurali!")
Parte 3: Costruzione Rete Neurale Fully Connected
▶ Creazione Rete Neurale Dense (MLP)
Crea la tua prima rete neurale fully connected:
# 4. COSTRUZIONE RETE NEURALE FULLY CONNECTED
print("🔌 COSTRUZIONE RETE NEURALE FULLY CONNECTED (MLP)")
print("="*60)
print("🧱 Costruzione architettura rete neurale...")
# 4.1 Definisci architettura rete neurale
model_mlp = keras.Sequential([
# Flatten layer: trasforma immagine 28x28 in vettore di 784 pixel
layers.Flatten(input_shape=(28, 28, 1), name='flatten_input'),
# Primo hidden layer: 128 neuroni con attivazione ReLU
layers.Dense(128, activation='relu', name='hidden_layer_1'),
# Dropout per prevenire overfitting (20% neuroni disattivati casualmente)
layers.Dropout(0.2, name='dropout_1'),
# Secondo hidden layer: 64 neuroni
layers.Dense(64, activation='relu', name='hidden_layer_2'),
# Dropout
layers.Dropout(0.2, name='dropout_2'),
# Terzo hidden layer: 32 neuroni
layers.Dense(32, activation='relu', name='hidden_layer_3'),
# Output layer: 10 neuroni (uno per ogni cifra) con attivazione softmax
layers.Dense(10, activation='softmax', name='output_layer')
])
print(f"✅ Architettura rete neurale creata!")
# 4.2 Mostra architettura modello
print(f"\n📐 ARCHITETTURA MODELLO MLP:")
model_mlp.summary()
# 4.3 Calcolo parametri
print(f"\n🧮 CALCOLO PARAMETRI MODELLO:")
total_params = model_mlp.count_params()
print(f" • Parametri totali: {total_params:,}")
print(f" • Parametri trainable: {total_params:,}") # Tutti i parametri sono trainable
print(f" • Parametri non-trainable: 0")
# Spiegazione calcolo parametri
print(f"\n🔍 SPIEGAZIONE CALCOLO PARAMETRI:")
print("Layer Flatten → Dense(128):")
print(f" • Input: 28x28x1 = 784 pixel")
print(f" • Output: 128 neuroni")
print(f" • Parametri: (784 * 128) + 128 bias = {784*128 + 128:,}")
print("\nLayer Dense(128) → Dense(64):")
print(f" • Parametri: (128 * 64) + 64 bias = {128*64 + 64:,}")
print("\nLayer Dense(64) → Dense(32):")
print(f" • Parametri: (64 * 32) + 32 bias = {64*32 + 32:,}")
print("\nLayer Dense(32) → Dense(10):")
print(f" • Parametri: (32 * 10) + 10 bias = {32*10 + 10:,}")
# 4.4 Compilazione modello
print(f"\n⚙️ COMPILAZIONE MODELLO...")
model_mlp.compile(
optimizer='adam', # Ottimizzatore Adam (adaptive learning rate)
loss='categorical_crossentropy', # Loss per classificazione multi-classe
metrics=['accuracy'] # Metriche da monitorare
)
print(f"✅ Modello compilato con successo!")
print(f" • Optimizer: Adam (learning rate adattivo)")
print(f" • Loss function: Categorical Crossentropy")
print(f" • Metrics: Accuracy")
# 4.5 Visualizzazione architettura
print(f"\n🎨 VISUALIZZAZIONE ARCHITETTURA RETE...")
try:
# Prova a visualizzare con keras.utils.plot_model
keras.utils.plot_model(
model_mlp,
to_file='model_mlp.png',
show_shapes=True,
show_layer_names=True,
rankdir='TB', # Top to Bottom
expand_nested=False,
dpi=96
)
# Mostra immagine
from IPython.display import Image, display
display(Image(filename='model_mlp.png', width=600))
print("✅ Visualizzazione architettura generata!")
except Exception as e:
print(f"⚠️ Impossibile generare visualizzazione: {e}")
print("⚠️ Su Google Colab potrebbe servire graphviz installato")
# 4.6 Test forward pass (propagazione in avanti)
print(f"\n🔬 TEST FORWARD PASS (propagazione in avanti):")
# Seleziona una singola immagine
sample_image = X_train_final[0:1] # Batch di 1 immagine
print(f" • Input shape: {sample_image.shape}")
print(f" • Input valori: [{sample_image.min():.3f}, {sample_image.max():.3f}]")
# Esegui forward pass
with tf.device('/CPU:0'): # Forza CPU per test
output = model_mlp.predict(sample_image, verbose=0)
print(f" • Output shape: {output.shape}")
print(f" • Output valori (probabilità):")
for i, prob in enumerate(output[0]):
print(f" Cifra {i}: {prob:.4f}")
predicted_class = np.argmax(output[0])
true_class = np.argmax(y_train_final[0])
print(f" • Classe predetta: {predicted_class}")
print(f" • Classe vera: {true_class}")
print(f" • Corretta: {'✅' if predicted_class == true_class else '❌'}")
print("\n✅ Rete neurale MLP costruita con successo!")
Parte 4: Addestramento e Monitoraggio
▶ Training della Rete con Callbacks
Addestra la rete neurale con tecniche avanzate:
# 5. ADDESTRAMENTO RETE NEURALE
print("⚡ ADDESTRAMENTO RETE NEURALE MLP")
print("="*60)
# 5.1 Definizione callbacks per training avanzato
print("\n🎯 SETUP CALLBACKS PER TRAINING AVANZATO:")
callbacks_list = [
# Early Stopping: ferma training se validation loss non migliora
callbacks.EarlyStopping(
monitor='val_loss',
patience=5, # Aspetta 5 epoch senza miglioramenti
restore_best_weights=True, # Ripristina pesi migliori
verbose=1
),
# ReduceLROnPlateau: riduce learning rate se stagnazione
callbacks.ReduceLROnPlateau(
monitor='val_loss',
factor=0.5, # Riduci LR del 50%
patience=3, # Aspetta 3 epoch
min_lr=1e-6, # LR minimo
verbose=1
),
# Model Checkpoint: salva miglior modello
callbacks.ModelCheckpoint(
filepath='best_mlp_model.keras',
monitor='val_accuracy',
save_best_only=True,
save_weights_only=False,
mode='max',
verbose=1
),
# TensorBoard: logging per visualizzazione (opzionale)
# callbacks.TensorBoard(log_dir='./logs_mlp', histogram_freq=1)
]
print("✅ Callbacks configurati:")
print(" 1. EarlyStopping: previene overfitting")
print(" 2. ReduceLROnPlateau: adatta learning rate")
print(" 3. ModelCheckpoint: salva miglior modello")
print(" 4. TensorBoard: logging avanzato (opzionale)")
# 5.2 Training della rete
print(f"\n🚀 INIZIO ADDESTRAMENTO...")
print(f" • Batch size: 64")
print(f" • Epochs: 30")
print(f" • Training samples: {X_train_final.shape[0]}")
print(f" • Validation samples: {X_val.shape[0]}")
start_time = time.time()
history_mlp = model_mlp.fit(
X_train_final,
y_train_final,
batch_size=64,
epochs=30,
validation_data=(X_val, y_val),
callbacks=callbacks_list,
verbose=1
)
training_time = time.time() - start_time
print(f"\n✅ Addestramento completato in {training_time:.1f} secondi!")
print(f" • Epochs eseguite: {len(history_mlp.history['loss'])}")
print(f" • Tempo per epoch: {training_time/len(history_mlp.history['loss']):.2f} secondi")
# 5.3 Carica miglior modello salvato
print(f"\n📂 CARICAMENTO MIGLIOR MODELLO SALVATO...")
try:
best_model_mlp = keras.models.load_model('best_mlp_model.keras')
print(f"✅ Miglior modello caricato da 'best_mlp_model.keras'")
print(f" • Validation accuracy miglior modello: {max(history_mlp.history['val_accuracy']):.4f}")
except:
best_model_mlp = model_mlp
print(f"⚠️ Impossibile caricare modello salvato, uso modello corrente")
# 5.4 Visualizzazione andamento training
print(f"\n📈 VISUALIZZAZIONE ANDAMENTO TRAINING...")
fig, axes = plt.subplots(1, 2, figsize=(15, 5))
# Plot accuracy
axes[0].plot(history_mlp.history['accuracy'], label='Training Accuracy', color='blue', linewidth=2)
axes[0].plot(history_mlp.history['val_accuracy'], label='Validation Accuracy', color='orange', linewidth=2)
axes[0].set_title('Accuracy durante Training', fontsize=14)
axes[0].set_xlabel('Epoch')
axes[0].set_ylabel('Accuracy')
axes[0].legend()
axes[0].grid(True, alpha=0.3)
# Aggiungi miglior accuracy
best_epoch = np.argmax(history_mlp.history['val_accuracy'])
best_acc = history_mlp.history['val_accuracy'][best_epoch]
axes[0].axvline(x=best_epoch, color='red', linestyle='--', alpha=0.7,
label=f'Best: {best_acc:.3f} (epoch {best_epoch})')
axes[0].legend()
# Plot loss
axes[1].plot(history_mlp.history['loss'], label='Training Loss', color='blue', linewidth=2)
axes[1].plot(history_mlp.history['val_loss'], label='Validation Loss', color='orange', linewidth=2)
axes[1].set_title('Loss durante Training', fontsize=14)
axes[1].set_xlabel('Epoch')
axes[1].set_ylabel('Loss')
axes[1].legend()
axes[1].grid(True, alpha=0.3)
# Aggiungi miglior loss
best_epoch_loss = np.argmin(history_mlp.history['val_loss'])
best_loss = history_mlp.history['val_loss'][best_epoch_loss]
axes[1].axvline(x=best_epoch_loss, color='red', linestyle='--', alpha=0.7,
label=f'Best: {best_loss:.3f} (epoch {best_epoch_loss})')
axes[1].legend()
plt.tight_layout()
plt.show()
# 5.5 Valutazione finale su test set
print(f"\n🧪 VALUTAZIONE FINALE SU TEST SET...")
test_loss, test_accuracy = best_model_mlp.evaluate(
X_test_reshaped,
y_test_onehot,
verbose=0
)
print(f"✅ Valutazione completata!")
print(f"\n📊 PERFORMANCE FINALE MLP:")
print(f" • Test Loss: {test_loss:.4f}")
print(f" • Test Accuracy: {test_accuracy:.4f} ({test_accuracy*100:.2f}%)")
# Confronto con accuracy umana
print(f"\n👤 CONFRONTO CON PERFORMANCE UMANA:")
print(f" • Accuracy rete neurale: {test_accuracy*100:.1f}%")
print(f" • Accuracy umana tipica su MNIST: ~98-99%")
print(f" • Differenza: {abs(test_accuracy*100 - 98.5):.1f}% punti")
if test_accuracy > 0.98:
print(f" 🎉 La rete neurale supera/si avvicina alla performance umana!")
else:
print(f" 🔧 Possibile spazio per miglioramenti...")
print("\n✅ Addestramento rete neurale completato!")
Parte 5: Rete Neurale Convoluzionale Avanzata
▶ Costruzione e Addestramento CNN
Crea una rete neurale convoluzionale (CNN) più potente:
# 6. RETE NEURALE CONVOLUZIONALE (CNN) AVANZATA
print("🎨 COSTRUZIONE RETE NEURALE CONVOLUZIONALE (CNN)")
print("="*60)
print("Le CNN sono specializzate per il riconoscimento immagini!")
print("Utilizzano operazioni di convoluzione per estrarre features spaziali.")
# 6.1 Costruzione architettura CNN
print("\n🧱 COSTRUZIONE ARCHITETTURA CNN...")
model_cnn = keras.Sequential([
# Primo blocco convoluzionale
layers.Conv2D(32, (3, 3), activation='relu', input_shape=(28, 28, 1), padding='same', name='conv1'),
layers.BatchNormalization(name='bn1'),
layers.Conv2D(32, (3, 3), activation='relu', padding='same', name='conv2'),
layers.MaxPooling2D((2, 2), name='pool1'),
layers.Dropout(0.25, name='dropout1'),
# Secondo blocco convoluzionale
layers.Conv2D(64, (3, 3), activation='relu', padding='same', name='conv3'),
layers.BatchNormalization(name='bn2'),
layers.Conv2D(64, (3, 3), activation='relu', padding='same', name='conv4'),
layers.MaxPooling2D((2, 2), name='pool2'),
layers.Dropout(0.25, name='dropout2'),
# Terzo blocco convoluzionale
layers.Conv2D(128, (3, 3), activation='relu', padding='same', name='conv5'),
layers.BatchNormalization(name='bn3'),
layers.Dropout(0.25, name='dropout3'),
# Flatten per passare a fully connected
layers.Flatten(name='flatten_cnn'),
# Fully connected layers
layers.Dense(128, activation='relu', name='fc1'),
layers.BatchNormalization(name='bn4'),
layers.Dropout(0.5, name='dropout4'),
layers.Dense(64, activation='relu', name='fc2'),
layers.Dropout(0.5, name='dropout5'),
# Output layer
layers.Dense(10, activation='softmax', name='output_cnn')
])
print(f"✅ Architettura CNN creata!")
# 6.2 Mostra architettura CNN
print(f"\n📐 ARCHITETTURA MODELLO CNN:")
model_cnn.summary()
# 6.3 Compilazione CNN
print(f"\n⚙️ COMPILAZIONE CNN...")
# Ottimizzatore personalizzato
optimizer = keras.optimizers.Adam(learning_rate=0.001)
model_cnn.compile(
optimizer=optimizer,
loss='categorical_crossentropy',
metrics=['accuracy']
)
print(f"✅ CNN compilata con ottimizzatore Adam (lr=0.001)")
# 6.4 Callbacks per CNN
print(f"\n🎯 SETUP CALLBACKS AVANZATI PER CNN...")
cnn_callbacks = [
callbacks.EarlyStopping(
monitor='val_accuracy',
patience=10,
restore_best_weights=True,
verbose=1
),
callbacks.ReduceLROnPlateau(
monitor='val_loss',
factor=0.5,
patience=5,
min_lr=1e-7,
verbose=1
),
callbacks.ModelCheckpoint(
filepath='best_cnn_model.keras',
monitor='val_accuracy',
save_best_only=True,
mode='max',
verbose=1
)
]
# 6.5 Addestramento CNN
print(f"\n🚀 ADDESTRAMENTO CNN (potrebbe richiedere qualche minuto)...")
start_time_cnn = time.time()
history_cnn = model_cnn.fit(
X_train_final,
y_train_final,
batch_size=128, # Batch più grande per CNN
epochs=50,
validation_data=(X_val, y_val),
callbacks=cnn_callbacks,
verbose=1
)
cnn_training_time = time.time() - start_time_cnn
print(f"\n✅ Addestramento CNN completato in {cnn_training_time:.1f} secondi!")
# 6.6 Carica miglior CNN
print(f"\n📂 CARICAMENTO MIGLIOR CNN...")
try:
best_model_cnn = keras.models.load_model('best_cnn_model.keras')
print(f"✅ Miglior CNN caricata!")
except:
best_model_cnn = model_cnn
print(f"⚠️ Uso CNN corrente")
# 6.7 Valutazione CNN su test set
print(f"\n🧪 VALUTAZIONE CNN SU TEST SET...")
cnn_test_loss, cnn_test_accuracy = best_model_cnn.evaluate(
X_test_reshaped,
y_test_onehot,
verbose=0
)
print(f"✅ Valutazione CNN completata!")
print(f"\n📊 PERFORMANCE FINALE CNN:")
print(f" • Test Loss: {cnn_test_loss:.4f}")
print(f" • Test Accuracy: {cnn_test_accuracy:.4f} ({cnn_test_accuracy*100:.2f}%)")
# 6.8 Confronto MLP vs CNN
print(f"\n⚖️ CONFRONTO MLP vs CNN:")
comparison_data = {
'Modello': ['MLP (Fully Connected)', 'CNN (Convolutional)'],
'Test Accuracy': [test_accuracy, cnn_test_accuracy],
'Test Accuracy %': [f"{test_accuracy*100:.2f}%", f"{cnn_test_accuracy*100:.2f}%"],
'Training Time (s)': [f"{training_time:.1f}", f"{cnn_training_time:.1f}"],
'Parametri': [f"{model_mlp.count_params():,}", f"{model_cnn.count_params():,}"],
'Architettura': ['3 Dense Layers', '3 Conv Blocks + 2 Dense Layers']
}
comparison_df = pd.DataFrame(comparison_data)
print("\n" + comparison_df.to_string(index=False))
# 6.9 Visualizzazione risultati CNN
print(f"\n🎨 VISUALIZZAZIONE RISULTATI CNN...")
fig, axes = plt.subplots(2, 3, figsize=(15, 10))
# 1. Confronto accuracy MLP vs CNN
epochs_mlp = range(1, len(history_mlp.history['val_accuracy']) + 1)
epochs_cnn = range(1, len(history_cnn.history['val_accuracy']) + 1)
axes[0, 0].plot(epochs_mlp, history_mlp.history['val_accuracy'], 'b-', label='MLP', linewidth=2)
axes[0, 0].plot(epochs_cnn, history_cnn.history['val_accuracy'], 'r-', label='CNN', linewidth=2)
axes[0, 0].set_title('Confronto Validation Accuracy: MLP vs CNN')
axes[0, 0].set_xlabel('Epoch')
axes[0, 0].set_ylabel('Accuracy')
axes[0, 0].legend()
axes[0, 0].grid(True, alpha=0.3)
# 2. Matrice confusione CNN
y_pred_cnn = best_model_cnn.predict(X_test_reshaped, verbose=0)
y_pred_classes = np.argmax(y_pred_cnn, axis=1)
y_true_classes = np.argmax(y_test_onehot, axis=1)
conf_matrix = tf.math.confusion_matrix(y_true_classes, y_pred_classes)
sns.heatmap(conf_matrix.numpy(), annot=True, fmt='d', cmap='Blues', ax=axes[0, 1])
axes[0, 1].set_title('Matrice Confusione CNN')
axes[0, 1].set_xlabel('Predetto')
axes[0, 1].set_ylabel('Vero')
# 3. Esempi predizioni corrette/errate
correct_indices = np.where(y_pred_classes == y_true_classes)[0]
wrong_indices = np.where(y_pred_classes != y_true_classes)[0]
# Seleziona esempi
correct_samples = np.random.choice(correct_indices, 3, replace=False)
wrong_samples = np.random.choice(wrong_indices, min(3, len(wrong_indices)), replace=False)
# Plot esempi corretti
for i, idx in enumerate(correct_samples[:3]):
ax = axes[1, i]
ax.imshow(X_test[idx], cmap='gray')
ax.set_title(f'Corretto\nVero: {y_true_classes[idx]}, Pred: {y_pred_classes[idx]}', fontsize=11)
ax.axis('off')
# Plot esempi errati
for i, idx in enumerate(wrong_samples[:3]):
ax = axes[1, i+3] if i+3 < 3 else axes[1, 2]
ax.imshow(X_test[idx], cmap='gray')
ax.set_title(f'Errato\nVero: {y_true_classes[idx]}, Pred: {y_pred_classes[idx]}', fontsize=11)
ax.axis('off')
plt.tight_layout()
plt.show()
# 6.10 Test su immagini personalizzate (simulate)
print(f"\n🔮 TEST SU IMMAGINI PERSONALIZZATE (SIMULATE)...")
# Crea alcune "immagini personalizzate" (in realtà dal test set ma presentate come nuove)
custom_test_indices = np.random.choice(len(X_test), 5, replace=False)
print("\n{:^5} | {:^25} | {:^15} | {:^10}".format(
"Img", "Descrizione", "Predizione CNN", "Corretto?"))
print("-" * 60)
for i, idx in enumerate(custom_test_indices):
# Prepara immagine
img = X_test_reshaped[idx:idx+1]
true_label = y_true_classes[idx]
# Predizione CNN
pred_proba = best_model_cnn.predict(img, verbose=0)[0]
pred_label = np.argmax(pred_proba)
confidence = pred_proba[pred_label] * 100
# Descrizione
if true_label in [0, 6, 8, 9]:
descrizione = "Cifra con loop/chiusure"
elif true_label in [1, 7]:
descrizione = "Cifra con tratti retti"
else:
descrizione = "Cifra con curve"
correct = "✅" if pred_label == true_label else "❌"
print("{:^5} | {:^25} | {:^15} | {:^10}".format(
f"#{i+1}", descrizione, f"{pred_label} ({confidence:.0f}%)", correct))
# 6.11 Salvataggio modelli
print(f"\n💾 SALVATAGGIO MODELLI PER USO FUTURO...")
# Salva CNN (il modello migliore)
best_model_cnn.save('mnist_cnn_final.keras')
print(f"✅ CNN salvata come 'mnist_cnn_final.keras'")
# Salva anche MLP per confronto
best_model_mlp.save('mnist_mlp_final.keras')
print(f"✅ MLP salvata come 'mnist_mlp_final.keras'")
print(f"\n🎉 ESERCITAZIONE COMPLETATA CON SUCCESSO!")
print("="*60)
print(f"🎯 RISULTATI FINALI:")
print(f" • MLP Accuracy: {test_accuracy*100:.2f}%")
print(f" • CNN Accuracy: {cnn_test_accuracy*100:.2f}%")
print(f" • Miglioramento CNN vs MLP: {(cnn_test_accuracy - test_accuracy)*100:.2f}% punti")
print(f"\n💡 COSA HAI IMPARATO:")
print(f" 1. Le CNN sono superiori per riconoscimento immagini")
print(f" 2. La convoluzione estrae features spaziali")
print(f" 3. BatchNormalization stabilizza il training")
print(f" 4. Dropout previene l'overfitting")
print(f" 5. EarlyStopping ottimizza le epoche di training")
print(f"\n🚀 Prossimi passi: Prova a superare il 99.5% di accuracy con:")
print(f" • Data augmentation")
print(f" • Architetture più complesse (ResNet)")
print(f" • Ensemble di più modelli")
print(f" • Transfer learning")
Concetti di Deep Learning Appresi
Congratulazioni! Ora conosci:
- ✓ TensorFlow & Keras: Framework più usato per Deep Learning
- ✓ Dataset MNIST: Benchmark per riconoscimento cifre
- ✓ Preprocessing Immagini: Normalizzazione, reshape, one-hot encoding
- ✓ Reti Fully Connected (MLP): Layer Dense, attivazioni ReLU/Softmax
- ✓ Reti Convoluzionali (CNN): Conv2D, Pooling, BatchNorm
- ✓ Ottimizzatori: Adam con learning rate adattivo
- ✓ Funzioni di Loss: Categorical Crossentropy per classificazione
- ✓ Callbacks Avanzati: EarlyStopping, ReduceLROnPlateau, ModelCheckpoint
- ✓ Regularizzazione: Dropout per prevenire overfitting
- ✓ Batch Normalization: Stabilizzazione e accelerazione training
- ✓ Valutazione Modelli: Accuracy, Confusion Matrix, confronto MLP vs CNN
Prossimi Passi nel Deep Learning
Nella prossima esercitazione imparerai:
- 📸 CNN avanzate per riconoscimento oggetti
- 🗣️ Reti neurali ricorrenti (RNN/LSTM) per testo
- 🎨 Generative Adversarial Networks (GAN)
- 🚀 Transfer learning con modelli pre-addestrati
- 🧠 Reti neurali con attention e transformer
Hai costruito reti neurali che riconoscono cifre con accuratezza superiore al 99%!