sycod commited on
Commit
fc06865
·
1 Parent(s): 38dbb38

oversampling ok

Browse files
Files changed (4) hide show
  1. EDA.ipynb +0 -0
  2. README.md +2 -9
  3. config.yaml +1 -10
  4. src/load_data.py +69 -5
EDA.ipynb CHANGED
The diff for this file is too large to render. See raw diff
 
README.md CHANGED
@@ -4,10 +4,6 @@ datasets:
4
  - frugal-ai-challenge/public-leaderboard-image
5
  ---
6
 
7
- # Notices
8
-
9
- - **Stratification is not used for data split** (splits are predefined in project)
10
-
11
  # Installation
12
 
13
  ✅ Checklist:
@@ -16,14 +12,11 @@ datasets:
16
 
17
  # 🚧 TODO
18
 
19
- - faire data augmentation en amont pour **no_smoke oversampling**
20
  - réduire **n_epochs**
21
  - voir répartition partenaires, caméras, temporalité, annotations
22
  - métriques : **matrice de confusion** complète
23
  - décrire voir **erreurs de types et conséquences**
24
  - tester plusieurs pré-entraînements (est-ce que charger un modèle pré-entraîné ImageNet aide vraiment ?)
25
- - modèles :
26
- - [ ] EfficientNet
27
- - [ ] EfficientDet
28
- - tester si amélioration avec et sans égalisation
29
  - voir répartition physique des annotations sur l'image
 
4
  - frugal-ai-challenge/public-leaderboard-image
5
  ---
6
 
 
 
 
 
7
  # Installation
8
 
9
  ✅ Checklist:
 
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
config.yaml CHANGED
@@ -14,13 +14,4 @@ models_common:
14
  models_root_dir: "models"
15
  classes: ["no_smoke", "smoke"]
16
  log_dir: "runs"
17
- chkpts_dir : "chkpts"
18
- # model_EffNet:
19
- # model_name: "EfficientNetB0"
20
- # input_size: (224, 224)
21
- # batch_size: 48
22
- # n_epochs: 100
23
- # loss: "binary_crossentropy"
24
- # metrics: ["accuracy"]
25
- # optimizer: AdamW(learning_rate=0.0002, weight_decay=0.05)
26
- # trained_model_uri: "EfficientNetB0_app.keras"
 
14
  models_root_dir: "models"
15
  classes: ["no_smoke", "smoke"]
16
  log_dir: "runs"
17
+ chkpts_dir : "chkpts"
 
 
 
 
 
 
 
 
 
src/load_data.py CHANGED
@@ -4,6 +4,7 @@ from datasets import load_dataset
4
  import logging
5
  import os
6
  import pandas as pd
 
7
  import shutil
8
  import subprocess
9
  import yaml
@@ -201,15 +202,78 @@ def format_data_keras(df):
201
  return df
202
 
203
 
204
- def oversample_class(df):
205
- """Oversample an under-represented class"""
206
- count_df = df.groupby(["split", "label"]).size().reset_index(name="count")
207
- count_df = count_df.loc[count_df["split"] != "val"]
 
 
208
 
 
 
 
209
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
210
 
 
 
 
211
 
212
- return df
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
213
 
214
 
215
  if __name__ == "__main__":
 
4
  import logging
5
  import os
6
  import pandas as pd
7
+ from PIL import Image, ImageOps, ImageEnhance, ImageFilter
8
  import shutil
9
  import subprocess
10
  import yaml
 
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)
210
+ df_sample.loc[:, "uri"] = df_sample["input_uri"].str.replace(".jpg", f"_DA-{aug_name[:-4]}.jpg")
211
 
212
+ # Transform and export image
213
+ for _, row in df_sample.iterrows():
214
+ img = Image.open(row['input_uri'])
215
 
216
+ if aug_name == "blur_img":
217
+ img_aug = img.filter(ImageFilter.GaussianBlur(radius=2))
218
+ elif aug_name == "flip_img":
219
+ img_aug = img.transpose(Image.FLIP_LEFT_RIGHT)
220
+ elif aug_name == "blur_flip_img":
221
+ img_aug = img.filter(ImageFilter.GaussianBlur(radius=2)).transpose(Image.FLIP_LEFT_RIGHT)
222
+ elif aug_name == "eq_img":
223
+ img_aug = ImageOps.equalize(img)
224
+ elif aug_name == "180_img":
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...")
250
+ _ = count_df.loc[count_df["split"] == split].reset_index(drop=True)
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
 
278
 
279
  if __name__ == "__main__":