sycod commited on
Commit
0fe2514
·
1 Parent(s): fc06865

start EfficientNetV2M

Browse files
Files changed (4) hide show
  1. EDA.ipynb +262 -310
  2. README.md +2 -3
  3. config.yaml +3 -1
  4. src/load_data.py +69 -26
EDA.ipynb CHANGED
@@ -16,14 +16,13 @@
16
  " - [🚧 Image exploration](#toc3_3_) \n",
17
  "- [Data preprocessing](#toc4_) \n",
18
  "- [🦄🦄 CHECKPOINT 🦄🦄](#toc5_) \n",
19
- "- [🚧 OVERSAMPLING](#toc6_) \n",
20
- "- [Model Training](#toc7_) \n",
21
- " - [Load configuration](#toc7_1_) \n",
22
- "- [🚧 MODEL CHOICE](#toc8_) \n",
23
- " - [Prepare data](#toc8_1_) \n",
24
- " - [Prepare model](#toc8_2_) \n",
25
- " - [Training](#toc8_3_) \n",
26
- " - [Random Baseline](#toc8_4_) \n",
27
  "\n",
28
  "<!-- vscode-jupyter-toc-config\n",
29
  "\tnumbering=false\n",
@@ -71,25 +70,25 @@
71
  "text": [
72
  "/Users/julmat/Documents/hugging_face/frugal_cviz/.venv/lib/python3.12/site-packages/tqdm/auto.py:21: TqdmWarning: IProgress not found. Please update jupyter and ipywidgets. See https://ipywidgets.readthedocs.io/en/stable/user_install.html\n",
73
  " from .autonotebook import tqdm as notebook_tqdm\n",
74
- "[codecarbon WARNING @ 00:53:58] Multiple instances of codecarbon are allowed to run at the same time.\n",
75
- "[codecarbon INFO @ 00:53:58] [setup] RAM Tracking...\n",
76
- "[codecarbon INFO @ 00:53:58] [setup] CPU Tracking...\n",
77
- "[codecarbon WARNING @ 00:53:58] No CPU tracking mode found. Falling back on CPU constant mode. \n",
78
  " Mac OS and ARM processor detected: Please enable PowerMetrics sudo to measure CPU\n",
79
  "\n",
80
- "[codecarbon INFO @ 00:53:58] CPU Model on constant consumption mode: Apple M1\n",
81
- "[codecarbon INFO @ 00:53:58] [setup] GPU Tracking...\n",
82
- "[codecarbon INFO @ 00:53:58] No GPU found.\n",
83
- "[codecarbon INFO @ 00:53:58] >>> Tracker's metadata:\n",
84
- "[codecarbon INFO @ 00:53:58] Platform system: macOS-15.2-arm64-arm-64bit\n",
85
- "[codecarbon INFO @ 00:53:58] Python version: 3.12.7\n",
86
- "[codecarbon INFO @ 00:53:58] CodeCarbon version: 2.8.3\n",
87
- "[codecarbon INFO @ 00:53:58] Available RAM : 16.000 GB\n",
88
- "[codecarbon INFO @ 00:53:58] CPU count: 8\n",
89
- "[codecarbon INFO @ 00:53:58] CPU model: Apple M1\n",
90
- "[codecarbon INFO @ 00:53:58] GPU count: None\n",
91
- "[codecarbon INFO @ 00:53:58] GPU model: None\n",
92
- "[codecarbon INFO @ 00:54:00] Saving emissions data to file /Users/julmat/Documents/hugging_face/frugal_cviz/emissions.csv\n"
93
  ]
94
  }
95
  ],
@@ -106,8 +105,8 @@
106
  "\n",
107
  "# ML\n",
108
  "from keras import Model\n",
109
- "from keras.applications import EfficientNetB0\n",
110
- "from keras.layers import Flatten, Dense\n",
111
  "from keras.metrics import Precision, Recall\n",
112
  "from keras.optimizers import AdamW\n",
113
  "from keras.utils import image_dataset_from_directory\n",
@@ -126,7 +125,7 @@
126
  "from tasks.image import parse_boxes, compute_iou, compute_max_iou\n",
127
  "\n",
128
  "\n",
129
- "# Logging configuration (see all outputs, even DEBUG or INFO)\n",
130
  "logger = logging.getLogger()\n",
131
  "logger.setLevel(logging.INFO)"
132
  ]
@@ -149,7 +148,9 @@
149
  " cfg = yaml.safe_load(f)\n",
150
  "# Data\n",
151
  "OUTPUT_DIR = cfg[\"data_root_dir\"]\n",
152
- "DB_INFO_URI = cfg[\"db_info_uri\"]\n",
 
 
153
  "REPO_ID = cfg[\"repo_id\"]\n",
154
  "SPLIT_SIZE = cfg[\"split_size\"]\n",
155
  "RDM_SEED = cfg[\"rdm_seed\"]\n",
@@ -1858,16 +1859,14 @@
1858
  "metadata": {},
1859
  "outputs": [
1860
  {
1861
- "name": "stdout",
1862
  "output_type": "stream",
1863
  "text": [
1864
- "CPU times: user 1.15 s, sys: 5.92 s, total: 7.07 s\n",
1865
- "Wall time: 17.5 s\n"
1866
  ]
1867
  }
1868
  ],
1869
  "source": [
1870
- "%%time\n",
1871
  "df_format = format_data_keras(df_clean.copy())"
1872
  ]
1873
  },
@@ -1880,6 +1879,15 @@
1880
  "- update dataframe"
1881
  ]
1882
  },
 
 
 
 
 
 
 
 
 
1883
  {
1884
  "cell_type": "code",
1885
  "execution_count": 13,
@@ -1889,34 +1897,12 @@
1889
  "name": "stderr",
1890
  "output_type": "stream",
1891
  "text": [
1892
- "INFO:root:⚙️ Processing train split...\n",
1893
- "INFO:root:⚙️ Transforming 16025 images...\n",
1894
- "INFO:root:✅ Transformed 3802 images with blur_img\n",
1895
- "INFO:root:✅ Transformed 3802 images with flip_img\n",
1896
- "INFO:root:✅ Transformed 3802 images with blur_flip_img\n",
1897
- "INFO:root:✅ Transformed 3802 images with eq_img\n",
1898
- "INFO:root:✅ Transformed 817 images with 180_img\n",
1899
- "INFO:root:⚙️ Processing val split...\n",
1900
- "INFO:root:⚙️ Transforming 2571 images...\n",
1901
- "INFO:root:✅ Transformed 764 images with blur_img\n",
1902
- "INFO:root:✅ Transformed 764 images with flip_img\n",
1903
- "INFO:root:✅ Transformed 764 images with blur_flip_img\n",
1904
- "INFO:root:✅ Transformed 279 images with eq_img\n",
1905
- "INFO:root:✅ Transformed 0 images with 180_img\n"
1906
- ]
1907
- },
1908
- {
1909
- "name": "stdout",
1910
- "output_type": "stream",
1911
- "text": [
1912
- "CPU times: user 3min 54s, sys: 15.9 s, total: 4min 10s\n",
1913
- "Wall time: 4min 19s\n"
1914
  ]
1915
  }
1916
  ],
1917
  "source": [
1918
- "%%time\n",
1919
- "df_aug = oversample_class(df_format.copy())"
1920
  ]
1921
  },
1922
  {
@@ -1928,7 +1914,7 @@
1928
  },
