sycod commited on
Commit ·
fc06865
1
Parent(s): 38dbb38
oversampling ok
Browse files- EDA.ipynb +0 -0
- README.md +2 -9
- config.yaml +1 -10
- 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 |
-
-
|
| 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 |
-
-
|
| 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
|
| 205 |
-
"""
|
| 206 |
-
|
| 207 |
-
|
|
|
|
|
|
|
| 208 |
|
|
|
|
|
|
|
|
|
|
| 209 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 210 |
|
|
|
|
|
|
|
|
|
|
| 211 |
|
| 212 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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__":
|