Vai al contenuto

Classificazione con il dataset Iris

Impara algoritmi di classificazione ML con il dataset più famoso.

Introduzione

Benvenuto nel mondo della classificazione con Machine Learning! Useremo il famoso dataset Iris per imparare a classificare specie di fiori basandoci sulle dimensioni di petali e sepali. Questo è il dataset "Hello World" del ML ed è perfetto per imparare gli algoritmi di classificazione.

Scenario Reale: Lavorerai come botanico digitale che deve creare un sistema per identificare automaticamente specie di fiori Iris. Useremo SVM, KNN e altri algoritmi per costruire classificatori accurati.

ATTENZIONE: COPIA BLOCCATA

Il codice è protetto da sistema anti-copia. Dovrai SCRIVERE manualmente tutto il codice per imparare veramente! 🚫📋

Parte 1: Caricamento e Esplorazione Dataset

Importazione e Caricamento Iris Dataset

1 Crea una nuova cella su Google Colab e carica il dataset:

PYTHON

# IMPORT LIBRERIE PER CLASSIFICAZIONE ML
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
from sklearn import datasets
from sklearn.model_selection import train_test_split, cross_val_score
from sklearn.preprocessing import StandardScaler
from sklearn.metrics import accuracy_score, classification_report, confusion_matrix

# Algoritmi di classificazione che useremo
from sklearn.neighbors import KNeighborsClassifier
from sklearn.svm import SVC
from sklearn.tree import DecisionTreeClassifier
from sklearn.ensemble import RandomForestClassifier
from sklearn.naive_bayes import GaussianNB

# Configurazione visualizzazione
sns.set_style("whitegrid")
plt.rcParams['figure.figsize'] = (12, 8)
plt.rcParams['font.size'] = 12

print("✅ Librerie ML importate correttamente!")
print("📊 Algoritmi disponibili: KNN, SVM, Decision Tree, Random Forest, Naive Bayes")

Caricamento e Analisi Dataset Iris

2 Carica e esplora il dataset Iris:

PYTHON

# 2. CARICAMENTO DATASET IRIS
print("🌺 CARICAMENTO DATASET IRIS")
print("="*50)

# Carica dataset Iris da Scikit-Learn
iris = datasets.load_iris()
X = iris.data  # Features: [sepal_length, sepal_width, petal_length, petal_width]
y = iris.target  # Target: 0=setosa, 1=versicolor, 2=virginica
feature_names = iris.feature_names
target_names = iris.target_names

# Converti in DataFrame per analisi più facile
df_iris = pd.DataFrame(X, columns=feature_names)
df_iris['species'] = y
df_iris['species_name'] = df_iris['species'].map({i: name for i, name in enumerate(target_names)})

print(f"📊 INFORMAZIONI DATASET:")
print(f"  • Numero campioni: {X.shape[0]}")
print(f"  • Numero features: {X.shape[1]}")
print(f"  • Features: {', '.join(feature_names)}")
print(f"  • Classi target: {', '.join(target_names)}")
print(f"  • Valori target: 0={target_names[0]}, 1={target_names[1]}, 2={target_names[2]}")

print(f"\n📋 PRIME 10 RIGHE DEL DATASET:")
print(df_iris.head(10))

print(f"\n📈 STATISTICHE DESCRITTIVE:")
print(df_iris.describe())

print(f"\n🎯 DISTRIBUZIONE CLASSI:")
distribuzione = df_iris['species_name'].value_counts()
for specie, count in distribuzione.items():
    percentuale = (count / len(df_iris)) * 100
    print(f"  • {specie}: {count} campioni ({percentuale:.1f}%)")

print(f"\n🔍 VALORI MANCANTI:")
print(df_iris.isnull().sum())

Parte 2: Visualizzazione e Analisi Dati

Visualizzazioni per Capire i Dati

3 Visualizza le relazioni tra features e classi:

PYTHON

# 3. VISUALIZZAZIONE DATASET IRIS
print("🎨 VISUALIZZAZIONE COMPLETA DATASET IRIS")
print("="*50)

# Configura colori per le specie
colors = {'setosa': '#FF6B6B', 'versicolor': '#4ECDC4', 'virginica': '#45B7D1'}
markers = {'setosa': 'o', 'versicolor': 's', 'virginica': '^'}

print("Creazione visualizzazioni...")

