{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.11.13","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"none","dataSources":[{"sourceId":3649,"databundleVersionId":46718,"sourceType":"competition"}],"dockerImageVersionId":31153,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"import numpy as np\nimport pandas as pd\nimport matplotlib.pyplot as plt\nfrom keras.datasets import cifar10\nfrom skimage.feature import hog\nfrom skimage import color, exposure\nfrom sklearn.preprocessing import StandardScaler\nfrom sklearn.svm import SVC\nfrom sklearn.neighbors import KNeighborsClassifier\nfrom sklearn.tree import DecisionTreeClassifier\nfrom sklearn.metrics import confusion_matrix, accuracy_score, precision_score, recall_score, f1_score\nimport warnings\n\n# Ignorer les avertissements pour une sortie plus propre\nwarnings.filterwarnings('ignore')\n\n# --- 1. Chargement et Filtrage des Données (CIFAR-10) ---\n\nprint(\"Étape 1 : Chargement et filtrage des données...\")\n\n# Définition des classes cibles\n# Classe 2 = Oiseau (Bird)\n# Classe 9 = Camion (Truck)\nCLASSES_CIBLES = [2, 9]\nlabels_noms = {2: 'Oiseau', 9: 'Camion'}\n\n# Chargement du dataset\n(X_train, y_train), (X_test, y_test) = cifar10.load_data()\n\n# Aplatir y pour faciliter le filtrage\ny_train = y_train.flatten()\ny_test = y_test.flatten()\n\n# Filtrage pour ne garder que les Oiseaux (2) et les Camions (9)\nmasque_train = np.isin(y_train, CLASSES_CIBLES)\nmasque_test = np.isin(y_test, CLASSES_CIBLES)\n\nX_train_filtre = X_train[masque_train]\ny_train_filtre = y_train[masque_train]\nX_test_filtre = X_test[masque_test]\ny_test_filtre = y_test[masque_test]\n\nprint(f\"Données d'entraînement : {len(X_train_filtre)} images\")\nprint(f\"Données de validation : {len(X_test_filtre)} images\")\n\n\n# --- 2. Extraction des Caractéristiques (HOG) ---\n\nprint(\"\\nÉtape 2 : Extraction des caractéristiques HOG...\")\n\ndef extraire_hog(images):\n    features_list = []\n    for image in images:\n        # Convertir l'image en niveaux de gris\n        image_gray = color.rgb2gray(image)\n        \n        # Calculer les caractéristiques HOG\n        # Les paramètres (pixels_per_cell, cells_per_block) sont cruciaux\n        features = hog(image_gray, pixels_per_cell=(8, 8),\n                       cells_per_block=(2, 2), visualize=False,\n                       block_norm='L2-Hys')\n        features_list.append(features)\n    return np.array(features_list)\n\nX_train_hog = extraire_hog(X_train_filtre)\nX_test_hog = extraire_hog(X_test_filtre)\n\nprint(f\"Taille du vecteur de caractéristiques HOG : {X_train_hog.shape[1]}\")\n\n\n# --- 3. Prétraitement (Mise à l'échelle) ---\n\nprint(\"\\nÉtape 3 : Mise à l'échelle des caractéristiques...\")\n\n# Le scaling est crucial pour SVM et KNN\nscaler = StandardScaler()\nX_train_scaled = scaler.fit_transform(X_train_hog)\nX_test_scaled = scaler.transform(X_test_hog)\n\n\n# --- 4. Partie 1 : Modèle SVM (Entraînement, Prédiction, Évaluation) ---\n\nprint(\"\\n--- Partie 1 : Modèle SVM ---\")\n\n# 4.1. Entraînement\nprint(\"Entraînement du modèle SVM (Noyau RBF)...\")\n# Nous utilisons un noyau RBF (Gaussien) qui est souvent performant\nsvm_model = SVC(kernel='rbf', C=10, random_state=42)\nsvm_model.fit(X_train_scaled, y_train_filtre)\n\n# 4.2. Prédiction sur 2 images de validation\nprint(\"\\nPrédiction sur 2 images de validation :\")\nimages_a_predire = X_test_scaled[:2]\nlabels_reels = y_test_filtre[:2]\npredictions = svm_model.predict(images_a_predire)\n\nfor i in range(2):\n    label_pred = labels_noms[predictions[i]]\n    label_reel = labels_noms[labels_reels[i]]\n    print(f\"  Image {i+1} : Prédit='{label_pred}', Réel='{label_reel}'\")\n\n# 4.3. Évaluation complète et Matrice de Confusion\nprint(\"\\nÉvaluation complète du modèle SVM :\")\ny_pred_svm = svm_model.predict(X_test_scaled)\n\n# Définir l'ordre des labels : Camion (Négatif), Oiseau (Positif)\n# pos_label=2 signifie que 'Oiseau' est notre classe positive\nlabels_ordre = [9, 2] \n\ncm = confusion_matrix(y_test_filtre, y_pred_svm, labels=labels_ordre)\n\nprint(\"Matrice de Confusion :\")\nprint(f\"         Prédit: Camion | Prédit: Oiseau\")\nprint(f\"Réel: Camion   {cm[0][0]:<13} | {cm[0][1]:<13}\")\nprint(f\"Réel: Oiseau   {cm[1][0]:<13} | {cm[1][1]:<13}\")\n\n# Extraction des TP, TN, FP, FN\n# Classe Positive = Oiseau (2)\n# Classe Négative = Camion (9)\nVN, FP, FN, VP = cm.ravel()\n\nprint(f\"\\nValeurs extraites (Positif = Oiseau):\")\nprint(f\"  Vrais Positifs (VP) : {VP} (Oiseaux correctement identifiés)\")\nprint(f\"  Vrais Négatifs (VN) : {VN} (Camions correctement identifiés)\")\nprint(f\"  Faux Positifs (FP)  : {FP} (Camions pris pour des Oiseaux)\")\nprint(f\"  Faux Négatifs (FN)  : {FN} (Oiseaux pris pour des Camions)\")\n\n# 4.4. Calcul des Métriques\n# Nous utilisons pos_label=2 pour nous assurer que Précision et Rappel\n# sont calculés pour la classe \"Oiseau\".\naccuracy = accuracy_score(y_test_filtre, y_pred_svm)\nprecision = precision_score(y_test_filtre, y_pred_svm, pos_label=2)\nrecall = recall_score(y_test_filtre, y_pred_svm, pos_label=2)\nf1 = f1_score(y_test_filtre, y_pred_svm, pos_label=2)\n\nprint(\"\\nMétriques de performance :\")\nprint(f\"  Accuracy (Exactitude) : {accuracy:.4f}\")\nprint(f\"  Précision (Oiseau)    : {precision:.4f}\")\nprint(f\"  Rappel (Oiseau)       : {recall:.4f}\")\nprint(f\"  F-mesure (Oiseau)     : {f1:.4f}\")\n\n\n# --- 5. Partie 2 : Étude Comparative (SVM, KNN, DT) ---\n\nprint(\"\\n\\n--- Partie 2 : Étude Comparative ---\")\n\n# Définition des modèles et hyperparamètres à tester\nmodeles = {\n    \"SVM (Linéaire, C=1)\": SVC(kernel='linear', C=1, random_state=42),\n    \"SVM (RBF, C=1)\": SVC(kernel='rbf', C=1, random_state=42),\n    \"SVM (RBF, C=10)\": SVC(kernel='rbf', C=10, random_state=42),\n    \"KNN (k=3)\": KNeighborsClassifier(n_neighbors=3),\n    \"KNN (k=7)\": KNeighborsClassifier(n_neighbors=7),\n    \"KNN (k=11)\": KNeighborsClassifier(n_neighbors=11),\n    \"Arbre (Prof=5)\": DecisionTreeClassifier(max_depth=5, random_state=42),\n    \"Arbre (Prof=10)\": DecisionTreeClassifier(max_depth=10, random_state=42),\n    \"Arbre (Non limité)\": DecisionTreeClassifier(random_state=42)\n}\n\nresultats = []\n\nprint(\"Entraînement et évaluation des différents modèles...\")\n\nfor nom, modele in modeles.items():\n    # Entraînement\n    modele.fit(X_train_scaled, y_train_filtre)\n    \n    # Prédiction\n    y_pred = modele.predict(X_test_scaled)\n    \n    # Évaluation (toujours avec 'Oiseau' (2) comme classe positive)\n    acc = accuracy_score(y_test_filtre, y_pred)\n    prec = precision_score(y_test_filtre, y_pred, pos_label=2)\n    rapp = recall_score(y_test_filtre, y_pred, pos_label=2)\n    \n    resultats.append({\n        \"Modèle\": nom,\n        \"Accuracy\": acc,\n        \"Précision (Oiseau)\": prec,\n        \"Rappel (Oiseau)\": rapp\n    })\n\n# Affichage du tableau des résultats\nresultats_df = pd.DataFrame(resultats)\nresultats_df = resultats_df.set_index(\"Modèle\")\n\nprint(\"\\nTableau comparatif des résultats :\")\nprint(resultats_df.to_string(float_format=\"%.4f\"))","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2025-10-25T22:41:58.908670Z","iopub.execute_input":"2025-10-25T22:41:58.909009Z","iopub.status.idle":"2025-10-25T22:41:59.893487Z","shell.execute_reply.started":"2025-10-25T22:41:58.908983Z","shell.execute_reply":"2025-10-25T22:41:59.892214Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"\"\"\"\nClassification Supervisée CIFAR-10 (Oiseau vs Camion)\nUtilisation des caractéristiques CBIR avec SVM, KNN, et Decision Tree\n\"\"\"\n\nimport numpy as np\nimport cv2\nimport matplotlib.pyplot as plt\nfrom sklearn.svm import SVC\nfrom sklearn.neighbors import KNeighborsClassifier\nfrom sklearn.tree import DecisionTreeClassifier\nfrom sklearn.model_selection import train_test_split\nfrom sklearn.metrics import confusion_matrix, accuracy_score, precision_score, recall_score, f1_score\nfrom sklearn.preprocessing import StandardScaler\nfrom skimage.feature import graycomatrix, graycoprops\nimport pandas as pd\nimport seaborn as sns\nfrom tensorflow.keras.datasets import cifar10\n\n# ============================================================================\n# 1. CHARGEMENT ET PRÉPARATION DES DONNÉES CIFAR-10\n# ============================================================================\n\ndef load_cifar10_bird_truck():\n    \"\"\"\n    Charge CIFAR-10 et filtre les classes Oiseau (2) et Camion (9)\n    \"\"\"\n    (x_train, y_train), (x_test, y_test) = cifar10.load_data()\n    \n    # Filtrer les classes: Oiseau (2) et Camion (9)\n    train_mask = np.isin(y_train, [2, 9]).flatten()\n    test_mask = np.isin(y_test, [2, 9]).flatten()\n    \n    x_train_filtered = x_train[train_mask]\n    y_train_filtered = y_train[train_mask].flatten()\n    x_test_filtered = x_test[test_mask]\n    y_test_filtered = y_test[test_mask].flatten()\n    \n    # Convertir les labels: Oiseau=0, Camion=1\n    y_train_filtered = (y_train_filtered == 9).astype(int)\n    y_test_filtered = (y_test_filtered == 9).astype(int)\n    \n    print(f\"Train set: {x_train_filtered.shape[0]} images\")\n    print(f\"Test set: {x_test_filtered.shape[0]} images\")\n    print(f\"Classes: Oiseau (0), Camion (1)\")\n    \n    return x_train_filtered, y_train_filtered, x_test_filtered, y_test_filtered\n\n# ============================================================================\n# 2. EXTRACTION DES CARACTÉRISTIQUES CBIR\n# ============================================================================\n\ndef extract_color_moments(image):\n    \"\"\"\n    A. Couleurs 1: Moments statistiques (Moyenne et Std) pour R, G, B\n    Retourne 6 caractéristiques\n    \"\"\"\n    features = []\n    for i in range(3):  # R, G, B\n        channel = image[:, :, i]\n        mean = np.mean(channel)\n        std = np.std(channel)\n        features.extend([mean, std])\n    return np.array(features)\n\ndef extract_hsv_histogram(image, h_bins=8, s_bins=2, v_bins=2):\n    \"\"\"\n    B. Couleurs 2: Histogramme quantifié HSV\n    H=8 bins, S=2 bins, V=2 bins => 32 caractéristiques\n    \"\"\"\n    hsv = cv2.cvtColor(image, cv2.COLOR_RGB2HSV)\n    \n    # Calculer l'histogramme 3D\n    hist = cv2.calcHist([hsv], [0, 1, 2], None, \n                        [h_bins, s_bins, v_bins],\n                        [0, 180, 0, 256, 0, 256])\n    \n    # Normaliser\n    hist = hist.flatten()\n    hist = hist / (hist.sum() + 1e-7)\n    \n    return hist\n\ndef extract_texture_features(image):\n    \"\"\"\n    C. Texture: GLCM (Contrast, Correlation, Energy, Homogeneity)\n    Retourne 4 caractéristiques\n    \"\"\"\n    # Convertir en niveaux de gris\n    gray = cv2.cvtColor(image, cv2.COLOR_RGB2GRAY)\n    \n    # Calculer GLCM\n    glcm = graycomatrix(gray, distances=[1], angles=[0], levels=256,\n                        symmetric=True, normed=True)\n    \n    # Extraire les propriétés\n    contrast = graycoprops(glcm, 'contrast')[0, 0]\n    correlation = graycoprops(glcm, 'correlation')[0, 0]\n    energy = graycoprops(glcm, 'energy')[0, 0]\n    homogeneity = graycoprops(glcm, 'homogeneity')[0, 0]\n    \n    return np.array([contrast, correlation, energy, homogeneity])\n\ndef extract_hu_moments(image):\n    \"\"\"\n    D. Forme: 7 moments invariants de Hu\n    Retourne 7 caractéristiques\n    \"\"\"\n    # Convertir en niveaux de gris\n    gray = cv2.cvtColor(image, cv2.COLOR_RGB2GRAY)\n    \n    # Calculer les moments de Hu\n    moments = cv2.HuMoments(cv2.moments(gray)).flatten()\n    \n    # Appliquer log pour normaliser\n    moments = -np.sign(moments) * np.log10(np.abs(moments) + 1e-10)\n    \n    return moments\n\ndef extract_all_features(image):\n    \"\"\"\n    Extraire et concaténer toutes les caractéristiques\n    Total: 6 + 32 + 4 + 7 = 49 caractéristiques\n    \"\"\"\n    feat1 = extract_color_moments(image)           # 6 features\n    feat2 = extract_hsv_histogram(image)           # 32 features\n    feat3 = extract_texture_features(image)        # 4 features\n    feat4 = extract_hu_moments(image)              # 7 features\n    \n    return np.concatenate([feat1, feat2, feat3, feat4])\n\ndef extract_features_dataset(images):\n    \"\"\"\n    Extraire les caractéristiques pour un ensemble d'images\n    \"\"\"\n    features_list = []\n    for i, img in enumerate(images):\n        if i % 500 == 0:\n            print(f\"Extraction: {i}/{len(images)}\")\n        features = extract_all_features(img)\n        features_list.append(features)\n    return np.array(features_list)\n\n# ============================================================================\n# 3. ENTRAÎNEMENT ET ÉVALUATION\n# ============================================================================\n\ndef evaluate_model(y_true, y_pred, model_name):\n    \"\"\"\n    Évaluer un modèle et afficher les métriques\n    \"\"\"\n    # Matrice de confusion\n    cm = confusion_matrix(y_true, y_pred)\n    tn, fp, fn, tp = cm.ravel()\n    \n    # Métriques\n    accuracy = accuracy_score(y_true, y_pred)\n    precision = precision_score(y_true, y_pred, zero_division=0)\n    recall = recall_score(y_true, y_pred, zero_division=0)\n    f1 = f1_score(y_true, y_pred, zero_division=0)\n    \n    print(f\"\\n{'='*60}\")\n    print(f\"Résultats pour {model_name}\")\n    print(f\"{'='*60}\")\n    print(f\"Matrice de confusion:\")\n    print(f\"  TN: {tn:5d}  |  FP: {fp:5d}\")\n    print(f\"  FN: {fn:5d}  |  TP: {tp:5d}\")\n    print(f\"\\nMétriques:\")\n    print(f\"  Accuracy:  {accuracy:.4f}\")\n    print(f\"  Precision: {precision:.4f}\")\n    print(f\"  Recall:    {recall:.4f}\")\n    print(f\"  F-measure: {f1:.4f}\")\n    \n    return {\n        'Model': model_name,\n        'Accuracy': accuracy,\n        'Precision': precision,\n        'Recall': recall,\n        'F1-Score': f1,\n        'TN': tn, 'FP': fp, 'FN': fn, 'TP': tp\n    }\n\ndef plot_confusion_matrix(y_true, y_pred, model_name):\n    \"\"\"\n    Afficher la matrice de confusion\n    \"\"\"\n    cm = confusion_matrix(y_true, y_pred)\n    plt.figure(figsize=(6, 5))\n    sns.heatmap(cm, annot=True, fmt='d', cmap='Blues', \n                xticklabels=['Oiseau', 'Camion'],\n                yticklabels=['Oiseau', 'Camion'])\n    plt.title(f'Matrice de Confusion - {model_name}')\n    plt.ylabel('Vraie Classe')\n    plt.xlabel('Classe Prédite')\n    plt.tight_layout()\n    plt.show()\n\n# ============================================================================\n# 4. PROGRAMME PRINCIPAL\n# ============================================================================\n\ndef main():\n    print(\"Chargement des données CIFAR-10...\")\n    x_train, y_train, x_test, y_test = load_cifar10_bird_truck()\n    \n    # Créer un ensemble de validation\n    x_train, x_val, y_train, y_val = train_test_split(\n        x_train, y_train, test_size=0.2, random_state=42, stratify=y_train\n    )\n    \n    print(f\"\\nEnsembles finaux:\")\n    print(f\"  Train: {x_train.shape[0]} images\")\n    print(f\"  Validation: {x_val.shape[0]} images\")\n    print(f\"  Test: {x_test.shape[0]} images\")\n    \n    # Extraction des caractéristiques\n    print(\"\\n\" + \"=\"*60)\n    print(\"EXTRACTION DES CARACTÉRISTIQUES\")\n    print(\"=\"*60)\n    \n    print(\"\\nExtraction pour l'ensemble d'entraînement...\")\n    X_train = extract_features_dataset(x_train)\n    \n    print(\"\\nExtraction pour l'ensemble de validation...\")\n    X_val = extract_features_dataset(x_val)\n    \n    print(\"\\nExtraction pour l'ensemble de test...\")\n    X_test = extract_features_dataset(x_test)\n    \n    print(f\"\\nDimension des features: {X_train.shape[1]}\")\n    \n    # Normalisation\n    scaler = StandardScaler()\n    X_train_scaled = scaler.fit_transform(X_train)\n    X_val_scaled = scaler.transform(X_val)\n    X_test_scaled = scaler.transform(X_test)\n    \n    # ========================================================================\n    # PARTIE 1: ENTRAÎNEMENT SVM ET PRÉDICTION\n    # ========================================================================\n    \n    print(\"\\n\" + \"=\"*60)\n    print(\"PARTIE 1: ENTRAÎNEMENT DU MODÈLE SVM\")\n    print(\"=\"*60)\n    \n    svm_model = SVC(kernel='rbf', C=10, gamma='scale', random_state=42)\n    svm_model.fit(X_train_scaled, y_train)\n    \n    # Prédire sur 2 images de validation\n    print(\"\\nPrédiction sur 2 images de validation:\")\n    for i in range(2):\n        pred = svm_model.predict(X_val_scaled[i:i+1])[0]\n        true = y_val[i]\n        pred_label = \"Camion\" if pred == 1 else \"Oiseau\"\n        true_label = \"Camion\" if true == 1 else \"Oiseau\"\n        print(f\"  Image {i+1}: Prédiction={pred_label}, Vraie classe={true_label}\")\n        \n        # Afficher l'image\n        plt.figure(figsize=(3, 3))\n        plt.imshow(x_val[i])\n        plt.title(f'Pred: {pred_label} | True: {true_label}')\n        plt.axis('off')\n        plt.show()\n    \n    # Évaluation sur validation\n    y_val_pred = svm_model.predict(X_val_scaled)\n    svm_results = evaluate_model(y_val, y_val_pred, \"SVM (RBF, C=10)\")\n    plot_confusion_matrix(y_val, y_val_pred, \"SVM\")\n    \n    # ========================================================================\n    # PARTIE 2: ÉTUDE COMPARATIVE\n    # ========================================================================\n    \n    print(\"\\n\" + \"=\"*60)\n    print(\"PARTIE 2: ÉTUDE COMPARATIVE DES ALGORITHMES\")\n    print(\"=\"*60)\n    \n    results_list = [svm_results]\n    \n    # Définir les modèles à tester\n    models_to_test = [\n        # SVM avec différents hyperparamètres\n        ('SVM (Linear, C=1)', SVC(kernel='linear', C=1, random_state=42)),\n        ('SVM (Linear, C=10)', SVC(kernel='linear', C=10, random_state=42)),\n        ('SVM (RBF, C=1)', SVC(kernel='rbf', C=1, gamma='scale', random_state=42)),\n        ('SVM (RBF, C=100)', SVC(kernel='rbf', C=100, gamma='scale', random_state=42)),\n        ('SVM (Poly, C=10)', SVC(kernel='poly', degree=3, C=10, random_state=42)),\n        \n        # KNN avec différents k\n        ('KNN (k=3)', KNeighborsClassifier(n_neighbors=3)),\n        ('KNN (k=5)', KNeighborsClassifier(n_neighbors=5)),\n        ('KNN (k=7)', KNeighborsClassifier(n_neighbors=7)),\n        ('KNN (k=10, weighted)', KNeighborsClassifier(n_neighbors=10, weights='distance')),\n        \n        # Decision Tree avec différents paramètres\n        ('DT (max_depth=5)', DecisionTreeClassifier(max_depth=5, random_state=42)),\n        ('DT (max_depth=10)', DecisionTreeClassifier(max_depth=10, random_state=42)),\n        ('DT (max_depth=20)', DecisionTreeClassifier(max_depth=20, random_state=42)),\n        ('DT (min_samples=10)', DecisionTreeClassifier(min_samples_split=10, random_state=42)),\n        ('DT (entropy)', DecisionTreeClassifier(criterion='entropy', max_depth=15, random_state=42)),\n    ]\n    \n    # Entraîner et évaluer chaque modèle\n    for model_name, model in models_to_test:\n        print(f\"\\nEntraînement: {model_name}...\")\n        model.fit(X_train_scaled, y_train)\n        y_pred = model.predict(X_val_scaled)\n        results = evaluate_model(y_val, y_pred, model_name)\n        results_list.append(results)\n    \n    # ========================================================================\n    # PARTIE 3: TABLEAU COMPARATIF\n    # ========================================================================\n    \n    print(\"\\n\" + \"=\"*60)\n    print(\"TABLEAU COMPARATIF DES RÉSULTATS\")\n    print(\"=\"*60)\n    \n    df_results = pd.DataFrame(results_list)\n    df_results = df_results.sort_values('Accuracy', ascending=False)\n    \n    # Afficher le tableau\n    print(\"\\n\" + df_results.to_string(index=False))\n    \n    # Visualisation des résultats\n    fig, axes = plt.subplots(1, 3, figsize=(18, 6))\n    \n    metrics = ['Accuracy', 'Precision', 'Recall']\n    colors = ['#2ecc71', '#3498db', '#e74c3c']\n    \n    for idx, (metric, color) in enumerate(zip(metrics, colors)):\n        ax = axes[idx]\n        data = df_results.sort_values(metric, ascending=True)\n        ax.barh(range(len(data)), data[metric], color=color, alpha=0.7)\n        ax.set_yticks(range(len(data)))\n        ax.set_yticklabels(data['Model'], fontsize=8)\n        ax.set_xlabel(metric, fontsize=12, fontweight='bold')\n        ax.set_xlim([0, 1])\n        ax.grid(axis='x', alpha=0.3)\n        \n        # Ajouter les valeurs\n        for i, v in enumerate(data[metric]):\n            ax.text(v + 0.01, i, f'{v:.3f}', va='center', fontsize=8)\n    \n    plt.suptitle('Comparaison des Performances des Modèles', fontsize=16, fontweight='bold')\n    plt.tight_layout()\n    plt.show()\n    \n    # ========================================================================\n    # PARTIE 4: DISCUSSION DES RÉSULTATS\n    # ========================================================================\n    \n    print(\"\\n\" + \"=\"*60)\n    print(\"DISCUSSION DES RÉSULTATS\")\n    print(\"=\"*60)\n    \n    best_model = df_results.iloc[0]\n    worst_model = df_results.iloc[-1]\n    \n    print(f\"\\n1. MEILLEUR MODÈLE:\")\n    print(f\"   {best_model['Model']} avec Accuracy = {best_model['Accuracy']:.4f}\")\n    \n    print(f\"\\n2. ANALYSE PAR ALGORITHME:\")\n    \n    # SVM\n    svm_models = df_results[df_results['Model'].str.contains('SVM')]\n    print(f\"\\n   a) SVM:\")\n    print(f\"      - Meilleure performance: {svm_models.iloc[0]['Model']}\")\n    print(f\"      - Accuracy moyenne: {svm_models['Accuracy'].mean():.4f}\")\n    print(f\"      - Le noyau RBF performe généralement mieux que linéaire\")\n    \n    # KNN\n    knn_models = df_results[df_results['Model'].str.contains('KNN')]\n    print(f\"\\n   b) KNN:\")\n    print(f\"      - Meilleure performance: {knn_models.iloc[0]['Model']}\")\n    print(f\"      - Accuracy moyenne: {knn_models['Accuracy'].mean():.4f}\")\n    print(f\"      - Sensible au choix de k et à la pondération\")\n    \n    # Decision Tree\n    dt_models = df_results[df_results['Model'].str.contains('DT')]\n    print(f\"\\n   c) Decision Tree:\")\n    print(f\"      - Meilleure performance: {dt_models.iloc[0]['Model']}\")\n    print(f\"      - Accuracy moyenne: {dt_models['Accuracy'].mean():.4f}\")\n    print(f\"      - Profondeur optimale importante pour éviter sur-apprentissage\")\n    \n    print(f\"\\n3. OBSERVATIONS GÉNÉRALES:\")\n    print(f\"   - Les caractéristiques CBIR sont efficaces pour cette tâche\")\n    print(f\"   - SVM et KNN montrent des performances similaires\")\n    print(f\"   - La normalisation des features est cruciale\")\n    print(f\"   - Les hyperparamètres ont un impact significatif\")\n    \n    print(\"\\n\" + \"=\"*60)\n    print(\"FIN DE L'ANALYSE\")\n    print(\"=\"*60)\n\nif __name__ == \"__main__\":\n    main()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-25T22:45:40.970224Z","iopub.execute_input":"2025-10-25T22:45:40.970712Z","iopub.status.idle":"2025-10-25T22:48:00.267107Z","shell.execute_reply.started":"2025-10-25T22:45:40.970686Z","shell.execute_reply":"2025-10-25T22:48:00.265789Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}