1929
  {
1930
  "cell_type": "code",
1931
- "execution_count": 17,
1932
  "metadata": {},
1933
  "outputs": [
1934
  {
@@ -2841,21 +2827,23 @@
2841
  "cell_type": "markdown",
2842
  "metadata": {},
2843
  "source": [
2844
- "> 💡 Much better... but a quality balance issue just appeared: \"no_smoke\" images are often blured, flipped, equalized or rotated whereas \"smoke\" images are not."
2845
  ]
2846
  },
2847
  {
2848
- "cell_type": "markdown",
 
2849
  "metadata": {},
 
2850
  "source": [
2851
- "Balance classes quality by applying the same amount of image for the other class"
2852
  ]
2853
  },
2854
  {
2855
  "cell_type": "markdown",
2856
  "metadata": {},
2857
  "source": [
2858
- "# <a id='toc5_'></a>[🦄🦄 CHECKPOINT 🦄🦄](#toc0_)"
2859
  ]
2860
  },
2861
  {
@@ -2864,68 +2852,96 @@
2864
  "metadata": {},
2865
  "outputs": [
2866
  {
2867
- "ename": "NameError",
2868
- "evalue": "name 'stop' is not defined",
2869
- "output_type": "error",
2870
- "traceback": [
2871
- "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
2872
- "\u001b[0;31mNameError\u001b[0m Traceback (most recent call last)",
2873
- "Cell \u001b[0;32mIn[16], line 1\u001b[0m\n\u001b[0;32m----> 1\u001b[0m \u001b[43mstop\u001b[49m\n",
2874
- "\u001b[0;31mNameError\u001b[0m: name 'stop' is not defined"
2875
- ]
2876
  }
2877
  ],
2878
  "source": [
2879
- "stop"
 
 
 
 
 
 
 
2880
  ]
2881
  },
2882
  {
2883
  "cell_type": "markdown",
2884
  "metadata": {},
2885
  "source": [
2886
- "# <a id='toc7_'></a>[Model Training](#toc0_)"
2887
  ]
2888
  },
2889
  {
2890
  "cell_type": "markdown",
2891
  "metadata": {},
2892
  "source": [
2893
- "## <a id='toc7_1_'></a>[Load configuration](#toc0_)"
2894
  ]
2895
  },
2896
  {
2897
  "cell_type": "markdown",
2898
  "metadata": {},
2899
  "source": [
2900
- "# <a id='toc8_'></a>[🚧 MODEL CHOICE](#toc0_)\n",
2901
  "\n",
2902
  "- https://paperswithcode.com/sota/image-classification-on-imagenet\n",
2903
- "- https://keras.io/api/applications/efficientnet_v2/#efficientnetv2m-function"
 
 
 
 
 
 
 
 
2904
  ]
2905
  },
2906
  {
2907
  "cell_type": "code",
2908
- "execution_count": null,
2909
  "metadata": {},
2910
- "outputs": [],
 
 
 
 
 
 
 
 
 
 
 
 
2911
  "source": [
2912
- "# from keras.applications import EfficientNetV2M\n",
2913
- "\n",
2914
- "# model = EfficientNetV2M("
2915
  ]
2916
  },
2917
  {
2918
  "cell_type": "code",
2919
- "execution_count": 183,
2920
  "metadata": {},
2921
  "outputs": [],
2922
  "source": [
2923
  "# Model config\n",
2924
- "model_name = \"EfficientNetB0\"\n",
2925
  "input_size = (224, 224)\n",
2926
- "batch_size = 48\n",
2927
- "n_epochs = 100\n",
2928
  "optimizer = AdamW(learning_rate=0.0002, weight_decay=0.05)\n",
 
 
 
 
 
2929
  "loss = \"binary_crossentropy\"\n",
2930
  "metrics = [\"accuracy\", Precision(), Recall()]\n",
2931
  "# metrics = [\"accuracy\", Precision(), Recall(), \"f1_score\"]\n",
@@ -2945,97 +2961,22 @@
2945
  "cell_type": "markdown",
2946
  "metadata": {},
2947
  "source": [
2948
- "## <a id='toc8_1_'></a>[Prepare data](#toc0_)"
2949
- ]
2950
- },
2951
- {
2952
- "cell_type": "markdown",
2953
- "metadata": {},
2954
- "source": [
2955
- "Create datasets from local images and labels"
2956
- ]
2957
- },
2958
- {
2959
- "cell_type": "code",
2960
- "execution_count": 184,
2961
- "metadata": {},
2962
- "outputs": [
2963
- {
2964
- "name": "stdout",
2965
- "output_type": "stream",
2966
- "text": [
2967
- "Train dataset:\n",
2968
- "Found 23629 files belonging to 2 classes.\n",
2969
- "\n",
2970
- "Val dataset:\n",
2971
- "Found 4099 files belonging to 2 classes.\n",
2972
- "\n",
2973
- "Test dataset:\n",
2974
- "Found 5908 files belonging to 2 classes.\n"
2975
- ]
2976
- }
2977
- ],
2978
- "source": [
2979
- "# Prepare for outputs\n",
2980
- "os.makedirs(MODELS_ROOT_DIR, exist_ok=True)\n",
2981
- "y = df[\"label\"]\n",
2982
- "X = df[\"uri\"]\n",
2983
- "\n",
2984
- "# Create datasets\n",
2985
- "print(\"Train dataset:\")\n",
2986
- "train_ds = image_dataset_from_directory(\n",
2987
- " train_dir,\n",
2988
- " labels=\"inferred\", # class names upon folders structure\n",
2989
- " label_mode=\"int\", # integer encoding\n",
2990
- " shuffle=True, # shuffle images\n",
2991
- " seed=42, # random seed\n",
2992
- " image_size=input_size, # automatic resizing\n",
2993
- " batch_size=batch_size, # tensor shape[0]\n",
2994
- ")\n",
2995
- "\n",
2996
- "print(\"\\nVal dataset:\")\n",
2997
- "val_ds = image_dataset_from_directory(\n",
2998
- " val_dir,\n",
2999
- " labels=\"inferred\", # class names upon folders structure\n",
3000
- " label_mode=\"int\", # integer encoding\n",
3001
- " shuffle=True, # shuffle images\n",
3002
- " seed=42, # random seed\n",
3003
- " image_size=input_size, # automatic resizing\n",
3004
- " batch_size=batch_size, # tensor shape[0]\n",
3005
- ")\n",
3006
- "\n",
3007
- "print(\"\\nTest dataset:\")\n",
3008
- "test_ds = image_dataset_from_directory(\n",
3009
- " test_dir,\n",
3010
- " labels=\"inferred\", # class names upon folders structure\n",
3011
- " label_mode=\"int\", # integer encoding\n",
3012
- " shuffle=False, # do not shuffle images\n",
3013
- " seed=42, # random seed\n",
3014
- " image_size=input_size, # automatic resizing\n",
3015
- " batch_size=batch_size, # tensor shape[0]\n",
3016
- ")"
3017
- ]
3018
- },
3019
- {
3020
- "cell_type": "markdown",
3021
- "metadata": {},
3022
- "source": [
3023
- "## <a id='toc8_2_'></a>[Prepare model](#toc0_)"
3024
  ]
3025
  },
3026
  {
3027
  "cell_type": "code",
3028
- "execution_count": 187,
3029
  "metadata": {},
3030
  "outputs": [
3031
  {
3032
  "data": {
3033
  "text/html": [
3034
- "<pre style=\"white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace\"><span style=\"font-weight: bold\">Model: \"functional_6\"</span>\n",
3035
  "</pre>\n"
3036
  ],
3037
  "text/plain": [
3038
- "\u001b[1mModel: \"functional_6\"\u001b[0m\n"
3039
  ]
3040
  },
3041
  "metadata": {},
@@ -3044,31 +2985,33 @@
3044
  {
3045
  "data": {
3046
  "text/html": [
3047
- "<pre style=\"white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace\">┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━┓\n",
3048
- "┃<span style=\"font-weight: bold\"> Layer (type) </span>┃<span style=\"font-weight: bold\"> Output Shape </span>┃<span style=\"font-weight: bold\"> Param # </span>┃\n",
3049
- "┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━┩\n",
3050
- "│ input_layer_13 (<span style=\"color: #0087ff; text-decoration-color: #0087ff\">InputLayer</span>) │ (<span style=\"color: #00d7ff; text-decoration-color: #00d7ff\">None</span>, <span style=\"color: #00af00; text-decoration-color: #00af00\">224</span>, <span style=\"color: #00af00; text-decoration-color: #00af00\">224</span>, <span style=\"color: #00af00; text-decoration-color: #00af00\">3</span>) <span style=\"color: #00af00; text-decoration-color: #00af00\">0</span> │\n",
3051
- "├─────────────────────────────────┼────────────────────────┼───────────────┤\n",
3052
- "│ efficientnetb0 (<span style=\"color: #0087ff; text-decoration-color: #0087ff\">Functional</span>) │ (<span style=\"color: #00d7ff; text-decoration-color: #00d7ff\">None</span>, <span style=\"color: #00af00; text-decoration-color: #00af00\">7</span>, <span style=\"color: #00af00; text-decoration-color: #00af00\">7</span>, <span style=\"color: #00af00; text-decoration-color: #00af00\">1280</span>) <span style=\"color: #00af00; text-decoration-color: #00af00\">4,049,571</span> │\n",
3053
- "├─────────────────────────────────┼────────────────────────┼───────────────┤\n",
3054
- "│ flatten_6 (<span style=\"color: #0087ff; text-decoration-color: #0087ff\">Flatten</span>) │ (<span style=\"color: #00d7ff; text-decoration-color: #00d7ff\">None</span>, <span style=\"color: #00af00; text-decoration-color: #00af00\">62720</span>) │ <span style=\"color: #00af00; text-decoration-color: #00af00\">0</span> │\n",
3055
- "├─────────────────────────────────┼────────────────────────┼───────────────┤\n",
3056
- "│ dense_6 (<span style=\"color: #0087ff; text-decoration-color: #0087ff\">Dense</span>) │ (<span style=\"color: #00d7ff; text-decoration-color: #00d7ff\">None</span>, <span style=\"color: #00af00; text-decoration-color: #00af00\">1</span>) │ <span style=\"color: #00af00; text-decoration-color: #00af00\">62,721</span> │\n",
3057
- "└─────────────────────────────────┴────────────────────────┴───────────────┘\n",
 
3058
  "</pre>\n"
3059
  ],
3060
  "text/plain": [
3061
- "┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━┓\n",
3062
- "┃\u001b[1m \u001b[0m\u001b[1mLayer (type) \u001b[0m\u001b[1m \u001b[0m┃\u001b[1m \u001b[0m\u001b[1mOutput Shape \u001b[0m\u001b[1m \u001b[0m┃\u001b[1m \u001b[0m\u001b[1m Param #\u001b[0m\u001b[1m \u001b[0m┃\n",
3063
- "┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━┩\n",
3064
- "│ input_layer_13 (\u001b[38;5;33mInputLayer\u001b[0m) │ (\u001b[38;5;45mNone\u001b[0m, \u001b[38;5;34m224\u001b[0m, \u001b[38;5;34m224\u001b[0m, \u001b[38;5;34m3\u001b[0m) \u001b[38;5;34m0\u001b[0m │\n",
3065
- "├─────────────────────────────────┼────────────────────────┼───────────────┤\n",
3066
- "│ efficientnetb0 (\u001b[38;5;33mFunctional\u001b[0m) │ (\u001b[38;5;45mNone\u001b[0m, \u001b[38;5;34m7\u001b[0m, \u001b[38;5;34m7\u001b[0m, \u001b[38;5;34m1280\u001b[0m) \u001b[38;5;34m4,049,571\u001b[0m │\n",
3067
- "├─────────────────────────────────┼────────────────────────┼───────────────┤\n",
3068
- "│ flatten_6 (\u001b[38;5;33mFlatten\u001b[0m) │ (\u001b[38;5;45mNone\u001b[0m, \u001b[38;5;34m62720\u001b[0m) │ \u001b[38;5;34m0\u001b[0m │\n",
3069
- "├─────────────────────────────────┼────────────────────────┼───────────────┤\n",
3070
- "│ dense_6 (\u001b[38;5;33mDense\u001b[0m) │ (\u001b[38;5;45mNone\u001b[0m, \u001b[38;5;34m1\u001b[0m) │ \u001b[38;5;34m62,721\u001b[0m │\n",
3071
- "└─────────────────────────────────┴────────────────────────┴───────────────┘\n"
 
3072
  ]
3073
  },
3074
  "metadata": {},
@@ -3077,11 +3020,11 @@
3077
  {
3078
  "data": {
3079
  "text/html": [
3080
- "<pre style=\"white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace\"><span style=\"font-weight: bold\"> Total params: </span><span style=\"color: #00af00; text-decoration-color: #00af00\">4,112,292</span> (15.69 MB)\n",
3081
  "</pre>\n"
3082
  ],
3083
  "text/plain": [
3084
- "\u001b[1m Total params: \u001b[0m\u001b[38;5;34m4,112,292\u001b[0m (15.69 MB)\n"
3085
  ]
3086
  },
3087
  "metadata": {},
@@ -3103,11 +3046,11 @@
3103
  {
3104
  "data": {
3105
  "text/html": [
3106
- "<pre style=\"white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace\"><span style=\"font-weight: bold\"> Non-trainable params: </span><span style=\"color: #00af00; text-decoration-color: #00af00\">4,049,571</span> (15.45 MB)\n",
3107
  "</pre>\n"
3108
  ],
3109
  "text/plain": [
3110
- "\u001b[1m Non-trainable params: \u001b[0m\u001b[38;5;34m4,049,571\u001b[0m (15.45 MB)\n"
3111
  ]
3112
  },
3113
  "metadata": {},
@@ -3116,176 +3059,152 @@
3116
  ],
3117
  "source": [
3118
  "# Load pre-trained model without top layers\n",
3119
- "model = EfficientNetB0(\n",
3120
  " weights=\"imagenet\", # pre-trained weights\n",
3121
  " include_top=False, # no dense layer\n",
3122
- " input_shape=(input_size[0], input_size[1], 3), # input shape\n",
3123
  ")\n",
 
 
 
 
 
 
 
3124
  "# Create explicit input layer\n",
3125
  "inputs = tf.keras.Input(shape=(input_size[0], input_size[1], 3))\n",
3126
- "# add data augmentation\n",
3127
- "# augmented = ConditionalAugmentation(rate=0.4)(inputs)\n",
3128
- "# x = model(augmented)\n",
3129
- "x = model(inputs)\n",
3130
  "\n",
 
 
 
 
3131
  "# Flatten output\n",
3132
  "x = Flatten()(x)\n",
3133
- "# For feature extraction only\n",
3134
- "for layer in model.layers:\n",
3135
- " layer.trainable = False\n",
3136
  "# New FC layer for binary classification\n",
3137
  "predictions = Dense(1, activation=\"sigmoid\")(x)\n",
 
3138
  "# Define new model\n",
3139
- "effnetB0 = Model(inputs=inputs, outputs=predictions)\n",
 
3140
  "# Display model summary\n",
3141
- "effnetB0.summary()"
3142
  ]
3143
  },
3144
  {
3145
  "cell_type": "markdown",
3146
  "metadata": {},
3147
  "source": [
3148
- "## <a id='toc8_3_'></a>[Training](#toc0_)"
3149
  ]
3150
  },
3151
  {
3152
  "cell_type": "markdown",
3153
  "metadata": {},
3154
  "source": [
3155
- "Follow with : `tensorboard --logdir models/EfficientNetB0/runs`"
3156
  ]
3157
  },
3158
  {
3159
  "cell_type": "code",
3160
- "execution_count": 32,
3161
  "metadata": {},
3162
  "outputs": [
3163
- {
3164
- "name": "stderr",
3165
- "output_type": "stream",
3166
- "text": [
3167
- "INFO:root:⚙️ compiling\n",
3168
- "INFO:root:🛎️ declaring callbacks\n",
3169
- "INFO:root:💪 starting training\n"
3170
- ]
3171
- },
3172
- {
3173
- "name": "stdout",
3174
- "output_type": "stream",
3175
- "text": [
3176
- "Epoch 1/100\n",
3177
- "\u001b[1m493/493\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m406s\u001b[0m 815ms/step - accuracy: 0.8178 - loss: 0.4710 - precision_3: 0.8521 - recall_3: 0.9467 - val_accuracy: 0.7821 - val_loss: 0.5143 - val_precision_3: 0.8256 - val_recall_3: 0.9283\n",
3178
- "Epoch 2/100\n",
3179
- "\u001b[1m493/493\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m398s\u001b[0m 808ms/step - accuracy: 0.8334 - loss: 0.4258 - precision_3: 0.8697 - recall_3: 0.9422 - val_accuracy: 0.8046 - val_loss: 0.5463 - val_precision_3: 0.8188 - val_recall_3: 0.9757\n",
3180
- "Epoch 3/100\n",
3181
- "\u001b[1m493/493\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m394s\u001b[0m 799ms/step - accuracy: 0.8461 - loss: 0.4060 - precision_3: 0.8763 - recall_3: 0.9506 - val_accuracy: 0.7590 - val_loss: 0.6189 - val_precision_3: 0.8438 - val_recall_3: 0.8636\n",
3182
- "Epoch 4/100\n",
3183
- "\u001b[1m493/493\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m383s\u001b[0m 776ms/step - accuracy: 0.8472 - loss: 0.3938 - precision_3: 0.8793 - recall_3: 0.9476 - val_accuracy: 0.7785 - val_loss: 0.5448 - val_precision_3: 0.8263 - val_recall_3: 0.9214\n",
3184
- "Epoch 5/100\n",
3185
- "\u001b[1m493/493\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m361s\u001b[0m 733ms/step - accuracy: 0.8579 - loss: 0.3752 - precision_3: 0.8865 - recall_3: 0.9524 - val_accuracy: 0.7856 - val_loss: 0.6732 - val_precision_3: 0.8133 - val_recall_3: 0.9559\n",
3186
- "Epoch 6/100\n",
3187
- "\u001b[1m493/493\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m368s\u001b[0m 747ms/step - accuracy: 0.8504 - loss: 0.3840 - precision_3: 0.8866 - recall_3: 0.9417 - val_accuracy: 0.8104 - val_loss: 0.5844 - val_precision_3: 0.8233 - val_recall_3: 0.9766\n",
3188
- "Epoch 7/100\n",
3189
- "\u001b[1m493/493\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m366s\u001b[0m 743ms/step - accuracy: 0.8543 - loss: 0.3848 - precision_3: 0.8870 - recall_3: 0.9468 - val_accuracy: 0.7868 - val_loss: 0.6400 - val_precision_3: 0.8209 - val_recall_3: 0.9439\n",
3190
- "Epoch 8/100\n",
3191
- "\u001b[1m493/493\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m359s\u001b[0m 728ms/step - accuracy: 0.8572 - loss: 0.3700 - precision_3: 0.8884 - recall_3: 0.9486 - val_accuracy: 0.7965 - val_loss: 0.5693 - val_precision_3: 0.8252 - val_recall_3: 0.9514\n",
3192
- "Epoch 9/100\n",
3193
- "\u001b[1m493/493\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m363s\u001b[0m 737ms/step - accuracy: 0.8564 - loss: 0.3684 - precision_3: 0.8890 - recall_3: 0.9467 - val_accuracy: 0.7939 - val_loss: 0.6147 - val_precision_3: 0.8341 - val_recall_3: 0.9319\n",
3194
- "Epoch 10/100\n",
3195
- "\u001b[1m493/493\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m369s\u001b[0m 749ms/step - accuracy: 0.8603 - loss: 0.3629 - precision_3: 0.8904 - recall_3: 0.9500 - val_accuracy: 0.8026 - val_loss: 0.5798 - val_precision_3: 0.8311 - val_recall_3: 0.9505\n",
3196
- "Epoch 11/100\n",
3197
- "\u001b[1m493/493\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m368s\u001b[0m 746ms/step - accuracy: 0.8634 - loss: 0.3589 - precision_3: 0.8931 - recall_3: 0.9507 - val_accuracy: 0.7573 - val_loss: 0.6606 - val_precision_3: 0.8164 - val_recall_3: 0.9052\n"
3198
- ]
3199
- },
3200
- {
3201
- "name": "stderr",
3202
- "output_type": "stream",
3203
- "text": [
3204
- "INFO:root:🧐 evaluating model\n"
3205
- ]
3206
- },
3207
- {
3208
- "name": "stdout",
3209
- "output_type": "stream",
3210
- "text": [
3211
- "\u001b[1m124/124\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m72s\u001b[0m 579ms/step - accuracy: 0.6824 - loss: 0.8199 - precision_3: 0.6028 - recall_3: 0.7630\n",
3212
- "\u001b[1m124/124\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m74s\u001b[0m 586ms/step\n"
3213
- ]
3214
- },
3215
- {
3216
- "name": "stderr",
3217
- "output_type": "stream",
3218
- "text": [
3219
- "INFO:root:📈 plotting results\n"
3220
- ]
3221
- },
3222
  {
3223
  "name": "stdout",
3224
  "output_type": "stream",
3225
  "text": [
3226
- " precision recall f1-score support\n",
 
3227
  "\n",
3228
- " no_smoke 0.16 1.00 0.28 967\n",
3229
- " smoke 0.00 0.00 0.00 4941\n",
3230
  "\n",
3231
- " accuracy 0.16 5908\n",
3232
- " macro avg 0.08 0.50 0.14 5908\n",
3233
- "weighted avg 0.03 0.16 0.05 5908\n",
3234
- "\n"
3235
  ]
3236
- },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3237
  {
3238
  "name": "stderr",
3239
  "output_type": "stream",
3240
  "text": [
3241
- "2025-01-27 12:32:42.266301: W tensorflow/core/framework/local_rendezvous.cc:404] Local rendezvous is aborting with status: OUT_OF_RANGE: End of sequence\n",
3242
- "/Users/julmat/Documents/hugging_face/frugal_cviz/.venv/lib/python3.12/site-packages/sklearn/metrics/_classification.py:1565: UndefinedMetricWarning:\n",
3243
- "\n",
3244
- "Precision is ill-defined and being set to 0.0 in labels with no predicted samples. Use `zero_division` parameter to control this behavior.\n",
3245
- "\n",
3246
- "/Users/julmat/Documents/hugging_face/frugal_cviz/.venv/lib/python3.12/site-packages/sklearn/metrics/_classification.py:1565: UndefinedMetricWarning:\n",
3247
- "\n",
3248
- "Precision is ill-defined and being set to 0.0 in labels with no predicted samples. Use `zero_division` parameter to control this behavior.\n",
3249
- "\n",
3250
- "/Users/julmat/Documents/hugging_face/frugal_cviz/.venv/lib/python3.12/site-packages/sklearn/metrics/_classification.py:1565: UndefinedMetricWarning:\n",
3251
- "\n",
3252
- "Precision is ill-defined and being set to 0.0 in labels with no predicted samples. Use `zero_division` parameter to control this behavior.\n",
3253
- "\n",
3254
- "/Users/julmat/Documents/hugging_face/frugal_cviz/.venv/lib/python3.12/site-packages/sklearn/metrics/_classification.py:1565: UndefinedMetricWarning:\n",
3255
- "\n",
3256
- "Precision is ill-defined and being set to 0.0 in labels with no predicted samples. Use `zero_division` parameter to control this behavior.\n",
3257
- "\n",
3258
- "/Users/julmat/Documents/hugging_face/frugal_cviz/.venv/lib/python3.12/site-packages/sklearn/metrics/_classification.py:1565: UndefinedMetricWarning:\n",
3259
- "\n",
3260
- "Precision is ill-defined and being set to 0.0 in labels with no predicted samples. Use `zero_division` parameter to control this behavior.\n",
3261
- "\n",
3262
- "/Users/julmat/Documents/hugging_face/frugal_cviz/.venv/lib/python3.12/site-packages/sklearn/metrics/_classification.py:1565: UndefinedMetricWarning:\n",
3263
- "\n",
3264
- "Precision is ill-defined and being set to 0.0 in labels with no predicted samples. Use `zero_division` parameter to control this behavior.\n",
3265
- "\n"
3266
  ]
3267
  },
3268
  {
3269
- "data": {
3270
- "image/png": "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",
3271
- "text/plain": [
3272
- "<Figure size 600x400 with 2 Axes>"
3273
- ]
3274
- },
3275
- "metadata": {},
3276
- "output_type": "display_data"
3277
- },
3278
- {
3279
- "name": "stderr",
3280
  "output_type": "stream",
3281
  "text": [
3282
- "INFO:root:📓 logging results\n"
 
3283
  ]
3284
  }
3285
  ],
3286
  "source": [
3287
  "model_trained, history = eval_pretrained_model(\n",
3288
- " model=effnetB0,\n",
3289
  " train_ds=train_ds,\n",
3290
  " val_ds=val_ds,\n",
3291
  " test_ds=test_ds,\n",
@@ -3366,7 +3285,7 @@
3366
  "cell_type": "markdown",
3367
  "metadata": {},
3368
  "source": [
3369
- "## <a id='toc8_4_'></a>[Random Baseline](#toc0_)"
3370
  ]
3371
  },
3372
  {
@@ -3389,6 +3308,39 @@
3389
  "tracker.start_task(\"inference\")"
3390
  ]
3391
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3392
  {
3393
  "cell_type": "code",
3394
  "execution_count": 11,
 
16
  " - [🚧 Image exploration](#toc3_3_) \n",
17
  "- [Data preprocessing](#toc4_) \n",
18
  "- [🦄🦄 CHECKPOINT 🦄🦄](#toc5_) \n",
19
+ "- [Model Training](#toc6_) \n",
20
+ " - [Load configuration](#toc6_1_) \n",
21
+ "- [🚧 MODEL CHOICE](#toc7_) \n",
22
+ " - [Prepare data](#toc7_1_) \n",
23
+ " - [Prepare model](#toc7_2_) \n",
24
+ " - [Training](#toc7_3_) \n",
25
+ " - [Random Baseline](#toc7_4_) \n",
 
26
  "\n",
27
  "<!-- vscode-jupyter-toc-config\n",
28
  "\tnumbering=false\n",
 
70
  "text": [
71
  "/Users/julmat/Documents/hugging_face/frugal_cviz/.venv/lib/python3.12/site-packages/tqdm/auto.py:21: TqdmWarning: IProgress not found. Please update jupyter and ipywidgets. See https://ipywidgets.readthedocs.io/en/stable/user_install.html\n",
72
  " from .autonotebook import tqdm as notebook_tqdm\n",
73
+ "[codecarbon WARNING @ 23:01:31] Multiple instances of codecarbon are allowed to run at the same time.\n",
74
+ "[codecarbon INFO @ 23:01:31] [setup] RAM Tracking...\n",
75
+ "[codecarbon INFO @ 23:01:31] [setup] CPU Tracking...\n",
76
+ "[codecarbon WARNING @ 23:01:31] No CPU tracking mode found. Falling back on CPU constant mode. \n",
77
  " Mac OS and ARM processor detected: Please enable PowerMetrics sudo to measure CPU\n",
78
  "\n",
79
+ "[codecarbon INFO @ 23:01:31] CPU Model on constant consumption mode: Apple M1\n",
80
+ "[codecarbon INFO @ 23:01:31] [setup] GPU Tracking...\n",
81
+ "[codecarbon INFO @ 23:01:31] No GPU found.\n",
82
+ "[codecarbon INFO @ 23:01:31] >>> Tracker's metadata:\n",
83
+ "[codecarbon INFO @ 23:01:31] Platform system: macOS-15.2-arm64-arm-64bit\n",
84
+ "[codecarbon INFO @ 23:01:31] Python version: 3.12.7\n",
85
+ "[codecarbon INFO @ 23:01:31] CodeCarbon version: 2.8.3\n",
86
+ "[codecarbon INFO @ 23:01:31] Available RAM : 16.000 GB\n",
87
+ "[codecarbon INFO @ 23:01:31] CPU count: 8\n",
88
+ "[codecarbon INFO @ 23:01:31] CPU model: Apple M1\n",
89
+ "[codecarbon INFO @ 23:01:31] GPU count: None\n",
90
+ "[codecarbon INFO @ 23:01:31] GPU model: None\n",
91
+ "[codecarbon INFO @ 23:01:34] Saving emissions data to file /Users/julmat/Documents/hugging_face/frugal_cviz/emissions.csv\n"
92
  ]
93
  }
94
  ],
 
105
  "\n",
106
  "# ML\n",
107
  "from keras import Model\n",
108
+ "from keras.applications import EfficientNetB0, EfficientNetV2M\n",
109
+ "from keras.layers import Flatten, Dense, Dropout\n",
110
  "from keras.metrics import Precision, Recall\n",
111
  "from keras.optimizers import AdamW\n",
112
  "from keras.utils import image_dataset_from_directory\n",
 
125
  "from tasks.image import parse_boxes, compute_iou, compute_max_iou\n",
126
  "\n",
127
  "\n",
128
+ "# Logging outputs config (DEBUG < INFO)\n",
129
  "logger = logging.getLogger()\n",
130
  "logger.setLevel(logging.INFO)"
131
  ]
 
148
  " cfg = yaml.safe_load(f)\n",
149
  "# Data\n",
150
  "OUTPUT_DIR = cfg[\"data_root_dir\"]\n",
151
+ "DB_RAW_INFO_URI = os.path.join(OUTPUT_DIR, cfg[\"db_raw_info_uri\"])\n",
152
+ "DB_KERAS_INFO_URI = os.path.join(OUTPUT_DIR, cfg[\"db_keras_info_uri\"])\n",
153
+ "DB_AUG_INFO_URI = os.path.join(OUTPUT_DIR, cfg[\"db_aug_info_uri\"])\n",
154
  "REPO_ID = cfg[\"repo_id\"]\n",
155
  "SPLIT_SIZE = cfg[\"split_size\"]\n",
156
  "RDM_SEED = cfg[\"rdm_seed\"]\n",
 
1859
  "metadata": {},
1860
  "outputs": [
1861
  {
1862
+ "name": "stderr",
1863
  "output_type": "stream",
1864
  "text": [
1865
+ "INFO:root:data/data_keras_info.csv already exists: data already formatted\n"
 
1866
  ]
1867
  }
1868
  ],
1869
  "source": [
 
1870
  "df_format = format_data_keras(df_clean.copy())"
1871
  ]
1872
  },
 
1879
  "- update dataframe"
1880
  ]
1881
  },
1882
+ {
1883
+ "cell_type": "markdown",
1884
+ "metadata": {},
1885
+ "source": [
1886
+ "> 💡 Much better... but a quality balance issue just appeared: \"no_smoke\" images are often blured, flipped, equalized or rotated whereas \"smoke\" images are not.\n",
1887
+ "\n",
1888
+ "👉 Following algorithm will oversample and balance classes quality, by applying the same amount of image for the other class"
1889
+ ]
1890
+ },
1891
  {
1892
  "cell_type": "code",
1893
  "execution_count": 13,
 
1897
  "name": "stderr",
1898
  "output_type": "stream",
1899
  "text": [
1900
+ "INFO:root:data/data_aug_info.csv already exists: data already formatted\n"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1901
  ]
1902
  }
1903
  ],
1904
  "source": [
1905
+ "df_aug = oversample_class(df_format)"
 
1906
  ]
1907
  },
1908
  {
 
1914
  },
1915
  {
1916
  "cell_type": "code",
1917
+ "execution_count": 14,
1918
  "metadata": {},
1919
  "outputs": [
1920
  {
 
2827
  "cell_type": "markdown",
2828
  "metadata": {},
2829
  "source": [
2830
+ "Keep last dataframe as model basis"
2831
  ]
2832
  },
2833
  {
2834
+ "cell_type": "code",
2835
+ "execution_count": 15,
2836
  "metadata": {},
2837
+ "outputs": [],
2838
  "source": [
2839
+ "df = df_aug.copy()"
2840
  ]
2841
  },
2842
  {
2843
  "cell_type": "markdown",
2844
  "metadata": {},
2845
  "source": [
2846
+ "New splits distribution"
2847
  ]
2848
  },
2849
  {
 
2852
  "metadata": {},
2853
  "outputs": [
2854
  {
2855
+ "data": {
2856
+ "image/png": "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",
2857
+ "text/plain": [
2858
+ "<Figure size 640x480 with 1 Axes>"
2859
+ ]
2860
+ },
2861
+ "metadata": {},
2862
+ "output_type": "display_data"
 
2863
  }
2864
  ],
2865
  "source": [
2866
+ "labels_distr = df.split.value_counts()\n",
2867
+ "\n",
2868
+ "plt.pie(\n",
2869
+ " labels_distr,\n",
2870
+ " labels=labels_distr.index,\n",
2871
+ " autopct=make_autopct(labels_distr),\n",
2872
+ ")\n",
2873
+ "plt.show()"
2874
  ]
2875
  },
2876
  {
2877
  "cell_type": "markdown",
2878
  "metadata": {},
2879
  "source": [
2880
+ "# <a id='toc6_'></a>[Model Training](#toc0_)"
2881
  ]
2882
  },
2883
  {
2884
  "cell_type": "markdown",
2885
  "metadata": {},
2886
  "source": [
2887
+ "## <a id='toc6_1_'></a>[Load configuration](#toc0_)"
2888
  ]
2889
  },
2890
  {
2891
  "cell_type": "markdown",
2892
  "metadata": {},
2893
  "source": [
2894
+ "# <a id='toc7_'></a>[🚧 MODEL CHOICE](#toc0_)\n",
2895
  "\n",
2896
  "- https://paperswithcode.com/sota/image-classification-on-imagenet\n",
2897
+ "- https://keras.io/api/applications/efficientnet_v2/#efficientnetv2m-function\n",
2898
+ "- [ViTForImageClassification](https://huggingface.co/docs/transformers/main/en/model_doc/vit)"
2899
+ ]
2900
+ },
2901
+ {
2902
+ "cell_type": "markdown",
2903
+ "metadata": {},
2904
+ "source": [
2905
+ "# <a id='toc5_'></a>[🦄🦄 CHECKPOINT 🦄🦄](#toc0_)"
2906
  ]
2907
  },
2908
  {
2909
  "cell_type": "code",
2910
+ "execution_count": 17,
2911
  "metadata": {},
2912
+ "outputs": [
2913
+ {
2914
+ "ename": "NameError",
2915
+ "evalue": "name 'stop' is not defined",
2916
+ "output_type": "error",
2917
+ "traceback": [
2918
+ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
2919
+ "\u001b[0;31mNameError\u001b[0m Traceback (most recent call last)",
2920
+ "Cell \u001b[0;32mIn[17], line 1\u001b[0m\n\u001b[0;32m----> 1\u001b[0m \u001b[43mstop\u001b[49m\n",
2921
+ "\u001b[0;31mNameError\u001b[0m: name 'stop' is not defined"
2922
+ ]
2923
+ }
2924
+ ],
2925
  "source": [
2926
+ "stop"
 
 
2927
  ]
2928
  },
2929
  {
2930
  "cell_type": "code",
2931
+ "execution_count": 18,
2932
  "metadata": {},
2933
  "outputs": [],
2934
  "source": [
2935
  "# Model config\n",
2936
+ "model_name = \"EfficientNetV2M\"\n",
2937
  "input_size = (224, 224)\n",
2938
+ "batch_size = 32\n",
 
2939
  "optimizer = AdamW(learning_rate=0.0002, weight_decay=0.05)\n",
2940
+ "# model_name = \"EfficientNetB0\"\n",
2941
+ "# input_size = (224, 224)\n",
2942
+ "# batch_size = 48\n",
2943
+ "# optimizer = AdamW(learning_rate=0.0002, weight_decay=0.05)\n",
2944
+ "n_epochs = 100\n",
2945
  "loss = \"binary_crossentropy\"\n",
2946
  "metrics = [\"accuracy\", Precision(), Recall()]\n",
2947
  "# metrics = [\"accuracy\", Precision(), Recall(), \"f1_score\"]\n",
 
2961
  "cell_type": "markdown",
2962
  "metadata": {},
2963
  "source": [
2964
+ "## <a id='toc7_2_'></a>[Prepare model](#toc0_)"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2965
  ]
2966
  },
2967
  {
2968
  "cell_type": "code",
2969
+ "execution_count": 19,
2970
  "metadata": {},
2971
  "outputs": [
2972
  {
2973
  "data": {
2974
  "text/html": [
2975
+ "<pre style=\"white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace\"><span style=\"font-weight: bold\">Model: \"functional\"</span>\n",
2976
  "</pre>\n"
2977
  ],
2978
  "text/plain": [
2979
+ "\u001b[1mModel: \"functional\"\u001b[0m\n"
2980
  ]
2981
  },
2982
  "metadata": {},
 
2985
  {
2986
  "data": {
2987
  "text/html": [
2988
+ "<pre style=\"white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace\">┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━━┓\n",
2989
+ "┃<span style=\"font-weight: bold\"> Layer (type) </span>┃<span style=\"font-weight: bold\"> Output Shape </span>┃<span style=\"font-weight: bold\"> Param # </span>┃<span style=\"font-weight: bold\"> Trai… </span>┃\n",
2990
+ "┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━━┩\n",
2991
+ "│ input_layer_1 (<span style=\"color: #0087ff; text-decoration-color: #0087ff\">InputLayer</span>) │ (<span style=\"color: #00d7ff; text-decoration-color: #00d7ff\">None</span>, <span style=\"color: #00af00; text-decoration-color: #00af00\">224</span>, <span style=\"color: #00af00; text-decoration-color: #00af00\">224</span>, <span style=\"color: #00af00; text-decoration-color: #00af00\">3</span>) <span style=\"color: #00af00; text-decoration-color: #00af00\">0</span> │ <span style=\"font-weight: bold\">-</span> │\n",
2992
+ "├─────────────────────────────┼───────────────────────┼────────────┼───────┤\n",
2993
+ "│ efficientnetv2-m │ (<span style=\"color: #00d7ff; text-decoration-color: #00d7ff\">None</span>, <span style=\"color: #00af00; text-decoration-color: #00af00\">7</span>, <span style=\"color: #00af00; text-decoration-color: #00af00\">7</span>, <span style=\"color: #00af00; text-decoration-color: #00af00\">1280</span>) │ <span style=\"color: #00af00; text-decoration-color: #00af00\">53,150,388</span> <span style=\"color: #ff0000; text-decoration-color: #ff0000; font-weight: bold\">N</span> │\n",
2994
+ "│ (<span style=\"color: #0087ff; text-decoration-color: #0087ff\">Functional</span>) │ │ │ │\n",
2995
+ "├─────────────────────────────┼───────────────────────┼────────────┼───────┤\n",
2996
+ "│ flatten (<span style=\"color: #0087ff; text-decoration-color: #0087ff\">Flatten</span>) │ (<span style=\"color: #00d7ff; text-decoration-color: #00d7ff\">None</span>, <span style=\"color: #00af00; text-decoration-color: #00af00\">62720</span>) │ <span style=\"color: #00af00; text-decoration-color: #00af00\">0</span> │ <span style=\"font-weight: bold\">-</span> │\n",
2997
+ "├─────────────────────────────┼───────────────────────┼────────────┼───────┤\n",
2998
+ "│ dense (<span style=\"color: #0087ff; text-decoration-color: #0087ff\">Dense</span>) │ (<span style=\"color: #00d7ff; text-decoration-color: #00d7ff\">None</span>, <span style=\"color: #00af00; text-decoration-color: #00af00\">1</span>) │ <span style=\"color: #00af00; text-decoration-color: #00af00\">62,721</span> │ <span style=\"color: #00af00; text-decoration-color: #00af00; font-weight: bold\">Y</span> │\n",
2999
+ "└─────────────────────────────┴───────────────────────┴────────────┴───────┘\n",
3000
  "</pre>\n"
3001
  ],
3002
  "text/plain": [
3003
+ "┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━━┓\n",
3004
+ "┃\u001b[1m \u001b[0m\u001b[1mLayer (type) \u001b[0m\u001b[1m \u001b[0m┃\u001b[1m \u001b[0m\u001b[1mOutput Shape \u001b[0m\u001b[1m \u001b[0m┃\u001b[1m \u001b[0m\u001b[1m Param #\u001b[0m\u001b[1m \u001b[0m┃\u001b[1m \u001b[0m\u001b[1mTrai…\u001b[0m\u001b[1m \u001b[0m┃\n",
3005
+ "┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━━┩\n",
3006
+ "│ input_layer_1 (\u001b[38;5;33mInputLayer\u001b[0m) │ (\u001b[38;5;45mNone\u001b[0m, \u001b[38;5;34m224\u001b[0m, \u001b[38;5;34m224\u001b[0m, \u001b[38;5;34m3\u001b[0m) \u001b[38;5;34m0\u001b[0m │ \u001b[1m-\u001b[0m │\n",
3007
+ "├─────────────────────────────┼───────────────────────┼────────────┼───────┤\n",
3008
+ "│ efficientnetv2-m │ (\u001b[38;5;45mNone\u001b[0m, \u001b[38;5;34m7\u001b[0m, \u001b[38;5;34m7\u001b[0m, \u001b[38;5;34m1280\u001b[0m) │ \u001b[38;5;34m53,150,388\u001b[0m \u001b[1;91mN\u001b[0m │\n",
3009
+ "│ (\u001b[38;5;33mFunctional\u001b[0m) │ �� │ │\n",
3010
+ "├─────────────────────────────┼───────────────────────┼────────────┼───────┤\n",
3011
+ "│ flatten (\u001b[38;5;33mFlatten\u001b[0m) │ (\u001b[38;5;45mNone\u001b[0m, \u001b[38;5;34m62720\u001b[0m) │ \u001b[38;5;34m0\u001b[0m │ \u001b[1m-\u001b[0m │\n",
3012
+ "├─────────────────────────────┼───────────────────────┼────────────┼───────┤\n",
3013
+ "│ dense (\u001b[38;5;33mDense\u001b[0m) │ (\u001b[38;5;45mNone\u001b[0m, \u001b[38;5;34m1\u001b[0m) │ \u001b[38;5;34m62,721\u001b[0m │ \u001b[1;38;5;34mY\u001b[0m │\n",
3014
+ "└─────────────────────────────┴───────────────────────┴────────────┴───────┘\n"
3015
  ]
3016
  },
3017
  "metadata": {},
 
3020
  {
3021
  "data": {
3022
  "text/html": [
3023
+ "<pre style=\"white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace\"><span style=\"font-weight: bold\"> Total params: </span><span style=\"color: #00af00; text-decoration-color: #00af00\">53,213,109</span> (202.99 MB)\n",
3024
  "</pre>\n"
3025
  ],
3026
  "text/plain": [
3027
+ "\u001b[1m Total params: \u001b[0m\u001b[38;5;34m53,213,109\u001b[0m (202.99 MB)\n"
3028
  ]
3029
  },
3030
  "metadata": {},
 
3046
  {
3047
  "data": {
3048
  "text/html": [
3049
+ "<pre style=\"white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace\"><span style=\"font-weight: bold\"> Non-trainable params: </span><span style=\"color: #00af00; text-decoration-color: #00af00\">53,150,388</span> (202.75 MB)\n",
3050
  "</pre>\n"
3051
  ],
3052
  "text/plain": [
3053
+ "\u001b[1m Non-trainable params: \u001b[0m\u001b[38;5;34m53,150,388\u001b[0m (202.75 MB)\n"
3054
  ]
3055
  },
3056
  "metadata": {},
 
3059
  ],
3060
  "source": [
3061
  "# Load pre-trained model without top layers\n",
3062
+ "base_model = EfficientNetV2M(\n",
3063
  " weights=\"imagenet\", # pre-trained weights\n",
3064
  " include_top=False, # no dense layer\n",
3065
+ " input_shape=(input_size[0], input_size[1], 3), # input shape,\n",
3066
  ")\n",
3067
+ "\n",
3068
+ "# For feature extraction\n",
3069
+ "base_model.trainable = False\n",
3070
+ "# # For partial fine-tuning: freeze bottom layers\n",
3071
+ "# for layer in base_model.layers[:735]:\n",
3072
+ "# layer.trainable = False\n",
3073
+ "\n",
3074
  "# Create explicit input layer\n",
3075
  "inputs = tf.keras.Input(shape=(input_size[0], input_size[1], 3))\n",
 
 
 
 
3076
  "\n",
3077
+ "x = base_model(inputs)\n",
3078
+ "\n",
3079
+ "# # Dropout for regularization\n",
3080
+ "# x = Dropout(0.2)(x)\n",
3081
  "# Flatten output\n",
3082
  "x = Flatten()(x)\n",
 
 
 
3083
  "# New FC layer for binary classification\n",
3084
  "predictions = Dense(1, activation=\"sigmoid\")(x)\n",
3085
+ "\n",
3086
  "# Define new model\n",
3087
+ "model_ready = Model(inputs=inputs, outputs=predictions)\n",
3088
+ "\n",
3089
  "# Display model summary\n",
3090
+ "model_ready.summary(show_trainable=True)"
3091
  ]
3092
  },
3093
  {
3094
  "cell_type": "markdown",
3095
  "metadata": {},
3096
  "source": [
3097
+ "## <a id='toc7_1_'></a>[Prepare data](#toc0_)"
3098
  ]
3099
  },
3100
  {
3101
  "cell_type": "markdown",
3102
  "metadata": {},
3103
  "source": [
3104
+ "Create datasets from local images and labels"
3105
  ]
3106
  },
3107
  {
3108
  "cell_type": "code",
3109
+ "execution_count": 20,
3110
  "metadata": {},
3111
  "outputs": [
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3112
  {
3113
  "name": "stdout",
3114
  "output_type": "stream",
3115
  "text": [
3116
+ "Train dataset:\n",
3117
+ "Found 39654 files belonging to 2 classes.\n",
3118
  "\n",
3119
+ "Val dataset:\n",
3120
+ "Found 6670 files belonging to 2 classes.\n",
3121
  "\n",
3122
+ "Test dataset:\n",
3123
+ "Found 5908 files belonging to 2 classes.\n"
 
 
3124
  ]
3125
+ }
3126
+ ],
3127
+ "source": [
3128
+ "# Prepare for outputs\n",
3129
+ "os.makedirs(MODELS_ROOT_DIR, exist_ok=True)\n",
3130
+ "y = df[\"label\"]\n",
3131
+ "X = df[\"uri\"]\n",
3132
+ "\n",
3133
+ "# Create datasets\n",
3134
+ "print(\"Train dataset:\")\n",
3135
+ "train_ds = image_dataset_from_directory(\n",
3136
+ " train_dir,\n",
3137
+ " labels=\"inferred\", # class names upon folders structure\n",
3138
+ " label_mode=\"int\", # integer encoding\n",
3139
+ " shuffle=True, # shuffle images\n",
3140
+ " seed=42, # random seed\n",
3141
+ " image_size=input_size, # automatic resizing\n",
3142
+ " batch_size=batch_size, # tensor shape[0]\n",
3143
+ ")\n",
3144
+ "\n",
3145
+ "print(\"\\nVal dataset:\")\n",
3146
+ "val_ds = image_dataset_from_directory(\n",
3147
+ " val_dir,\n",
3148
+ " labels=\"inferred\", # class names upon folders structure\n",
3149
+ " label_mode=\"int\", # integer encoding\n",
3150
+ " shuffle=True, # shuffle images\n",
3151
+ " seed=42, # random seed\n",
3152
+ " image_size=input_size, # automatic resizing\n",
3153
+ " batch_size=batch_size, # tensor shape[0]\n",
3154
+ ")\n",
3155
+ "\n",
3156
+ "print(\"\\nTest dataset:\")\n",
3157
+ "test_ds = image_dataset_from_directory(\n",
3158
+ " test_dir,\n",
3159
+ " labels=\"inferred\", # class names upon folders structure\n",
3160
+ " label_mode=\"int\", # integer encoding\n",
3161
+ " shuffle=False, # do not shuffle images\n",
3162
+ " seed=42, # random seed\n",
3163
+ " image_size=input_size, # automatic resizing\n",
3164
+ " batch_size=batch_size, # tensor shape[0]\n",
3165
+ ")"
3166
+ ]
3167
+ },
3168
+ {
3169
+ "cell_type": "markdown",
3170
+ "metadata": {},
3171
+ "source": [
3172
+ "## <a id='toc7_3_'></a>[Training](#toc0_)"
3173
+ ]
3174
+ },
3175
+ {
3176
+ "cell_type": "markdown",
3177
+ "metadata": {},
3178
+ "source": [
3179
+ "Follow with : `tensorboard --logdir models/EfficientNetB0/runs`"
3180
+ ]
3181
+ },
3182
+ {
3183
+ "cell_type": "code",
3184
+ "execution_count": 101,
3185
+ "metadata": {},
3186
+ "outputs": [
3187
  {
3188
  "name": "stderr",
3189
  "output_type": "stream",
3190
  "text": [
3191
+ "INFO:root:⚙️ compiling\n",
3192
+ "INFO:root:🛎️ declaring callbacks\n",
3193
+ "INFO:root:💪 starting training\n"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3194
  ]
3195
  },
3196
  {
3197
+ "name": "stdout",
 
 
 
 
 
 
 
 
 
 
3198
  "output_type": "stream",
3199
  "text": [
3200
+ "Epoch 1/100\n",
3201
+ "\u001b[1m 7/1240\u001b[0m \u001b[37m━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[1m36:58\u001b[0m 2s/step - accuracy: 0.4936 - loss: 0.8204 - precision: 0.5297 - recall: 0.5054"
3202
  ]
3203
  }
3204
  ],
3205
  "source": [
3206
  "model_trained, history = eval_pretrained_model(\n",
3207
+ " model=model_ready,\n",
3208
  " train_ds=train_ds,\n",
3209
  " val_ds=val_ds,\n",
3210
  " test_ds=test_ds,\n",
 
3285
  "cell_type": "markdown",
3286
  "metadata": {},
3287
  "source": [
3288
+ "## <a id='toc7_4_'></a>[Random Baseline](#toc0_)"
3289
  ]
3290
  },
3291
  {
 
3308
  "tracker.start_task(\"inference\")"
3309
  ]
3310
  },
3311
+ {
3312
+ "cell_type": "markdown",
3313
+ "metadata": {},
3314
+ "source": [
3315
+ "# 🚧 INFERENCE"
3316
+ ]
3317
+ },
3318
+ {
3319
+ "cell_type": "code",
3320
+ "execution_count": null,
3321
+ "metadata": {},
3322
+ "outputs": [],
3323
+ "source": [
3324
+ "# import keras\n",
3325
+ "# from keras.applications.resnet50 import ResNet50\n",
3326
+ "# from keras.applications.resnet50 import preprocess_input, decode_predictions\n",
3327
+ "# import numpy as np\n",
3328
+ "\n",
3329
+ "# model = ResNet50(weights='imagenet')\n",
3330
+ "\n",
3331
+ "# img_path = 'elephant.jpg'\n",
3332
+ "# img = keras.utils.load_img(img_path, target_size=(224, 224))\n",
3333
+ "# x = keras.utils.img_to_array(img)\n",
3334
+ "# x = np.expand_dims(x, axis=0)\n",
3335
+ "# x = preprocess_input(x)\n",
3336
+ "\n",
3337
+ "# preds = model.predict(x)\n",
3338
+ "# # decode the results into a list of tuples (class, description, probability)\n",
3339
+ "# # (one such list for each sample in the batch)\n",
3340
+ "# print('Predicted:', decode_predictions(preds, top=3)[0])\n",
3341
+ "# # Predicted: [(u'n02504013', u'Indian_elephant', 0.82658225), (u'n01871265', u'tusker', 0.1122357), (u'n02504458', u'African_elephant', 0.061040461)]\n"
3342
+ ]
3343
+ },
3344
  {
3345
  "cell_type": "code",
3346
  "execution_count": 11,
README.md CHANGED
@@ -12,11 +12,10 @@ datasets:
12
 
13
  # 🚧 TODO
14
 
15
- - 👉 randomly blur & flip smoke images
16
- - réduire **n_epochs**
17
  - voir répartition partenaires, caméras, temporalité, annotations
18
  - métriques : **matrice de confusion** complète
19
- - décrire voir **erreurs de types et conséquences**
20
  - tester plusieurs pré-entraînements (est-ce que charger un modèle pré-entraîné ImageNet aide vraiment ?)
21
  - tester si amélioration inférence avec et sans égalisation
22
  - voir répartition physique des annotations sur l'image
 
12
 
13
  # 🚧 TODO
14
 
15
+ - 👉 tester EfficientNetV2M
 
16
  - voir répartition partenaires, caméras, temporalité, annotations
17
  - métriques : **matrice de confusion** complète
18
+ - décrire **erreurs de types et conséquences**
19
  - tester plusieurs pré-entraînements (est-ce que charger un modèle pré-entraîné ImageNet aide vraiment ?)
20
  - tester si amélioration inférence avec et sans égalisation
21
  - voir répartition physique des annotations sur l'image
config.yaml CHANGED
@@ -7,7 +7,9 @@ rdm_seed: 42
7
  data_root_dir: "data"
8
  raw_data_dir: "raw"
9
  clr_hf_cache_script_abs_path: './src/clear_hf_cache.sh'
10
- db_info_uri: "data_info.csv"
 
 
11
 
12
  # Models
13
  models_common:
 
7
  data_root_dir: "data"
8
  raw_data_dir: "raw"
9
  clr_hf_cache_script_abs_path: './src/clear_hf_cache.sh'
10
+ db_raw_info_uri: "data_raw_info.csv"
11
+ db_keras_info_uri: "data_keras_info.csv"
12
+ db_aug_info_uri: "data_aug_info.csv"
13
 
14
  # Models
15
  models_common:
src/load_data.py CHANGED
@@ -6,13 +6,14 @@ import os
6
  import pandas as pd
7
  from PIL import Image, ImageOps, ImageEnhance, ImageFilter
8
  import shutil
 
9
  import subprocess
10
  import yaml
11
 
12
 
13
- # Logging configuration (see all outputs, even DEBUG or INFO)
14
  logger = logging.getLogger()
15
- logger.setLevel(logging.INFO)
16
 
17
  # local config
18
  with open("config.yaml", "r") as f:
@@ -23,7 +24,9 @@ RDM_SEED = cfg["rdm_seed"]
23
  OUTPUT_DIR = cfg["data_root_dir"]
24
  RAW_DATA_DIR = os.path.join(OUTPUT_DIR, cfg["raw_data_dir"])
25
  CLR_CACHE_SCRIPT = cfg["clr_hf_cache_script_abs_path"]
26
- DB_INFO_URI = os.path.join(OUTPUT_DIR, cfg["db_info_uri"])
 
 
27
 
28
 
29
  # Save in Ultralytics format
@@ -81,8 +84,8 @@ def load_raw_data():
81
  """Main function for downloading, splitting and formatting data"""
82
 
83
  # Check if data information already exists before eventually loading model
84
- if os.path.exists(DB_INFO_URI):
85
- df = pd.read_csv(DB_INFO_URI, index_col=0)
86
  return df
87
 
88
  # Load data
@@ -116,7 +119,7 @@ def load_raw_data():
116
  df_train_4 = create_df(ds_train[18000:], "train", RAW_DATA_DIR)
117
  df_val = create_df(ds_val, "val", RAW_DATA_DIR)
118
  df_test = create_df(ds_test, "test", RAW_DATA_DIR)
119
- # Save as one CSV
120
  df = pd.concat(
121
  [df_train_1, df_train_2, df_train_3, df_train_4, df_val, df_test],
122
  axis=0,
@@ -144,7 +147,8 @@ def load_raw_data():
144
  ],
145
  ]
146
  # Save as CSV
147
- with open(DB_INFO_URI, "wb") as f:
 
148
  df.to_csv(f)
149
 
150
  # Clear HF default cache folder after it is done (6GB)
@@ -173,12 +177,14 @@ def format_data_keras(df):
173
  logging.warning(f"{OUTPUT_DIR} doesn't exist: (re)load data first")
174
  return df
175
 
176
- # Create Keras parent folder
177
- keras_dir = os.path.join(OUTPUT_DIR, "keras")
178
  # Check if data already exists
179
- if os.path.exists(keras_dir) and len(os.listdir("./data/keras")) > 0:
180
- logging.info(f"{keras_dir} already exists: data already formatted")
 
181
  return df
 
 
 
182
  os.makedirs(keras_dir, exist_ok=True)
183
  # Create splits folders
184
  for split in df.split.unique():
@@ -199,11 +205,16 @@ def format_data_keras(df):
199
  df.drop(columns="uri", inplace=True)
200
  df.rename(columns={"uri_dest": "uri"}, inplace=True)
201
 
 
 
 
 
 
202
  return df
203
 
204
 
205
- def add_data_aug(aug_name, df_sample, df_aug):
206
- """Add data augmentation for under-represented class"""
207
  # Rename images and update URI
208
  df_sample.loc[:, "name"] += f"_DA-{aug_name[:-4]}"
209
  df_sample.rename(columns={'uri': 'input_uri'}, inplace=True)
@@ -225,25 +236,32 @@ def add_data_aug(aug_name, df_sample, df_aug):
225
  img_aug = img.rotate(180)
226
  else:
227
  logging.warn("Wrong data augmentation name: passing")
228
- return df_aug
229
 
230
  img_aug.save(row['uri'])
 
 
231
 
232
- # Add to dataframe
233
  df_sample.drop(columns='input_uri', inplace=True)
234
- result = pd.concat([df_aug, df_sample], axis=0, ignore_index=True)
235
 
236
- logging.info(f"✅ Transformed {len(df_sample)} images with {aug_name}")
237
 
238
- return result
239
 
240
 
241
  def oversample_class(df):
242
  """Oversample an under-represented class"""
 
 
 
 
 
 
243
  count_df = df.groupby(["split", "label"]).size().reset_index(name="count")
244
  count_df = count_df.loc[count_df["split"] != "test"]
245
 
246
  df_aug = df.copy()
 
247
 
248
  for split in count_df.split.unique():
249
  logging.info(f"⚙️ Processing {split} split...")
@@ -251,27 +269,52 @@ def oversample_class(df):
251
  # Minimum label
252
  idxmin = _["count"].idxmin()
253
  min_row = _.loc[idxmin, :]
254
- min_label = min_row["label"]
255
  min_count = min_row["count"]
 
256
  # Maximum label
257
  idxmax = _["count"].idxmax()
258
  max_row = _.loc[idxmax, :]
259
  max_count = max_row["count"]
 
260
  # Needed labels
261
  need = max_count - min_count
262
 
263
- logging.info(f"⚙️ Transforming {need} images...")
 
264
 
265
  # Loop over augmentation techniques until need is covered
266
  for aug_name in ["blur_img", "flip_img", "blur_flip_img", "eq_img", "180_img"]:
267
- # Create a new DF sample from which images will be transformed
268
- df_sample = df.loc[(df["split"] == split) & (df["label"] == min_label)].copy()
 
 
 
 
 
 
 
 
269
 
270
- if need < len(df_sample):
271
- df_sample = df_sample.iloc[:need]
 
 
272
 
273
- df_aug = add_data_aug(aug_name, df_sample, df_aug)
274
- need -= len(df_sample)
 
 
 
 
 
 
 
 
 
 
 
 
 
275
 
276
  return df_aug
277
 
 
6
  import pandas as pd
7
  from PIL import Image, ImageOps, ImageEnhance, ImageFilter
8
  import shutil
9
+ from sklearn.model_selection import train_test_split
10
  import subprocess
11
  import yaml
12
 
13
 
14
+ # Logging outputs config (DEBUG < INFO)
15
  logger = logging.getLogger()
16
+ logger.setLevel(logging.DEBUG)
17
 
18
  # local config
19
  with open("config.yaml", "r") as f:
 
24
  OUTPUT_DIR = cfg["data_root_dir"]
25
  RAW_DATA_DIR = os.path.join(OUTPUT_DIR, cfg["raw_data_dir"])
26
  CLR_CACHE_SCRIPT = cfg["clr_hf_cache_script_abs_path"]
27
+ DB_RAW_INFO_URI = os.path.join(OUTPUT_DIR, cfg["db_raw_info_uri"])
28
+ DB_KERAS_INFO_URI = os.path.join(OUTPUT_DIR, cfg["db_keras_info_uri"])
29
+ DB_AUG_INFO_URI = os.path.join(OUTPUT_DIR, cfg["db_aug_info_uri"])
30
 
31
 
32
  # Save in Ultralytics format
 
84
  """Main function for downloading, splitting and formatting data"""
85
 
86
  # Check if data information already exists before eventually loading model
87
+ if os.path.exists(DB_RAW_INFO_URI):
88
+ df = pd.read_csv(DB_RAW_INFO_URI, index_col=0)
89
  return df
90
 
91
  # Load data
 
119
  df_train_4 = create_df(ds_train[18000:], "train", RAW_DATA_DIR)
120
  df_val = create_df(ds_val, "val", RAW_DATA_DIR)
121
  df_test = create_df(ds_test, "test", RAW_DATA_DIR)
122
+ # Save as one dataframe
123
  df = pd.concat(
124
  [df_train_1, df_train_2, df_train_3, df_train_4, df_val, df_test],
125
  axis=0,
 
147
  ],
148
  ]
149
  # Save as CSV
150
+ logging.info(f"Dataframe saved in: {DB_RAW_INFO_URI}")
151
+ with open(DB_RAW_INFO_URI, "wb") as f:
152
  df.to_csv(f)
153
 
154
  # Clear HF default cache folder after it is done (6GB)
 
177
  logging.warning(f"{OUTPUT_DIR} doesn't exist: (re)load data first")
178
  return df
179
 
 
 
180
  # Check if data already exists
181
+ if os.path.exists(DB_KERAS_INFO_URI):
182
+ logging.info(f"{DB_KERAS_INFO_URI} already exists: data already formatted")
183
+ df = pd.read_csv(DB_KERAS_INFO_URI, index_col=0)
184
  return df
185
+
186
+ # Create Keras parent folder
187
+ keras_dir = os.path.join(OUTPUT_DIR, "keras")
188
  os.makedirs(keras_dir, exist_ok=True)
189
  # Create splits folders
190
  for split in df.split.unique():
 
205
  df.drop(columns="uri", inplace=True)
206
  df.rename(columns={"uri_dest": "uri"}, inplace=True)
207
 
208
+ # Save as CSV
209
+ logging.info(f"Dataframe saved in: {DB_KERAS_INFO_URI}")
210
+ with open(DB_KERAS_INFO_URI, "wb") as f:
211
+ df.to_csv(f)
212
+
213
  return df
214
 
215
 
216
+ def add_data_aug(aug_name, df_sample, replace=False):
217
+ """Add data augmentation to a ataframe sample"""
218
  # Rename images and update URI
219
  df_sample.loc[:, "name"] += f"_DA-{aug_name[:-4]}"
220
  df_sample.rename(columns={'uri': 'input_uri'}, inplace=True)
 
236
  img_aug = img.rotate(180)
237
  else:
238
  logging.warn("Wrong data augmentation name: passing")
239
+ return None
240
 
241
  img_aug.save(row['uri'])
242
+ if replace:
243
+ os.remove(row['input_uri'])
244
 
 
245
  df_sample.drop(columns='input_uri', inplace=True)
 
246
 
247
+ logging.debug(f"\t✅ Transformed {len(df_sample)} images with {aug_name} (replace={replace})")
248
 
249
+ return df_sample
250
 
251
 
252
  def oversample_class(df):
253
  """Oversample an under-represented class"""
254
+ # Check if data already exists
255
+ if os.path.exists(DB_AUG_INFO_URI):
256
+ logging.info(f"{DB_AUG_INFO_URI} already exists: data already formatted")
257
+ df = pd.read_csv(DB_AUG_INFO_URI, index_col=0)
258
+ return df
259
+
260
  count_df = df.groupby(["split", "label"]).size().reset_index(name="count")
261
  count_df = count_df.loc[count_df["split"] != "test"]
262
 
263
  df_aug = df.copy()
264
+ df_aug.loc[:, "augmented"] = False
265
 
266
  for split in count_df.split.unique():
267
  logging.info(f"⚙️ Processing {split} split...")
 
269
  # Minimum label
270
  idxmin = _["count"].idxmin()
271
  min_row = _.loc[idxmin, :]
 
272
  min_count = min_row["count"]
273
+ min_label = min_row["label"]
274
  # Maximum label
275
  idxmax = _["count"].idxmax()
276
  max_row = _.loc[idxmax, :]
277
  max_count = max_row["count"]
278
+ max_label = max_row["label"]
279
  # Needed labels
280
  need = max_count - min_count
281
 
282
+ logging.info(f"Min class count = {min_count} ; Max class count = {max_count}")
283
+ logging.info(f"⚙️ Transforming {need} images...\n")
284
 
285
  # Loop over augmentation techniques until need is covered
286
  for aug_name in ["blur_img", "flip_img", "blur_flip_img", "eq_img", "180_img"]:
287
+ logging.info(f"\tApplying {aug_name[:-4]}")
288
+ if need <= 0:
289
+ break
290
+
291
+ # Prepare data samples to apply image transformation
292
+ df_sample_min = df.loc[(df["split"] == split) & (df["label"] == min_label)].iloc[:need].copy()
293
+
294
+ df_sample_max_full = df_aug.loc[(df_aug["split"] == split) & (df_aug["label"] == max_label) & (df_aug["augmented"] == False)].copy()
295
+ _, df_sample_max = train_test_split(df_sample_max_full, test_size=len(df_sample_min), random_state=RDM_SEED)
296
+ df_aug = df_aug[~df_aug['uri'].isin(df_sample_max['uri'])]
297
 
298
+ # Apply data augmentation for smaller class
299
+ df_sample_min_transf = add_data_aug(aug_name, df_sample_min)
300
+ if df_sample_min_transf is not None:
301
+ df_aug = pd.concat([df_aug, df_sample_min_transf], axis=0, ignore_index=True)
302
 
303
+ # Apply data quality balance by samely transforming most frequent class
304
+ df_sample_max_transf = add_data_aug(aug_name, df_sample_max, replace=True)
305
+ df_sample_max_transf.loc[:, "augmented"] = True
306
+ df_aug = pd.concat([df_aug, df_sample_max_transf], axis=0, ignore_index=True)
307
+
308
+ need -= len(df_sample_min_transf)
309
+
310
+ df_aug.drop(columns='augmented', inplace=True)
311
+
312
+ logging.info(f"Augmented dataframe shape = {df_aug.shape}\n")
313
+
314
+ # Save as CSV
315
+ logging.info(f"Dataframe saved in: {DB_AUG_INFO_URI}")
316
+ with open(DB_AUG_INFO_URI, "wb") as f:
317
+ df_aug.to_csv(f)
318
 
319
  return df_aug
320