# Creazione figura con multiple visualizzazioni
fig = plt.figure(figsize=(18, 12))
fig.suptitle('Analisi Visuale Dataset Iris', fontsize=20, fontweight='bold')

# 3.1 Pairplot (Scatter Matrix)
print("\n1. Pairplot - Relazioni tra tutte le features...")
import warnings
warnings.filterwarnings('ignore')

# Pairplot semplificato
axes = []
for i in range(4):
    for j in range(4):
        ax = plt.subplot(4, 4, i*4 + j + 1)
        axes.append(ax)

        if i == j:
            # Diagonali: istogrammi
            for idx, specie in enumerate(target_names):
                data = df_iris[df_iris['species_name'] == specie][feature_names[i]]
                ax.hist(data, alpha=0.5, label=specie, color=colors[specie])
            ax.set_xlabel(feature_names[i])
            if i == 0:
                ax.legend(loc='upper right', fontsize=8)
        else:
            # Off-diagonal: scatter plots
            for idx, specie in enumerate(target_names):
                x_data = df_iris[df_iris['species_name'] == specie][feature_names[j]]
                y_data = df_iris[df_iris['species_name'] == specie][feature_names[i]]
                ax.scatter(x_data, y_data, alpha=0.6, label=specie,
                          color=colors[specie], marker=markers[specie], s=30)
            ax.set_xlabel(feature_names[j])
            ax.set_ylabel(feature_names[i])
            if i == 0 and j == 3:
                ax.legend(loc='upper right', fontsize=8)

plt.tight_layout()
plt.show()

# 3.2 Boxplot per feature
print("\n2. Boxplot - Distribuzione per specie...")
fig, axes = plt.subplots(2, 2, figsize=(14, 10))
fig.suptitle('Distribuzione Features per Specie di Iris', fontsize=16)

for idx, feature in enumerate(feature_names):
    ax = axes[idx // 2, idx % 2]
    box_data = [df_iris[df_iris['species_name'] == specie][feature] for specie in target_names]
    bp = ax.boxplot(box_data, labels=target_names, patch_artist=True)

    # Colori per i box
    for patch, color in zip(bp['boxes'], [colors[s] for s in target_names]):
        patch.set_facecolor(color)
        patch.set_alpha(0.6)

    ax.set_title(f'Distribuzione {feature}')
    ax.set_ylabel('cm')
    ax.grid(True, alpha=0.3)

plt.tight_layout()
plt.show()

# 3.3 Heatmap correlazioni
print("\n3. Heatmap - Correlazioni tra features...")
plt.figure(figsize=(10, 8))
correlation_matrix = df_iris[feature_names].corr()
sns.heatmap(correlation_matrix, annot=True, cmap='coolwarm', center=0,
            square=True, linewidths=1, cbar_kws={"shrink": 0.8})
plt.title('Matrice di Correlazione tra Features', fontsize=14, pad=20)
plt.tight_layout()
plt.show()

print("✅ Visualizzazioni completate!")

Parte 3: Preparazione Dati per Classificazione

Split Train/Test e Preprocessing

4 Prepara i dati per gli algoritmi di classificazione:

PYTHON

# 4. PREPARAZIONE DATI PER CLASSIFICAZIONE
print("⚙️  PREPARAZIONE DATI PER GLI ALGORITMI DI CLASSIFICAZIONE")
print("="*60)

# 4.1 Split in training e test set
X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.3, random_state=42, stratify=y
)

print(f"📊 SPLIT DATI:")
print(f"  • Dimensione dataset completo: {X.shape}")
print(f"  • Training set: {X_train.shape[0]} campioni (70%)")
print(f"  • Test set: {X_test.shape[0]} campioni (30%)")

# Verifica distribuzione classi nei due set
print(f"\n🎯 DISTRIBUZIONE CLASSI NEI SET:")
train_dist = np.bincount(y_train)
test_dist = np.bincount(y_test)

for i, specie in enumerate(target_names):
    train_pct = (train_dist[i] / len(y_train)) * 100
    test_pct = (test_dist[i] / len(y_test)) * 100
    print(f"  • {specie}:")
    print(f"    Training: {train_dist[i]} campioni ({train_pct:.1f}%)")
    print(f"    Test: {test_dist[i]} campioni ({test_pct:.1f}%)")

# 4.2 Normalizzazione delle features
print(f"\n🔢 NORMALIZZAZIONE FEATURES:")
scaler = StandardScaler()
X_train_scaled = scaler.fit_transform(X_train)
X_test_scaled = scaler.transform(X_test)

