{"metadata":{"kernelspec":{"name":"python3","display_name":"Python 3","language":"python"},"language_info":{"name":"python","version":"3.12.12","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"gpu","dataSources":[{"sourceId":124046,"databundleVersionId":14573278,"sourceType":"competition"}],"dockerImageVersionId":31261,"isInternetEnabled":false,"language":"python","sourceType":"notebook","isGpuEnabled":true},"papermill":{"default_parameters":{},"duration":3396.253506,"end_time":"2026-01-10T03:34:35.318672","environment_variables":{},"exception":null,"input_path":"__notebook__.ipynb","output_path":"__notebook__.ipynb","parameters":{},"start_time":"2026-01-10T02:37:59.065166","version":"2.6.0"},"widgets":{"application/vnd.jupyter.widget-state+json":{"state":{"00716a593cb948c989e8a752187448c7":{"model_module":"@jupyter-widgets/base","model_module_version":"2.0.0","model_name":"LayoutModel","state":{"_model_module":"@jupyter-widgets/base","_model_module_version":"2.0.0","_model_name":"LayoutModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"2.0.0","_view_name":"LayoutView","align_content":null,"align_items":null,"align_self":null,"border_bottom":null,"border_left":null,"border_right":null,"border_top":null,"bottom":null,"display":null,"flex":null,"flex_flow":null,"grid_area":null,"grid_auto_columns":null,"grid_auto_flow":null,"grid_auto_rows":null,"grid_column":null,"grid_gap":null,"grid_row":null,"grid_template_areas":null,"grid_template_columns":null,"grid_template_rows":null,"height":null,"justify_content":null,"justify_items":null,"left":null,"margin":null,"max_height":null,"max_width":null,"min_height":null,"min_width":null,"object_fit":null,"object_position":null,"order":null,"overflow":null,"padding":null,"right":null,"top":null,"visibility":null,"width":null}},"0b0911a084854e7ebca240cf75a60551":{"model_module":"@jupyter-widgets/base","model_module_version":"2.0.0","model_name":"LayoutModel","state":{"_model_module":"@jupyter-widgets/base","_model_module_version":"2.0.0","_model_name":"LayoutModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"2.0.0","_view_name":"LayoutView","align_content":null,"align_items":null,"align_self":null,"border_bottom":null,"border_left":null,"border_right":null,"border_top":null,"bottom":null,"display":null,"flex":null,"flex_flow":null,"grid_area":null,"grid_auto_columns":null,"grid_auto_flow":null,"grid_auto_rows":null,"grid_column":null,"grid_gap":null,"grid_row":null,"grid_template_areas":null,"grid_template_columns":null,"grid_template_rows":null,"height":null,"justify_content":null,"justify_items":null,"left":null,"margin":null,"max_height":null,"max_width":null,"min_height":null,"min_width":null,"object_fit":null,"object_position":null,"order":null,"overflow":null,"padding":null,"right":null,"top":null,"visibility":null,"width":null}},"0dfa3d261676432eb38ea621c8a0684a":{"model_module":"@jupyter-widgets/controls","model_module_version":"2.0.0","model_name":"ProgressStyleModel","state":{"_model_module":"@jupyter-widgets/controls","_model_module_version":"2.0.0","_model_name":"ProgressStyleModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"2.0.0","_view_name":"StyleView","bar_color":null,"description_width":""}},"12ad50f858d1431a8a58fbaf6dcbd29f":{"model_module":"@jupyter-widgets/base","model_module_version":"2.0.0","model_name":"LayoutModel","state":{"_model_module":"@jupyter-widgets/base","_model_module_version":"2.0.0","_model_name":"LayoutModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"2.0.0","_view_name":"LayoutView","align_content":null,"align_items":null,"align_self":null,"border_bottom":null,"border_left":null,"border_right":null,"border_top":null,"bottom":null,"display":null,"flex":null,"flex_flow":null,"grid_area":null,"grid_auto_columns":null,"grid_auto_flow":null,"grid_auto_rows":null,"grid_column":null,"grid_gap":null,"grid_row":null,"grid_template_areas":null,"grid_template_columns":null,"grid_template_rows":null,"height":null,"justify_content":null,"justify_items":null,"left":null,"margin":null,"max_height":null,"max_width":null,"min_height":null,"min_width":null,"object_fit":null,"object_position":null,"order":null,"overflow":null,"padding":null,"right":null,"top":null,"visibility":null,"width":null}},"14c278d07c164ad1890416f264814a0e":{"model_module":"@jupyter-widgets/controls","model_module_version":"2.0.0","model_name":"FloatProgressModel","state":{"_dom_classes":[],"_model_module":"@jupyter-widgets/controls","_model_module_version":"2.0.0","_model_name":"FloatProgressModel","_view_count":null,"_view_module":"@jupyter-widgets/controls","_view_module_version":"2.0.0","_view_name":"ProgressView","bar_style":"success","description":"","description_allow_html":false,"layout":"IPY_MODEL_ddc1a1684e034e49997f8fe6b170c397","max":14000,"min":0,"orientation":"horizontal","style":"IPY_MODEL_0dfa3d261676432eb38ea621c8a0684a","tabbable":null,"tooltip":null,"value":14000}},"2b89c280940146fd9a10900ffd7264e2":{"model_module":"@jupyter-widgets/controls","model_module_version":"2.0.0","model_name":"ProgressStyleModel","state":{"_model_module":"@jupyter-widgets/controls","_model_module_version":"2.0.0","_model_name":"ProgressStyleModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"2.0.0","_view_name":"StyleView","bar_color":null,"description_width":""}},"307fc2b786594671b667f2b5f01ab414":{"model_module":"@jupyter-widgets/controls","model_module_version":"2.0.0","model_name":"FloatProgressModel","state":{"_dom_classes":[],"_model_module":"@jupyter-widgets/controls","_model_module_version":"2.0.0","_model_name":"FloatProgressModel","_view_count":null,"_view_module":"@jupyter-widgets/controls","_view_module_version":"2.0.0","_view_name":"ProgressView","bar_style":"success","description":"","description_allow_html":false,"layout":"IPY_MODEL_0b0911a084854e7ebca240cf75a60551","max":6000,"min":0,"orientation":"horizontal","style":"IPY_MODEL_2b89c280940146fd9a10900ffd7264e2","tabbable":null,"tooltip":null,"value":6000}},"362ae2072d7e47ccbdc2584e0890447d":{"model_module":"@jupyter-widgets/controls","model_module_version":"2.0.0","model_name":"HTMLModel","state":{"_dom_classes":[],"_model_module":"@jupyter-widgets/controls","_model_module_version":"2.0.0","_model_name":"HTMLModel","_view_count":null,"_view_module":"@jupyter-widgets/controls","_view_module_version":"2.0.0","_view_name":"HTMLView","description":"","description_allow_html":false,"layout":"IPY_MODEL_12ad50f858d1431a8a58fbaf6dcbd29f","placeholder":"​","style":"IPY_MODEL_df64bd2ab3de476b925077c7001dd9a8","tabbable":null,"tooltip":null,"value":" 6000/6000 [00:56&lt;00:00, 114.05it/s]"}},"3cef2e7399fb4b3b8b0a6162b5ac2ea4":{"model_module":"@jupyter-widgets/base","model_module_version":"2.0.0","model_name":"LayoutModel","state":{"_model_module":"@jupyter-widgets/base","_model_module_version":"2.0.0","_model_name":"LayoutModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"2.0.0","_view_name":"LayoutView","align_content":null,"align_items":null,"align_self":null,"border_bottom":null,"border_left":null,"border_right":null,"border_top":null,"bottom":null,"display":null,"flex":null,"flex_flow":null,"grid_area":null,"grid_auto_columns":null,"grid_auto_flow":null,"grid_auto_rows":null,"grid_column":null,"grid_gap":null,"grid_row":null,"grid_template_areas":null,"grid_template_columns":null,"grid_template_rows":null,"height":null,"justify_content":null,"justify_items":null,"left":null,"margin":null,"max_height":null,"max_width":null,"min_height":null,"min_width":null,"object_fit":null,"object_position":null,"order":null,"overflow":null,"padding":null,"right":null,"top":null,"visibility":null,"width":null}},"525568fa69ae4ef6b8765101f7d0b0a8":{"model_module":"@jupyter-widgets/base","model_module_version":"2.0.0","model_name":"LayoutModel","state":{"_model_module":"@jupyter-widgets/base","_model_module_version":"2.0.0","_model_name":"LayoutModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"2.0.0","_view_name":"LayoutView","align_content":null,"align_items":null,"align_self":null,"border_bottom":null,"border_left":null,"border_right":null,"border_top":null,"bottom":null,"display":null,"flex":null,"flex_flow":null,"grid_area":null,"grid_auto_columns":null,"grid_auto_flow":null,"grid_auto_rows":null,"grid_column":null,"grid_gap":null,"grid_row":null,"grid_template_areas":null,"grid_template_columns":null,"grid_template_rows":null,"height":null,"justify_content":null,"justify_items":null,"left":null,"margin":null,"max_height":null,"max_width":null,"min_height":null,"min_width":null,"object_fit":null,"object_position":null,"order":null,"overflow":null,"padding":null,"right":null,"top":null,"visibility":null,"width":null}},"55c429a44420449cbac0a9ec6469f11d":{"model_module":"@jupyter-widgets/controls","model_module_version":"2.0.0","model_name":"HTMLStyleModel","state":{"_model_module":"@jupyter-widgets/controls","_model_module_version":"2.0.0","_model_name":"HTMLStyleModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"2.0.0","_view_name":"StyleView","background":null,"description_width":"","font_size":null,"text_color":null}},"7dcc127f175e434dae11b576afd72e5b":{"model_module":"@jupyter-widgets/base","model_module_version":"2.0.0","model_name":"LayoutModel","state":{"_model_module":"@jupyter-widgets/base","_model_module_version":"2.0.0","_model_name":"LayoutModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"2.0.0","_view_name":"LayoutView","align_content":null,"align_items":null,"align_self":null,"border_bottom":null,"border_left":null,"border_right":null,"border_top":null,"bottom":null,"display":null,"flex":null,"flex_flow":null,"grid_area":null,"grid_auto_columns":null,"grid_auto_flow":null,"grid_auto_rows":null,"grid_column":null,"grid_gap":null,"grid_row":null,"grid_template_areas":null,"grid_template_columns":null,"grid_template_rows":null,"height":null,"justify_content":null,"justify_items":null,"left":null,"margin":null,"max_height":null,"max_width":null,"min_height":null,"min_width":null,"object_fit":null,"object_position":null,"order":null,"overflow":null,"padding":null,"right":null,"top":null,"visibility":null,"width":null}},"8788d0061a634e0d98a839b45d7961f1":{"model_module":"@jupyter-widgets/controls","model_module_version":"2.0.0","model_name":"HTMLStyleModel","state":{"_model_module":"@jupyter-widgets/controls","_model_module_version":"2.0.0","_model_name":"HTMLStyleModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"2.0.0","_view_name":"StyleView","background":null,"description_width":"","font_size":null,"text_color":null}},"944972bc34574ef79d93a3b6d503e58a":{"model_module":"@jupyter-widgets/controls","model_module_version":"2.0.0","model_name":"HTMLStyleModel","state":{"_model_module":"@jupyter-widgets/controls","_model_module_version":"2.0.0","_model_name":"HTMLStyleModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"2.0.0","_view_name":"StyleView","background":null,"description_width":"","font_size":null,"text_color":null}},"9cda4da9b73a43b5b2d6c9b39a0b7881":{"model_module":"@jupyter-widgets/base","model_module_version":"2.0.0","model_name":"LayoutModel","state":{"_model_module":"@jupyter-widgets/base","_model_module_version":"2.0.0","_model_name":"LayoutModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"2.0.0","_view_name":"LayoutView","align_content":null,"align_items":null,"align_self":null,"border_bottom":null,"border_left":null,"border_right":null,"border_top":null,"bottom":null,"display":null,"flex":null,"flex_flow":null,"grid_area":null,"grid_auto_columns":null,"grid_auto_flow":null,"grid_auto_rows":null,"grid_column":null,"grid_gap":null,"grid_row":null,"grid_template_areas":null,"grid_template_columns":null,"grid_template_rows":null,"height":null,"justify_content":null,"justify_items":null,"left":null,"margin":null,"max_height":null,"max_width":null,"min_height":null,"min_width":null,"object_fit":null,"object_position":null,"order":null,"overflow":null,"padding":null,"right":null,"top":null,"visibility":null,"width":null}},"bad10033ad784f02ab49f8e30ae86aac":{"model_module":"@jupyter-widgets/controls","model_module_version":"2.0.0","model_name":"HTMLModel","state":{"_dom_classes":[],"_model_module":"@jupyter-widgets/controls","_model_module_version":"2.0.0","_model_name":"HTMLModel","_view_count":null,"_view_module":"@jupyter-widgets/controls","_view_module_version":"2.0.0","_view_name":"HTMLView","description":"","description_allow_html":false,"layout":"IPY_MODEL_00716a593cb948c989e8a752187448c7","placeholder":"​","style":"IPY_MODEL_55c429a44420449cbac0a9ec6469f11d","tabbable":null,"tooltip":null,"value":"100%"}},"c76c3675718d4cbf81168d274f6b8a7b":{"model_module":"@jupyter-widgets/controls","model_module_version":"2.0.0","model_name":"HTMLModel","state":{"_dom_classes":[],"_model_module":"@jupyter-widgets/controls","_model_module_version":"2.0.0","_model_name":"HTMLModel","_view_count":null,"_view_module":"@jupyter-widgets/controls","_view_module_version":"2.0.0","_view_name":"HTMLView","description":"","description_allow_html":false,"layout":"IPY_MODEL_7dcc127f175e434dae11b576afd72e5b","placeholder":"​","style":"IPY_MODEL_8788d0061a634e0d98a839b45d7961f1","tabbable":null,"tooltip":null,"value":" 14000/14000 [01:47&lt;00:00, 136.08it/s]"}},"da7a2d41f259458584facd0d73816ff0":{"model_module":"@jupyter-widgets/controls","model_module_version":"2.0.0","model_name":"HBoxModel","state":{"_dom_classes":[],"_model_module":"@jupyter-widgets/controls","_model_module_version":"2.0.0","_model_name":"HBoxModel","_view_count":null,"_view_module":"@jupyter-widgets/controls","_view_module_version":"2.0.0","_view_name":"HBoxView","box_style":"","children":["IPY_MODEL_bad10033ad784f02ab49f8e30ae86aac","IPY_MODEL_307fc2b786594671b667f2b5f01ab414","IPY_MODEL_362ae2072d7e47ccbdc2584e0890447d"],"layout":"IPY_MODEL_3cef2e7399fb4b3b8b0a6162b5ac2ea4","tabbable":null,"tooltip":null}},"ddc1a1684e034e49997f8fe6b170c397":{"model_module":"@jupyter-widgets/base","model_module_version":"2.0.0","model_name":"LayoutModel","state":{"_model_module":"@jupyter-widgets/base","_model_module_version":"2.0.0","_model_name":"LayoutModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"2.0.0","_view_name":"LayoutView","align_content":null,"align_items":null,"align_self":null,"border_bottom":null,"border_left":null,"border_right":null,"border_top":null,"bottom":null,"display":null,"flex":null,"flex_flow":null,"grid_area":null,"grid_auto_columns":null,"grid_auto_flow":null,"grid_auto_rows":null,"grid_column":null,"grid_gap":null,"grid_row":null,"grid_template_areas":null,"grid_template_columns":null,"grid_template_rows":null,"height":null,"justify_content":null,"justify_items":null,"left":null,"margin":null,"max_height":null,"max_width":null,"min_height":null,"min_width":null,"object_fit":null,"object_position":null,"order":null,"overflow":null,"padding":null,"right":null,"top":null,"visibility":null,"width":null}},"df64bd2ab3de476b925077c7001dd9a8":{"model_module":"@jupyter-widgets/controls","model_module_version":"2.0.0","model_name":"HTMLStyleModel","state":{"_model_module":"@jupyter-widgets/controls","_model_module_version":"2.0.0","_model_name":"HTMLStyleModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"2.0.0","_view_name":"StyleView","background":null,"description_width":"","font_size":null,"text_color":null}},"e9172705834545159ce9f7ddc26caf00":{"model_module":"@jupyter-widgets/controls","model_module_version":"2.0.0","model_name":"HTMLModel","state":{"_dom_classes":[],"_model_module":"@jupyter-widgets/controls","_model_module_version":"2.0.0","_model_name":"HTMLModel","_view_count":null,"_view_module":"@jupyter-widgets/controls","_view_module_version":"2.0.0","_view_name":"HTMLView","description":"","description_allow_html":false,"layout":"IPY_MODEL_525568fa69ae4ef6b8765101f7d0b0a8","placeholder":"​","style":"IPY_MODEL_944972bc34574ef79d93a3b6d503e58a","tabbable":null,"tooltip":null,"value":"Chargement: 100%"}},"e9721ae44a3e46949df436a1f36a2920":{"model_module":"@jupyter-widgets/controls","model_module_version":"2.0.0","model_name":"HBoxModel","state":{"_dom_classes":[],"_model_module":"@jupyter-widgets/controls","_model_module_version":"2.0.0","_model_name":"HBoxModel","_view_count":null,"_view_module":"@jupyter-widgets/controls","_view_module_version":"2.0.0","_view_name":"HBoxView","box_style":"","children":["IPY_MODEL_e9172705834545159ce9f7ddc26caf00","IPY_MODEL_14c278d07c164ad1890416f264814a0e","IPY_MODEL_c76c3675718d4cbf81168d274f6b8a7b"],"layout":"IPY_MODEL_9cda4da9b73a43b5b2d6c9b39a0b7881","tabbable":null,"tooltip":null}}},"version_major":2,"version_minor":0}}},"nbformat_minor":4,"nbformat":4,"cells":[{"id":"7de962f1","cell_type":"markdown","source":"# Import des images","metadata":{"papermill":{"duration":0.00275,"end_time":"2026-01-10T02:38:01.508726","exception":false,"start_time":"2026-01-10T02:38:01.505976","status":"completed"},"tags":[]}},{"id":"afb7e393","cell_type":"code","source":"import os\nimport numpy as np\nimport pandas as pd\nfrom tensorflow.keras.preprocessing.image import load_img, img_to_array\nfrom tensorflow.keras.utils import to_categorical\nfrom tqdm.notebook import tqdm # Barre de progression pour Kaggle\n\nprojectFolder = \"/kaggle/input/2025-competition-apprentissage-i-3-d/\"\ntrainFolder = os.path.join(projectFolder, \"train\")\ntestFolder = os.path.join(projectFolder, \"test\")\nlabelsFile = os.path.join(projectFolder, \"train.csv\")\n\nim_dim = 129\nTARGET_SIZE = (im_dim, im_dim)\nSAMPLE_SIZE = 14000\nRANDOM_STATE = 13\n\ndf = pd.read_csv(labelsFile)\n\nif SAMPLE_SIZE and SAMPLE_SIZE < len(df):\n    print(f\"Chargement d'un sous-ensemble aléatoire de {SAMPLE_SIZE} images...\")\n    df_subset = df.sample(n=SAMPLE_SIZE, random_state=RANDOM_STATE).reset_index(drop=True)\nelse:\n    print(\"Chargement de toutes les données...\")\n    df_subset = df\n\ndef load_single_image(id_image, folder_path):\n    path = os.path.join(folder_path, f\"{id_image}.png\") \n    \n    if os.path.exists(path):\n        img = load_img(path, target_size=TARGET_SIZE, color_mode=\"grayscale\",interpolation=\"nearest\") \n        img_array = img_to_array(img) / 255.0 # Normalisation entre 0 et 1\n        return img_array\n    else:\n        return None\n\ntrain_images = []\ntrain_labels = []\n\n# tqdm permet de voir l'avancement (cosmétique pour avoir la barre de chargement)\nfor idx, row in tqdm(df_subset.iterrows(), total=df_subset.shape[0], desc=\"Chargement\"):\n    img = load_single_image(row['ID'], trainFolder) \n    \n    if img is not None:\n        train_images.append(img)\n        train_labels.append(row['Category']) \n\nX_train = np.array(train_images)\ny_train = np.array(train_labels)\n\nnum_classes = df['Category'].nunique()\ny_train_encoded = to_categorical(y_train, num_classes=num_classes)\n\nprint(f\"Images chargées : {X_train.shape}\")\nprint(f\"Labels chargés : {y_train_encoded.shape}\")","metadata":{"_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","papermill":{"duration":123.879374,"end_time":"2026-01-10T02:40:05.390179","exception":false,"start_time":"2026-01-10T02:38:01.510805","status":"completed"},"tags":[],"trusted":true,"execution":{"iopub.status.busy":"2026-01-21T19:50:33.515293Z","iopub.execute_input":"2026-01-21T19:50:33.515588Z","iopub.status.idle":"2026-01-21T19:52:19.488866Z","shell.execute_reply.started":"2026-01-21T19:50:33.515551Z","shell.execute_reply":"2026-01-21T19:52:19.487883Z"}},"outputs":[],"execution_count":null},{"id":"faa70551","cell_type":"markdown","source":"# Réseau de neurones","metadata":{"papermill":{"duration":0.00216,"end_time":"2026-01-10T02:40:05.669268","exception":false,"start_time":"2026-01-10T02:40:05.667108","status":"completed"},"tags":[]}},{"id":"095ef3c8","cell_type":"code","source":"from tensorflow.keras.models import Sequential\nfrom tensorflow.keras.layers import Conv2D, MaxPooling2D, Flatten, Dense, Dropout, BatchNormalization\nfrom tensorflow.keras.optimizers import AdamW\nfrom tensorflow.keras.layers import LeakyReLU\n\nimage_size = TARGET_SIZE # << Important est synchronisé avec la lecture des fichiers.\nnum_classes = df['Category'].nunique()\n\n\nmodel = Sequential([\n    Conv2D(16, (3, 3), padding='same', input_shape=(im_dim, im_dim, 1)),\n    LeakyReLU(alpha=0.1),\n    BatchNormalization(),\n    MaxPooling2D((2,2), padding='same'),\n    Conv2D(32, (3, 3), strides=(2,2), padding='same', input_shape=(im_dim, im_dim, 1)),\n    LeakyReLU(alpha=0.1),\n    BatchNormalization(),\n    MaxPooling2D((2,2), padding='same'),\n    \n\n    Conv2D(128, (3, 3), padding='same'),\n    Dropout(0.2),\n    LeakyReLU(alpha=0.1),\n    Conv2D(256, (3, 3), padding='same'),\n    LeakyReLU(alpha=0.1),\n\n    BatchNormalization(),\n    MaxPooling2D((2,2), padding='same'),\n\n    Conv2D(128, (3, 3), padding='same'),\n    LeakyReLU(alpha=0.1),\n    Conv2D(256, (3, 3), padding='same'),\n    Dropout(0.25),\n    LeakyReLU(alpha=0.1),\n\n    BatchNormalization(),\n    MaxPooling2D((2,2), padding='same'),\n\n    Flatten(),\n    Dropout(0.3),\n    \n    Dense(512),\n    LeakyReLU(alpha=0.1),\n    Dropout(0.5),\n    Dense(256),\n    LeakyReLU(alpha=0.1),\n    Dropout(0.5),\n    Dense(128),\n    LeakyReLU(alpha=0.1),\n    Dropout(0.6),\n    \n    Dense(5, activation='softmax')\n])\n\noptimizer = AdamW(learning_rate=0.0002,weight_decay=0.1)\n\nmodel.compile(\n    optimizer=optimizer,\n    loss='categorical_crossentropy',\n    metrics=['accuracy']\n)\n\nmodel.summary()","metadata":{"papermill":{"duration":2.303234,"end_time":"2026-01-10T02:40:07.974760","exception":false,"start_time":"2026-01-10T02:40:05.671526","status":"completed"},"tags":[],"trusted":true,"execution":{"iopub.status.busy":"2026-01-21T19:52:19.490588Z","iopub.execute_input":"2026-01-21T19:52:19.490890Z","iopub.status.idle":"2026-01-21T19:52:21.670693Z","shell.execute_reply.started":"2026-01-21T19:52:19.490865Z","shell.execute_reply":"2026-01-21T19:52:21.670087Z"}},"outputs":[],"execution_count":null},{"id":"66a32984","cell_type":"markdown","source":"# Préparation des données","metadata":{"papermill":{"duration":0.002845,"end_time":"2026-01-10T02:40:07.980681","exception":false,"start_time":"2026-01-10T02:40:07.977836","status":"completed"},"tags":[]}},{"id":"6c05df39","cell_type":"code","source":"import numpy as np\nfrom sklearn.model_selection import train_test_split\nfrom tensorflow.keras.callbacks import EarlyStopping, ReduceLROnPlateau\nfrom tensorflow.keras.preprocessing.image import ImageDataGenerator\nfrom sklearn.utils import class_weight\n\n# split des données en test et validation.\nX_train_split, X_val, y_train_split, y_val = train_test_split(\n    X_train, y_train_encoded, \n    test_size=0.15,\n    random_state=64,\n    stratify=y_train_encoded\n)\n\n\n\n# On augmente la quantité d'image de la classe 4\nclasse_cible = 4\nindices_classe_4 = np.where(np.argmax(y_train_split, axis=1) == classe_cible)[0]\nprint(f\"Nombre original d'images classe {classe_cible} : {len(indices_classe_4)}\")\nfacteur_duplication = 7\nX_add = X_train_split[indices_classe_4]\ny_add = y_train_split[indices_classe_4]\n\nfor _ in range(facteur_duplication):\n    X_train_split = np.concatenate((X_train_split, X_add), axis=0)\n    y_train_split = np.concatenate((y_train_split, y_add), axis=0)\n\n# On mélange pour ne pas avoir tous les 4 à la fin\nindices = np.arange(X_train_split.shape[0])\nnp.random.shuffle(indices)\nX_train_split = X_train_split[indices]\ny_train_split = y_train_split[indices]\n\nprint(f\"Nouveau total d'images d'entraînement : {X_train_split.shape[0]}\")\n\n\n# On calcule les poids de chaque class\ny_train_integers = np.argmax(y_train_split, axis=1)\n\nclass_weights = class_weight.compute_class_weight(\n    class_weight='balanced',\n    classes=np.unique(y_train_integers),\n    y=y_train_integers\n)\nclass_weights_dict = dict(enumerate(class_weights))\n\nprint(\"Poids des classes (réajustés après augmentation) :\", class_weights_dict)\n\n# On créer un vecteur de poids selon les class des images\nsample_weights = np.array([class_weights_dict[label] for label in y_train_integers])\n\n# On créer un Générateur de modification pour nos images\ntrain_datagen = ImageDataGenerator(\n    rotation_range=90,\n    zoom_range=0.2,\n    horizontal_flip=True,\n    vertical_flip=True,\n    width_shift_range=0.0,\n    height_shift_range=0.0,\n    fill_mode='nearest'\n)\n\nBATCH_SIZE = 64 \nTransformer_factor = 10 # Nombre de fois que chaque image de test passera, donc 20 modifications possible par image\n\n#Notre génateur d'entrainement \ntrain_generator = train_datagen.flow(\n    X_train_split, \n    y_train_split, \n    batch_size=BATCH_SIZE,\n    shuffle=True,\n    seed=215,\n    sample_weight=sample_weights\n)\n# On force le generateur à boucler pour bien répéter les images avec de nouvelles modifications.\ndef infinite_generator(generator):\n    while True:\n        for batch in generator:\n            yield batch\n\ninfinite_train_generator = infinite_generator(train_generator)\n","metadata":{"papermill":{"duration":3187.993304,"end_time":"2026-01-10T03:33:15.976807","exception":false,"start_time":"2026-01-10T02:40:07.983503","status":"completed"},"tags":[],"trusted":true,"execution":{"iopub.status.busy":"2026-01-21T19:52:21.671575Z","iopub.execute_input":"2026-01-21T19:52:21.671767Z","iopub.status.idle":"2026-01-21T19:52:25.183621Z","shell.execute_reply.started":"2026-01-21T19:52:21.671748Z","shell.execute_reply":"2026-01-21T19:52:25.182953Z"}},"outputs":[],"execution_count":null},{"id":"75fd8c2d","cell_type":"markdown","source":"# Exemples d'images augmentées ","metadata":{}},{"id":"4bcb35c9","cell_type":"code","source":"import matplotlib.pyplot as plt\nimport numpy as np\n\n# next() déclenche la génération d'un lot avec les transformations aléatoires\nbatch = next(train_generator)\nimages = batch[0] \nlabels = batch[1] \n\nplt.figure(figsize=(10, 10))\nfor i in range(9):\n    ax = plt.subplot(3, 3, i + 1)\n    \n    # Vos images sont en (129, 129, 1), .squeeze() retire la dimension de canal inutile pour l'affichage\n    img_to_show = images[i].squeeze()\n    \n    plt.imshow(img_to_show, cmap='gray')\n    plt.title(f\"Label: {np.argmax(labels[i])}\") # Affiche la classe\n    plt.axis(\"off\")\n\nplt.tight_layout()\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-21T19:52:25.184521Z","iopub.execute_input":"2026-01-21T19:52:25.185015Z","iopub.status.idle":"2026-01-21T19:52:26.015519Z","shell.execute_reply.started":"2026-01-21T19:52:25.184989Z","shell.execute_reply":"2026-01-21T19:52:26.014789Z"}},"outputs":[],"execution_count":null},{"id":"767fb486","cell_type":"markdown","source":"# Entraînement du modèle","metadata":{}},{"id":"feaf4b2d","cell_type":"code","source":"early_stopping = EarlyStopping(\n    monitor='val_loss',\n    patience=7,\n    restore_best_weights=True\n)\n\nreduce_lr = ReduceLROnPlateau(\n    monitor='val_loss',\n    factor=0.7,\n    patience=3,\n    min_lr=1e-8,\n    verbose=1\n)\n\nspe = len(X_train_split) // BATCH_SIZE * Transformer_factor\n\nhistory = model.fit(\n    infinite_train_generator,\n    steps_per_epoch=spe,\n    epochs=64,\n    validation_data=(X_val, y_val), \n    validation_batch_size=BATCH_SIZE,\n    callbacks=[early_stopping, reduce_lr]\n)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-21T19:52:26.016524Z","iopub.execute_input":"2026-01-21T19:52:26.016757Z"}},"outputs":[],"execution_count":null},{"id":"04f1d5e1","cell_type":"markdown","source":"# Analyse des résultats","metadata":{"papermill":{"duration":0.966365,"end_time":"2026-01-10T03:33:17.810410","exception":false,"start_time":"2026-01-10T03:33:16.844045","status":"completed"},"tags":[]}},{"id":"962ce0ff","cell_type":"code","source":"import matplotlib.pyplot as plt\nimport seaborn as sns\nfrom sklearn.metrics import confusion_matrix\nfrom sklearn.metrics import classification_report\n# Perte\nplt.plot(history.history['loss'], label='Train Loss')\nplt.plot(history.history['val_loss'], label='Validation Loss')\nplt.legend()\nplt.title('Loss')\nplt.show()\n\n# Précision\nplt.plot(history.history['accuracy'], label='Train Accuracy')\nplt.plot(history.history['val_accuracy'], label='Validation Accuracy')\nplt.legend()\nplt.title('Accuracy')\nplt.show()\n\nprint(history.history['accuracy'][-1])\n\n# Génère une prédiction pour calculer la matrice de confusion\ny_pred_probs = model.predict(X_val)\ny_pred_classes = np.argmax(y_pred_probs, axis=1)\ny_true_classes = np.argmax(y_val, axis=1) # Convertir le one-hot en index (0, 1, 2...)\n\ncm = confusion_matrix(y_true_classes, y_pred_classes)\n\n# Affiche la matrice\nplt.figure(figsize=(10, 8))\nsns.heatmap(cm, annot=True, fmt='d', cmap='Blues', cbar=False)\nplt.xlabel('Classe Prédite')\nplt.ylabel('Classe Réelle')\nplt.title('Matrice de Confusion (Données de Validation)')\nplt.show()\n\nprint(\"\\nRapport de classification détaillé :\")\n#Rapport détaillé\nprint(classification_report(y_true_classes, y_pred_classes))","metadata":{"papermill":{"duration":1.230171,"end_time":"2026-01-10T03:33:19.896062","exception":false,"start_time":"2026-01-10T03:33:18.665891","status":"completed"},"tags":[],"trusted":true},"outputs":[],"execution_count":null},{"id":"a050e053","cell_type":"markdown","source":"# Génération finale","metadata":{"papermill":{"duration":0.934771,"end_time":"2026-01-10T03:33:21.684708","exception":false,"start_time":"2026-01-10T03:33:20.749937","status":"completed"},"tags":[]}},{"id":"93c0a021","cell_type":"code","source":"from sklearn.preprocessing import LabelEncoder\nfrom tensorflow.keras.preprocessing.image import load_img, img_to_array\nfrom tqdm.notebook import tqdm\n\nlabel_encoder = LabelEncoder()\nlabel_encoder.fit(y_train)\n\ntest_files = sorted(os.listdir(testFolder))\ntest_images = []\ntest_ids = []\n\nprint(\"Chargement des images de test...\")\nfor filename in tqdm(test_files):\n    if filename.endswith(\".png\"):\n        img_path = os.path.join(testFolder, filename)\n        img = load_img(img_path, target_size=TARGET_SIZE, color_mode=\"grayscale\",interpolation=\"nearest\")\n        img_array = img_to_array(img) / 255.0\n        test_images.append(img_array)\n        test_ids.append(filename.replace('.png', ''))\n\nX_test = np.array(test_images)\nprint(f\"Images de test prêtes : {X_test.shape}\")\n\nprint(\"Génération des prédictions...\")\npredictions = model.predict(X_test, batch_size=1)\n\npredicted_classes_indices = np.argmax(predictions, axis=1)\n\npredicted_labels = label_encoder.inverse_transform(predicted_classes_indices)\nsubmission_df = pd.DataFrame({\n    'ID': test_ids,\n    'Category': predicted_labels\n})\n\nprint(submission_df.head())\n\nsubmission_file = 'submission.csv'\nsubmission_df.to_csv(submission_file, index=False)\n\nprint(f\"Fichier {submission_file} généré avec succès ({len(submission_df)} lignes).\")","metadata":{"papermill":{"duration":67.928533,"end_time":"2026-01-10T03:34:30.561034","exception":false,"start_time":"2026-01-10T03:33:22.632501","status":"completed"},"tags":[],"trusted":true},"outputs":[],"execution_count":null},{"id":"1e9828c9","cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}