print("  Features scalate con StandardScaler (media=0, deviazione=1)")
print(f"  Esempio prima riga training (originale): {X_train[0]}")
print(f"  Esempio prima riga training (scalata): {X_train_scaled[0]}")

# 4.3 Visualizzazione dati dopo scaling
print(f"\n🎨 CONFRONTO DATI PRIMA E DOPO SCALING...")

fig, axes = plt.subplots(1, 2, figsize=(15, 6))

# Dati originali
for i, specie in enumerate(target_names):
    mask = y_train == i
    axes[0].scatter(X_train[mask, 2], X_train[mask, 3],
                   alpha=0.6, label=specie, color=colors[specie],
                   marker=markers[specie], s=50)
axes[0].set_title('Dati Originali (Training Set)')
axes[0].set_xlabel('Petal Length (cm)')
axes[0].set_ylabel('Petal Width (cm)')
axes[0].legend()
axes[0].grid(True, alpha=0.3)

# Dati scalati
for i, specie in enumerate(target_names):
    mask = y_train == i
    axes[1].scatter(X_train_scaled[mask, 2], X_train_scaled[mask, 3],
                   alpha=0.6, label=specie, color=colors[specie],
                   marker=markers[specie], s=50)
axes[1].set_title('Dati Scalati (Training Set)')
axes[1].set_xlabel('Petal Length (scalata)')
axes[1].set_ylabel('Petal Width (scalata)')
axes[1].legend()
axes[1].grid(True, alpha=0.3)

plt.tight_layout()
plt.show()

print("✅ Preparazione dati completata!")

Parte 4: Implementazione Algoritmi di Classificazione

Addestramento Multipli Algoritmi

5 Addestra e confronta diversi algoritmi di classificazione:

PYTHON

# 5. IMPLEMENTAZIONE ALGORITMI DI CLASSIFICAZIONE
print("🤖 ADDESTRAMENTO ALGORITMI DI CLASSIFICAZIONE")
print("="*60)

# Definizione dei modelli da testare
models = {
    'K-Nearest Neighbors': KNeighborsClassifier(n_neighbors=5),
    'Support Vector Machine': SVC(kernel='rbf', C=1.0, gamma='scale', probability=True),
    'Decision Tree': DecisionTreeClassifier(max_depth=4, random_state=42),
    'Random Forest': RandomForestClassifier(n_estimators=100, max_depth=4, random_state=42),
    'Naive Bayes': GaussianNB()
}

# Dizionario per salvare risultati
results = {
    'Model': [],
    'Train Accuracy': [],
    'Test Accuracy': [],
    'Cross-Val Score': []
}

print("⚡ Addestramento modelli in corso...\n")

# Addestra e valuta ogni modello
for name, model in models.items():
    # Addestramento
    model.fit(X_train_scaled, y_train)

    # Predizioni
    y_train_pred = model.predict(X_train_scaled)
    y_test_pred = model.predict(X_test_scaled)

    # Calcolo accuracy
    train_acc = accuracy_score(y_train, y_train_pred)
    test_acc = accuracy_score(y_test, y_test_pred)

    # Cross-validation score (5-fold)
    cv_scores = cross_val_score(model, X_train_scaled, y_train, cv=5)
    cv_mean = cv_scores.mean()

    # Salva risultati
    results['Model'].append(name)
    results['Train Accuracy'].append(train_acc)
    results['Test Accuracy'].append(test_acc)
    results['Cross-Val Score'].append(cv_mean)

    # Stampa risultati parziali
    print(f"📊 {name}:")
    print(f"  • Training Accuracy: {train_acc:.3f}")
    print(f"  • Test Accuracy: {test_acc:.3f}")
    print(f"  • Cross-Val Mean: {cv_mean:.3f}")
    print()

# Converti risultati in DataFrame
results_df = pd.DataFrame(results)
results_df = results_df.sort_values('Test Accuracy', ascending=False)

print("🏆 CLASSIFICA MODELLI PER TEST ACCURACY:")
print(results_df.to_string(index=False))

# Visualizzazione confronto modelli
print("\n🎨 VISUALIZZAZIONE CONFRONTO MODELLI...")

fig, ax = plt.subplots(figsize=(12, 8))
x = np.arange(len(results_df))
width = 0.25

bars1 = ax.bar(x - width, results_df['Train Accuracy'], width, label='Train Accuracy',
              color='skyblue', alpha=0.8)
bars2 = ax.bar(x, results_df['Test Accuracy'], width, label='Test Accuracy',
              color='lightgreen', alpha=0.8)
bars3 = ax.bar(x + width, results_df['Cross-Val Score'], width, label='Cross-Val Score',
              color='salmon', alpha=0.8)

ax.set_xlabel('Modelli')
ax.set_ylabel('Accuracy')
ax.set_title('Confronto Performance Algoritmi di Classificazione')
ax.set_xticks(x)
ax.set_xticklabels(results_df['Model'], rotation=45, ha='right')
ax.legend(loc='lower right')
ax.grid(True, alpha=0.3, axis='y')

# Aggiungi valori sulle barre
for bars in [bars1, bars2, bars3]:
    for bar in bars:
        height = bar.get_height()
        ax.annotate(f'{height:.3f}',
                   xy=(bar.get_x() + bar.get_width() / 2, height),
                   xytext=(0, 3),  # 3 points vertical offset
                   textcoords="offset points",
                   ha='center', va='bottom', fontsize=9)

plt.tight_layout()
plt.show()

print("✅ Addestramento e valutazione modelli completati!")

Parte 5: Analisi Dettagliata e Utilizzo Pratico

Analisi Modello Migliore e Previsioni

6 Analizza il miglior modello e usalo per previsioni:

PYTHON

# 6. ANALISI DETTAGLIATA MODELLO MIGLIORE
print("🏆 ANALISI MODELLO MIGLIORE: RANDOM FOREST")
print("="*60)

# Seleziona il modello migliore (Random Forest in questo caso)
best_model_name = results_df.iloc[0]['Model']
best_model = models[best_model_name]

print(f"Modello selezionato: {best_model_name}")
print(f"Test Accuracy: {results_df.iloc[0]['Test Accuracy']:.3f}")

# 6.1 Matrice di confusione dettagliata
print(f"\n📊 MATRICE DI CONFUSIONE DETTAGLIATA:")

y_pred_best = best_model.predict(X_test_scaled)
cm = confusion_matrix(y_test, y_pred_best)

# Visualizzazione matrice di confusione
plt.figure(figsize=(10, 8))
sns.heatmap(cm, annot=True, fmt='d', cmap='Blues',
            xticklabels=target_names, yticklabels=target_names)
plt.title(f'Matrice di Confusione - {best_model_name}', fontsize=16, pad=20)
plt.xlabel('Predicted')
plt.ylabel('Actual')
plt.tight_layout()
plt.show()

# 6.2 Classification report
print(f"\n📋 CLASSIFICATION REPORT:")
cr = classification_report(y_test, y_pred_best, target_names=target_names)
print(cr)

# 6.3 Feature importance (solo per Random Forest e Decision Tree)
if hasattr(best_model, 'feature_importances_'):
    print(f"\n🔍 FEATURE IMPORTANCE:")
    importances = best_model.feature_importances_
    feature_importance_df = pd.DataFrame({
        'Feature': feature_names,
        'Importance': importances
    }).sort_values('Importance', ascending=False)

    print(feature_importance_df.to_string(index=False))

    # Visualizzazione feature importance
    plt.figure(figsize=(10, 6))
    bars = plt.barh(feature_importance_df['Feature'], feature_importance_df['Importance'],
                   color='steelblue')
    plt.xlabel('Importance')
    plt.title(f'Feature Importance - {best_model_name}', fontsize=14)
    plt.gca().invert_yaxis()  # Mostra la feature più importante in alto

    # Aggiungi valori sulle barre
    for bar in bars:
        width = bar.get_width()
        plt.text(width + 0.001, bar.get_y() + bar.get_height()/2,
                f'{width:.3f}', va='center', fontsize=10)

    plt.tight_layout()
    plt.show()

# 6.4 Esempi di previsioni sul test set
print(f"\n🔮 ESEMPI DI PREVISIONI SUL TEST SET:")
print("="*50)

# Seleziona alcuni esempi dal test set
sample_indices = np.random.choice(len(X_test), 10, replace=False)

print("\n{:^5} | {:^25} | {:^25} | {:^8}".format(
    "IDX", "Caratteristiche Reali", "Predizione", "Corretto?"))
print("-" * 70)

for idx in sample_indices:
    features_real = X_test[idx]
    features_scaled = X_test_scaled[idx]
    true_label = y_test[idx]
    pred_label = best_model.predict([features_scaled])[0]
    pred_proba = best_model.predict_proba([features_scaled])[0]

    # Formatta le caratteristiche
    features_str = f"SL:{features_real[0]:.1f}, SW:{features_real[1]:.1f}, PL:{features_real[2]:.1f}, PW:{features_real[3]:.1f}"

    # Formatta predizione con probabilità
    pred_name = target_names[pred_label]
    true_name = target_names[true_label]
    proba = pred_proba[pred_label] * 100

    correct = "✅" if pred_label == true_label else "❌"

    print("{:^5} | {:^25} | {:^25} | {:^8}".format(
        idx, features_str, f"{pred_name} ({proba:.1f}%)", correct))

# 6.5 Previsioni su nuovi dati (fiori sconosciuti)
print(f"\n🌺 PREVISIONI SU NUOVI FIORI SCONOSCIUTI:")
print("="*50)

# Crea nuovi fiori "sconosciuti" per la classificazione
nuovi_fiori = np.array([
    [5.1, 3.5, 1.4, 0.2],  # Sembra una setosa
    [6.0, 2.9, 4.5, 1.5],  # Sembra una versicolor
    [7.2, 3.0, 5.8, 2.1],  # Sembra una virginica
    [5.8, 2.7, 3.9, 1.2],  # Caso intermedio
    [6.7, 3.1, 5.6, 2.4]   # Caso estremo
])

# Normalizza i nuovi dati
nuovi_fiori_scaled = scaler.transform(nuovi_fiori)

# Fai previsioni
predizioni = best_model.predict(nuovi_fiori_scaled)
probabilita = best_model.predict_proba(nuovi_fiori_scaled)

print("\n{:^5} | {:^30} | {:^20} | {:^20}".format(
    "Fiore", "Caratteristiche (SL,SW,PL,PW)", "Specie Predetta", "Probabilità"))
print("-" * 90)

for i, (fiore, pred, prob) in enumerate(zip(nuovi_fiori, predizioni, probabilita)):
    caratteristiche = f"{fiore[0]:.1f}, {fiore[1]:.1f}, {fiore[2]:.1f}, {fiore[3]:.1f}"
    specie_predetta = target_names[pred]
    prob_max = prob[pred] * 100

    # Mostra anche le altre probabilità
    altre_probab = ", ".join([f"{target_names[j]}: {prob[j]*100:.1f}%"
                            for j in range(3) if j != pred])

    print("{:^5} | {:^30} | {:^20} | {:^20}".format(
        f"F{i+1}", caratteristiche, specie_predetta, f"{prob_max:.1f}%"))
    print("{:^5} | {:^30} | {:^20} | {:^20}".format(
        "", "", "", f"Altro: {altre_probab}"))

print(f"\n✅ Sistema di classificazione Iris completato con successo!")
print(f"🎯 Il modello {best_model_name} classifica correttamente il {results_df.iloc[0]['Test Accuracy']*100:.1f}% dei fiori!")

Concetti di Classificazione ML Appresi

Congratulazioni! Ora conosci:

  • ✓ Classificazione ML: Algoritmi per categorizzare dati in classi
  • ✓ Dataset Iris: Il benchmark standard per classificazione
  • ✓ Algoritmi Multipli: KNN, SVM, Decision Tree, Random Forest, Naive Bayes
  • ✓ StandardScaler: Normalizzazione features per ML
  • ✓ Stratified Split: Mantenere distribuzione classi in train/test
  • ✓ Metriche di Valutazione: Accuracy, Confusion Matrix, Classification Report
  • ✓ Cross-Validation: Valutazione robusta dei modelli
  • ✓ Feature Importance: Importanza delle variabili nei modelli
  • ✓ Predizioni Probabilistiche: Probabilità associate a ogni classe

Prossimi Passi nella Classificazione ML

Nella prossima esercitazione imparerai:

  • 🔍 Clustering con K-Means e DBSCAN
  • 📊 Classificazione multi-classe avanzata
  • 🤖 Ensemble methods e voting classifiers
  • 🧠 Reti neurali per classificazione immagini
  • 🎯 Hyperparameter tuning con GridSearch

Hai padroneggiato le basi della classificazione con Machine Learning!